@@ -2,7 +2,7 @@ import type { Instrumentation } from '@opentelemetry/instrumentation';
22// When importing CJS modules into an ESM module, we cannot import the named exports directly.
33import * as prismaInstrumentation from '@prisma/instrumentation' ;
44import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN , consoleSandbox , defineIntegration , spanToJSON } from '@sentry/core' ;
5- import { generateInstrumentOnce , instrumentWhenWrapped } from '../../otel/instrument' ;
5+ import { generateInstrumentOnce } from '../../otel/instrument' ;
66import type { PrismaV5TracingHelper } from './prisma/vendor/v5-tracing-helper' ;
77import type { PrismaV6TracingHelper } from './prisma/vendor/v6-tracing-helper' ;
88
@@ -19,6 +19,18 @@ function isPrismaV6TracingHelper(helper: unknown): helper is PrismaV6TracingHelp
1919 return ! ! helper && typeof helper === 'object' && 'dispatchEngineSpans' in helper ;
2020}
2121
22+ function getPrismaTracingHelper ( ) : unknown | undefined {
23+ const prismaInstrumentationObject = ( globalThis as Record < string , unknown > ) . PRISMA_INSTRUMENTATION ;
24+ const prismaTracingHelper =
25+ prismaInstrumentationObject &&
26+ typeof prismaInstrumentationObject === 'object' &&
27+ 'helper' in prismaInstrumentationObject
28+ ? prismaInstrumentationObject . helper
29+ : undefined ;
30+
31+ return prismaTracingHelper ;
32+ }
33+
2234class SentryPrismaInteropInstrumentation extends EsmInteropPrismaInstrumentation {
2335 public constructor ( ) {
2436 super ( ) ;
@@ -30,13 +42,7 @@ class SentryPrismaInteropInstrumentation extends EsmInteropPrismaInstrumentation
3042 // The PrismaIntegration (super class) defines a global variable `global["PRISMA_INSTRUMENTATION"]` when `enable()` is called. This global variable holds a "TracingHelper" which Prisma uses internally to create tracing data. It's their way of not depending on OTEL with their main package. The sucky thing is, prisma broke the interface of the tracing helper with the v6 major update. This means that if you use Prisma 5 with the v6 instrumentation (or vice versa) Prisma just blows up, because tries to call methods on the helper that no longer exist.
3143 // Because we actually want to use the v6 instrumentation and not blow up in Prisma 5 user's faces, what we're doing here is backfilling the v5 method (`createEngineSpan`) with a noop so that no longer crashes when it attempts to call that function.
3244 // We still won't fully emit all the spans, but this could potentially be implemented in the future.
33- const prismaInstrumentationObject = ( globalThis as Record < string , unknown > ) . PRISMA_INSTRUMENTATION ;
34- const prismaTracingHelper =
35- prismaInstrumentationObject &&
36- typeof prismaInstrumentationObject === 'object' &&
37- 'helper' in prismaInstrumentationObject
38- ? prismaInstrumentationObject . helper
39- : undefined ;
45+ const prismaTracingHelper = getPrismaTracingHelper ( ) ;
4046
4147 let emittedWarning = false ;
4248
@@ -113,34 +119,35 @@ export const prismaIntegration = defineIntegration(
113119 */
114120 prismaInstrumentation ?: Instrumentation ;
115121 } = { } ) => {
116- let instrumentationWrappedCallback : undefined | ( ( callback : ( ) => void ) => void ) ;
117-
118122 return {
119123 name : INTEGRATION_NAME ,
120124 setupOnce ( ) {
121- const instrumentation = instrumentPrisma ( { prismaInstrumentation } ) ;
122- instrumentationWrappedCallback = instrumentWhenWrapped ( instrumentation ) ;
125+ instrumentPrisma ( { prismaInstrumentation } ) ;
123126 } ,
124127 setup ( client ) {
125- instrumentationWrappedCallback ?.( ( ) =>
126- client . on ( 'spanStart' , span => {
127- const spanJSON = spanToJSON ( span ) ;
128- if ( spanJSON . description ?. startsWith ( 'prisma:' ) ) {
129- span . setAttribute ( SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN , 'auto.db.otel.prisma' ) ;
130- }
131-
132- // Make sure we use the query text as the span name, for ex. SELECT * FROM "User" WHERE "id" = $1
133- if ( spanJSON . description === 'prisma:engine:db_query' && spanJSON . data [ 'db.query.text' ] ) {
134- span . updateName ( spanJSON . data [ 'db.query.text' ] as string ) ;
135- }
136-
137- // In Prisma v5.22+, the `db.system` attribute is automatically set
138- // On older versions, this is missing, so we add it here
139- if ( spanJSON . description === 'prisma:engine:db_query' && ! spanJSON . data [ 'db.system' ] ) {
140- span . setAttribute ( 'db.system' , 'prisma' ) ;
141- }
142- } ) ,
143- ) ;
128+ // If no tracing helper exists, we skip any work here
129+ // this means that prisma is not being used
130+ if ( ! getPrismaTracingHelper ( ) ) {
131+ return ;
132+ }
133+
134+ client . on ( 'spanStart' , span => {
135+ const spanJSON = spanToJSON ( span ) ;
136+ if ( spanJSON . description ?. startsWith ( 'prisma:' ) ) {
137+ span . setAttribute ( SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN , 'auto.db.otel.prisma' ) ;
138+ }
139+
140+ // Make sure we use the query text as the span name, for ex. SELECT * FROM "User" WHERE "id" = $1
141+ if ( spanJSON . description === 'prisma:engine:db_query' && spanJSON . data [ 'db.query.text' ] ) {
142+ span . updateName ( spanJSON . data [ 'db.query.text' ] as string ) ;
143+ }
144+
145+ // In Prisma v5.22+, the `db.system` attribute is automatically set
146+ // On older versions, this is missing, so we add it here
147+ if ( spanJSON . description === 'prisma:engine:db_query' && ! spanJSON . data [ 'db.system' ] ) {
148+ span . setAttribute ( 'db.system' , 'prisma' ) ;
149+ }
150+ } ) ;
144151 } ,
145152 } ;
146153 } ,
0 commit comments