Tracing
Connectum provides distributed tracing through OpenTelemetry interceptors for both server-side and client-side RPC calls, plus deep tracing utilities for your business logic.
Server Interceptor
createOtelInterceptor() automatically creates spans for all incoming RPC calls with OpenTelemetry semantic conventions:
import { createOtelInterceptor } from '@connectum/otel';
const interceptor = createOtelInterceptor({
filter: ({ service }) => !service.includes('grpc.health'),
serverPort: 5000,
recordMessages: false,
trustRemote: false,
});All server interceptor options are optional. Configure only the filtering, endpoint attributes, signal toggles, or data recording that the service requires; use OtelInterceptorOptions for the exact fields. Keep message recording disabled unless its privacy and volume impact has been reviewed.
Client Interceptor
For outgoing RPC calls, use createOtelClientInterceptor() to propagate trace context:
import { createConnectTransport } from '@connectrpc/connect-node';
import { createClient } from '@connectrpc/connect';
import { createOtelClientInterceptor } from '@connectum/otel';
import { UserService } from '#gen/user_pb.js';
const transport = createConnectTransport({
baseUrl: 'http://user-service:5001',
interceptors: [
createOtelClientInterceptor({
serverAddress: 'user-service', // Required
serverPort: 5001,
}),
],
});
const client = createClient(UserService, transport);The client interceptor:
- Injects trace context into outgoing requests via
propagation.inject() - Creates
SpanKind.CLIENTspans - Records
rpc.client.*metrics
Deep Tracing with traced()
Wrap individual functions to create spans for your business logic:
import { traced } from '@connectum/otel';
const findUser = traced(async (id: string) => {
return await db.users.findById(id);
}, {
name: 'UserRepository.findUser',
recordArgs: true, // Record function arguments as span attributes
});
// Each call creates a span: "UserRepository.findUser"
const user = await findUser('123');Deep Tracing with traceAll()
Wrap all methods of an object via Proxy:
import { traceAll } from '@connectum/otel';
class OrderRepository {
async findById(id: string) {
return await db.orders.findById(id);
}
async create(data: OrderData) {
return await db.orders.create(data);
}
async updateStatus(id: string, status: string) {
return await db.orders.update(id, { status });
}
}
// Auto-instrument all methods (does NOT mutate the original)
const repository = traceAll(new OrderRepository(), {
prefix: 'OrderRepository',
exclude: ['internalHelper'],
recordArgs: true,
});
// Calls automatically create spans:
// "OrderRepository.findById", "OrderRepository.create", etc.
await repository.findById('order-123');Performance
traceAll() uses ES6 Proxy and creates method wrappers lazily on first access. It prevents double-wrapping automatically.
Distributed Tracing Across Services
A complete example showing trace context propagation between two services:
// ---- Service A (Gateway) ----
import { createServer } from '@connectum/core';
import { createConnectTransport } from '@connectrpc/connect-node';
import { createClient } from '@connectrpc/connect';
import { createOtelInterceptor, createOtelClientInterceptor } from '@connectum/otel';
import { UserService } from '#gen/user_pb.js';
import gatewayRoutes from '#gen/routes.js';
// Server: trace incoming requests
const server = createServer({
services: [gatewayRoutes],
port: 5000,
interceptors: [
createOtelInterceptor({
serverPort: 5000,
filter: ({ service }) => !service.includes('grpc.health'),
}),
],
});
// Client: propagate trace context to Service B
const transport = createConnectTransport({
baseUrl: 'http://user-service:5001',
interceptors: [
createOtelClientInterceptor({
serverAddress: 'user-service',
serverPort: 5001,
}),
],
});
const userClient = createClient(UserService, transport);
// Trace context flows automatically:
// Service A (server span) -> Service A (client span) -> Service B (server span)Related
- Observability Overview -- back to overview
- Service Communication -- inter-service calls, transport configuration
- Client Interceptors -- OTel client interceptor, resilience
- Metrics -- counters, histograms, automatic RPC metrics
- Logging -- structured logging with trace correlation
- @connectum/otel -- Package Guide
- @connectum/otel API -- Full API Reference