Skip to content

Commit 4b8472e

Browse files
committed
feat(firebase): add error handling and rename hooks to align with otel standards
1 parent f201221 commit 4b8472e

File tree

6 files changed

+172
-80
lines changed

6 files changed

+172
-80
lines changed

dev-packages/e2e-tests/test-applications/node-firebase/functions/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ export const helloWorld = onRequest(async (request, response) => {
1818
response.send('Hello from Firebase!');
1919
});
2020

21+
export const unhandeledError = onRequest(async (request, response) => {
22+
throw new Error('There is an error!');
23+
});
24+
2125
export const onCallSomething = onRequest(async (request, response) => {
2226
const data = {
2327
name: request.body?.name || 'Sample Document',

dev-packages/e2e-tests/test-applications/node-firebase/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
},
2222
"devDependencies": {
2323
"@playwright/test": "~1.53.2",
24-
"@sentry-internal/test-utils": "link:../../../test-utils"
24+
"@sentry-internal/test-utils": "link:../../../test-utils",
25+
"firebase": "^12.0.0"
2526
},
2627
"volta": {
2728
"extends": "../../package.json"

dev-packages/e2e-tests/test-applications/node-firebase/tests/functions.test.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect, test } from '@playwright/test';
2-
import { waitForTransaction } from '@sentry-internal/test-utils';
2+
import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';
33

44
test('should only call the function once without any extra calls', async () => {
55
const serverTransactionPromise = waitForTransaction('node-firebase', span => {
@@ -15,6 +15,7 @@ test('should only call the function once without any extra calls', async () => {
1515
expect.objectContaining({
1616
trace: expect.objectContaining({
1717
data: {
18+
'cloud.project_id': 'demo-functions',
1819
'faas.name': 'helloWorld',
1920
'faas.provider': 'firebase',
2021
'faas.trigger': 'http.request',
@@ -34,6 +35,35 @@ test('should only call the function once without any extra calls', async () => {
3435
);
3536
});
3637

38+
test('should send failed transaction when the function fails', async () => {
39+
const errorEventPromise = waitForError('node-firebase', () => true);
40+
const serverTransactionPromise = waitForTransaction('node-firebase', span => {
41+
return !!span.transaction;
42+
});
43+
44+
await fetch(`http://localhost:5001/demo-functions/default/unhandeledError`);
45+
46+
const transactionEvent = await serverTransactionPromise;
47+
const errorEvent = await errorEventPromise;
48+
49+
expect(transactionEvent.transaction).toEqual('firebase.function.http.request');
50+
expect(transactionEvent.contexts?.trace?.trace_id).toEqual(errorEvent.contexts?.trace?.trace_id);
51+
expect(errorEvent).toMatchObject({
52+
exception: {
53+
values: [
54+
{
55+
type: 'Error',
56+
value: 'There is an error!',
57+
mechanism: {
58+
type: 'auto.firebase.otel.functions',
59+
handled: false,
60+
},
61+
},
62+
],
63+
},
64+
});
65+
});
66+
3767
test('should create a document and trigger onDocumentCreated and another with authContext', async () => {
3868
const serverTransactionPromise = waitForTransaction('node-firebase', span => {
3969
return span.transaction === 'firebase.function.http.request';
@@ -62,6 +92,7 @@ test('should create a document and trigger onDocumentCreated and another with au
6292
expect(transactionEvent.transaction).toEqual('firebase.function.http.request');
6393
expect(transactionEvent.contexts?.trace).toEqual({
6494
data: {
95+
'cloud.project_id': 'demo-functions',
6596
'faas.name': 'onCallSomething',
6697
'faas.provider': 'firebase',
6798
'faas.trigger': 'http.request',
@@ -80,6 +111,7 @@ test('should create a document and trigger onDocumentCreated and another with au
80111
expect(transactionEvent.spans).toHaveLength(3);
81112
expect(transactionEventOnDocumentCreate.contexts?.trace).toEqual({
82113
data: {
114+
'cloud.project_id': 'demo-functions',
83115
'faas.name': 'onDocumentCreate',
84116
'faas.provider': 'firebase',
85117
'faas.trigger': 'firestore.document.created',
@@ -98,6 +130,7 @@ test('should create a document and trigger onDocumentCreated and another with au
98130
expect(transactionEventOnDocumentCreate.spans).toHaveLength(2);
99131
expect(transactionEventOnDocumentWithAuthContextCreate.contexts?.trace).toEqual({
100132
data: {
133+
'cloud.project_id': 'demo-functions',
101134
'faas.name': 'onDocumentCreateWithAuthContext',
102135
'faas.provider': 'firebase',
103136
'faas.trigger': 'firestore.document.created',

packages/node/src/integrations/tracing/firebase/firebase.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { IntegrationFn } from '@sentry/core';
2-
import { defineIntegration, SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core';
2+
import { captureException, defineIntegration, flush, SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core';
33
import { addOriginToSpan, generateInstrumentOnce } from '@sentry/node-core';
44
import { type FirebaseInstrumentationConfig, FirebaseInstrumentation } from './otel';
55

@@ -11,10 +11,23 @@ const config: FirebaseInstrumentationConfig = {
1111

1212
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'db.query');
1313
},
14-
functionsSpanCreationHook: span => {
15-
addOriginToSpan(span, 'auto.firebase.otel.functions');
14+
functions: {
15+
requestHook: span => {
16+
addOriginToSpan(span, 'auto.firebase.otel.functions');
1617

17-
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'http.request');
18+
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'http.request');
19+
},
20+
errorHook: async (_, error) => {
21+
if (error) {
22+
captureException(error, {
23+
mechanism: {
24+
type: 'auto.firebase.otel.functions',
25+
handled: false,
26+
},
27+
});
28+
await flush(2000);
29+
}
30+
},
1831
},
1932
};
2033

0 commit comments

Comments
 (0)