Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(server-utils): Migrate FirebaseInstrumentation to orchestrion#22141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
195e8b34fb81b2de43341e9128bb69cd11f230353e9c65bd0f5e200bFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,22 @@ | ||
| import * as Sentry from '@sentry/node'; | ||
| // When `E2E_ORCHESTRION=true`, exercise the diagnostics-channel injection path (the orchestrion-based | ||
| // `Firebase` integration) instead of the OTel one. Opting in before `init()` is enough: this file is | ||
| // imported before `app.ts` imports `firebase/firestore/lite`, so the channel-injection hooks are | ||
| // installed before firestore loads. | ||
| const useOrchestrion = process.env.E2E_ORCHESTRION === 'true'; | ||
| if (useOrchestrion) { | ||
| Sentry.experimentalUseDiagnosticsChannelInjection(); | ||
| } | ||
| Sentry.init({ | ||
| dsn: 'https://public@dsn.ingest.sentry.io/1337', | ||
| release: '1.0', | ||
| tracesSampleRate: 1.0, | ||
| integrations: [Sentry.firebaseIntegration()], | ||
| integrations: useOrchestrion | ||
| ? [Sentry.diagnosticsChannelInjectionIntegrations().firebaseIntegration()] | ||
| : [Sentry.firebaseIntegration()], | ||
| defaultIntegrations: false, | ||
| tunnel: `http://localhost:3031/`, // proxy server | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,105 +1,47 @@ | ||
| import { expect, test } from '@playwright/test'; | ||
| import { waitForTransaction } from '@sentry-internal/test-utils'; | ||
| const spanAddDoc = expect.objectContaining({ | ||
| description: 'addDoc cities', | ||
| data: expect.objectContaining({ | ||
| // The same suite runs against both the OTel integration and (with `E2E_ORCHESTRION=true`) the | ||
| // orchestrion diagnostics-channel one. The spans are identical apart from the origin — and the | ||
| // orchestrion spans are Sentry-native, so they carry no OTel-only `otel.kind` attribute. | ||
| const orchestrion = process.env.E2E_ORCHESTRION === 'true'; | ||
| const origin = orchestrion ? 'auto.firebase.orchestrion.firestore' : 'auto.firebase.otel.firestore'; | ||
| function firestoreSpan(operation: string): unknown { | ||
| const data: Record<string, unknown> = { | ||
| 'db.collection.name': 'cities', | ||
| 'db.namespace': '[DEFAULT]', | ||
| 'db.operation.name': 'addDoc', | ||
| 'db.operation.name': operation, | ||
| 'db.system.name': 'firebase.firestore', | ||
| 'firebase.firestore.options.projectId': 'sentry-15d85', | ||
| 'firebase.firestore.type': 'collection', | ||
| 'otel.kind': 'CLIENT', | ||
| 'server.address': '127.0.0.1', | ||
| 'server.port': 8080, | ||
| 'sentry.origin': 'auto.firebase.otel.firestore', | ||
| 'sentry.origin': origin, | ||
| 'sentry.op': 'db.query', | ||
| }), | ||
| op: 'db.query', | ||
| origin: 'auto.firebase.otel.firestore', | ||
| parent_span_id: expect.any(String), | ||
| trace_id: expect.any(String), | ||
| span_id: expect.any(String), | ||
| timestamp: expect.any(Number), | ||
| start_timestamp: expect.any(Number), | ||
| status: 'ok', | ||
| }); | ||
| const spanSetDocs = expect.objectContaining({ | ||
| description: 'setDoc cities', | ||
| data: expect.objectContaining({ | ||
| 'db.collection.name': 'cities', | ||
| 'db.namespace': '[DEFAULT]', | ||
| 'db.operation.name': 'setDoc', | ||
| 'db.system.name': 'firebase.firestore', | ||
| 'firebase.firestore.options.projectId': 'sentry-15d85', | ||
| 'firebase.firestore.type': 'collection', | ||
| 'otel.kind': 'CLIENT', | ||
| 'server.address': '127.0.0.1', | ||
| 'server.port': 8080, | ||
| 'sentry.origin': 'auto.firebase.otel.firestore', | ||
| 'sentry.op': 'db.query', | ||
| }), | ||
| op: 'db.query', | ||
| origin: 'auto.firebase.otel.firestore', | ||
| parent_span_id: expect.any(String), | ||
| trace_id: expect.any(String), | ||
| span_id: expect.any(String), | ||
| timestamp: expect.any(Number), | ||
| start_timestamp: expect.any(Number), | ||
| status: 'ok', | ||
| }); | ||
| const spanGetDocs = expect.objectContaining({ | ||
| description: 'getDocs cities', | ||
| data: expect.objectContaining({ | ||
| 'db.collection.name': 'cities', | ||
| 'db.namespace': '[DEFAULT]', | ||
| 'db.operation.name': 'getDocs', | ||
| 'db.system.name': 'firebase.firestore', | ||
| 'firebase.firestore.options.projectId': 'sentry-15d85', | ||
| 'firebase.firestore.type': 'collection', | ||
| 'otel.kind': 'CLIENT', | ||
| 'server.address': '127.0.0.1', | ||
| 'server.port': 8080, | ||
| 'sentry.origin': 'auto.firebase.otel.firestore', | ||
| 'sentry.op': 'db.query', | ||
| }), | ||
| op: 'db.query', | ||
| origin: 'auto.firebase.otel.firestore', | ||
| parent_span_id: expect.any(String), | ||
| trace_id: expect.any(String), | ||
| span_id: expect.any(String), | ||
| timestamp: expect.any(Number), | ||
| start_timestamp: expect.any(Number), | ||
| status: 'ok', | ||
| }); | ||
| }; | ||
| if (!orchestrion) { | ||
| data['otel.kind'] = 'CLIENT'; | ||
| } | ||
| return expect.objectContaining({ | ||
| description: `${operation} cities`, | ||
| data: expect.objectContaining(data), | ||
cursor[bot] marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| op: 'db.query', | ||
| origin, | ||
| parent_span_id: expect.any(String), | ||
| trace_id: expect.any(String), | ||
| span_id: expect.any(String), | ||
| timestamp: expect.any(Number), | ||
| start_timestamp: expect.any(Number), | ||
| status: 'ok', | ||
| }); | ||
| } | ||
| const spanDeleteDoc = expect.objectContaining({ | ||
| description: 'deleteDoc cities', | ||
| data: expect.objectContaining({ | ||
| 'db.collection.name': 'cities', | ||
| 'db.namespace': '[DEFAULT]', | ||
| 'db.operation.name': 'deleteDoc', | ||
| 'db.system.name': 'firebase.firestore', | ||
| 'firebase.firestore.options.projectId': 'sentry-15d85', | ||
| 'firebase.firestore.type': 'collection', | ||
| 'otel.kind': 'CLIENT', | ||
| 'server.address': '127.0.0.1', | ||
| 'server.port': 8080, | ||
| 'sentry.origin': 'auto.firebase.otel.firestore', | ||
| 'sentry.op': 'db.query', | ||
| }), | ||
| op: 'db.query', | ||
| origin: 'auto.firebase.otel.firestore', | ||
| parent_span_id: expect.any(String), | ||
| trace_id: expect.any(String), | ||
| span_id: expect.any(String), | ||
| timestamp: expect.any(Number), | ||
| start_timestamp: expect.any(Number), | ||
| status: 'ok', | ||
| }); | ||
| const spanAddDoc = firestoreSpan('addDoc'); | ||
| const spanSetDocs = firestoreSpan('setDoc'); | ||
| const spanGetDocs = firestoreSpan('getDocs'); | ||
| const spanDeleteDoc = firestoreSpan('deleteDoc'); | ||
| test('should add, set, get and delete document', async ({ baseURL, page }) => { | ||
| const serverTransactionPromise = waitForTransaction('node-firebase', span => { | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,10 @@ | ||
| import { debug } from '@sentry/core'; | ||
| import * as path from 'path'; | ||
| import { getOrchestrionLoaderPath, getSentryInstrumentations } from '@sentry/server-utils/orchestrion/webpack'; | ||
| import { | ||
| getOrchestrionLoaderPath, | ||
| getSentryInstrumentations, | ||
| serializeInstrumentations, | ||
| } from '@sentry/server-utils/orchestrion/webpack'; | ||
| import type { VercelCronsConfig } from '../../common/types'; | ||
| import type { RouteManifest } from '../manifest/types'; | ||
| import type { | ||
| @@ -141,8 +145,10 @@ function maybeAddOrchestrionRule( | ||
| loaders: [ | ||
| { | ||
| loader: getOrchestrionLoaderPath(), | ||
| // `instrumentations` is JSON-serializable | ||
| options: { instrumentations: getSentryInstrumentations() as unknown as JSONValue[] }, | ||
| // Turbopack JSON-serializes loader options, so a RegExp `filePath` must be encoded first. | ||
| options: { | ||
| instrumentations: serializeInstrumentations(getSentryInstrumentations()) as unknown as JSONValue[], | ||
| }, | ||
nicohrubec marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| }, | ||
| ], | ||
| }, | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| // Minimal structural types inlined from `firebase/app` and `firebase/firestore`, kept just wide enough | ||
| // for the attributes the subscriber reads off a Firestore reference. Inlined (rather than imported) so | ||
| // `@sentry/server-utils` needs no firebase dependency. | ||
| export interface FirebaseOptions { | ||
| [key: string]: unknown; | ||
| apiKey?: string; | ||
| projectId?: string; | ||
| appId?: string; | ||
| messagingSenderId?: string; | ||
| storageBucket?: string; | ||
| } | ||
| export interface FirebaseApp { | ||
| name: string; | ||
| options: FirebaseOptions; | ||
| } | ||
| export interface FirestoreSettings { | ||
| host?: string; | ||
| } | ||
| interface FirestoreLike { | ||
| app: FirebaseApp; | ||
| toJSON: () => { settings?: FirestoreSettings }; | ||
| } | ||
| export interface DocumentReference { | ||
| id: string; | ||
| firestore: FirestoreLike; | ||
| type: string; | ||
| path: string; | ||
| parent: CollectionReference | null; | ||
| } | ||
| export interface CollectionReference { | ||
| id: string; | ||
| firestore: FirestoreLike; | ||
| type: string; | ||
| path: string; | ||
| parent: DocumentReference | null; | ||
| } | ||
| export type FirestoreReference = CollectionReference | DocumentReference; |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.