Skip to content

Commit d0af51d

Browse files
committed
fixes
1 parent ee30339 commit d0af51d

File tree

2 files changed

+40
-32
lines changed

2 files changed

+40
-32
lines changed

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

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
33
import * as prismaInstrumentation from '@prisma/instrumentation';
44
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, consoleSandbox, defineIntegration, spanToJSON } from '@sentry/core';
5-
import { generateInstrumentOnce, instrumentWhenWrapped } from '../../otel/instrument';
5+
import { generateInstrumentOnce } from '../../otel/instrument';
66
import type { PrismaV5TracingHelper } from './prisma/vendor/v5-tracing-helper';
77
import 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+
2234
class 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
},

packages/node/test/utils/instrument.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,11 @@ describe('instrumentWhenWrapped', () => {
6565
_wrap: originalWrap,
6666
} as any;
6767

68+
const registerCallback = instrumentWhenWrapped(instrumentation);
69+
6870
// Call _wrap first
6971
instrumentation._wrap();
7072

71-
const registerCallback = instrumentWhenWrapped(instrumentation);
7273
registerCallback(callback);
7374

7475
// Callback should be called immediately

0 commit comments

Comments
 (0)