diff --git a/plugins/node/opentelemetry-plugin-express/src/express.ts b/plugins/node/opentelemetry-plugin-express/src/express.ts index 3bcf44d78a..058328e58a 100644 --- a/plugins/node/opentelemetry-plugin-express/src/express.ts +++ b/plugins/node/opentelemetry-plugin-express/src/express.ts @@ -218,9 +218,15 @@ export class ExpressPlugin extends BasePlugin { }; } const result = original.apply(this, arguments); - // If the callback is never called, we need to close the span. - setImmediate(() => { + /** + * As this point if the callback wasn't called, that means either the + * layer is asynchronous (so it will call the callback later on) or that + * the layer directly end the http response, so we'll hook into the "finish" + * event to handle the later case. + */ + req.res?.once('finish', () => { if (spanHasEnded === false) { + spanHasEnded = true; span.end(startTime); } }); diff --git a/plugins/node/opentelemetry-plugin-express/test/express.test.ts b/plugins/node/opentelemetry-plugin-express/test/express.test.ts index c9cdf84276..6f3a798ef6 100644 --- a/plugins/node/opentelemetry-plugin-express/test/express.test.ts +++ b/plugins/node/opentelemetry-plugin-express/test/express.test.ts @@ -90,8 +90,10 @@ describe('Express Plugin', () => { }); const router = express.Router(); app.use('/toto', router); - router.get('/:id', (req, res, next) => { - return res.status(200).end(); + router.get('/:id', (req, res) => { + setTimeout(() => { + return res.status(200).end(); + }, 5); }); const server = http.createServer(app); await new Promise(resolve => server.listen(0, resolve));