Skip to content

Commit d9138ff

Browse files
authored
feat(node/v8): Add prismaInstrumentation option to Prisma integration as escape hatch for all Prisma versions (#15128)
1 parent d7aa93f commit d9138ff

File tree

1 file changed

+74
-45
lines changed
  • packages/node/src/integrations/tracing

1 file changed

+74
-45
lines changed
Lines changed: 74 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,33 @@
1+
import type { Instrumentation } from '@opentelemetry/instrumentation';
12
// When importing CJS modules into an ESM module, we cannot import the named exports directly.
23
import * as prismaInstrumentation from '@prisma/instrumentation';
34
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, defineIntegration, spanToJSON } from '@sentry/core';
4-
import type { IntegrationFn } from '@sentry/core';
55
import { generateInstrumentOnce } from '../../otel/instrument';
66

77
const INTEGRATION_NAME = 'Prisma';
88

9-
export const instrumentPrisma = generateInstrumentOnce(INTEGRATION_NAME, () => {
10-
const EsmInteropPrismaInstrumentation: typeof prismaInstrumentation.PrismaInstrumentation =
11-
// @ts-expect-error We need to do the following for interop reasons
12-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
13-
prismaInstrumentation.default?.PrismaInstrumentation || prismaInstrumentation.PrismaInstrumentation;
9+
export const instrumentPrisma = generateInstrumentOnce<{ prismaInstrumentation?: Instrumentation }>(
10+
INTEGRATION_NAME,
11+
options => {
12+
// Use a passed instrumentation instance to support older Prisma versions
13+
if (options?.prismaInstrumentation) {
14+
return options.prismaInstrumentation;
15+
}
1416

15-
return new EsmInteropPrismaInstrumentation({});
16-
});
17+
const EsmInteropPrismaInstrumentation: typeof prismaInstrumentation.PrismaInstrumentation =
18+
// @ts-expect-error We need to do the following for interop reasons
19+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
20+
prismaInstrumentation.default?.PrismaInstrumentation || prismaInstrumentation.PrismaInstrumentation;
1721

18-
const _prismaIntegration = (() => {
19-
return {
20-
name: INTEGRATION_NAME,
21-
setupOnce() {
22-
instrumentPrisma();
23-
},
24-
25-
setup(client) {
26-
client.on('spanStart', span => {
27-
const spanJSON = spanToJSON(span);
28-
if (spanJSON.description?.startsWith('prisma:')) {
29-
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.prisma');
30-
}
31-
32-
// In Prisma v5.22+, the `db.system` attribute is automatically set
33-
// On older versions, this is missing, so we add it here
34-
if (spanJSON.description === 'prisma:engine:db_query' && !spanJSON.data?.['db.system']) {
35-
span.setAttribute('db.system', 'prisma');
36-
}
37-
});
38-
},
39-
};
40-
}) satisfies IntegrationFn;
22+
return new EsmInteropPrismaInstrumentation({});
23+
},
24+
);
4125

4226
/**
43-
* Adds Sentry tracing instrumentation for the [prisma](https://www.npmjs.com/package/prisma) library.
44-
*
27+
* Adds Sentry tracing instrumentation for the [Prisma](https://www.npmjs.com/package/prisma) ORM.
4528
* For more information, see the [`prismaIntegration` documentation](https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/prisma/).
4629
*
47-
* @example
48-
*
49-
* Make sure `previewFeatures = ["tracing"]` is set in the prisma client generator block. See the
50-
* [prisma docs](https://www.prisma.io/docs/concepts/components/prisma-client/opentelemetry-tracing) for more details.
30+
* Make sure `previewFeatures = ["tracing"]` is added to the generator block in of your Prisma schema.
5131
*
5232
* ```prisma
5333
* generator client {
@@ -56,14 +36,63 @@ const _prismaIntegration = (() => {
5636
* }
5737
* ```
5838
*
59-
* Then you can use the integration like this:
39+
* NOTE: By default, this integration works with Prisma version 5.
40+
* To get performance instrumentation for other Prisma versions,
41+
* 1. Install the `@prisma/instrumentation` package with the desired version.
42+
* 1. Pass a `new PrismaInstrumentation()` instance as exported from `@prisma/instrumentation` to the `prismaInstrumentation` option of this integration:
6043
*
61-
* ```javascript
62-
* const Sentry = require('@sentry/node');
44+
* ```js
45+
* import { PrismaInstrumentation } from '@prisma/instrumentation'
6346
*
64-
* Sentry.init({
65-
* integrations: [Sentry.prismaIntegration()],
66-
* });
67-
* ```
47+
* Sentry.init({
48+
* integrations: [
49+
* prismaIntegration({
50+
* // Override the default instrumentation that Sentry uses
51+
* prismaInstrumentation: new PrismaInstrumentation()
52+
* })
53+
* ]
54+
* })
55+
* ```
56+
*
57+
* The passed instrumentation instance will override the default instrumentation instance the integration would use, while the `prismaIntegration` will still ensure data compatibility for the various Prisma versions.
6858
*/
69-
export const prismaIntegration = defineIntegration(_prismaIntegration);
59+
export const prismaIntegration = defineIntegration(
60+
({
61+
prismaInstrumentation,
62+
}: {
63+
/**
64+
* Overrides the instrumentation used by the Sentry SDK with the passed in instrumentation instance.
65+
*
66+
* NOTE: By default, the Sentry SDK uses the Prisma v5 instrumentation. Use this option if you need performance instrumentation different Prisma versions.
67+
*
68+
* For more information refer to the documentation of `prismaIntegration()` or see https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/prisma/
69+
*/
70+
prismaInstrumentation?: Instrumentation;
71+
} = {}) => {
72+
return {
73+
name: INTEGRATION_NAME,
74+
setupOnce() {
75+
instrumentPrisma({ prismaInstrumentation });
76+
},
77+
setup(client) {
78+
client.on('spanStart', span => {
79+
const spanJSON = spanToJSON(span);
80+
if (spanJSON.description?.startsWith('prisma:')) {
81+
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.prisma');
82+
}
83+
84+
// Make sure we use the query text as the span name, for ex. SELECT * FROM "User" WHERE "id" = $1
85+
if (spanJSON.description === 'prisma:engine:db_query' && spanJSON.data?.['db.query.text']) {
86+
span.updateName(spanJSON.data['db.query.text'] as string);
87+
}
88+
89+
// In Prisma v5.22+, the `db.system` attribute is automatically set
90+
// On older versions, this is missing, so we add it here
91+
if (spanJSON.description === 'prisma:engine:db_query' && !spanJSON.data?.['db.system']) {
92+
span.setAttribute('db.system', 'prisma');
93+
}
94+
});
95+
},
96+
};
97+
},
98+
);

0 commit comments

Comments
 (0)