OpenTelemetry Node.js Fetch Instrumentation

1.2.3 · active · verified Sun Apr 19

This package, `@gasbuddy/opentelemetry-instrumentation-fetch-node`, provides automatic OpenTelemetry instrumentation specifically for Node.js 18+ native `fetch` API calls. Unlike generic HTTP instrumentation, it is designed to work with the `undici`-based native `fetch` implementation in newer Node.js versions. The current stable version is 1.2.3, released in July 2024, with a consistent release cadence addressing bug fixes and features. A key differentiator is its use of Node.js's diagnostics channel for tracing and a unique workaround involving a 'phony fetch' to an unparseable URL, ensuring the instrumentation is active even with Node's lazy-loading behavior of the `fetch` API. It allows for advanced customization of spans and headers through an `onRequest` event. This is crucial for applications leveraging native `fetch` in modern Node environments that need comprehensive observability.

Common errors

Warnings

Install

Imports

Quickstart

Demonstrates how to set up and register `NodeFetchInstrumentation` with a basic OpenTelemetry Node SDK, make a traced native `fetch` request, and customize span attributes and request headers using `onRequest` and `applyCustomAttributesOnSpan` callbacks.

import { NodeSDK } from '@opentelemetry/sdk-node';
import { ConsoleSpanExporter } from '@opentelemetry/sdk-trace-node';
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { NodeFetchInstrumentation } from '@gasbuddy/opentelemetry-instrumentation-fetch-node';

const sdk = new NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'my-fetch-service',
  }),
  traceExporter: new ConsoleSpanExporter(),
});

// Initialize and register the fetch instrumentation
registerInstrumentations([
  new NodeFetchInstrumentation({
    propagateContext: true, // Propagate trace context in outgoing headers
    ignoreMethods: ['OPTIONS'], // Do not instrument OPTIONS requests
    onRequest: (span, request) => {
      // Add custom attributes to the span before the request is made
      span.setAttribute('http.request.method', request.method);
      span.setAttribute('http.request.headers.host', request.headers.get('host') ?? '');
      // Example: Add a custom header to the outgoing request
      // request.headers.set('x-custom-trace-id', span.spanContext().traceId);
    },
    applyCustomAttributesOnSpan: (span, request, response) => {
        // Add custom attributes to the span after the response is received
        if (response) {
            span.setAttribute('http.response.status_code', response.status);
            span.setAttribute('http.response.status_text', response.statusText);
        }
    }
  }),
]);

sdk.start();

async function makeFetchRequest() {
  try {
    console.log('Making a fetch request...');
    const response = await fetch('https://httpbin.org/get', {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
      },
    });
    const data = await response.json();
    console.log('Fetch request completed (URL):', data.url);
  } catch (error) {
    console.error('Fetch request failed:', error);
  }
}

// Execute the request and then shut down the SDK gracefully
makeFetchRequest().finally(() => {
  console.log('Shutting down OpenTelemetry SDK...');
  // A small delay to ensure all spans are processed before shutdown
  setTimeout(() => sdk.shutdown().then(() => console.log('SDK shutdown complete.')), 500);
});

view raw JSON →