table of contents Table of contents

Node.js

This guide will help you instrument your Node.js application(s) with OpenTelemetry and send traces to Checkly.

Step 1: Get the API endpoint and an API key

To start sending traces to Checkly, you need to have the following prerequisites in place:

  1. Grab your OTel API key in the Send traces section of the Open Telemetry Integration page in the Checkly app.

  2. Select the preferred API endpoint based on your region.

    EU region

    https://otel.eu-west-1.checklyhq.com/v1/traces
    

    US region

    https://otel.us-east-1.checklyhq.com/v1/traces
    

Step 2: Install the OpenTelemetry SDK

Install the relevant OpenTelemetry packages:

npm install --save \
    @opentelemetry/sdk-node \
    @opentelemetry/exporter-trace-otlp-proto \
    @opentelemetry/auto-instrumentations-node

Step 2: Initialize the instrumentation

Create a file called tracing.js at the root of your project and add the following code:

// tracing.js
const { NodeSDK } = require('@opentelemetry/sdk-node')
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-proto')
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node')
const { BatchSpanProcessor } = require('@opentelemetry/sdk-trace-base')
const { Resource } = require('@opentelemetry/resources')
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions')
const { SamplingDecision } = require('@opentelemetry/sdk-trace-base')
const { trace } = require('@opentelemetry/api')

const checklyExporter = new OTLPTraceExporter({
  url: process.env.CHECKLY_OTEL_API_ENDPOINT,
  headers: {
    Authorization: `Bearer ${process.env.CHECKLY_OTEL_API_KEY}`,
  }
})

const sdk = new NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: `my-node-app`,
  }),
  instrumentations: [getNodeAutoInstrumentations()],
  spanProcessors: new BatchSpanProcessor(checklyExporter),
  sampler: {
    shouldSample: (context, traceId, spanName, spanKind, attributes, links) => {
      const isChecklySpan = trace.getSpan(context)?.spanContext()?.traceState?.get('checkly')
      if (isChecklySpan) {
        return { decision: SamplingDecision.RECORD_AND_SAMPLED }
      } else {
        return { decision: SamplingDecision.NOT_RECORD }
      }
    },
  },
})

sdk.start()

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

Note the sampler configuration. This is a custom, head-based sampler that will only sample spans that are generated by Checkly by inspecting the trace state. This way you only pay for the egress traffic generated by Checkly and not for any other traffic.

Step 3: Start your app with the instrumentation

Export your API key and endpoint as environment variables in your shell.

export CHECKLY_OTEL_API_ENDPOINT="https://otel.us-east-1.checklyhq.com/v1/traces" # US instance
#export CHECKLY_OTEL_API_ENDPOINT="https://otel.eu-west-1.checklyhq.com/v1/traces" # EU instance
export CHECKLY_OTEL_API_KEY="<your Checkly OTel API key>"

Then start your app with the extra -r flag to load the tracing.js file before any other files are loaded.

node -r ./tracing.js index.js

🎉 You are done. Any interactions with your app that are triggered by a Checkly synthetic monitoring check will now generate traces, which are sent back to Checkly and displayed in the Checkly UI.

Reducing noise in the auto Node.js instrumentation

We found the Node.js auto-instrumentation a bit noisy. There can be a lot of file i/o and a ton of DNS calls you might not be interested in. Luckily, you can easily tweak that by providing some extra options to the getNodeAutoInstrumentations() function. We use the following configuration to filter out some of the noise:

instrumentations: [getNodeAutoInstrumentations({
  '@opentelemetry/instrumentation-fs': {
    enabled: false,
  },
  '@opentelemetry/instrumentation-net': {
    enabled: false,
  },
  '@opentelemetry/instrumentation-dns': {
    enabled: false,
  },
  '@opentelemetry/instrumentation-http': {
    enabled: true,
  },
})]

Last updated on April 30, 2024. You can contribute to this documentation by editing this page on Github