opentelemetry-js
opentelemetry-js copied to clipboard
instrumentation-http: ESM instrumentation does not work on outgoing requests
What happened?
Steps to Reproduce
with node v20.17.0 or v18.20.4, install latest (0.53.0) opentelemetry sdk-node and instrumentation-http enable ESM (set type: module in package.json)
a client makes a http request in context with trace id using http.request()
Expected Result
server receives traceparent
header and runs in a context with same traceId.
Actual Result
server does not receive traceparent
header and runs handler in a separate trace context.
Additional Details
might be related to #5001
OpenTelemetry Setup Code
// register.js
import { register } from 'node:module';
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
import { node, NodeSDK } from '@opentelemetry/sdk-node';
const OPENTELEMETRY_SDK = Symbol('openTelemetrySdk');
register('@opentelemetry/instrumentation/hook.mjs', import.meta.url);
globalThis[OPENTELEMETRY_SDK] = new NodeSDK({
serviceName: 'test',
instrumentations: [new HttpInstrumentation({ enabled: true })],
});
globalThis[OPENTELEMETRY_SDK].start();
const shutdownFn = async () => globalThis[OPENTELEMETRY_SDK].shutdown().then(() => console.log('shutdown')).catch((error) => console.error(error));
let shutdownStarted = false;
const signals = ['SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT', 'SIGBUS', 'SIGFPE', 'SIGSEGV', 'SIGUSR2', 'SIGTERM'];
const signalHandler = (signal) => {
if (shutdownStarted) return;
shutdownStarted = true;
shutdownFn().then(() => {
signals.forEach((s) => process.removeListener(s, signalHandler));
process.kill(process.pid, signal);
});
};
signals.forEach((s) => process.on(s, signalHandler));
// index.js
import { api } from '@opentelemetry/sdk-node';
import http from 'node:http';
const span = api.trace.getTracer().startSpan('test');
const ctx = api.trace.setSpan(api.context.active(), span);
const server = http.Server((req, res) => {
console.log('server', { heades: req.headers, span: api.trace.getActiveSpan().spanContext() });
res.writeHead(204);
res.end();
});
await new Promise(res => server.listen(8080).once('listening', res));
await api.context.with(ctx, async () => {
console.log('client', { span: api.trace.getActiveSpan().spanContext() });
await new Promise(resolve => {
const req = http.request('http://localhost:8080/ping', (res) => {
res.on('data', () => {});
res.on('end', resolve);
});
req.end();
});
});
process.kill(process.pid, 'SIGTERM');
package.json
{
"name": "otel-reproduce",
"version": "0.0.1",
"main": "index.js",
"type": "module",
"scripts": {
"start": "cross-env OTEL_PROPAGATORS=tracecontext OTEL_TRACES_EXPORTER=console OTEL_NODE_RESOURCE_DETECTORS=none node --import ./register.js ./index.js"
},
"author": "",
"license": "UNLICENSED",
"description": "",
"dependencies": {
"@opentelemetry/instrumentation": "0.53.0",
"@opentelemetry/instrumentation-http": "0.53.0",
"@opentelemetry/sdk-node": "0.53.0",
"cross-env": "7.0.3"
}
}
Relevant log output
client {
span: {
traceId: '92a751c98e9a4c3d782cc6c877fd8502',
spanId: '74ae11ca029180a3',
traceFlags: 1,
traceState: undefined
}
}
server {
heades: { host: 'localhost:8080', connection: 'close' },
span: {
traceId: 'c94c82377ce8920746fc79e664303ebc',
spanId: '64f65ad6b638654a',
traceFlags: 1,
traceState: undefined
}
}