11import * as diagnosticsChannel from 'node:diagnostics_channel' ;
22import type { IntegrationFn } from '@sentry/core' ;
3- import { debug , defineIntegration , waitForTracingChannelBinding } from '@sentry/core' ;
4- import { DEBUG_BUILD } from '../../../debug-build' ;
5- import { CHANNELS } from '../../../orchestrion/channels' ;
6- import { bindTracingChannelToSpan } from '../../../tracing-channel' ;
7- import type { FirestoreReference } from './firestore-types' ;
8- import { startFirestoreSpan } from './firestore' ;
9- import { wrapFunctionsRegistration } from './functions' ;
3+ import { defineIntegration , waitForTracingChannelBinding } from '@sentry/core' ;
4+ import { instrumentFirebase } from './instrumentation' ;
105
11- // NOTE: this uses the same name as the OTel integration by design. When enabled, the OTel 'Firebase'
12- // integration is omitted from the default set.
136const INTEGRATION_NAME = 'Firebase' as const ;
147
15- // The context orchestrion's transform attaches to each firestore channel: `arguments` is the live args
16- // of the wrapped `addDoc`/`getDocs`/`setDoc`/`deleteDoc` call, `arguments[0]` the reference.
17- interface FirestoreChannelContext {
18- arguments : unknown [ ] ;
19- self ?: unknown ;
20- result ?: unknown ;
21- error ?: unknown ;
22- }
23-
24- // The firestore operations, keyed by channel. `useParent` mirrors the OTel integration: `setDoc`/
25- // `deleteDoc` take a *document* reference but the span is named after its parent *collection*.
26- const FIRESTORE_OPERATIONS : Array < { channel : string ; spanName : string ; useParent : boolean } > = [
27- { channel : CHANNELS . FIREBASE_FIRESTORE_ADD_DOC , spanName : 'addDoc' , useParent : false } ,
28- { channel : CHANNELS . FIREBASE_FIRESTORE_GET_DOCS , spanName : 'getDocs' , useParent : false } ,
29- { channel : CHANNELS . FIREBASE_FIRESTORE_SET_DOC , spanName : 'setDoc' , useParent : true } ,
30- { channel : CHANNELS . FIREBASE_FIRESTORE_DELETE_DOC , spanName : 'deleteDoc' , useParent : true } ,
31- ] ;
32-
33- // The firebase-functions triggers, keyed by channel. The value is the faas trigger type used for the
34- // span name (`firebase.function.<trigger>`) and `faas.trigger` attribute.
35- const FUNCTIONS_TRIGGERS : Array < { channel : string ; triggerType : string } > = [
36- { channel : CHANNELS . FIREBASE_FUNCTIONS_HTTP_REQUEST , triggerType : 'http.request' } ,
37- { channel : CHANNELS . FIREBASE_FUNCTIONS_HTTP_CALL , triggerType : 'http.call' } ,
38- { channel : CHANNELS . FIREBASE_FUNCTIONS_FIRESTORE_CREATED , triggerType : 'firestore.document.created' } ,
39- { channel : CHANNELS . FIREBASE_FUNCTIONS_FIRESTORE_UPDATED , triggerType : 'firestore.document.updated' } ,
40- { channel : CHANNELS . FIREBASE_FUNCTIONS_FIRESTORE_DELETED , triggerType : 'firestore.document.deleted' } ,
41- { channel : CHANNELS . FIREBASE_FUNCTIONS_FIRESTORE_WRITTEN , triggerType : 'firestore.document.written' } ,
42- { channel : CHANNELS . FIREBASE_FUNCTIONS_SCHEDULER , triggerType : 'scheduler.scheduled' } ,
43- { channel : CHANNELS . FIREBASE_FUNCTIONS_STORAGE_FINALIZED , triggerType : 'storage.object.finalized' } ,
44- { channel : CHANNELS . FIREBASE_FUNCTIONS_STORAGE_ARCHIVED , triggerType : 'storage.object.archived' } ,
45- { channel : CHANNELS . FIREBASE_FUNCTIONS_STORAGE_DELETED , triggerType : 'storage.object.deleted' } ,
46- { channel : CHANNELS . FIREBASE_FUNCTIONS_STORAGE_METADATA_UPDATED , triggerType : 'storage.object.metadataUpdated' } ,
47- ] ;
48-
49- const NOOP = ( ) : void => { } ;
50-
51- /**
52- * Runs a span-building callback so a throw inside it can never break the user's firebase call: these run
53- * inside the `tracingChannel(...)` machinery wrapping the real function, where an unguarded throw would
54- * propagate into the traced call.
55- */
56- function safe < T > ( fn : ( ) => T ) : T | undefined {
57- try {
58- return fn ( ) ;
59- } catch ( error ) {
60- DEBUG_BUILD && debug . warn ( '[orchestrion:firebase] error handling channel event' , error ) ;
61- return undefined ;
62- }
63- }
64-
658const _firebaseChannelIntegration = ( ( ) => {
669 return {
6710 name : INTEGRATION_NAME ,
@@ -72,31 +15,7 @@ const _firebaseChannelIntegration = (() => {
7215 }
7316
7417 waitForTracingChannelBinding ( ( ) => {
75- for ( const { channel, spanName, useParent } of FIRESTORE_OPERATIONS ) {
76- bindTracingChannelToSpan ( diagnosticsChannel . tracingChannel < FirestoreChannelContext > ( channel ) , data =>
77- safe ( ( ) => {
78- const reference = data . arguments [ 0 ] as FirestoreReference | undefined ;
79- if ( ! reference ) {
80- return undefined ;
81- }
82- const spanReference = useParent ? reference . parent || reference : reference ;
83- return startFirestoreSpan ( spanName , spanReference ) ;
84- } ) ,
85- ) ;
86- }
87-
88- for ( const { channel, triggerType } of FUNCTIONS_TRIGGERS ) {
89- // Functions are wrapped, not span-bound: the handler runs long after this synchronous
90- // registration call, so we only rewrap the handler argument here (in `start`) and open the
91- // span inside that wrapper. The other lifecycle events are irrelevant, so no-op them.
92- diagnosticsChannel . tracingChannel ( channel ) . subscribe ( {
93- start : data => void safe ( ( ) => wrapFunctionsRegistration ( data as { arguments : unknown [ ] } , triggerType ) ) ,
94- end : NOOP ,
95- asyncStart : NOOP ,
96- asyncEnd : NOOP ,
97- error : NOOP ,
98- } ) ;
99- }
18+ instrumentFirebase ( ) ;
10019 } ) ;
10120 } ,
10221 } ;
0 commit comments