Node

Embed
Preview of the card
<a href="https://watchtowerops.net/docs/instrumentation-node"><img src="https://watchtowerops.net/docs/_api/public/orgs/aa5959cb-1a0b-4a9a-bc9a-913e7ac31cd9/spaces/b7aac181-9916-4805-b253-becc5fd2ca62/pages/e83f395d-758c-4410-97b0-5ab78302196d/social-card.svg?v=3e7ae33eec8b" alt="Node" width="600" height="315"></a>

<!-- status: ga language: node -->

Node.js: zero-code instrumentation

@opentelemetry/auto-instrumentations-node registers instrumentations for http, express, pg, mysql, redis, grpc and more before your code loads, with no code change. Nothing Watchtower-specific is installed: it reads the standard OTEL_* variables.

  1. Create a project, then an ingestion key (wti_...) with the ingest:traces scope. The key is shown once.

  2. Point the instrumentation at the gateway and pass the key in the x-watchtower-key header:

export OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.example.com
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_HEADERS=x-watchtower-key=wti_YOUR_KEY
export OTEL_SERVICE_NAME=my-service
  1. Install the package and preload it:

npm install @opentelemetry/auto-instrumentations-node
node --require @opentelemetry/auto-instrumentations-node/register app.js

For containers set NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register" instead of editing the command line.

ES modules

--require only patches CommonJS. For an app with "type": "module" (or .mjs files) register the import-in-the-middle hooks first, then preload with --import:

// register-hooks.mjs
import { register } from 'node:module';
register('@opentelemetry/instrumentation/hook.mjs', import.meta.url);
node --import ./register-hooks.mjs --import @opentelemetry/auto-instrumentations-node/register app.js
# or: NODE_OPTIONS="--import ./register-hooks.mjs --import @opentelemetry/auto-instrumentations-node/register"

Fastify

@opentelemetry/instrumentation-fastify was deprecated and removed upstream; do not install it. The http instrumentation already gives Fastify 5 server spans; for per-route and hook spans add the Fastify team's @fastify/otel plugin.

  1. Send one request to the service, then open the project's onboarding screen: it turns green when the first span arrives.

BullMQ and postgres.js

The auto-instrumentations cover neither BullMQ nor postgres.js. @watchtowerobservability/node has both:

import * as bullmq from 'bullmq';
import { instrumentBullMQ } from '@watchtowerobservability/node/bullmq';
import { instrumentPostgres } from '@watchtowerobservability/node/postgres';
instrumentBullMQ(bullmq); // producer spans on Queue.add/addBulk, consumer spans per job, one trace end to end
instrumentPostgres();     // postgres.js >= 4: a client span per query, summary and SQLSTATE, no statement text

Both work under ESM. Details: the SDK README sections "BullMQ" and "postgres.js".

Datastores, caches, brokers and ORMs

The SDK also instruments embedded stores (node:sqlite, better-sqlite3, sqlite3, libSQL, DuckDB, LMDB, LevelDB/RocksDB), in-memory caches (node-cache, lru-cache, Keyv: hit/miss metrics), brokers and job queues (amqplib, rabbitmq-client, NATS, MQTT, pg-boss, Graphile Worker), MongoDB and mysql2 pool metrics, and Drizzle, Kysely, TypeORM, Sequelize and MikroORM. Each lives at its own subpath and takes the module or instance you pass. SQL spans carry db.query.summary and db.query.hash (the server's statement hash, see SQL normalization) and an N+1 hint; statement text is opt-in and literal-free. Details: the SDK README sections from "Datastore spans" on.

Updated