|
| 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