OpenTelemetry for JavaScript | Honeycomb

We use cookies or similar technologies to personalize your online experience & tailor marketing to you. Many of our product features require cookies to function properly.

Read our privacy policy I accept cookies from this site

OpenTelemetry for JavaScript

OpenTelemetry for JavaScript can be used to add automatic and manual instrumentation to your applications and have trace data sent to Honeycomb. Automatic instrumentation is enabled by adding instrumentation packages. Manual instrumentation can be added using the OpenTelemetry API.

Requirements  🔗

These instructions will explain how to set up automatic and manual instrumentation for a Node.js service. In order to follow along, you will need:

  • Node.js version 8.5.0 or newer.
  • A NodeJS application.
  • A Honeycomb API Key. You can find your API key on your Team Settings page. If you don’t have an API key yet, sign up for a free Honeycomb account.

Automatic Instrumentation  🔗

To auto-instrument your Node.js Express application, you need to add the following packages:

npm install --save @grpc/grpc-js
npm install --save @opentelemetry/api
npm install --save @opentelemetry/sdk-node
npm install --save @opentelemetry/exporter-trace-otlp-grpc
npm install --save @opentelemetry/auto-instrumentations-node

auto-instrumentations-node is a meta package that provides a simple way to initialize multiple Node.js instrumentations. Alternatively, you can install individual instrumentations.

Initialization  🔗

The OpenTelemetry initialization needs to happen first in your application lifecycle. You can accomplish this by creating an initialization file. You can then import the file as the first step in you application initialization, or include it with the -r, --require Node.js CLI option.

OpenTelemetry initialization:

// tracing.js

const process = require('process');
const { Metadata, credentials } = require("@grpc/grpc-js");

const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
const { OTLPTraceExporter } = require("@opentelemetry/exporter-trace-otlp-grpc");

const metadata = new Metadata()
metadata.set('x-honeycomb-team', '<YOUR_API_KEY>');
metadata.set('x-honeycomb-dataset', '<YOUR_DATASET>');
const traceExporter = new OTLPTraceExporter({
  url: 'grpc://api.honeycomb.io:443/',
  credentials: credentials.createSsl(),
  metadata
});

const sdk = new NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: '<YOUR_SERVICE_NAME>',
  }),
  traceExporter,
  instrumentations: [getNodeAutoInstrumentations()]
});

sdk.start()
  .then(() => console.log('Tracing initialized'))
  .catch((error) => console.log('Error initializing tracing', error));

process.on('SIGTERM', () => {
  sdk.shutdown()
    .then(() => console.log('Tracing terminated'))
    .catch((error) => console.log('Error terminating tracing', error))
    .finally(() => process.exit(0));
});

Running the Node.js app with OpenTelemetry initialization file:

node -r ./tracing.js app.js

You can see a complete example here.

Adding Manual Instrumentation  🔗

Auto-instrumentation is the easiest way to get started with instrumenting your code, but in order to get the most insight into your system, you should add manual instrumentation where appropriate. To do this, use the OpenTelemetry API to access the currently executing span and add attributes to it, and/or to create new spans.

To add manual instrumentation, you need to add the OpenTelemetry API package:

npm install --save @opentelemetry/api

Adding Context to Spans  🔗

It’s often beneficial to add context to a currently executing span in a trace. For example, you may have an application or service that handles users, and you want to associate the user with the span when querying your dataset in Honeycomb. In order to do this, get the current span from the context and set an attribute with the user ID:

const api = require("@opentelemetry/api");

function handleUser(user) {
  let activeSpan = api.trace.getSpan(api.context.active());
  activeSpan.setAttribute("user_id", user.getId());
}

This will add a user_id field to the current span so that you can use the field in WHERE, GROUP BY or ORDER clauses in the Honeycomb query builder.

Creating New Spans  🔗

Auto-instrumentation can show the shape of requests to your system, but only you know the really important parts. In order to get the full picture of what’s happening, you will have to add manual instrumentation and create some custom spans. To do this, grab the tracer from the OpenTelemetry API:

const api = require("@opentelemetry/api");

function runQuery() {
  let tracer = api.trace.getTracer("my-tracer");
  let span = tracer.startSpan("expensive-query");
  // ... do cool stuff
  span.end();
}

Sampling  🔗

To control how many spans are exported to Honeycomb, you can configure a sampler to send a portion of your traffic. The sampler is configured with a sample ratio between 0 (send nothing) and 1 (send everything). In the example below, approximately 8/10 traces will be exported. All spans in an exported trace are sent to Honeycomb.

You can configure a sampler when you initialize OpenTelemetry:

npm install --save @opentelemetry/core
// tracing.js

const { TraceIdRatioBasedSampler } = require("@opentelemetry/core");

const provider = new NodeTracerProvider({sampler: new TraceIdRatioBasedSampler(0.8)})

Distributed Trace Propagation  🔗

When a service calls another service, you want to be sure that the relevant trace information is propagated from one service to the other. This allows Honeycomb to connect the two services in a trace. Trace context propagation is done by sending and parsing headers that conform to the W3C Trace Context specification. The OpenTelemetry library enables W3C propagation by default: headers will be included in calls made from supported HTTP or gRPC clients and parsed by supported web frameworks.

In order for this to work, both the sending and receiving service must be using the same propagation format, and both services must be configured to send data to the same Honeycomb dataset.