Skip to content

Commit 353dbb0

Browse files
feat(instrumentation-express)!: propagate context and measure full handler spans (#2638)
1 parent b947650 commit 353dbb0

File tree

2 files changed

+13
-7
lines changed

2 files changed

+13
-7
lines changed

plugins/node/opentelemetry-instrumentation-express/src/instrumentation.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,9 @@ export class ExpressInstrumentation extends InstrumentationBase<ExpressInstrumen
215215
attributes: Object.assign(attributes, metadata.attributes),
216216
});
217217

218+
const parentContext = context.active();
219+
let currentContext = trace.setSpan(parentContext, span);
220+
218221
const { requestHook } = instrumentation.getConfig();
219222
if (requestHook) {
220223
safeExecuteInTheMiddle(
@@ -234,12 +237,15 @@ export class ExpressInstrumentation extends InstrumentationBase<ExpressInstrumen
234237
}
235238

236239
let spanHasEnded = false;
240+
// TODO: Fix router spans (getRouterPath does not work properly) to
241+
// have useful names before removing this branch
237242
if (
238-
metadata.attributes[AttributeNames.EXPRESS_TYPE] !==
239-
ExpressLayerType.MIDDLEWARE
243+
metadata.attributes[AttributeNames.EXPRESS_TYPE] ===
244+
ExpressLayerType.ROUTER
240245
) {
241246
span.end();
242247
spanHasEnded = true;
248+
currentContext = parentContext;
243249
}
244250
// listener for response.on('finish')
245251
const onResponseFinish = () => {
@@ -278,12 +284,12 @@ export class ExpressInstrumentation extends InstrumentationBase<ExpressInstrumen
278284
(req[_LAYERS_STORE_PROPERTY] as string[]).pop();
279285
}
280286
const callback = args[callbackIdx] as Function;
281-
return callback.apply(this, arguments);
287+
return context.bind(parentContext, callback).apply(this, arguments);
282288
};
283289
}
284290

285291
try {
286-
return original.apply(this, arguments);
292+
return context.bind(currentContext, original).apply(this, arguments);
287293
} catch (anyError) {
288294
const [error, message] = asErrorAndMessage(anyError);
289295
span.recordException(error);
@@ -296,7 +302,7 @@ export class ExpressInstrumentation extends InstrumentationBase<ExpressInstrumen
296302
/**
297303
* At this point if the callback wasn't called, that means either the
298304
* layer is asynchronous (so it will call the callback later on) or that
299-
* the layer directly end the http response, so we'll hook into the "finish"
305+
* the layer directly ends the http response, so we'll hook into the "finish"
300306
* event to handle the later case.
301307
*/
302308
if (!spanHasEnded) {

plugins/node/opentelemetry-instrumentation-express/test/express.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ describe('ExpressInstrumentation', () => {
9797
);
9898
assert.strictEqual(response, 'tata');
9999
rootSpan.end();
100-
assert.strictEqual(finishListenerCount, 2);
100+
assert.strictEqual(finishListenerCount, 3);
101101
assert.notStrictEqual(
102102
memoryExporter
103103
.getFinishedSpans()
@@ -202,7 +202,7 @@ describe('ExpressInstrumentation', () => {
202202
);
203203
assert.strictEqual(response, 'tata');
204204
rootSpan.end();
205-
assert.strictEqual(finishListenerCount, 2);
205+
assert.strictEqual(finishListenerCount, 3);
206206
assert.notStrictEqual(
207207
memoryExporter
208208
.getFinishedSpans()

0 commit comments

Comments
 (0)