mirror of
https://github.com/mozilla/fxa.git
synced 2025-12-28 07:03:55 +00:00
feat(payments-next): Capture error presence in CaptureTimingWithStatsD
Because: * Currently failing methods and successful methods are in the same bucket, and separating out the timing of the two distribution populations is not trivial This commit: * Adds in `error: 'true' | 'false'` to the StatsD call depending on whether the wrapped method throws an error Closes #PAY-3201
This commit is contained in:
parent
4f18ae8fb1
commit
c4d9b53b19
@ -31,6 +31,16 @@ class TestClass {
|
||||
syncMethodWithCustomHandler() {
|
||||
return 'Custom Handler';
|
||||
}
|
||||
|
||||
@CaptureTimingWithStatsD()
|
||||
syncMethodWithError() {
|
||||
throw new Error('Sync error');
|
||||
}
|
||||
|
||||
@CaptureTimingWithStatsD()
|
||||
async asyncMethodWithError() {
|
||||
throw new Error('Async error');
|
||||
}
|
||||
}
|
||||
|
||||
describe('CaptureTimingWithStatsD', () => {
|
||||
@ -54,6 +64,7 @@ describe('CaptureTimingWithStatsD', () => {
|
||||
expect.any(Number),
|
||||
{
|
||||
sourceClass: 'TestClass',
|
||||
error: 'false',
|
||||
}
|
||||
);
|
||||
});
|
||||
@ -66,6 +77,7 @@ describe('CaptureTimingWithStatsD', () => {
|
||||
expect.any(Number),
|
||||
{
|
||||
sourceClass: 'TestClass',
|
||||
error: 'false',
|
||||
}
|
||||
);
|
||||
});
|
||||
@ -75,4 +87,54 @@ describe('CaptureTimingWithStatsD', () => {
|
||||
expect(mockCallback).toHaveBeenCalledTimes(1);
|
||||
expect(mockCallback).toHaveBeenCalledWith(expect.any(Number));
|
||||
});
|
||||
|
||||
it('should track error=true for synchronous methods that throw', () => {
|
||||
expect(() => instance.syncMethodWithError()).toThrow('Sync error');
|
||||
expect(mockStatsD.timing).toHaveBeenCalledTimes(2);
|
||||
expect(mockStatsD.timing).toHaveBeenCalledWith(
|
||||
'TestClass_syncMethodWithError',
|
||||
expect.any(Number),
|
||||
{
|
||||
sourceClass: 'TestClass',
|
||||
error: 'true',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should track error=true for asynchronous methods that throw', async () => {
|
||||
await expect(instance.asyncMethodWithError()).rejects.toThrow('Async error');
|
||||
expect(mockStatsD.timing).toHaveBeenCalledTimes(2);
|
||||
expect(mockStatsD.timing).toHaveBeenCalledWith(
|
||||
'TestClass_asyncMethodWithError',
|
||||
expect.any(Number),
|
||||
{
|
||||
sourceClass: 'TestClass',
|
||||
error: 'true',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should track error=false for successful synchronous methods', () => {
|
||||
instance.syncMethod();
|
||||
expect(mockStatsD.timing).toHaveBeenCalledWith(
|
||||
'TestClass',
|
||||
expect.any(Number),
|
||||
{
|
||||
methodName: 'syncMethod',
|
||||
error: 'false',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should track error=false for successful asynchronous methods', async () => {
|
||||
await instance.asyncMethod();
|
||||
expect(mockStatsD.timing).toHaveBeenCalledWith(
|
||||
'TestClass',
|
||||
expect.any(Number),
|
||||
{
|
||||
methodName: 'asyncMethod',
|
||||
error: 'false',
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -22,39 +22,49 @@ export function CaptureTimingWithStatsD<
|
||||
const originalDef = descriptor.value;
|
||||
|
||||
descriptor.value = function (this: T, ...args: any[]) {
|
||||
const defaultHandler = function (this: T, elapsed: number) {
|
||||
const defaultHandler = function (this: T, elapsed: number, error = false) {
|
||||
this.statsd.timing(`${this.constructor.name}_${key}`, elapsed, {
|
||||
sourceClass: this.constructor.name,
|
||||
error: error.toString(),
|
||||
...options?.tags,
|
||||
});
|
||||
this.statsd.timing(this.constructor.name, elapsed, {
|
||||
methodName: key,
|
||||
error: error.toString(),
|
||||
...options?.tags,
|
||||
});
|
||||
};
|
||||
const handler = options?.handle || defaultHandler;
|
||||
|
||||
const start = performance.now();
|
||||
const originalReturnValue = originalDef.apply(this, args);
|
||||
let originalReturnValue;
|
||||
|
||||
try {
|
||||
originalReturnValue = originalDef.apply(this, args);
|
||||
} catch (err) {
|
||||
const end = performance.now();
|
||||
handler.apply(this, [end - start, true]);
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (originalReturnValue instanceof Promise) {
|
||||
return originalReturnValue
|
||||
.then((value) => {
|
||||
const end = performance.now();
|
||||
handler.apply(this, [end - start]);
|
||||
handler.apply(this, [end - start, false]);
|
||||
|
||||
return value;
|
||||
})
|
||||
.catch((err) => {
|
||||
const end = performance.now();
|
||||
handler.apply(this, [end - start]);
|
||||
handler.apply(this, [end - start, true]);
|
||||
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
const end = performance.now();
|
||||
handler.apply(this, [end - start]);
|
||||
handler.apply(this, [end - start, false]);
|
||||
|
||||
return originalReturnValue;
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user