Skip to content

Commit

Permalink
feat(node/v8): Add prismaInstrumentation option to Prisma integrati…
Browse files Browse the repository at this point in the history
…on as escape hatch for all Prisma versions (#15128)
  • Loading branch information
lforst authored Jan 22, 2025
1 parent d7aa93f commit d9138ff
Showing 1 changed file with 74 additions and 45 deletions.
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');
}
});
},
};
},
);

0 comments on commit d9138ff

Please sign in to comment.