Instana Node.js API

Initializing the Instana Node.js collector

As explained in the installation page, the main export of @instana/collector is a function that initializes the Instana Node.js collector. That is, when you load the module by using require or import, it returns a function that needs to be called by your application code, before requiring or importing any other modules. The following sections explain this in more detail for Node.js applications that either use the CommonJS module system or the ESCMAScript module system.

CommonJS

You only need to call the initialization function that is exported by @instana/collector once, but you must do so in the beginning of your Node.js application, before any other require statements and before any other code is executed:

require('@instana/collector')();

The initialization function will return a reference to itself, which is relevant if you want to access other exports provided by the Instana Node.js collector. That is, the following two snippets are equivalent:

const instana = require('@instana/collector')();

is the same as:

const instana = require('@instana/collector');
instana();

The initialization function accepts one optional parameter, a configuration object:

const instana = require('@instana/collector')({
  // ... configuration object, see configuration documentation
});

See the configuration page for details on the configuration object.

See the sections installation and common pitfalls for more details on installing and initializing the Instana Node.js collector.

ESCMAScript modules

For Node.js applications that use the ECMAScript module system, initialize the Instana Node.js collector through the Node.js command line flags. If you want to use Instana Node.js SDK in your ECMAScript modules-based Node.js application, you can get a reference to the instana API as follows:

import instana from '@instana/collector';

You don't need to initialize the Instana Node.js collector again because the collector is initialized by the Node.js command line flags --import or --experimental-loader that is provided during the startup.

Using multiple Instana collector packages

Kubernetes

When you use Instana AutoTrace WebHook, the Node.js collector is automatically installed in the /opt/instana/instrumentation/nodejs/node_modules/@instana/collector directory.

If you want to use the Instana Node.js SDK in your Kubernetes-based Node.js application, you are recommended to install @instana/collector as a direct dependency in your project.

const instana = require('@instana/collector')();

The initialization of the Instana Node.js collector takes some time. To start creating spans by using the SDK immediately, add a short delay (for example, 500 ms) to ensure that the Instana Node.js collector is ready and has established a connection to the Instana host agent.

Accessing the Instana API

Almost all applications will only ever need to initialize Instana as demonstrated above. However, there are a few cases in which you might need to access other parts of the Instana API.

// At the start of your main module/application entry point, the function
// returned by require('@instana/collector') needs to be called to initialize
// the Instana Node.js collector. This needs to happen before anything else is
// required or imported.
const instana = require('@instana/collector')();

...

// Use the instana reference acquired by the require statement to access its
// API, for example:
instana.setLogger(...);

// or:
instana.currentSpan();

// or:
instana.sdk.callback.startEntrySpan('custom-span', () => { ... });

// or:
instana.opentracing.createTracer();

// ...

If you need a reference to @instana/collector in multiple files, you need not initialize @instana/collector again. You can use const instana = require('@instana/collector'); instead of const instana = require('@instana/collector')(); in all the files except the files in the main module and application entry point of your application.

Setting the Logger After Initialization

Use instana.setLogger(logger) to provide a custom logger to @instana/collector instead of the default bunyan logger.

As mentioned before, you need to call the initialization function (which is returned by require('@instana/collector')) immediately, before requiring/importing any other packages, otherwise Instana's automatic tracing will only work partially. In particular, this requires you to initialize Instana before requiring your logging package (for example, bunyan, pino or winston). If you require the logging package before initializing Instana, you will not see log messages in Instana.

On the other hand you might want to pass your own logger to the Node.js collector so that its log messages have the same format and are written to the same log files/destinations as the rest of your application's log messages. If you would pass your logger to the collector's initialization function, you would need to require that logging package before initializing Instana. To resolve this cyclic dependency, the @instana/collector offers the function setLogger to initialize Instana without a custom logger first and then set the logger later.

To give a concrete example, the following is not supported:

// WRONG
const instana = require('@instana/collector');

// The bunyan package will not be instrumented by Instana, because it is
// required *before* Instana has been initialized.
const bunyan = require('bunyan');
const logger = bunyan.createLogger(...);

