Skip to content

Node.js

This guide covers adding OpenTelemetry instrumentation to Node.js applications. The Node.js OTel SDK supports auto-instrumentation for Express, Fastify, HTTP, gRPC, database clients, and many other libraries.

  • Node.js 16+
  • OTel Collector running at localhost:4317 (gRPC) or localhost:4318 (HTTP)
  • npm, yarn, or pnpm for package management

Install the core SDK and OTLP exporter:

Terminal window
npm install @opentelemetry/sdk-node \
@opentelemetry/api \
@opentelemetry/exporter-trace-otlp-grpc \
@opentelemetry/exporter-metrics-otlp-grpc \
@opentelemetry/exporter-logs-otlp-grpc \
@opentelemetry/resources \
@opentelemetry/semantic-conventions

For auto-instrumentation of common libraries:

Terminal window
npm install @opentelemetry/auto-instrumentations-node

Create a tracing.js (or tracing.ts) file that initializes the SDK:

const { NodeSDK } = require("@opentelemetry/sdk-node");
const {
OTLPTraceExporter,
} = require("@opentelemetry/exporter-trace-otlp-grpc");
const {
OTLPMetricExporter,
} = require("@opentelemetry/exporter-metrics-otlp-grpc");
const {
OTLPLogExporter,
} = require("@opentelemetry/exporter-logs-otlp-grpc");
const {
PeriodicExportingMetricReader,
} = require("@opentelemetry/sdk-metrics");
const { Resource } = require("@opentelemetry/resources");
const {
ATTR_SERVICE_NAME,
ATTR_SERVICE_VERSION,
} = require("@opentelemetry/semantic-conventions");
const {
getNodeAutoInstrumentations,
} = require("@opentelemetry/auto-instrumentations-node");
const resource = new Resource({
[ATTR_SERVICE_NAME]: "my-node-service",
[ATTR_SERVICE_VERSION]: "1.0.0",
"deployment.environment": "development",
});
const sdk = new NodeSDK({
resource,
traceExporter: new OTLPTraceExporter({
url: "grpc://localhost:4317",
}),
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({
url: "grpc://localhost:4317",
}),
exportIntervalMillis: 2000,
}),
logRecordExporter: new OTLPLogExporter({
url: "grpc://localhost:4317",
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
process.on("SIGTERM", () => {
sdk.shutdown().then(() => process.exit(0));
});

The simplest approach uses --require to load the tracing setup before your application:

Terminal window
node --require ./tracing.js app.js

The @opentelemetry/auto-instrumentations-node package automatically instruments:

  • HTTP/HTTPS clients and servers
  • Express, Fastify, Koa, Hapi
  • gRPC clients and servers
  • MySQL, PostgreSQL, MongoDB, Redis
  • AWS SDK, GraphQL, and more

To disable specific instrumentations:

const { getNodeAutoInstrumentations } = require(
"@opentelemetry/auto-instrumentations-node"
);
const instrumentations = getNodeAutoInstrumentations({
"@opentelemetry/instrumentation-fs": { enabled: false },
"@opentelemetry/instrumentation-dns": { enabled: false },
});
const { trace } = require("@opentelemetry/api");
const tracer = trace.getTracer("my-module");
async function processOrder(orderId) {
return tracer.startActiveSpan("process_order", async (span) => {
try {
span.setAttribute("order.id", orderId);
// Child span
await tracer.startActiveSpan("validate_payment", async (child) => {
await validatePayment(orderId);
child.end();
});
span.addEvent("order_processed", { "order.id": orderId });
} catch (err) {
span.recordException(err);
span.setStatus({ code: 2, message: err.message }); // ERROR
throw err;
} finally {
span.end();
}
});
}
const { metrics } = require("@opentelemetry/api");
const meter = metrics.getMeter("my-module");
const requestCounter = meter.createCounter("http.server.request_count", {
description: "Number of HTTP requests",
unit: "1",
});
const requestDuration = meter.createHistogram("http.server.duration", {
description: "HTTP request duration",
unit: "ms",
});
function handleRequest(req, res) {
requestCounter.add(1, { "http.method": req.method, "http.route": req.path });
const start = Date.now();
// ... handle request
requestDuration.record(Date.now() - start, {
"http.method": req.method,
"http.route": req.path,
});
}
const express = require("express");
const {
ExpressInstrumentation,
} = require("@opentelemetry/instrumentation-express");
const {
HttpInstrumentation,
} = require("@opentelemetry/instrumentation-http");
// If using NodeSDK, add these to the instrumentations array:
const sdk = new NodeSDK({
instrumentations: [new HttpInstrumentation(), new ExpressInstrumentation()],
// ... other config
});
const app = express();
app.get("/orders/:id", (req, res) => {
res.json({ orderId: req.params.id });
});
app.listen(3000);
const {
FastifyInstrumentation,
} = require("@opentelemetry/instrumentation-fastify");
const sdk = new NodeSDK({
instrumentations: [new FastifyInstrumentation()],
// ... other config
});

For Next.js applications, initialize the SDK in an instrumentation.ts file:

instrumentation.ts
export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
await import("./tracing");
}
}
VariableDescriptionExample
OTEL_SERVICE_NAMEService namemy-node-service
OTEL_EXPORTER_OTLP_ENDPOINTCollector endpointhttp://localhost:4317
OTEL_EXPORTER_OTLP_PROTOCOLExport protocolgrpc
OTEL_TRACES_SAMPLERSampler typeparentbased_traceidratio
OTEL_TRACES_SAMPLER_ARGSampler argument0.1
OTEL_NODE_ENABLED_INSTRUMENTATIONSComma-separated list of instrumentations to enablehttp,express
OTEL_NODE_DISABLED_INSTRUMENTATIONSComma-separated list of instrumentations to disablefs,dns
NODE_OPTIONSLoad tracing at startup--require ./tracing.js