OpenTelemetry in Node.js: Traces Without Changing Your Code

A
Author
10 Sep, 2026 1 min read 1.44K views

OpenTelemetry's auto-instrumentation patches popular libraries (http, Express, pg, Redis, fetch and many more) so you get traces with almost no code changes.

Zero-code setup

npm install @opentelemetry/api @opentelemetry/auto-instrumentations-node

export OTEL_SERVICE_NAME=shop-api
export OTEL_TRACES_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
node --require @opentelemetry/auto-instrumentations-node/register src/server.js

Every incoming request now creates a trace, with child spans for database queries and outgoing HTTP calls.

Programmatic setup

// instrumentation.js - load before anything else
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({
  serviceName: 'shop-api',
  traceExporter: new OTLPTraceExporter(), // uses OTEL_EXPORTER_OTLP_ENDPOINT
  instrumentations: [
    getNodeAutoInstrumentations({
      '@opentelemetry/instrumentation-fs': { enabled: false }, // very noisy
    }),
  ],
});

sdk.start();
process.on('SIGTERM', () => sdk.shutdown()); // flush spans on exit
node --import ./instrumentation.js src/server.js

Custom spans for business logic

import { trace, SpanStatusCode } from '@opentelemetry/api';

const tracer = trace.getTracer('checkout');

export async function placeOrder(cart, user) {
  return tracer.startActiveSpan('placeOrder', async (span) => {
    span.setAttributes({ 'cart.items': cart.items.length, 'user.plan': user.plan });
    try {
      const order = await createOrder(cart, user);
      span.setAttribute('order.id', order.id);
      return order;
    } catch (err) {
      span.recordException(err);
      span.setStatus({ code: SpanStatusCode.ERROR });
      throw err;
    } finally {
      span.end();
    }
  });
}

See it locally

docker run -d --name jaeger -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one
# open http://localhost:16686

In production, send OTLP to an OpenTelemetry Collector or straight to your vendor. Don't put personal data like emails or card numbers in span attributes.

Was this article helpful?

Comments

No comments yet. Have a question or a tip? Be the first to share it.

Leave a comment

Comments are reviewed before they appear.