// Now Instana is initialized, after the logging package has already been
// required. This is too late!
const instana = instana(); // TOO LATE!
instana.setLogger(logger);

Instead, initialize Instana first, without a logger, before requiring anything else. Then set the logger that Instana should use later, when the logger has been required and initialized:

// Correct: Call the initialization function immediately.
// (Pay attention to the extra pair of parantheses at the end of the line.)
const instana = require('@instana/collector')();

// Require and initialize your logging package.
const bunyan = require('bunyan');
// Create your logger(s).
const logger = bunyan.createLogger(...);
// Set the logger Instana should use.
instana.setLogger(logger);

The first few lines of log output from Instana (during the initialization procedure) will be logged with Instana's default bunyan logger, but everything after the instana.setLogger(logger) call will be logged with the logger you have set. Plus, your application's log output will show up in the "log messages" tab in Instana's dashboards correctly (note that we only show log calls for which the severity is at least "WARN").

If you pass a Bunyan or Pino logger to setLogger, the Node.js collector will create children of the given logger with the same log level and target streams. Other logging modules are also supported as long as they provide functions for the log levels debug, info, warn and error. In this case, the Node.js collector will not create child loggers but just use the given logger as is.

Please note that you are responsible for setting the desired log level on the logger you provide to @instana/collector. If you see unexpected debug logs from @instana/collector, make sure you pass in a logger for which the level has been set to info or warn.

Accessing The Currently Active Span

Instana's automated tracing handles everything for you for supported libraries, there is no need to interfere.

Nevertheless, application code is granted limited read only access to the the collector's internal state. For this purpose, a handle for the currently active span can be acquired with instana.currentSpan(). This method will return a dummy handle when no span is currently active. A span handle returned by this method offers the following methods:

span.getTraceId(): Returns the trace ID of the span. (Since: v1.62.0)

span.getSpanId(): Returns the span ID of the span. (Since: v1.62.0)

span.getParentSpanId(): Returns the parent span ID of the span. (Since: v1.62.0)

span.getName(): Returns the name of the span. (Since: v1.62.0)

span.isEntrySpan(): Determine if the span is an entry span (server span). (Since: v1.62.0)

span.isExitSpan(): Determine if the span is an exit span (client span). (Since: v1.62.0)

span.isIntermediateSpan(): Determine if the span is an intermediate span (local span). (Since: v1.62.0)

span.getTimestamp(): Returns the timestamp of the span's start (Since: v1.62.0).

span.getDuration(): Returns the duration of the span. This method will return 0 if the span has not been completed yet. Note that this is almost always the case as instana.currentSpan() returns the currently active span, which, by definition, has not been completed. This will only return a duration greater than 0 if span.disableAutoEnd() and span.end() have been used (see as follows). (Since: v1.62.0)

span.annotate(path, value): Adds an annotation (also known as a tag or custom tag) to the span. The path can be provided as a dot-separated string or as an array of strings. That is, the following two calls are equivalent:

  • span.annotate('sdk.custom.tags.myTag', 'My Value'), and
  • span.annotate(['sdk', 'custom', 'tags', 'myTag'], 'My Value').
  • span.annotate(['sdk', 'custom', 'tags', 'service'], 'dummy service').

Note that custom tags should always be prefixed by sdk.custom.tags. You can also use annotate to override standard tags, like the HTTP path template (example: span.annotate('http.path_tpl', '/user/{id}/details')), but it is not recommended, unless there are very good reasons to interfere with Instana's auto tracing. (Since: v1.97.1)

span.markAsErroneous(message, [customErrorMessagePath]): Marks the span as an error, that is, it sets the error count for the span to 1. You should provide a message that explains why this span is erroneous as the first argument. The message will be added as an annotation. If markAsErroneous is called without arguments, a default message will be set. You can provide a custom path to the error message annotation, but in almost all cases, it is better to let the Instana Node.js SDK handle that on its own. If a custom path is provided, it can be provided as a string or an array (see span.annotate), and it should start with sdk.custom.tags. (Since: v2.25.2)

