mrkeyoor.com_
Mon 21 Sept 02:02 UTC
npmInfraupdated 20 Sept 2026

dd-trace review

Our dd-trace 6.12.0 install was a 101 MB, Node-only addition that connects a Node process to Datadog APM. Load it before the application and it patches supported HTTP servers, frameworks, database clients, queues, and loggers as those modules initialize. You can add spans with trace() or startSpan(), attach tags, control sampling, and enable Datadog products such as profiling, AppSec, runtime metrics, and LLM Observability. The 6.12.0 release adds WAF and RASP timing metrics, feature-flag exposure routing, LLM dataset record tags, and lower carrier-extraction overhead. Its output and configuration assume a Datadog Agent, so this is an observability vendor client rather than a portable telemetry layer.

Verdict

dd-trace 6.12.0 took 11.8 seconds and 101 MB in our sandbox, passed npm audit, and failed a browser bundle, which makes it a serious Node-only agent for teams already committed to Datadog. Start with OpenTelemetry when backend choice matters more than Datadog's automatic integrations and product switches.

We installed it

Lab card: what happened when we installed dd-traceScreenshot of dd-trace documentation
Install✓ · 11.8s25 packages on disk · 101 MB
ImportESM import works · require() works · CommonJS package
Browsern/acould not be bundled for the browser (Node-only code, most likely)
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does dd-trace install cleanly?

Yes. In a fresh container with an empty cache, npm install dd-trace finished in 12 seconds, leaving 25 packages and 101 MB on disk. npm audit reported no known vulnerabilities.

Can dd-trace run in a browser?

Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.

Does dd-trace work with both ESM and CommonJS?

Yes. Both import 'dd-trace' and require('dd-trace') worked in Node 22 in our run. The package is published as CommonJS.

Does dd-trace include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

dd-trace or @opentelemetry/sdk-node: which should you use?

@opentelemetry/sdk-node: Use it when OTLP output and the option to change observability backends are requirements. dd-trace 6.12.0 took 11.8 seconds and 101 MB in our sandbox, passed npm audit, and failed a browser bundle, which makes it a serious Node-only agent for teams already committed to Datadog.

When should you not use dd-trace?

Telemetry must switch among OTLP, Jaeger, or another backend without replacing the SDK; this tracer sends through Datadog's Agent and uses Datadog-specific controls

API stability4/5The tracer still centers on init(), trace(), wrap(), startSpan(), scope(), and per-plugin use() settings, while the README says breaking behavior gets a new major release line. Runtime support changes more sharply than those method names: v6 requires Node 22, v5 covers Node 18 in maintenance until July 2027, and the older lines are end of life. A Node upgrade policy is therefore part of adopting the API.
Docs4/5Datadog publishes separate references for startup, every configuration variable, supported integrations, generated TypeScript API signatures, ESM loader hooks, bundling, and Node-version compatibility. The preload examples answer the most consequential setup question. The material is divided among several Datadog pages and generated GitHub documentation, so checking the v6 instructions and the exact plugin version still takes care.
Maintenance5/5Release 6.12.0 arrived on August 21, 2026, and the unarchived repository was pushed again on August 26. That release includes new AppSec and LLM Observability behavior, a carrier-extraction performance change, and fixes for dynamic instrumentation and test reporting. GitHub lists 222 open issues and pull requests together. The README also names the current, maintenance, and end-of-life status of every supported major line.
Ecosystem4/5npm recorded 10,216,999 dd-trace downloads for August 19 through August 25, 2026, and GitHub reports 834 stars. The generated plugin list covers Express, Fastify, Next, PostgreSQL, Redis, Kafka clients, popular loggers, test runners, and multiple AI SDKs. Those integrations save work inside Datadog deployments, while the Agent transport, DD_* settings, and optional product modules make the surrounding system deliberately vendor-specific.

Use it if

  • Datadog already receives your production telemetry and you want supported Node modules instrumented at load time
  • Trace and span identifiers need to appear in pino, winston, or bunyan records sent to Datadog Logs
  • One Node agent should supply APM plus optional profiling, AppSec, and runtime metrics
  • Automatic spans cover most calls, while your application still needs named spans around jobs or business operations
Skip it if

Setup reality

We installed dd-trace 6.12.0 in 11.8 seconds on a clean Node 22 Bookworm container. The result was 25 packages and 101 MB on disk. Its own package is 11,504 KB unpacked, with 3 direct dependencies, no peers, bundled declarations, and zero npm audit findings. CommonJS require and ESM import worked. A browser build failed because esbuild reached Node-only code.

Initialization order decides whether automatic spans appear. CommonJS services can start with --require dd-trace/init; ESM services need --import dd-trace/initialize.mjs. Both forms run before application imports. If Express or pg loads first, calling init() later cannot reliably patch work that already happened. Version 6 also requires Node 22 or newer.

Traces normally leave the process through a reachable Datadog Agent on port 8126. Runtime metrics take a separate DogStatsD path on port 8125. Set DD_SERVICE, DD_ENV, and DD_VERSION at deployment time so releases do not collapse into one service view. Datadog credentials belong with the Agent or its platform integration, rather than inside tracer setup.

