From 5981dde6d77dbfe443f1ece963a37c3035b4690d Mon Sep 17 00:00:00 2001 From: Benoit Zugmeyer Date: Fri, 19 Dec 2025 15:21:50 +0100 Subject: [PATCH] fix(instrumentation-web-exception): properly add bounded error listener The README.md advises to use the instrumentation like this: ``` registerInstrumentations({ instrumentations: [ new ExceptionInstrumentation({ ... }), ], }); ``` But this does not work because the function passed to `addEventListener('error', ...)` is the unbounded method, so accesses to `this.getConfig()` failed. I know this sounds surprising, because the `onError` method was properly reaffected with the bounded method in the constructor. But actually, `enable()` is called by the base class, during `super()`, which is called *before the `onError` is bound*! This wasn't caught in tests because in test, `enable()` is called two times, once when instantiating the instrumentation, and once explicitely when calling `instr.enable()`. This second call *do* register the correctly bounded method. To work around this, this PR make sure the `onError` method is proprely bound in `enable()`. --- .../src/instrumentation.ts | 2 +- .../test/instrumentation.test.ts | 29 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/instrumentation-web-exception/src/instrumentation.ts b/packages/instrumentation-web-exception/src/instrumentation.ts index 2972740906..0a5da75b46 100644 --- a/packages/instrumentation-web-exception/src/instrumentation.ts +++ b/packages/instrumentation-web-exception/src/instrumentation.ts @@ -49,7 +49,6 @@ export interface GlobalErrorsInstrumentationConfig export class ExceptionInstrumentation extends InstrumentationBase { constructor(config: GlobalErrorsInstrumentationConfig = {}) { super(PACKAGE_NAME, PACKAGE_VERSION, config); - this.onError = this.onError.bind(this); } init() {} @@ -101,6 +100,7 @@ export class ExceptionInstrumentation extends InstrumentationBase { }); describe('throwing an error', () => { - const instr = new ExceptionInstrumentation(); + let disableInstrumentations: () => void; beforeEach(() => { - registerInstrumentations({ - instrumentations: [instr], + disableInstrumentations = registerInstrumentations({ + instrumentations: [new ExceptionInstrumentation()], }); - - instr.enable(); }); afterEach(() => { - instr.disable(); + disableInstrumentations(); exporter.reset(); }); @@ -179,21 +177,24 @@ describe('ExceptionInstrumentation', () => { 'app.custom.exception': error.message.toLocaleUpperCase(), }; }; - const instr = new ExceptionInstrumentation({ - applyCustomAttributes: applyCustomAttrs, - }); + + let disableInstrumentations: () => void; + beforeEach(() => { - registerInstrumentations({ - instrumentations: [instr], + disableInstrumentations = registerInstrumentations({ + instrumentations: [ + new ExceptionInstrumentation({ + applyCustomAttributes: applyCustomAttrs, + }), + ], }); - - instr.enable(); }); afterEach(() => { - instr.disable(); + disableInstrumentations(); exporter.reset(); }); + it('should add custom attributes to the event', async () => { setTimeout(() => { throwErr('Something happened!');