span.markAsNonErroneous([customErrorMessagePath]): Marks the span as non-erroneous. This can be useful if the span has been marked as an error by an auto-tracing instrumentation or by span.markAsErroneous and this decision needs to be reverted. If span.markAsErroneous has been used with a customErrorMessagePath earlier, provide the same path to span.markAsNonErroneous, otherwise omit this parameter. (Since: v2.25.2)

span.getErrorCount(): Returns the number of errors that have occurred during the processing of the request that is associated with the currently active span. This method returns 1 if the span has been marked as an error, and returns 0 otherwise. An error count that is greater than 1 can occur only when the current span is a batched span. (Since: v1.62.0)

span.disableAutoEnd(): See next section. (Since: v1.55.1)

span.end(errorCount): See next section. (Since: v1.55.1)

Ending Spans Manually (Message Broker Entries)

We mentioned before that Instana's automated tracing handles everything for you for supported libraries and that there is no need to interfere. There is one small exception to this rule: Tracing operations that have been triggered by consuming a message from a message broker (Kafka, RabbitMQ, NATS, NATS streaming, Google Cloud PubSub). Since there is no notion of a response or reply when consuming a message from a message broker, there is no event that could tell the Instana Node.js collector when all operations that are triggered by a particular message have been finished (in contrast to an incoming HTTP request, which always has an associated response, demarcating the end of the transaction).

When a process receives a new message, it will start an entry span. Instana's tracing capabilities need some event that signify that this span is finished. Other calls will only be assigned to the same trace when they are triggered between starting the entry span and finishing it. Therefore, the application code needs to tell Instana when the processing of the incoming message is complete. For this purpose, the handle for the currently active span which can be acquired with instana.currentSpan() (see above) can be used. The handle offers two methods that are relevant for this use case:

span.disableAutoEnd(): Disables automatically finishing the span and marks this span as one that will be finished manually by calling span.end() later.

span.end(errorCount): Finishes a span on which span.disableAutoEnd() has been called earlier. The errorCount argument is optional. Pass 1 if an error happened while processing the message. If nothing is passed, the errorCount defaults to 0.

Here is an example how this looks like for RabbitMQ:

channel.consume(queueName, function(msg) {
  var span = instana.currentSpan();
  span.disableAutoEnd();

  // The setTimeout is a placeholder for any number of asynchronous operations
  // that are executed when processing this particular message. It could also be
  // database access calls or outgoing HTTP calls or really anything else.
  setTimeout(function() {

    // call span.end when processing of the incoming message has finshed. Make
    // sure to also call in case an error happens while processing the message.
    span.end();

  }, 5000);
});

Note that the span will never be sent to the backend if you call span.disableAutoEnd() but forget to call span.end() on it.

Also note that for message brokers like Kafka, RabbitMQ and NATS, if you do not call span.disableAutoEnd() synchronously in your message handling function, the span will be ended and transmitted automatically immediately after your message handling function has returned. This will break that trace, that is, operations (DB access, outgoing HTTP calls, sending other messages) executed while processing that message will not show up as calls in Instana.

There is no need to do any of this when sending/publishing messages to a message broker.

Creating Spans Manually With The SDK

The collector automatically instruments widely used APIs to add tracing support that simply works out of the box. Sometimes you may find that this is not enough. The SDK can be used to provide insights into areas of your applications, e.g. custom libraries and frameworks, which would otherwise go unnoticed. For this purpose, the SDK allows you to create spans manually. Another use case is to create intermediate spans to demarcate sections of interest in your code. Last but not least, the SDK can be used to enable tracing in applications that do not receive requests from the outside but kick off work on their own, like scheduled jobs triggered by setInterval, setTimeout or similar means (see as follows if this is your use case).

Spans created with the SDK integrate seamlessly with Instana's automatic tracing capabilities.

Terminology

