Documentation navigation

Async tracing and Scopes

Another concept unique to the Node.js integration is the concept of Scopes, which allow Spans to be recalled across asynchronous code paths that may not be linked to each other by being on the same direct code path.

What is a "Scope"?

As mentioned in the Span documentation, the currently active Span can be recalled by calling tracer.currentSpan() to add data to it or create ChildSpans from it.

1
const span = tracer.currentSpan();

Before it can be recalled again from elsewhere in your application (for example, in a completely different file), the Span must be given a Scope. This is not to be confused with "lexical scope", which is a language construct - it is a separate, but not unrelated, concept.

A Scope is a wrapper for a Span that allows it to be recalled across asynchronous code paths that may not be directly linked to each other (in an EventEmitter or a timer, for example, or even in a completely different file or function scope). This wrapper is invisible to you and is managed internally via the Tracer objects ScopeManager and the internal async_hooks module.

These Scopes are stored in a stack, meaning that the most recent Span to be given a scope will be the next Span returned by tracer.currentSpan().

Assigning a Scope to a Span

A Span can be given a Scope like this:

1
2
3
4
5
6
const tracer = appsignal.tracer();

tracer.withSpan(tracer.createSpan(), (span) => {
  // the span returned by `tracer.createSpan()` now has a Scope
  // it and will be the next span to be returned by `tracer.currentSpan()`!
});

Once assigned to a Scope, the Span passed to tracer.withSpan() can be recalled using tracer.currentSpan() in a different file or lexical scope.

1
2
3
4
tracer.withSpan(tracer.currentSpan(), (span) => {
  // `span` is the same span created by `tracer.createSpan()` in the example above!
  // http://gph.is/1KjihQe
});

We'd like to set cookies, read why.