@elastic/opentelemetry-node
Elastic's zero-code OpenTelemetry distribution for Node.js, giving production apps tracing, metrics, and logs with a single --import flag.
Repository Health
Technical Analysis
EDOT Node.js (@elastic/opentelemetry-node) is Elastic’s distribution of the OpenTelemetry SDK for Node.js. Rather than reimplementing tracing, metrics, or log collection, it wraps @opentelemetry/sdk-node and bundles dozens of first-party OpenTelemetry instrumentation packages (HTTP, Express, Fastify, Koa, Postgres, MongoDB, Redis, Kafka, AWS SDK, and many more) so a Node.js application gets full auto-instrumentation by adding one line to its start command: node --import @elastic/opentelemetry-node app.js.
Beyond bundling upstream OpenTelemetry packages, the distribution adds Elastic-specific behavior: dynamic central configuration (letting an Elastic backend adjust sampling and other SDK settings at runtime without redeploying), extra resource detectors, host-metrics collection enabled by default, a custom JSON-formatted diagnostic logger, and OTLP exporters wired up out of the box. It is designed to be a drop-in path to Elastic Observability while remaining a standard, spec-compliant OpenTelemetry SDK underneath.
The package is part of a small monorepo alongside @elastic/opamp-client-node (an OpAMP protocol client used for the central-configuration channel) and mock OTLP/OpAMP servers used in the project’s own test suite.
What You Get
- Zero-code instrumentation activated with a single
node --import @elastic/opentelemetry-nodeflag, no manual SDK wiring required - Dozens of bundled OpenTelemetry instrumentation packages covering HTTP frameworks (Express, Fastify, Koa, Hapi, Restify), databases (Postgres, MySQL, MongoDB, Redis, Cassandra, Oracle), messaging (AWS SQS/SNS, Kafka, AMQP), and logging frameworks (Pino, Winston, Bunyan)
- Dynamic central configuration via an OpAMP client, letting sampling and other settings be adjusted from an Elastic backend without redeploying the app
- Default OTLP exporters for traces, metrics, and logs (gRPC, HTTP, and protobuf variants) configured from standard
OTEL_EXPORTER_OTLP_*environment variables - Host metrics collection enabled by default via
@opentelemetry/instrumentation-host-metrics, unlike the upstream auto-instrumentations package - A
/sdksubpath export for programmatic (in-code) SDK setup when the CLI-flag approach doesn’t fit the deployment model
Common Use Cases
- Adding distributed tracing and metrics to an existing Node.js service shipping telemetry to Elastic Observability, without touching application code
- Standardizing OpenTelemetry instrumentation across many Node.js microservices where each team would otherwise hand-roll SDK setup and pick a different subset of instrumentations
- Running EDOT Node.js inside a Kubernetes pod via the OpenTelemetry Operator’s auto-instrumentation injection, using the bundled
autoinstrumentation.jsentry point - Getting host-level metrics (CPU, memory, event loop lag) alongside request tracing in one dependency instead of assembling several packages manually
- Migrating from
@opentelemetry/auto-instrumentations-nodeto a distribution with dynamic central configuration and Elastic-specific defaults
Under The Hood
Architecture
The package’s entry points (import.mjs, require.js, hook.mjs) are thin bootstrap shims that call into lib/sdk.js, which assembles and starts an @opentelemetry/sdk-node NodeSDK instance. Supporting modules under lib/ each own one concern: instrumentations.js builds the list of enabled instrumentation instances from environment configuration, detectors.js resolves resource detectors, central-config.js and dynconf.js implement the OpAMP-driven dynamic configuration channel that can swap exporters and samplers at runtime, environment.js manages EDOT-specific environment variable handling (including scrubbing them before user code sees them), and logging.js/luggite.js implement the custom diagnostic logger. This is a layered, single-responsibility module structure rather than a monolith, and the OpAMP-based dynamic reconfiguration is the one piece of real architectural complexity — most other modules are straightforward composition over upstream OpenTelemetry APIs.
Tech Stack
Plain CommonJS Node.js (with an ESM entry point for --import usage), no build step or transpilation for the runtime code — TypeScript is used only to generate the published .d.ts type declarations from JSDoc-annotated source (tsc --outDir under gen:types). It depends directly on @opentelemetry/sdk-node, @opentelemetry/core, @opentelemetry/sdk-logs, and a long list of @opentelemetry/instrumentation-* packages, plus import-in-the-middle for ESM module hooking and its own sibling package @elastic/opamp-client-node for the central-config protocol. Supports Node.js ^18.19 or >=20.6.
Code Quality
Tests use tape (not a heavier framework like Jest/Vitest) with fixtures and Docker Compose-backed integration tests (test-services:start) exercising real databases and brokers rather than mocks alone — 45+ test files cover individual instrumentations, ESM usage, central config, and host metrics. Linting runs ESLint, Prettier, tsc type-checking against hand-written .d.ts files, a dependency-check step (lint:deps), and a license-file/changelog linter, all wired into CI via GitHub Actions (lint.yml, test-edot.yml). JSDoc type annotations stand in for TypeScript source, which is a deliberate but unusual choice for a package of this size.
What Makes It Unique
What differentiates this from the upstream @opentelemetry/auto-instrumentations-node package it closely tracks is the OpAMP-based dynamic central configuration channel, letting an operator adjust sampling and other SDK behavior for a running fleet of services without redeploying, plus defaults tuned for the Elastic backend (delta temporality, host metrics on by default, a distro-identifying resource attribute, and a structured JSON diagnostic log format). It is a distribution in the OpenTelemetry sense — same spec, curated defaults and an extra control-plane feature — rather than a from-scratch reimplementation.