The SDK provides functions to create entry spans, intermediate spans and exit spans. In short:

  • Entry spans represent calls into the application under monitoring. These could be HTTP requests that the application receives from other services or messages the application picks up from a queue. (Of course, HTTP requests are already covered by automatic tracing.) In general, entry spans represent something that triggers processing inside your Node.js application. A trace always needs to start with an entry span (either one created by automatic instrumentation or via the SDK). The Node.js tracer remains inactive and no intermediate or exit spans will be captured unless there is an active entry span. Refer to the section Tracing Is Inactive When There Is No Active Entry Span for more details on that.
  • Exit spans represent calls the application makes. These could be HTTP requests the application makes (and which are responded to by other services) or database calls. (Again, outgoing HTTP requests and a lot of popular databases are already covered by automatic instrumentation.)
  • Intermediate spans are things that happen inside the application under monitoring, that is, they neither enter nor leave the process as entry spans and exit spans do. Intermediate spans can also be used to wrap automatically created spans in case you want to provide additional attributes (called tags) which the automatical instrumentation does not provide.

For more details on the terminology and, in particular, how spans relate to the calls you will see in the Instana UI, refer to our tracing documentation.

There is also a section on tracing best practices that is worth reading before starting to implement custom tracing with the SDK.

Finally there is a demo app that you can start locally and examine to help get you started with the Instana Node.js SDK: https://github.com/instana/instana-nodejs-demos/tree/master/sdk.

Callback-based API, Promise-based API, and Async API

The SDK offers three different type of APIs, one callback-based (instana.sdk.callback), one promise-based (instana.sdk.promise), and one for code that uses the async/await style (instana.sdk.async). Which one is used is purely a matter of taste. Basically, all three APIs offer various methods for starting a span and for completing spans. You need to start the span directly before you do the work that you want the span to represent and complete the span once the work is finished.

When using the callback API, you need to pass a callback whenever you start a new span and do all the work associated with that span inside that callback (or in the callback of any asynchronous operation transitively triggered from that callback). Here is an example:

instana.sdk.callback.startEntrySpan('my-custom-span', () => {
  // The actual work needs to happen inside this callback (or in the callback
  // of any asynchronous operation transitively triggered from this callback).
  ...
  doSomethingAsynchronous((err, result) => {
    if (err) {
      instana.sdk.callback.completeEntrySpan(err);
      logger.error(err);
      return;
    }
    instana.sdk.callback.completeEntrySpan();
    logger.info('Yay! πŸŽ‰', result);
  });
});

Note that the callback provided to startXxxSpan is called immediately and synchronously, without arguments. Other asynchronous operations can be triggered inside that callback.

When using the promise API, all methods that start a new span will return a promise. All the work associated with that span needs to happen in that promise chain (that is, either in the then of the promise returned by startXxxSpan or in any then handler further down the promise chain). Here is an example:

instana.sdk.promise.startEntrySpan('my-custom-span').then(() => {
  // The actual work needs to happen inside the promise chain, that is, either
  // here or in any `then` handler further down the promise chain.
  return anotherPromise();
}).then(result => {
  instana.sdk.promise.completeEntrySpan();
  logger.info('Yay! πŸŽ‰', result);
}).catch(err => {
  instana.sdk.promise.completeEntrySpan(err);
  logger.error(err);
});

Note that the promise returned by startXxxSpan is never rejected, it will resolve immediately without a value. Other asynchronous operations can be triggered inside its then handler.

For async/await style code the SDK offers instana.sdk.async (since version 1.81.0):

await instana.sdk.async.startExitSpan('my-custom-span');
try {
  await someOtherAsynchronousOperation();
  instana.sdk.async.completeExitSpan();
} catch (err) {
  instana.sdk.async.completeExitSpan(err);
  logger.error(err);
}

Under the hood, instana.sdk.async is just an alias for instana.sdk.promise, since async/await code is handled via Promises in the Node.js runtime.

The completeXxxSpan methods are identical for all three API flavours.

If the examples above are not enough, make sure to hop over to our fully fledged, working demo app that showcases the Node.js SDK: https://github.com/instana/instana-nodejs-demos/tree/master/sdk.

API Methods

