OpenTelemetry tracing in Node.js services
OpenTelemetry standardizes traces, metrics, and logs so backends (Jaeger, Grafana Tempo, Honeycomb, Datadog) ingest the same data model. In Node, @opentelemetry/sdk-node plus auto-instrumentation gets you HTTP spans quickly; custom spans document business operations.
Bootstrapping
Load instrumentation before other imports:
// instrumentation.ts (loaded first)
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({ url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT }),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
Custom spans
import { trace } from '@opentelemetry/api';
const tracer = trace.getTracer('checkout');
await tracer.startActiveSpan('capture_payment', async (span) => {
try {
span.setAttribute('order.id', orderId);
await charge();
} catch (e) {
span.recordException(e);
throw e;
} finally {
span.end();
}
});
Attributes become queryable dimensions in your trace UI.
Sampling
100% trace volume is expensive. Use parent-based or ratio sampling in production, with higher rates for error traces or specific routes (tail sampling if your vendor supports it).
Propagation
Ensure fetch/undici and your HTTP client propagate traceparent headers to downstream services so spans stitch into one trace.
Summary
OpenTelemetry is the lingua franca of observability. Start with auto-instrumentation, add business spans at critical paths, and tune sampling so telemetry cost scales with value—not raw request volume alone.