Documentation navigation

The Tracer object

AppSignal for Node.js contains a new concept that is different from the current Ruby implementation - the Tracer object. The Tracer object contains various methods that you might use when creating your own custom instrumentation.

The Tracer is responsible for tracking the currently active Span, and exposes functions for creating and activating new Spans.

Retrieving the Tracer

1
const tracer = appsignal.tracer();

If the agent is currently inactive (you must actively set it as such by setting active: true), then the AppSignal client will return an instance of NoopTracer, which is safe to call within your code as if the agent were currently active but does not record any data.

Retrieving the current Span

In most cases, a Span will be created by one of our automatic instrumentations, e.g. the http module integration. To add any custom instrumentation to your trace, you'll need to retrieve that Span using the Tracer instance.

1
const span = tracer.currentSpan();

Once you have the current Span, you'll be able to add data to it and create ChildSpans from it. If a current Span is not available, then tracer.currentSpan() will return a NoopSpan.

Creating a new Span

A Span can be created by calling tracer.createSpan(), which initializes a new Span object.

1
const span = tracer.createSpan();

A ChildSpan, a Span that is the child of another Span, can also be created by passing an optional second argument to tracer.createSpan().

1
const childSpan = tracer.createSpan(undefined, span);

Using an existing Span

Instrumenting a block of code, or operation can be done with the help of tracer.withSpan().

Any code executed within the callback passed as the second argument to tracer.withSpan() can be said have been executed within the context of the Span passed as the first argument. If you consider that a Span has a start time and an end time, the context of a Span is the empty space between those two points. In this "empty space", you can execute any code that you wish to instrument, as well as create children of the current Span in order to create more granular measurements.

This context can later be recreated in a completely different place (such as a different file in your project, or in a different lexical scope) by simply calling tracer.withSpan() again with the same Span (which can be recalled using tracer.currentSpan()).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// the span returned by `tracer.currentSpan()` is passed to the callback as arg1: `span`
// it has a scope and will be the next `Span` to be returned by `tracer.currentSpan()`
tracer.withSpan(tracer.currentSpan(), (span) => {
  // your code goes here...
  someCode();

  // if you want to create a child span, you can call `tracer.withSpan()` with `span.child()`
  // to create context or that span
  tracer.withSpan(span.child(), (child) => {
    someOtherCode();
    child.close();
  });

  span.close(); // don't forget to close the span if you're done with it!
});

The callback passed to tracer.withSpan() can also be an async function:

1
2
3
4
5
6
tracer.withSpan(tracer.currentSpan(), async (span) => {
  // all your async code goes here...
  await someCode();

  span.close(); // don't forget to close the span if you're done with it!
});

If the callback returns a value, it will become the return value of the tracer.withSpan() function.

The most recent Span passed to tracer.withSpan() also now has a scope, which means that it will be Span to be returned the next time you call tracer.currentSpan(). When the operation you wish to instrument is complete, call span.close() to stop the timer.

For more information on scopes, see the Async tracing and Scopes page.

We'd like to set cookies, read why.