From cae1f0f4e5fcaa2d60101c66270ed7332dbb7a9e Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 31 Jul 2025 11:03:02 +0200 Subject: [PATCH 1/2] ref(google-cloud-serverless): Add `mechanism.type` to captured errors --- .../google-cloud-serverless/src/gcpfunction/cloud_events.ts | 4 ++-- packages/google-cloud-serverless/src/gcpfunction/events.ts | 4 ++-- packages/google-cloud-serverless/src/gcpfunction/http.ts | 2 +- packages/google-cloud-serverless/src/utils.ts | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/google-cloud-serverless/src/gcpfunction/cloud_events.ts b/packages/google-cloud-serverless/src/gcpfunction/cloud_events.ts index a5e9f886165d..b230c0dab865 100644 --- a/packages/google-cloud-serverless/src/gcpfunction/cloud_events.ts +++ b/packages/google-cloud-serverless/src/gcpfunction/cloud_events.ts @@ -49,7 +49,7 @@ function _wrapCloudEventFunction( const newCallback = domainify((...args: unknown[]) => { if (args[0] !== null && args[0] !== undefined) { - captureException(args[0], scope => markEventUnhandled(scope)); + captureException(args[0], scope => markEventUnhandled(scope, 'google-cloud-serverless.cloud_event')); } span.end(); @@ -69,7 +69,7 @@ function _wrapCloudEventFunction( return handleCallbackErrors( () => (fn as CloudEventFunctionWithCallback)(context, newCallback), err => { - captureException(err, scope => markEventUnhandled(scope)); + captureException(err, scope => markEventUnhandled(scope, 'google-cloud-serverless.cloud_event')); }, ); } diff --git a/packages/google-cloud-serverless/src/gcpfunction/events.ts b/packages/google-cloud-serverless/src/gcpfunction/events.ts index 2fece298ea05..ced4d96f27d4 100644 --- a/packages/google-cloud-serverless/src/gcpfunction/events.ts +++ b/packages/google-cloud-serverless/src/gcpfunction/events.ts @@ -52,7 +52,7 @@ function _wrapEventFunction const newCallback = domainify((...args: unknown[]) => { if (args[0] !== null && args[0] !== undefined) { - captureException(args[0], scope => markEventUnhandled(scope)); + captureException(args[0], scope => markEventUnhandled(scope, 'google-cloud-serverless.event')); } span.end(); @@ -72,7 +72,7 @@ function _wrapEventFunction return handleCallbackErrors( () => (fn as EventFunctionWithCallback)(data, context, newCallback), err => { - captureException(err, scope => markEventUnhandled(scope)); + captureException(err, scope => markEventUnhandled(scope, 'google-cloud-serverless.event')); }, ); } diff --git a/packages/google-cloud-serverless/src/gcpfunction/http.ts b/packages/google-cloud-serverless/src/gcpfunction/http.ts index 46683372fa53..ea5533f1f946 100644 --- a/packages/google-cloud-serverless/src/gcpfunction/http.ts +++ b/packages/google-cloud-serverless/src/gcpfunction/http.ts @@ -78,7 +78,7 @@ function _wrapHttpFunction(fn: HttpFunction, options: Partial): return handleCallbackErrors( () => fn(req, res), err => { - captureException(err, scope => markEventUnhandled(scope)); + captureException(err, scope => markEventUnhandled(scope, 'google-cloud-serverless.http')); }, ); }, diff --git a/packages/google-cloud-serverless/src/utils.ts b/packages/google-cloud-serverless/src/utils.ts index cdefdb460ee3..88c11f0274c8 100644 --- a/packages/google-cloud-serverless/src/utils.ts +++ b/packages/google-cloud-serverless/src/utils.ts @@ -43,9 +43,9 @@ export function proxyFunction R>( /** * Marks an event as unhandled by adding a span processor to the passed scope. */ -export function markEventUnhandled(scope: Scope): Scope { +export function markEventUnhandled(scope: Scope, type = 'google-cloud-serverless'): Scope { scope.addEventProcessor(event => { - addExceptionMechanism(event, { handled: false }); + addExceptionMechanism(event, { handled: false, type }); return event; }); From 11ceebffa3162140ec1522c6c9224c347dc11a20 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Mon, 4 Aug 2025 12:40:14 +0200 Subject: [PATCH 2/2] trace origin scheme, adjust tests, add assertions --- .../src/gcpfunction/cloud_events.ts | 4 +- .../src/gcpfunction/events.ts | 4 +- .../src/gcpfunction/http.ts | 2 +- packages/google-cloud-serverless/src/utils.ts | 2 +- .../test/gcpfunction/cloud_event.test.ts | 51 +++++++++++++++++++ .../test/gcpfunction/events.test.ts | 2 +- .../test/gcpfunction/http.test.ts | 13 +++++ 7 files changed, 71 insertions(+), 7 deletions(-) diff --git a/packages/google-cloud-serverless/src/gcpfunction/cloud_events.ts b/packages/google-cloud-serverless/src/gcpfunction/cloud_events.ts index b230c0dab865..538da163004c 100644 --- a/packages/google-cloud-serverless/src/gcpfunction/cloud_events.ts +++ b/packages/google-cloud-serverless/src/gcpfunction/cloud_events.ts @@ -49,7 +49,7 @@ function _wrapCloudEventFunction( const newCallback = domainify((...args: unknown[]) => { if (args[0] !== null && args[0] !== undefined) { - captureException(args[0], scope => markEventUnhandled(scope, 'google-cloud-serverless.cloud_event')); + captureException(args[0], scope => markEventUnhandled(scope, 'auto.function.serverless.gcp_cloud_event')); } span.end(); @@ -69,7 +69,7 @@ function _wrapCloudEventFunction( return handleCallbackErrors( () => (fn as CloudEventFunctionWithCallback)(context, newCallback), err => { - captureException(err, scope => markEventUnhandled(scope, 'google-cloud-serverless.cloud_event')); + captureException(err, scope => markEventUnhandled(scope, 'auto.function.serverless.gcp_cloud_event')); }, ); } diff --git a/packages/google-cloud-serverless/src/gcpfunction/events.ts b/packages/google-cloud-serverless/src/gcpfunction/events.ts index ced4d96f27d4..bd320e7ca870 100644 --- a/packages/google-cloud-serverless/src/gcpfunction/events.ts +++ b/packages/google-cloud-serverless/src/gcpfunction/events.ts @@ -52,7 +52,7 @@ function _wrapEventFunction const newCallback = domainify((...args: unknown[]) => { if (args[0] !== null && args[0] !== undefined) { - captureException(args[0], scope => markEventUnhandled(scope, 'google-cloud-serverless.event')); + captureException(args[0], scope => markEventUnhandled(scope, 'auto.function.serverless.gcp_event')); } span.end(); @@ -72,7 +72,7 @@ function _wrapEventFunction return handleCallbackErrors( () => (fn as EventFunctionWithCallback)(data, context, newCallback), err => { - captureException(err, scope => markEventUnhandled(scope, 'google-cloud-serverless.event')); + captureException(err, scope => markEventUnhandled(scope, 'auto.function.serverless.gcp_event')); }, ); } diff --git a/packages/google-cloud-serverless/src/gcpfunction/http.ts b/packages/google-cloud-serverless/src/gcpfunction/http.ts index ea5533f1f946..b5d5f011e3a8 100644 --- a/packages/google-cloud-serverless/src/gcpfunction/http.ts +++ b/packages/google-cloud-serverless/src/gcpfunction/http.ts @@ -78,7 +78,7 @@ function _wrapHttpFunction(fn: HttpFunction, options: Partial): return handleCallbackErrors( () => fn(req, res), err => { - captureException(err, scope => markEventUnhandled(scope, 'google-cloud-serverless.http')); + captureException(err, scope => markEventUnhandled(scope, 'auto.function.serverless.gcp_http')); }, ); }, diff --git a/packages/google-cloud-serverless/src/utils.ts b/packages/google-cloud-serverless/src/utils.ts index 88c11f0274c8..d1d1b672bbae 100644 --- a/packages/google-cloud-serverless/src/utils.ts +++ b/packages/google-cloud-serverless/src/utils.ts @@ -43,7 +43,7 @@ export function proxyFunction R>( /** * Marks an event as unhandled by adding a span processor to the passed scope. */ -export function markEventUnhandled(scope: Scope, type = 'google-cloud-serverless'): Scope { +export function markEventUnhandled(scope: Scope, type: string): Scope { scope.addEventProcessor(event => { addExceptionMechanism(event, { handled: false, type }); return event; diff --git a/packages/google-cloud-serverless/test/gcpfunction/cloud_event.test.ts b/packages/google-cloud-serverless/test/gcpfunction/cloud_event.test.ts index 4d34e630814c..0cb6d502e934 100644 --- a/packages/google-cloud-serverless/test/gcpfunction/cloud_event.test.ts +++ b/packages/google-cloud-serverless/test/gcpfunction/cloud_event.test.ts @@ -134,6 +134,19 @@ describe('wrapCloudEventFunction', () => { expect(mockStartSpanManual).toBeCalledWith(fakeTransactionContext, expect.any(Function)); expect(mockCaptureException).toBeCalledWith(error, expect.any(Function)); + + const scopeFunction = mockCaptureException.mock.calls[0][1]; + const event: Event = { exception: { values: [{}] } }; + let evtProcessor: ((e: Event) => Event) | undefined = undefined; + scopeFunction({ addEventProcessor: vi.fn().mockImplementation(proc => (evtProcessor = proc)) }); + + expect(evtProcessor).toBeInstanceOf(Function); + // @ts-expect-error just mocking around... + expect(evtProcessor(event).exception.values[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.serverless.gcp_cloud_event', + }); + expect(mockSpan.end).toBeCalled(); expect(mockFlush).toBeCalled(); }); @@ -158,6 +171,19 @@ describe('wrapCloudEventFunction', () => { expect(mockStartSpanManual).toBeCalledWith(fakeTransactionContext, expect.any(Function)); expect(mockCaptureException).toBeCalledWith(error, expect.any(Function)); + + const scopeFunction = mockCaptureException.mock.calls[0][1]; + const event: Event = { exception: { values: [{}] } }; + let evtProcessor: ((e: Event) => Event) | undefined = undefined; + scopeFunction({ addEventProcessor: vi.fn().mockImplementation(proc => (evtProcessor = proc)) }); + + expect(evtProcessor).toBeInstanceOf(Function); + // @ts-expect-error just mocking around... + expect(evtProcessor(event).exception.values[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.serverless.gcp_cloud_event', + }); + expect(mockSpan.end).toBeCalled(); expect(mockFlush).toBeCalled(); }); @@ -204,6 +230,19 @@ describe('wrapCloudEventFunction', () => { expect(mockStartSpanManual).toBeCalledWith(fakeTransactionContext, expect.any(Function)); expect(mockCaptureException).toBeCalledWith(error, expect.any(Function)); + + const scopeFunction = mockCaptureException.mock.calls[0][1]; + const event: Event = { exception: { values: [{}] } }; + let evtProcessor: ((e: Event) => Event) | undefined = undefined; + scopeFunction({ addEventProcessor: vi.fn().mockImplementation(proc => (evtProcessor = proc)) }); + + expect(evtProcessor).toBeInstanceOf(Function); + // @ts-expect-error just mocking around... + expect(evtProcessor(event).exception.values[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.serverless.gcp_cloud_event', + }); + expect(mockSpan.end).toBeCalled(); expect(mockFlush).toBeCalled(); }); @@ -227,6 +266,18 @@ describe('wrapCloudEventFunction', () => { expect(mockStartSpanManual).toBeCalledWith(fakeTransactionContext, expect.any(Function)); expect(mockCaptureException).toBeCalledWith(error, expect.any(Function)); + + const scopeFunction = mockCaptureException.mock.calls[0][1]; + const event: Event = { exception: { values: [{}] } }; + let evtProcessor: ((e: Event) => Event) | undefined = undefined; + scopeFunction({ addEventProcessor: vi.fn().mockImplementation(proc => (evtProcessor = proc)) }); + + expect(evtProcessor).toBeInstanceOf(Function); + // @ts-expect-error just mocking around... + expect(evtProcessor(event).exception.values[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.serverless.gcp_cloud_event', + }); }); }); diff --git a/packages/google-cloud-serverless/test/gcpfunction/events.test.ts b/packages/google-cloud-serverless/test/gcpfunction/events.test.ts index e3e8ed70897c..7cb41a3c36e9 100644 --- a/packages/google-cloud-serverless/test/gcpfunction/events.test.ts +++ b/packages/google-cloud-serverless/test/gcpfunction/events.test.ts @@ -245,7 +245,7 @@ describe('wrapEventFunction', () => { // @ts-expect-error just mocking around... expect(evtProcessor(event).exception.values[0]?.mechanism).toEqual({ handled: false, - type: 'generic', + type: 'auto.function.serverless.gcp_event', }); }); diff --git a/packages/google-cloud-serverless/test/gcpfunction/http.test.ts b/packages/google-cloud-serverless/test/gcpfunction/http.test.ts index ca7a3f4d6c60..73cd262144e0 100644 --- a/packages/google-cloud-serverless/test/gcpfunction/http.test.ts +++ b/packages/google-cloud-serverless/test/gcpfunction/http.test.ts @@ -125,6 +125,19 @@ describe('GCPFunction', () => { expect(mockStartSpanManual).toBeCalledWith(fakeTransactionContext, expect.any(Function)); expect(mockCaptureException).toBeCalledWith(error, expect.any(Function)); + + const scopeFunction = mockCaptureException.mock.calls[0][1]; + const event: Event = { exception: { values: [{}] } }; + let evtProcessor: ((e: Event) => Event) | undefined = undefined; + scopeFunction({ addEventProcessor: vi.fn().mockImplementation(proc => (evtProcessor = proc)) }); + + expect(evtProcessor).toBeInstanceOf(Function); + // @ts-expect-error just mocking around... + expect(evtProcessor(event).exception.values[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.serverless.gcp_http', + }); + expect(mockSpan.end).toBeCalled(); expect(mockFlush).toBeCalled(); });