|
| 1 | +import type { Instrumentation } from '@opentelemetry/instrumentation'; |
1 | 2 | // When importing CJS modules into an ESM module, we cannot import the named exports directly. |
2 | 3 | import * as prismaInstrumentation from '@prisma/instrumentation'; |
3 | 4 | import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, defineIntegration, spanToJSON } from '@sentry/core'; |
4 | | -import type { IntegrationFn } from '@sentry/core'; |
5 | 5 | import { generateInstrumentOnce } from '../../otel/instrument'; |
6 | 6 |
|
7 | 7 | const INTEGRATION_NAME = 'Prisma'; |
8 | 8 |
|
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 | + } |
14 | 16 |
|
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; |
17 | 21 |
|
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 | | - // Make sure we use the query text as the span name, for ex. SELECT * FROM "User" WHERE "id" = $1 |
33 | | - if (spanJSON.description === 'prisma:engine:db_query' && spanJSON.data?.['db.query.text']) { |
34 | | - span.updateName(spanJSON.data['db.query.text'] as string); |
35 | | - } |
36 | | - }); |
37 | | - }, |
38 | | - }; |
39 | | -}) satisfies IntegrationFn; |
| 22 | + return new EsmInteropPrismaInstrumentation({}); |
| 23 | + }, |
| 24 | +); |
40 | 25 |
|
41 | 26 | /** |
42 | 27 | * Adds Sentry tracing instrumentation for the [prisma](https://www.npmjs.com/package/prisma) library. |
43 | | - * |
44 | 28 | * For more information, see the [`prismaIntegration` documentation](https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/prisma/). |
45 | 29 | * |
46 | | - * @example |
47 | | - * ```javascript |
48 | | - * const Sentry = require('@sentry/node'); |
| 30 | + * NOTE: By default, this integration works with Prisma version 6. |
| 31 | + * To get performance instrumentation for other Prisma versions, |
| 32 | + * 1. Install the `@prisma/instrumentation` package with the desired version. |
| 33 | + * 1. Pass a `new PrismaInstrumentation()` instance as exported from `@prisma/instrumentation` to the `prismaInstrumentation` option of this integration: |
| 34 | + * |
| 35 | + * ```js |
| 36 | + * import { PrismaInstrumentation } from '@prisma/instrumentation' |
49 | 37 | * |
50 | | - * Sentry.init({ |
51 | | - * integrations: [Sentry.prismaIntegration()], |
52 | | - * }); |
53 | | - * ``` |
| 38 | + * Sentry.init({ |
| 39 | + * integrations: [ |
| 40 | + * prismaIntegration({ |
| 41 | + * // Override the default instrumentation that Sentry uses |
| 42 | + * prismaInstrumentation: new PrismaInstrumentation() |
| 43 | + * }) |
| 44 | + * ] |
| 45 | + * }) |
| 46 | + * ``` |
| 47 | + * |
| 48 | + * 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. |
| 49 | + * 1. Depending on your Prisma version (prior to version 6), add `previewFeatures = ["tracing"]` to the client generator block of your Prisma schema: |
| 50 | + * |
| 51 | + * ``` |
| 52 | + * generator client { |
| 53 | + * provider = "prisma-client-js" |
| 54 | + * previewFeatures = ["tracing"] |
| 55 | + * } |
| 56 | + * ``` |
54 | 57 | */ |
55 | | -export const prismaIntegration = defineIntegration(_prismaIntegration); |
| 58 | +export const prismaIntegration = defineIntegration( |
| 59 | + ({ |
| 60 | + prismaInstrumentation, |
| 61 | + }: { |
| 62 | + /** |
| 63 | + * Overrides the instrumentation used by the Sentry SDK with the passed in instrumentation instance. |
| 64 | + * |
| 65 | + * NOTE: By default, the Sentry SDK uses the Prisma v6 instrumentation. Use this option if you need performance instrumentation different Prisma versions. |
| 66 | + * |
| 67 | + * For more information refer to the documentation of `prismaIntegration()` or see https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/prisma/ |
| 68 | + */ |
| 69 | + prismaInstrumentation?: Instrumentation; |
| 70 | + } = {}) => { |
| 71 | + return { |
| 72 | + name: INTEGRATION_NAME, |
| 73 | + setupOnce() { |
| 74 | + instrumentPrisma({ prismaInstrumentation }); |
| 75 | + }, |
| 76 | + setup(client) { |
| 77 | + client.on('spanStart', span => { |
| 78 | + const spanJSON = spanToJSON(span); |
| 79 | + if (spanJSON.description?.startsWith('prisma:')) { |
| 80 | + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.prisma'); |
| 81 | + } |
| 82 | + |
| 83 | + // Make sure we use the query text as the span name, for ex. SELECT * FROM "User" WHERE "id" = $1 |
| 84 | + if (spanJSON.description === 'prisma:engine:db_query' && spanJSON.data['db.query.text']) { |
| 85 | + span.updateName(spanJSON.data['db.query.text'] as string); |
| 86 | + } |
| 87 | + |
| 88 | + // In Prisma v5.22+, the `db.system` attribute is automatically set |
| 89 | + // On older versions, this is missing, so we add it here |
| 90 | + if (spanJSON.description === 'prisma:engine:db_query' && !spanJSON.data['db.system']) { |
| 91 | + span.setAttribute('db.system', 'prisma'); |
| 92 | + } |
| 93 | + }); |
| 94 | + }, |
| 95 | + }; |
| 96 | + }, |
| 97 | +); |
0 commit comments