Bundling needs deliberate treatment even though direct Node imports succeeded. Datadog supplies an esbuild plugin, and profiler or AppSec native modules must remain external and present in the runtime image. Sampling rules also affect the bill and incident detail. Exclude predictable traffic such as health checks only after confirming the resource names emitted by your framework integration; 6.12.0 improves extraction work but does not set a suitable retention policy.

Patterns

Start a CommonJS service with tracing preload-commonjs

DD_SERVICE=orders-api \
DD_ENV=production \
DD_VERSION=2.7.0 \
NODE_OPTIONS='--require dd-trace/init' node server.js

The preload executes before server.js requires Express, database drivers, or loggers, which gives dd-trace a chance to patch them.

Start an ESM service with loader hooks preload-esm

DD_SERVICE=orders-api \
DD_ENV=production \
NODE_OPTIONS='--import dd-trace/initialize.mjs' node server.mjs

ESM applications need initialize.mjs at process startup; a normal import inside server.mjs runs after that module's static dependencies have loaded.

Configure the tracer before other modules initialize-with-code

// tracing.cjs
module.exports = require('dd-trace').init({
  service: 'billing-worker',
  env: process.env.NODE_ENV,
  logInjection: true
})

// worker.cjs
require('./tracing.cjs')
const { Worker } = require('bullmq')

The first require must stay above instrumented libraries; moving BullMQ ahead of it can leave the queue client unpatched.

Trace an asynchronous operation trace-promise

const tracer = require('dd-trace')

async function reserve(order) {
  return tracer.trace('inventory.reserve', {
    resource: order.warehouse
  }, async (span) => {
    span.setTag('order.item_count', order.items.length)
    return inventory.reserve(order.items)
  })
}

trace() activates the span for the callback and closes it when the returned promise settles; a rejection is recorded and rethrown.

Activate and finish a manual span manage-span-lifecycle

const span = tracer.startSpan('job.consume', {
  tags: { 'queue.name': job.queueName }
})

await tracer.scope().activate(span, async () => {
  try {
    await processJob(job)
  } catch (error) {
    span.setTag('error', error)
    throw error
  } finally {
    span.finish()
  }
})

startSpan() does not activate or finish its span; the scope call supplies parent context, and finally closes the span on both outcomes.

Attach application context tag-active-span

const span = tracer.scope().active()
if (span) {
  span.setTag('account.tier', account.tier)
  span.setTag('cart.item_count', cart.items.length)
}

active() returns null when no traced scope exists. Keep secrets and unbounded identifiers out of tags because Datadog indexes or retains this data.

Keep payments and drop health checks set-sampling-rules

require('dd-trace').init({
  sampleRate: 0.25,
  samplingRules: [
    { sampleRate: 0, resource: 'GET /healthz' },
    { sampleRate: 1, name: 'payment.capture' }
  ],
  rateLimit: 200
})

Rules match emitted operation and resource names. Inspect live traces first because each framework plugin decides its resource string.

Correlate supported logs with traces inject-log-context

require('dd-trace').init({ logInjection: true })

const pino = require('pino')
const log = pino()
log.info({ orderId }, 'order accepted')

The tracer must load before pino for automatic injection, and the resulting identifiers help only when the logs and traces reach Datadog.

Give PostgreSQL spans a service name configure-integration

const tracer = require('dd-trace').init()
tracer.use('pg', {
  service: 'orders-postgres'
})

const { Pool } = require('pg')

Call use() before requiring pg. Plugin options apply to the named integration rather than changing every database span.

Enable metrics and profiling enable-runtime-products

require('dd-trace').init({
  runtimeMetrics: true,
  profiling: true
})

Runtime metrics require DogStatsD on port 8125, separate from trace intake on 8126. Profiling can load an optional native package.

Reach an Agent from a container set-agent-address

DD_AGENT_HOST=datadog-agent \
DD_TRACE_AGENT_PORT=8126 \
DD_SERVICE=catalog-api \
NODE_OPTIONS='--require dd-trace/init' node app.js

Inside a container, localhost usually names that same container. Use an Agent hostname reachable on the deployment network.

Keep Datadog hooks in an esbuild output bundle-node-service

const esbuild = require('esbuild')
const datadog = require('dd-trace/esbuild')

esbuild.build({
  entryPoints: ['src/server.js'],
  platform: 'node',
  bundle: true,
  outfile: 'dist/server.js',
  plugins: [datadog],
  external: [
    '@datadog/pprof',
    '@datadog/native-appsec',
    '@datadog/native-metrics'
  ]
})

Native Datadog modules stay outside the bundle and must still be installed beside the deployed output. Our ordinary browser-target bundle failed.

Alternatives

PackageRegistryPick it when
@opentelemetry/sdk-nodenpmUse it when OTLP output and the option to change observability backends are requirements
elastic-apm-nodenpmUse it when Elastic already stores the application's logs, metrics, and traces
newrelicnpmUse it when New Relic owns your APM dashboards and alert workflow
@prometheus-io/clientnpmUse it when exported application metrics are enough and distributed tracing is unnecessary

More infra guides

boto3 · opentelemetry-api · psutil · distro · @opentelemetry/api · google-cloud-storage · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.