Skip to content

feat(node/v8): Add prismaInstrumentation option to Prisma integration as escape hatch for all Prisma versions #15128

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 22, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 74 additions & 45 deletions packages/node/src/integrations/tracing/prisma.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,33 @@
import type { Instrumentation } from '@opentelemetry/instrumentation';
// When importing CJS modules into an ESM module, we cannot import the named exports directly.
import * as prismaInstrumentation from '@prisma/instrumentation';
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, defineIntegration, spanToJSON } from '@sentry/core';
import type { IntegrationFn } from '@sentry/core';
import { generateInstrumentOnce } from '../../otel/instrument';

const INTEGRATION_NAME = 'Prisma';

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

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

const _prismaIntegration = (() => {
return {
name: INTEGRATION_NAME,
setupOnce() {
instrumentPrisma();
},

setup(client) {
client.on('spanStart', span => {
const spanJSON = spanToJSON(span);
if (spanJSON.description?.startsWith('prisma:')) {
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.prisma');
}

// In Prisma v5.22+, the `db.system` attribute is automatically set
// On older versions, this is missing, so we add it here
if (spanJSON.description === 'prisma:engine:db_query' && !spanJSON.data?.['db.system']) {
span.setAttribute('db.system', 'prisma');
}
});
},
};
}) satisfies IntegrationFn;
return new EsmInteropPrismaInstrumentation({});
},
);

/**
* Adds Sentry tracing instrumentation for the [prisma](https://www.npmjs.com/package/prisma) library.
*
* Adds Sentry tracing instrumentation for the [Prisma](https://www.npmjs.com/package/prisma) ORM.
* For more information, see the [`prismaIntegration` documentation](https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/prisma/).
*
* @example
*
* Make sure `previewFeatures = ["tracing"]` is set in the prisma client generator block. See the
* [prisma docs](https://www.prisma.io/docs/concepts/components/prisma-client/opentelemetry-tracing) for more details.
* Make sure `previewFeatures = ["tracing"]` is added to the generator block in of your Prisma schema.
*
* ```prisma
* generator client {
Expand All @@ -56,14 +36,63 @@ const _prismaIntegration = (() => {
* }
* ```
*
* Then you can use the integration like this:
* NOTE: By default, this integration works with Prisma version 5.
* To get performance instrumentation for other Prisma versions,
* 1. Install the `@prisma/instrumentation` package with the desired version.
* 1. Pass a `new PrismaInstrumentation()` instance as exported from `@prisma/instrumentation` to the `prismaInstrumentation` option of this integration:
*
* ```javascript
* const Sentry = require('@sentry/node');
* ```js
* import { PrismaInstrumentation } from '@prisma/instrumentation'
*
* Sentry.init({
* integrations: [Sentry.prismaIntegration()],
* });
* ```
* Sentry.init({
* integrations: [
* prismaIntegration({
* // Override the default instrumentation that Sentry uses
* prismaInstrumentation: new PrismaInstrumentation()
* })
* ]
* })
* ```
*
* 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.
*/
export const prismaIntegration = defineIntegration(_prismaIntegration);
export const prismaIntegration = defineIntegration(
({
prismaInstrumentation,
}: {
/**
* Overrides the instrumentation used by the Sentry SDK with the passed in instrumentation instance.
*
* NOTE: By default, the Sentry SDK uses the Prisma v5 instrumentation. Use this option if you need performance instrumentation different Prisma versions.
*
* For more information refer to the documentation of `prismaIntegration()` or see https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/prisma/
*/
prismaInstrumentation?: Instrumentation;
} = {}) => {
return {
name: INTEGRATION_NAME,
setupOnce() {
instrumentPrisma({ prismaInstrumentation });
},
setup(client) {
client.on('spanStart', span => {
const spanJSON = spanToJSON(span);
if (spanJSON.description?.startsWith('prisma:')) {
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.prisma');
}

// Make sure we use the query text as the span name, for ex. SELECT * FROM "User" WHERE "id" = $1
if (spanJSON.description === 'prisma:engine:db_query' && spanJSON.data?.['db.query.text']) {
span.updateName(spanJSON.data['db.query.text'] as string);
}

// In Prisma v5.22+, the `db.system` attribute is automatically set
// On older versions, this is missing, so we add it here
if (spanJSON.description === 'prisma:engine:db_query' && !spanJSON.data?.['db.system']) {
span.setAttribute('db.system', 'prisma');
}
});
},
};
},
);
Loading