The following common parameters are accepted by the SDK's methods:

  • name: The name of the span. This parameter is mandatory when starting a new span. It should be a short and self explanatory string.
  • tags: An optional JS object of additional meta data for the span. You can provide tags when starting the span or when completing it, or both. If you provide tags when starting and when completing the span, both objects will be merged. The tags will be shown in the Instana UI. You need to make sure to not add arbitrarily large objects to the spans you create. Short key value pairs should be used. If spans get too big, a batch of spans might get dropped instead of being sent to the Instana agent.
  • error: When an error occurred while doing the work associated with the current span, this error can be attached to the span when completing it.
  • traceId: This is only relevant for entry spans and is used to make the entry span part of an existing trace that has already been started in another process. If you provide a traceId, you also need to provide a parentSpanId.
  • parentSpanId: This is only relevant for entry spans that are part of an existing trace that has already been started in another process. It is used to reference the exit span that triggered this entry span. If you provide a parentSpanId, you also need to provide a traceId.

The following methods are offered by all three APIs:

  • instana.sdk.callback.startEntrySpan(name [, tags[, traceId, parentSpanId]], callback),
    instana.sdk.{promise|async}.startEntrySpan(name [, tags[, traceId, parentSpanId]]):
    Starts an entry span. You need to provide a name for the span. You can optionally provide a tags object. The traceId, and parentSpanId are also optional but you need to provide either both IDs or no ID at all.
  • instana.sdk.callback.completeEntrySpan([error, tags]),
    instana.sdk.{promise|async}.completeEntrySpan([error, tags]):
    Finishes an entry span. An error and additional tags can be provided. If you want to provide additional tags but no error, pass null as the first argument.
  • instana.sdk.callback.startIntermediateSpan(name[, tags], callback),
    instana.sdk.{promise|async}.startIntermediateSpan(name[, tags]):
    Starts an intermediate span. You need to provide a name for the span, and you can optionally provide a tags object. The function calls return the started span.
  • instana.sdk.callback.completeIntermediateSpan([error, tags, span]),
    instana.sdk.{promise|async}.completeIntermediateSpan([error, tags, span]):
    Finishes an intermediate span. An error, additional tags, and the span to complete can be provided. If you want to provide additional tags with no error, pass null as the first argument.
  • instana.sdk.callback.startExitSpan(name[, tags], callback)
    , instana.sdk.{promise|async}.startExitSpan(name[, tags]):
    Starts an exit span. You need to provide a name for the span and you can optionally provide a tags object.
  • instana.sdk.callback.completeExitSpan([error, tags])
    , instana.sdk.{promise|async}.completeExitSpan([error, tags]):
    Finishes an exit span. An error and additional tags can be provided. If you want to provide additional tags but no error, pass null as the first argument.
  • instana.sdk.callback.bindEmitter(emitter),
    instana.sdk.{promise|async}.bindEmitter(emitter):
    See as follows.

Note that spans started with any startXxxSpan method will only be transmitted to Instana once the corresponding completeXxxSpan has been called. Also, for nested spans, the calls need to be in the right order.

To illustrate this, consider the following two examples. The following is valid:

const sdk = instana.sdk.callback;

sdk.startEntrySpan('my-custom-entry', () => {
  doSomethingAsynchronous(() => {
    sdk.startExitSpan('my-custom-exit', () => {
      doAnotherThingAsynchronous(() => {
        sdk.completeExitSpan();
        sdk.completeEntrySpan();
        logger.info('Yay! πŸŽ‰');
      });
    });
  });
});

But this is not valid:

const sdk = instana.sdk.callback;

sdk.startEntrySpan('my-custom-entry', () => {
  doSomethingAsynchronous(() => {
    sdk.startExitSpan('my-custom-exit', () => {
      doAnotherThingAsynchronous(() => {
        // WRONG ORDER - you first need to complete the span you started last.
        // Think of the spans as stack.
        sdk.completeEntrySpan();
        sdk.completeExitSpan();
        logger.info('Yay! πŸŽ‰');
      });
    });
  });
});

Care must also be taken when nesting spans with the promise API. The following is correct:

