Tracing Axios with OpenTelemetry in Node.js

Integrating OpenTelemetry distributed tracing with the Axios HTTP client allows you to automatically monitor outbound HTTP requests, record latency, and propagate trace context headers across services. Because Axios relies on Node.js core http and https modules, OpenTelemetry captures Axios requests automatically when HTTP instrumentation is initialized before the Axios module is loaded. Below is the step-by-step guide to configuring and verifying OpenTelemetry tracing for Axios.

Step 1: Install Required Dependencies

Install the core OpenTelemetry SDK, the HTTP instrumentation package, and an exporter (such as the Console exporter for debugging or the OTLP exporter for production backends):

npm install @opentelemetry/sdk-node \
  @opentelemetry/instrumentation-http \
  @opentelemetry/sdk-trace-base \
  @opentelemetry/resources \
  @opentelemetry/semantic-conventions \
  axios

Step 2: Configure the Tracing Initialization File

Create a dedicated initialization file (e.g., tracing.js) to set up the OpenTelemetry Node SDK and configure the HTTP instrumentation:

// tracing.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { ConsoleSpanExporter } = require('@opentelemetry/sdk-trace-base');
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');

const sdk = new NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'axios-client-service',
  }),
  traceExporter: new ConsoleSpanExporter(), // Replace with OTLPTraceExporter for production
  instrumentations: [
    new HttpInstrumentation({
      // Optional: Add custom hooks to modify or filter request spans
      requestHook: (span, request) => {
        span.setAttribute('custom.client', 'axios');
      },
    }),
  ],
});

sdk.start();

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

Step 3: Implement Your Axios Client

Write your application logic using Axios as usual in a separate file (e.g., app.js):

// app.js
const axios = require('axios');

async function makeRequest() {
  try {
    const response = await axios.get('https://jsonplaceholder.typicode.com/todos/1');
    console.log('Response Status:', response.status);
    console.log('Response Data:', response.data);
  } catch (error) {
    console.error('Request failed:', error.message);
  }
}

makeRequest();

Step 4: Run the Application with Preloaded Tracing

OpenTelemetry must patch Node.js network modules before Axios is required. Run your application with the --require flag to load tracing.js before executing app.js:

node --require ./tracing.js app.js

Step 5: Verify Distributed Context Propagation

Once executed, the HttpInstrumentation automatically:

  1. Generates an active span for the outgoing Axios request containing metadata such as HTTP method, URL, and status code.
  2. Injects W3C Trace Context headers (traceparent and tracestate) into the outgoing HTTP headers, allowing downstream services to continue the trace.
  3. Outputs the span details to your configured trace exporter.