Skip to content

Commit 2c32e58

Browse files
david-lunaAbhiPrasadJamieDanielson
authored
fix(instr-express): fix handler patching for already patched router (#2294)
* fix(instr-express): fix handler patching for already patched router --------- Co-authored-by: Abhijeet Prasad <[email protected]> Co-authored-by: Jamie Danielson <[email protected]>
1 parent 9bfd0fc commit 2c32e58

File tree

2 files changed

+47
-3
lines changed

2 files changed

+47
-3
lines changed

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,11 @@ export class ExpressInstrumentation extends InstrumentationBase {
315315
// some properties holding metadata and state so we need to proxy them
316316
// through through patched function
317317
// ref: https://github.com/open-telemetry/opentelemetry-js-contrib/issues/1950
318-
Object.keys(original).forEach(key => {
318+
// Also some apps/libs do their own patching before OTEL and have these properties
319+
// in the proptotype. So we use a `for...in` loop to get own properties and also
320+
// any enumerable prop in the prototype chain
321+
// ref: https://github.com/open-telemetry/opentelemetry-js-contrib/issues/2271
322+
for (const key in original) {
319323
Object.defineProperty(patched, key, {
320324
get() {
321325
return original[key];
@@ -324,8 +328,7 @@ export class ExpressInstrumentation extends InstrumentationBase {
324328
original[key] = value;
325329
},
326330
});
327-
});
328-
331+
}
329332
return patched;
330333
});
331334
}

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,47 @@ describe('ExpressInstrumentation', () => {
517517
}
518518
);
519519
});
520+
521+
it('should keep the handle properties even if router is patched before instrumentation does it', async () => {
522+
const rootSpan = tracer.startSpan('rootSpan');
523+
let routerLayer: { name: string; handle: { stack: any[] } };
524+
525+
const expressApp = express();
526+
const router = express.Router();
527+
const CustomRouter: (...p: Parameters<typeof router>) => void = (
528+
req,
529+
res,
530+
next
531+
) => router(req, res, next);
532+
router.use('/:slug', (req, res, next) => {
533+
const stack = req.app._router.stack as any[];
534+
routerLayer = stack.find(router => router.name === 'CustomRouter');
535+
return res.status(200).end('bar');
536+
});
537+
// The patched router now has express router's own properties in its prototype so
538+
// they are not accessible through `Object.keys(...)`
539+
// https://github.com/TryGhost/Ghost/blob/fefb9ec395df8695d06442b6ecd3130dae374d94/ghost/core/core/frontend/web/site.js#L192
540+
Object.setPrototypeOf(CustomRouter, router);
541+
expressApp.use(CustomRouter);
542+
543+
const httpServer = await createServer(expressApp);
544+
server = httpServer.server;
545+
port = httpServer.port;
546+
await context.with(
547+
trace.setSpan(context.active(), rootSpan),
548+
async () => {
549+
const response = await httpRequest.get(
550+
`http://localhost:${port}/foo`
551+
);
552+
assert.strictEqual(response, 'bar');
553+
rootSpan.end();
554+
assert.ok(
555+
routerLayer.handle.stack.length === 1,
556+
'router layer stack is accessible'
557+
);
558+
}
559+
);
560+
});
520561
});
521562

522563
describe('Disabling plugin', () => {

0 commit comments

Comments
 (0)