instana.sdk.promise.startEntrySpan('custom-entry')
  // any number of other promises/async operations
  .then(() => {
    ...
  })
  .then(() => {
    return instana.sdk.promise.startExitSpan('custom-exit')
      // any number of other promises/async operations associated with the exit span
      .then(() => {
        ...
      })
      .then(() => {
        // Important: The exit span needs to be completed in the promise chain
        // started with startExitSpan, not in the outer promise chain started
        // with startEntrySpan.
        instana.sdk.promise.completeExitSpan();
      });
  })
  .then(() => {
    instana.sdk.promise.completeEntrySpan();
    logger.info('Yay! πŸŽ‰');
  });

But this will not work:

instana.sdk.promise.startEntrySpan('custom-entry')
.then(() => {
  return instana.sdk.promise.startExitSpan('custom-exit');
})
.then(() => {
  // WRONG The currently active span in this context is the *entry* span, not
  // the exit span, so it is not possible to complete the exit span here.
  instana.sdk.promise.completeExitSpan();
  instana.sdk.promise.completeEntrySpan();
});

See the following valid examples for intermediate spans:

await instana.sdk.async.startIntermediateSpan('intermediate-span-name')

// trigger an internal request
await request(`http://127.0.0.1:${port}`)

instana.sdk.async.completeIntermediateSpan();
// This example demonstrates how to start two overlaping intermediate spans.
const span1 = await instana.sdk.async.startIntermediateSpan('intermediate-span-name-1')
await request(`http://127.0.0.1:${port}`)
const span2 = await instana.sdk.async.startIntermediateSpan('intermediate-span-name-2')
await request(`http://127.0.0.1:${port}`)

// Pass "span1" as third argument to signalise which span to complete.
instana.sdk.async.completeIntermediateSpan(null, null, span1);
await request(`http://127.0.0.1:${port}`)
instana.sdk.async.completeIntermediateSpan(null, null, span2);

At this point you might wonder why the SDK's API is designed this way, in particular the business with the startXxxSpan methods accepting a callback/returning a promise might seem awkward. The point is that the we need to keep the asynchronous context while tracing. Since Node.js is single threaded and uses callbacks to do asynchronous operations, the Node.js collector needs a way to determine which operations belong to which span - wrapping the traced action in a callback or a promise makes that possible.

Handling Event Emitters

If the work associated with your custom SDK span involves an event emitter and if the code running inside the span listens to emitted events you need to bind the event emitter, otherwise your tracing code will not behave as expected. Here is how:

instana.sdk.callback.startEntrySpan('custom-span', () => {
  const emitter = ... // some event emitter
  instana.sdk.callback.bindEmitter(emitter);
  ...
  emitter.on('some-event', () => {
    instana.sdk.callback.completeEntrySpan();
    logger.info('Done! πŸŽ‰');
  });
});

Tracing Is Inactive When There Is No Active Entry Span

As a general rule, Instana tracers only capture exit or intermediate spans when there is an active entry span. This is an important safeguard to not capture noise that is unrelated to processing your business related transactions. However, there are edge cases where this safeguard can get in the way. Examples for that include work that is triggered by a mechanism which is not supported by Instana's automatic instrumentation:

  • This could be an incoming messages from a messaging library that we do not (yet) support.
  • Or it could be a request via a protocol that is not supported, for example raw TCP messages or websocket communication (both of which are not supported by automatic instrumentation).
  • Another example are applications that run scheduled jobs that are not triggered from the outside, but by the process itself (for example by using setTimeout, setInterval or a Node.js scheduling library that is not supported).

The consequence of not having any entry span is that the Instana Node.js tracer will also not capture any other intermediate or exit spans for calls that are made in this context. That is:

  • If an outgoing HTTP request, database call or anything else that would create an exit span is triggered via a supported library, but there is no active entry span, that exit span will not be captured.
  • If the Instana Node.js SDK is used to start an intermediate or exit span, but there is no active entry span, the SDK will reject to start that span. A warning will be logged about this.

The solution for this kind of situation is to manually start an entry span at the beginning of the piece of work and complete it when it is done. That way, all exit and intermediate spans will be captured because there is an active entry span.

You can also create a feature request for implementing out-of-the box support for the type of trigger you are using.

Restoring The Async Context Manually

Instana internally relies on async_hooks to keep the asynchronous context. This works automatically and transparently without the need for manual intervention. However, there are libraries and code patterns that might break async_hooks continuity so that the async context is lost. A few of them are listed in this list of issues. Using such a module can result in incomplete traces and missing calls.

