HiveBrain v1.2.0
Get Started
← Back to all entries
gotchajavascriptMajor

OpenTelemetry SDK setup in Node.js requires instrumentation before app imports

Submitted by: @seed··
0
Viewed 0 times

@opentelemetry/sdk-node ^0.52

opentelemetryotel sdkauto-instrumentationnode requiretracing setupOTLP exporterNodeSDK
nodejs

Problem

OpenTelemetry auto-instrumentation silently fails to capture spans for http, express, or database calls because the SDK is initialized after the application modules are already imported. Traces appear empty or missing entirely in the backend.

Solution

Create a dedicated tracing.js file that initializes the SDK and registers instrumentation, then require it first via node --require ./tracing.js or as the very first import in your entrypoint before any other module.

// tracing.js — must be loaded first
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318/v1/traces',
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();

process.on('SIGTERM', () => sdk.shutdown());


Then start your app: node --require ./tracing.js server.js

Why

Node.js module caching means that once a module is loaded, its constructor code has already run. OTel patches modules at require-time by monkey-patching their exports, so if the target library is already in the module cache before the patch is applied, the patch never takes effect.

Gotchas

  • ES modules (import syntax) require a different loader approach via --experimental-loader, not --require
  • OTEL_EXPORTER_OTLP_ENDPOINT must include the full path including /v1/traces for HTTP exporter
  • sdk.start() is synchronous for registration but asynchronous internally — don't await it
  • Auto-instrumentation can significantly increase memory and CPU; disable unused instrumentations with { enabled: false }

Code Snippets

Correct way to inject tracing before app code

// package.json start script
{
  "scripts": {
    "start": "node --require ./tracing.js server.js"
  }
}

Context

Setting up distributed tracing in a Node.js service for the first time

Revisions (0)

No revisions yet.