diff --git a/plugins/node/opentelemetry-instrumentation-express/src/instrumentation.ts b/plugins/node/opentelemetry-instrumentation-express/src/instrumentation.ts index 2e283158b8..256b477dfe 100644 --- a/plugins/node/opentelemetry-instrumentation-express/src/instrumentation.ts +++ b/plugins/node/opentelemetry-instrumentation-express/src/instrumentation.ts @@ -234,13 +234,6 @@ export class ExpressInstrumentation extends InstrumentationBase { if (spanHasEnded === false) { @@ -269,16 +262,20 @@ export class ExpressInstrumentation extends InstrumentationBase { ); assert.strictEqual(response, 'tata'); rootSpan.end(); - assert.strictEqual(finishListenerCount, 2); + assert.strictEqual(finishListenerCount, 4); assert.notStrictEqual( memoryExporter .getFinishedSpans() @@ -201,7 +201,7 @@ describe('ExpressInstrumentation', () => { ); assert.strictEqual(response, 'tata'); rootSpan.end(); - assert.strictEqual(finishListenerCount, 2); + assert.strictEqual(finishListenerCount, 4); assert.notStrictEqual( memoryExporter .getFinishedSpans() @@ -344,6 +344,49 @@ describe('ExpressInstrumentation', () => { ); }); + it('captures thrown errors', async () => { + const rootSpan = tracer.startSpan('rootSpan'); + let finishListenerCount: number | undefined; + const httpServer = await serverWithMiddleware(tracer, rootSpan, app => { + app.use((req, res, next) => { + res.on('finish', () => { + finishListenerCount = res.listenerCount('finish'); + }); + next(); + }); + + app.get('/', (req, res, next) => { + throw new Error('message'); + }); + }); + server = httpServer.server; + port = httpServer.port; + assert.strictEqual(memoryExporter.getFinishedSpans().length, 0); + + await context.with( + trace.setSpan(context.active(), rootSpan), + async () => { + await httpRequest.get(`http://localhost:${port}/`); + rootSpan.end(); + assert.strictEqual(finishListenerCount, 2); + + const errorSpan = memoryExporter + .getFinishedSpans() + .find(span => span.name.includes('request handler')); + assert.notStrictEqual(errorSpan, undefined); + + assert.deepStrictEqual(errorSpan!.status, { + code: SpanStatusCode.ERROR, + message: 'message', + }); + assert.notStrictEqual( + errorSpan!.events.find(event => event.name === 'exception'), + undefined + ); + } + ); + }); + it('should not create span because there are no parent', async () => { const app = express(); app.use(express.json()); @@ -620,23 +663,51 @@ describe('ExpressInstrumentation', () => { // `- span 'middleware - simpleMiddleware' // `- span 'request handler - /post/:id' const spans = collector.sortedSpans; + assert.strictEqual(spans[0].name, 'GET'); assert.strictEqual(spans[0].kind, testUtils.OtlpSpanKind.CLIENT); assert.strictEqual(spans[1].name, 'GET /post/:id'); assert.strictEqual(spans[1].kind, testUtils.OtlpSpanKind.SERVER); assert.strictEqual(spans[1].parentSpanId, spans[0].spanId); - assert.strictEqual(spans[2].name, 'middleware - query'); - assert.strictEqual(spans[2].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[2].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[3].name, 'middleware - expressInit'); - assert.strictEqual(spans[3].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[3].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[4].name, 'middleware - simpleMiddleware'); - assert.strictEqual(spans[4].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[4].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[5].name, 'request handler - /post/:id'); - assert.strictEqual(spans[5].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[5].parentSpanId, spans[1].spanId); + + const querySpan = spans.find( + span => span.name === 'middleware - query' + ); + + assert.strictEqual(querySpan?.kind, testUtils.OtlpSpanKind.INTERNAL); + assert.strictEqual(querySpan?.parentSpanId, spans[1].spanId); + + const expressInitSpan = spans.find( + span => span.name === 'middleware - expressInit' + ); + + assert.strictEqual( + expressInitSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(expressInitSpan?.parentSpanId, spans[1].spanId); + + const simpleMiddlewareSpan = spans.find( + span => span.name === 'middleware - simpleMiddleware' + ); + + assert.strictEqual( + simpleMiddlewareSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(simpleMiddlewareSpan?.parentSpanId, spans[1].spanId); + + const requestHandlerSpan = spans.find( + span => span.name === 'request handler - /post/:id' + ); + + assert.strictEqual( + requestHandlerSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(requestHandlerSpan?.parentSpanId, spans[1].spanId); + + assert.strictEqual(spans.length, 6); }, }); }); @@ -661,18 +732,51 @@ describe('ExpressInstrumentation', () => { assert.strictEqual(spans[1].name, 'GET /api/user/:id'); assert.strictEqual(spans[1].kind, testUtils.OtlpSpanKind.SERVER); assert.strictEqual(spans[1].parentSpanId, spans[0].spanId); - assert.strictEqual(spans[2].name, 'middleware - query'); - assert.strictEqual(spans[3].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[3].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[4].name, 'middleware - simpleMiddleware'); - assert.strictEqual(spans[4].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[4].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[5].name, 'router - /api/user/:id'); - assert.strictEqual(spans[5].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[5].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[6].name, 'request handler - /api/user/:id'); - assert.strictEqual(spans[6].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[6].parentSpanId, spans[1].spanId); + + const querySpan = spans.find( + span => span.name === 'middleware - query' + ); + + assert.strictEqual(querySpan?.kind, testUtils.OtlpSpanKind.INTERNAL); + assert.strictEqual(querySpan?.parentSpanId, spans[1].spanId); + + const expressInitSpan = spans.find( + span => span.name === 'middleware - expressInit' + ); + + assert.strictEqual( + expressInitSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(expressInitSpan?.parentSpanId, spans[1].spanId); + + const simpleMiddlewareSpan = spans.find( + span => span.name === 'middleware - simpleMiddleware' + ); + + assert.strictEqual( + simpleMiddlewareSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(simpleMiddlewareSpan?.parentSpanId, spans[1].spanId); + + const routerSpan = spans.find( + span => span.name === 'router - /api/user/:id' + ); + + assert.strictEqual(routerSpan?.kind, testUtils.OtlpSpanKind.INTERNAL); + assert.strictEqual(routerSpan?.parentSpanId, spans[1].spanId); + + const requestHandlerSpan = spans.find( + span => span.name === 'request handler - /api/user/:id' + ); + + assert.strictEqual( + requestHandlerSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(requestHandlerSpan?.parentSpanId, spans[1].spanId); + assert.strictEqual(spans.length, 7); }, }); }); @@ -696,27 +800,65 @@ describe('ExpressInstrumentation', () => { assert.strictEqual(spans[0].kind, testUtils.OtlpSpanKind.CLIENT); assert.strictEqual(spans[1].name, 'GET /api/user/:id/posts/:postId'); assert.strictEqual(spans[1].kind, testUtils.OtlpSpanKind.SERVER); - assert.strictEqual(spans[2].name, 'middleware - query'); - assert.strictEqual(spans[2].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[2].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[3].name, 'middleware - expressInit'); - assert.strictEqual(spans[3].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[3].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[4].name, 'middleware - simpleMiddleware'); - assert.strictEqual(spans[4].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[4].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[5].name, 'router - /api/user/:id'); - assert.strictEqual(spans[5].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[5].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[6].name, 'router - /:postId'); - assert.strictEqual(spans[6].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[6].parentSpanId, spans[1].spanId); + + const querySpan = spans.find( + span => span.name === 'middleware - query' + ); + + assert.strictEqual(querySpan?.kind, testUtils.OtlpSpanKind.INTERNAL); + assert.strictEqual(querySpan?.parentSpanId, spans[1].spanId); + + const expressInitSpan = spans.find( + span => span.name === 'middleware - expressInit' + ); + + assert.strictEqual( + expressInitSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(expressInitSpan?.parentSpanId, spans[1].spanId); + + const simpleMiddlewareSpan = spans.find( + span => span.name === 'middleware - simpleMiddleware' + ); + + assert.strictEqual( + simpleMiddlewareSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(simpleMiddlewareSpan?.parentSpanId, spans[1].spanId); + + const routerSpanOuter = spans.find( + span => span.name === 'router - /api/user/:id' + ); + + assert.strictEqual( + routerSpanOuter?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(routerSpanOuter?.parentSpanId, spans[1].spanId); + + const routerSpanInner = spans.find( + span => span.name === 'router - /:postId' + ); + assert.strictEqual( - spans[7].name, - 'request handler - /api/user/:id/posts/:postId' + routerSpanInner?.kind, + testUtils.OtlpSpanKind.INTERNAL ); - assert.strictEqual(spans[7].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[7].parentSpanId, spans[1].spanId); + assert.strictEqual(routerSpanInner?.parentSpanId, spans[1].spanId); + + const requestHandlerSpan = spans.find( + span => span.name === 'request handler - /api/user/:id/posts/:postId' + ); + + assert.strictEqual( + requestHandlerSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(requestHandlerSpan?.parentSpanId, spans[1].spanId); + + assert.strictEqual(spans.length, 8); }, }); }); @@ -742,21 +884,45 @@ describe('ExpressInstrumentation', () => { assert.strictEqual(spans[1].name, 'GET /\\/test\\/regex/'); assert.strictEqual(spans[1].parentSpanId, spans[0].spanId); assert.strictEqual(spans[1].kind, testUtils.OtlpSpanKind.SERVER); - assert.strictEqual(spans[2].name, 'middleware - query'); - assert.strictEqual(spans[2].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[2].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[3].name, 'middleware - expressInit'); - assert.strictEqual(spans[3].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[3].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[4].name, 'middleware - simpleMiddleware'); - assert.strictEqual(spans[4].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[4].parentSpanId, spans[1].spanId); + + const querySpan = spans.find( + span => span.name === 'middleware - query' + ); + + assert.strictEqual(querySpan?.kind, testUtils.OtlpSpanKind.INTERNAL); + assert.strictEqual(querySpan?.parentSpanId, spans[1].spanId); + + const expressInitSpan = spans.find( + span => span.name === 'middleware - expressInit' + ); + + assert.strictEqual( + expressInitSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(expressInitSpan?.parentSpanId, spans[1].spanId); + + const simpleMiddlewareSpan = spans.find( + span => span.name === 'middleware - simpleMiddleware' + ); + assert.strictEqual( - spans[5].name, - 'request handler - /\\/test\\/regex/' + simpleMiddlewareSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL ); - assert.strictEqual(spans[5].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[5].parentSpanId, spans[1].spanId); + assert.strictEqual(simpleMiddlewareSpan?.parentSpanId, spans[1].spanId); + + const requestHandlerSpan = spans.find( + span => span.name === 'request handler - /\\/test\\/regex/' + ); + + assert.strictEqual( + requestHandlerSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(requestHandlerSpan?.parentSpanId, spans[1].spanId); + + assert.strictEqual(spans.length, 6); }, }); }); @@ -781,18 +947,45 @@ describe('ExpressInstrumentation', () => { assert.strictEqual(spans[0].kind, testUtils.OtlpSpanKind.CLIENT); assert.strictEqual(spans[1].name, 'GET /test,6,/test/'); assert.strictEqual(spans[1].kind, testUtils.OtlpSpanKind.SERVER); - assert.strictEqual(spans[2].name, 'middleware - query'); - assert.strictEqual(spans[2].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[2].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[3].name, 'middleware - expressInit'); - assert.strictEqual(spans[3].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[3].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[4].name, 'middleware - simpleMiddleware'); - assert.strictEqual(spans[4].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[4].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[5].name, 'request handler - /test,6,/test/'); - assert.strictEqual(spans[5].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[5].parentSpanId, spans[1].spanId); + + const querySpan = spans.find( + span => span.name === 'middleware - query' + ); + + assert.strictEqual(querySpan?.kind, testUtils.OtlpSpanKind.INTERNAL); + assert.strictEqual(querySpan?.parentSpanId, spans[1].spanId); + + const expressInitSpan = spans.find( + span => span.name === 'middleware - expressInit' + ); + + assert.strictEqual( + expressInitSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(expressInitSpan?.parentSpanId, spans[1].spanId); + + const simpleMiddlewareSpan = spans.find( + span => span.name === 'middleware - simpleMiddleware' + ); + + assert.strictEqual( + simpleMiddlewareSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(simpleMiddlewareSpan?.parentSpanId, spans[1].spanId); + + const requestHandlerSpan = spans.find( + span => span.name === 'request handler - /test,6,/test/' + ); + + assert.strictEqual( + requestHandlerSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(requestHandlerSpan?.parentSpanId, spans[1].spanId); + + assert.strictEqual(spans.length, 6); }, }); }); @@ -821,21 +1014,50 @@ describe('ExpressInstrumentation', () => { 'GET /test/array1,/\\/test\\/array[2-9]/' ); assert.strictEqual(spans[1].kind, testUtils.OtlpSpanKind.SERVER); - assert.strictEqual(spans[2].name, 'middleware - query'); - assert.strictEqual(spans[2].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[2].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[3].name, 'middleware - expressInit'); - assert.strictEqual(spans[3].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[3].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[4].name, 'middleware - simpleMiddleware'); - assert.strictEqual(spans[4].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[4].parentSpanId, spans[1].spanId); + + const querySpan = spans.find( + span => span.name === 'middleware - query' + ); + + assert.strictEqual(querySpan?.kind, testUtils.OtlpSpanKind.INTERNAL); + assert.strictEqual(querySpan?.parentSpanId, spans[1].spanId); + + const expressInitSpan = spans.find( + span => span.name === 'middleware - expressInit' + ); + + assert.strictEqual( + expressInitSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(expressInitSpan?.parentSpanId, spans[1].spanId); + + const simpleMiddlewareSpan = spans.find( + span => span.name === 'middleware - simpleMiddleware' + ); + + assert.strictEqual( + simpleMiddlewareSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual( + simpleMiddlewareSpan?.parentSpanId, + spans[1].spanId + ); + + const requestHandlerSpan = spans.find( + span => + span.name === + 'request handler - /test/array1,/\\/test\\/array[2-9]/' + ); + assert.strictEqual( - spans[5].name, - 'request handler - /test/array1,/\\/test\\/array[2-9]/' + requestHandlerSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL ); - assert.strictEqual(spans[5].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[5].parentSpanId, spans[1].spanId); + assert.strictEqual(requestHandlerSpan?.parentSpanId, spans[1].spanId); + + assert.strictEqual(spans.length, 6); }, }); }); @@ -874,21 +1096,50 @@ describe('ExpressInstrumentation', () => { 'GET /test/arr/:id,/\\/test\\/arr[0-9]*\\/required(path)?(\\/optionalPath)?\\/(lastParam)?/' ); assert.strictEqual(spans[1].kind, testUtils.OtlpSpanKind.SERVER); - assert.strictEqual(spans[2].name, 'middleware - query'); - assert.strictEqual(spans[2].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[2].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[3].name, 'middleware - expressInit'); - assert.strictEqual(spans[3].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[3].parentSpanId, spans[1].spanId); - assert.strictEqual(spans[4].name, 'middleware - simpleMiddleware'); - assert.strictEqual(spans[4].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[4].parentSpanId, spans[1].spanId); + + const querySpan = spans.find( + span => span.name === 'middleware - query' + ); + + assert.strictEqual(querySpan?.kind, testUtils.OtlpSpanKind.INTERNAL); + assert.strictEqual(querySpan?.parentSpanId, spans[1].spanId); + + const expressInitSpan = spans.find( + span => span.name === 'middleware - expressInit' + ); + assert.strictEqual( - spans[5].name, - 'request handler - /test/arr/:id,/\\/test\\/arr[0-9]*\\/required(path)?(\\/optionalPath)?\\/(lastParam)?/' + expressInitSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(expressInitSpan?.parentSpanId, spans[1].spanId); + + const simpleMiddlewareSpan = spans.find( + span => span.name === 'middleware - simpleMiddleware' ); - assert.strictEqual(spans[5].kind, testUtils.OtlpSpanKind.INTERNAL); - assert.strictEqual(spans[5].parentSpanId, spans[1].spanId); + + assert.strictEqual( + simpleMiddlewareSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual( + simpleMiddlewareSpan?.parentSpanId, + spans[1].spanId + ); + + const requestHandlerSpan = spans.find( + span => + span.name === + 'request handler - /test/arr/:id,/\\/test\\/arr[0-9]*\\/required(path)?(\\/optionalPath)?\\/(lastParam)?/' + ); + + assert.strictEqual( + requestHandlerSpan?.kind, + testUtils.OtlpSpanKind.INTERNAL + ); + assert.strictEqual(requestHandlerSpan?.parentSpanId, spans[1].spanId); + + assert.strictEqual(spans.length, 6); }, }); });