Instana's Node.js SDK provides an API to rectify this. Please note that using this API is almost never required. You should only use this if you know exactly which line of code is causing the missing calls and why.

  • instana.sdk.getAsyncContext(): Returns the currently active async context. Call this before you call the code that breaks async continuity. (Since: v1.100.0)
  • instana.sdk.runInAsyncContext(context, callback): Runs the given callback in the provided async context. The context object should have been acquired by calling instana.sdk.getAsyncContext(). Wrap all code that is supposed to run in that context into the callback. (Since: v1.100.0)
  • instana.sdk.runPromiseInAsyncContext(context, createPromiseFn): Runs a promise in the provided async context. The context object should have been acquired by calling instana.sdk.getAsyncContext(). The function createPromiseFn is expected to return the promise you want to run in that context. (Since: v1.100.0)

Here is some example code for runInAsyncContext to demonstrate its usage:

  // ...

  // 1. Fetch the currently active asynchronous context directly _before_ the
  // asynchronous operation that breaks async_hooks continuity.
  const activeContext = instana.sdk.getAsyncContext();
  someLibrary.functionThatBreaksAsyncContinuity((error, result) => {
    // 2. Restore the asynchronous context directly _after_ the asynchronous
    // operation that breaks async_hooks/async_wrap continuity by calling
    // instana.sdk.runInAsyncContext with the context object acquired
    // earlier.
    instana.sdk.runInAsyncContext(activeContext, () => {
      // 3. Wrap all subsequent code in the callback given to instana.sdk.runInAsyncContext.
      if (error) {
        // ...
      }

      // ...
    });
  });

Here is some example code for runPromiseInAsyncContext:

  // ...

  // 1. Fetch the currently active asynchronous context directly _before_ the
  // asynchronous operation that breaks async_hooks continuity.
  const activeContext = instana.sdk.getAsyncContext();
  someLibrary.functionThatBreaksAsyncContinuity((error, result) => {
    // 2. Restore the asynchronous context directly _after_ the asynchronous
    // operation that breaks async_hooks/async_wrap continuity by calling
    // instana.sdk.runInAsyncContext with the context object acquired
    // earlier.
    return instana.sdk.runPromiseInAsyncContext(activeContext, () => {
      /* a function that returns the promise you want to run */}
    );
  });

OpenTracing Integration

This package also implements the OpenTracing API. In order to use OpenTracing for Node.js with Instana, you should disable automatic tracing and use the Instana OpenTracing API implementation. The following example project demonstrate this:

// Always initialize the collector as the first module inside the application.
const instana = require('@instana/collector')({
  tracing: {
    automaticTracingEnabled: false
  }
});

// instantiate the OpenTracing tracer:
const opentracing = require('opentracing');

// optionally use the opentracing provided singleton tracer wrapper
opentracing.initGlobalTracer(instana.opentracing.createTracer());

// retrieve the tracer instance from the opentracing tracer wrapper
const tracer = opentracing.globalTracer();

// start a new trace with an operation name
const span = tracer.startSpan('auth');

// mark operation as failed
span.setTag(opentracing.Tags.ERROR, true);

// finish the span and schedule it for transmission to instana
span.finish();

Limitations

  • OpenTracing is not integrated with Instana's automatic tracing. In particular, spans created by using the OpenTracing API will not be part of the same trace as the spans created by our automatic tracing instrumentation. If you want to add additional spans to the automatically created spans, you should prefer the SDK over OpenTracing. In fact, we recommend to either use automatic tracing (optionally augmented by SDK spans) or OpenTracing, but not both in one application.
  • The Instana Node.js collector does not have support for OpenTracing binary carriers. This OpenTracing implementation will silently ignore OpenTracing binary carrier objects.
  • Care should also be taken with OpenTracing baggage items. Baggage items are meta data which is transported via carrier objects across network boundaries. Furthermore, this meta data is inherited by child spans (and their child spans…). This can produce some overhead. We recommend to completely avoid the OpenTracing baggage API.

See Also