If you have not yet read the instructions for auto-instrumentation and setup, start with the Node.js Setup Instructions.

If you aren’t using supported library instrumentation (see library compatibility), you may want to manually instrument your code.

You may also want to extend the functionality of the dd-trace library or gain finer control over instrumenting your application. Several techniques are provided by the library to accomplish this.

Adding tags

The built-in instrumentation and your own custom instrumentation create spans around meaningful operations.

You can access the active span in order to include meaningful data by adding tags.

const span = tracer.scope().active()

To learn more, read API details for Scope.

You can add tags to a span using the setTag or addTags method on a span. Supported value types are string, number, and object.

// add a foo:bar tag
span.setTag('foo', 'bar')

// add a user_id:5 tag
span.setTag('user_id', 5)

// add a obj.first:foo and obj.second:bar tags
span.setTag('obj', { first: 'foo', second: 'bar' })

// add a foo:bar and baz:qux tags
span.addTags({
  foo: 'bar',
  baz: 'qux'
})

You can add tags to every span by configuring them directly on the tracer, either with with the comma-separated DD_TAGS environment variable or with the tags option on the tracer initialization:

// equivalent to DD_TAGS=foo:bar,baz:qux
tracer.init({
  tags: {
    foo: 'bar',
    baz: 'qux'
  }
})

// All spans will now have these tags

Some Datadog integrations support span hooks that can be used to update the span right before it’s finished. This is useful to modify or add tags to a span that is otherwise inaccessible from your code.

// at the top of the entry point right after tracer.init()
tracer.use('express', {
  // hook will be executed right before the request span is finished
  hooks: {
    request: (span, req, res) => {
      span.setTag('customer.id', req.query.customer_id)
    }
  }
})

To learn more, read API details for individual plugins.

Errors can be added to a span with the special error tag that supports error objects. This splits the error into three tags: error.type, error.msg, and error.stack.

try {
  getIngredients()
} catch (e) {
  span.setTag('error', e)
}

When using tracer.trace() or tracer.wrap() this is done automatically when an error is thrown.

Creating spans

The dd-trace library creates spans automatically with tracer.init() for many libraries and frameworks. However, you may want to gain visibility into your own code and this is achieved using spans.

Within your web request (for example, /make-sandwich), you may perform several operations, like getIngredients() and assembleSandwich(), which are useful to measure.

Synchronous code can be traced with tracer.trace() which automatically finishes the span when its callback returns and captures any thrown error automatically.

app.get('/make-sandwich', (req, res) => {
  const sandwich = tracer.trace('sandwich.make', { resource: 'resource_name' }, () => {
    const ingredients = tracer.trace('get_ingredients', { resource: 'resource_name' }, () => {
      return getIngredients()
    })

    return tracer.trace('assemble_sandwich', { resource: 'resource_name' }, () => {
      assembleSandwich(ingredients)
    })
  })

  res.end(sandwich)
})

To learn more, read API details for tracer.trace().

Promises can be traced with tracer.trace() which automatically finishes the span when the returned promise resolves, and captures any rejection error automatically.

const getIngredients = () => {
    return new Promise((resolve, reject) => {
        resolve('Salami');
    });
};

app.get('/make-sandwich', (req, res) => {
  return tracer.trace('sandwich.make', { resource: 'resource_name' }, () => {
    return tracer.trace('get_ingredients', { resource: 'resource_name' }, () => getIngredients())
      .then((ingredients) => {
        return tracer.trace('assemble_sandwich', { resource: 'resource_name' }, () => {
          return assembleSandwich(ingredients)
        })
      })
  }).then(sandwich => res.end(sandwich))
})

To learn more, read API details for tracer.trace().

Async/await can be traced with tracer.trace() which automatically finishes the span when the returned promise resolves, and captures any rejection error automatically.

app.get('/make-sandwich', async (req, res) => {
  const sandwich = await tracer.trace('sandwich.make', { resource: 'resource_name' }, async () => {
    const ingredients = await tracer.trace('get_ingredients', { resource: 'resource_name' }, () => {
      return getIngredients()
    })

    return tracer.trace('assemble_sandwich', { resource: 'resource_name' }, () => {
      return assembleSandwich(ingredients)
    })
  })

  res.end(sandwich)
})

To learn more, read API details for tracer.trace().

You can wrap an existing function without changing its code. This is useful to trace functions for which you don’t control the code. This can be done with tracer.wrap() which takes the same arguments as tracer.trace() except its last argument which is the function to wrap instead of a callback.


// After the functions are defined
getIngredients = tracer.wrap('get_ingredients', { resource: 'resource_name' }, getIngredients)
assembleSandwich = tracer.wrap('assemble_sandwich', { resource: 'resource_name' }, assembleSandwich)

// Where routes are defined
app.get('/make-sandwich', (req, res) => {

  const sandwich = tracer.trace('sandwich.make', { resource: 'resource_name' }, () => {
    const ingredients = getIngredients()

    return assembleSandwich(ingredients)
  })

  res.end(sandwich)
})

To learn more, read API details for tracer.trace().

Request filtering

You may not want some requests of an application to be instrumented. A common case would be health checks or other synthetic traffic. These can be ignored by using the blocklist or allowlist option on the http plugin.

// at the top of the entry point right after tracer.init()
tracer.use('http', {
  blocklist: ['/health', '/ping']
})

This configuration can be split between client and server if needed. For example,

tracer.use('http', {
  server: {
    blocklist: ['/ping']
  }
})

Additionally, traces can be excluded based on their resource name, so that the Agent doesn’t send them to Datadog. This and other security and fine-tuning Agent configurations can be found on the Security page or in Ignoring Unwanted Resources.

Further Reading