From 5c9d815bd74d9cc09268c5b24ef215ab93445373 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Mon, 6 May 2024 10:11:50 +0200 Subject: [PATCH 1/2] fix(node): Ensure prisma integration creates valid DB spans --- .../suites/tracing/prisma-orm/scenario.js | 7 +- .../suites/tracing/prisma-orm/test.ts | 76 ++++++++++--------- .../node/src/integrations/tracing/prisma.ts | 15 +++- 3 files changed, 57 insertions(+), 41 deletions(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm/scenario.js b/dev-packages/node-integration-tests/suites/tracing/prisma-orm/scenario.js index 7f291290bea2..82fbc044b973 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm/scenario.js +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm/scenario.js @@ -1,6 +1,3 @@ -const { randomBytes } = require('crypto'); -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -const { PrismaClient } = require('@prisma/client'); const Sentry = require('@sentry/node'); const { loggingTransport } = require('@sentry-internal/node-integration-tests'); @@ -12,6 +9,9 @@ Sentry.init({ integrations: [Sentry.prismaIntegration()], }); +const { randomBytes } = require('crypto'); +const { PrismaClient } = require('@prisma/client'); + // Stop the process from exiting before the transaction is sent setInterval(() => {}, 1000); @@ -49,5 +49,4 @@ async function run() { ); } -// eslint-disable-next-line @typescript-eslint/no-floating-promises run(); diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm/test.ts b/dev-packages/node-integration-tests/suites/tracing/prisma-orm/test.ts index 32bcdf168555..9826f4ce7a78 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm/test.ts @@ -7,100 +7,104 @@ conditionalTest({ min: 16 })('Prisma ORM Tests', () => { transaction: 'Test Transaction', spans: expect.arrayContaining([ expect.objectContaining({ - data: expect.objectContaining({ + data: { method: 'create', model: 'User', name: 'User.create', 'otel.kind': 'INTERNAL', - 'sentry.origin': 'manual', - }), + 'sentry.origin': 'auto.db.otel.prisma', + }, description: 'prisma:client:operation', status: 'ok', }), expect.objectContaining({ - data: expect.objectContaining({ + data: { 'otel.kind': 'INTERNAL', - 'sentry.origin': 'manual', - }), + 'sentry.origin': 'auto.db.otel.prisma', + }, description: 'prisma:client:serialize', status: 'ok', }), expect.objectContaining({ - data: expect.objectContaining({ + data: { 'otel.kind': 'INTERNAL', - 'sentry.origin': 'manual', - }), + 'sentry.origin': 'auto.db.otel.prisma', + }, description: 'prisma:client:connect', status: 'ok', }), expect.objectContaining({ - data: expect.objectContaining({ + data: { 'otel.kind': 'INTERNAL', - 'sentry.origin': 'manual', - }), + 'sentry.origin': 'auto.db.otel.prisma', + }, description: 'prisma:engine', status: 'ok', }), expect.objectContaining({ - data: expect.objectContaining({ + data: { 'db.type': 'postgres', 'otel.kind': 'INTERNAL', - 'sentry.origin': 'manual', - }), + 'sentry.origin': 'auto.db.otel.prisma', + }, description: 'prisma:engine:connection', status: 'ok', }), expect.objectContaining({ - data: expect.objectContaining({ - 'db.statement': expect.stringContaining( + data: { + 'db.statement': expect.any(String) /* expect.stringContaining( 'INSERT INTO "public"."User" ("createdAt","email","name") VALUES ($1,$2,$3) RETURNING "public"."User"."id", "public"."User"."createdAt", "public"."User"."email", "public"."User"."name" /* traceparent', - ), + ), */, 'otel.kind': 'INTERNAL', - 'sentry.origin': 'manual', - }), - description: 'prisma:engine:db_query', + 'sentry.origin': 'auto.db.otel.prisma', + 'db.system': 'prisma', + 'sentry.op': 'db', + }, + /* description: expect.stringContaining( + 'INSERT INTO "public"."User" ("createdAt","email","name") VALUES ($1,$2,$3) RETURNING "public"."User"."id", "public"."User"."createdAt", "public"."User"."email", "public"."User"."name" /* traceparent', + ), */ status: 'ok', }), expect.objectContaining({ - data: expect.objectContaining({ + data: { 'otel.kind': 'INTERNAL', - 'sentry.origin': 'manual', - }), + 'sentry.origin': 'auto.db.otel.prisma', + }, description: 'prisma:engine:serialize', status: 'ok', }), expect.objectContaining({ - data: expect.objectContaining({ + data: { 'otel.kind': 'INTERNAL', - 'sentry.origin': 'manual', - }), + 'sentry.origin': 'auto.db.otel.prisma', + }, description: 'prisma:engine:response_json_serialization', status: 'ok', }), expect.objectContaining({ - data: expect.objectContaining({ + data: { method: 'findMany', model: 'User', name: 'User.findMany', 'otel.kind': 'INTERNAL', - 'sentry.origin': 'manual', - }), + 'sentry.origin': 'auto.db.otel.prisma', + }, description: 'prisma:client:operation', status: 'ok', }), expect.objectContaining({ - data: expect.objectContaining({ + data: { 'otel.kind': 'INTERNAL', - 'sentry.origin': 'manual', - }), + 'sentry.origin': 'auto.db.otel.prisma', + }, description: 'prisma:client:serialize', status: 'ok', }), expect.objectContaining({ - data: expect.objectContaining({ + data: { 'otel.kind': 'INTERNAL', - 'sentry.origin': 'manual', - }), + 'sentry.origin': 'auto.db.otel.prisma', + }, description: 'prisma:engine', status: 'ok', }), diff --git a/packages/node/src/integrations/tracing/prisma.ts b/packages/node/src/integrations/tracing/prisma.ts index 13e51065dce8..7652ea793530 100644 --- a/packages/node/src/integrations/tracing/prisma.ts +++ b/packages/node/src/integrations/tracing/prisma.ts @@ -1,6 +1,6 @@ // When importing CJS modules into an ESM module, we cannot import the named exports directly. import * as prismaInstrumentation from '@prisma/instrumentation'; -import { defineIntegration } from '@sentry/core'; +import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, defineIntegration, spanToJSON } from '@sentry/core'; import { addOpenTelemetryInstrumentation } from '@sentry/opentelemetry'; import type { IntegrationFn } from '@sentry/types'; @@ -13,6 +13,19 @@ const _prismaIntegration = (() => { new prismaInstrumentation.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'); + } + + if (spanJSON.description === 'prisma:engine:db_query') { + span.setAttribute('db.system', 'prisma'); + } + }); + }, }; }) satisfies IntegrationFn; From ddee52fdc0659f407581f1d815b6f002fff6de91 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Mon, 6 May 2024 10:14:02 +0200 Subject: [PATCH 2/2] fix test --- .../suites/tracing/prisma-orm/test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm/test.ts b/dev-packages/node-integration-tests/suites/tracing/prisma-orm/test.ts index 9826f4ce7a78..3c96c6d80779 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm/test.ts @@ -52,17 +52,17 @@ conditionalTest({ min: 16 })('Prisma ORM Tests', () => { }), expect.objectContaining({ data: { - 'db.statement': expect.any(String) /* expect.stringContaining( + 'db.statement': expect.stringContaining( 'INSERT INTO "public"."User" ("createdAt","email","name") VALUES ($1,$2,$3) RETURNING "public"."User"."id", "public"."User"."createdAt", "public"."User"."email", "public"."User"."name" /* traceparent', - ), */, + ), 'otel.kind': 'INTERNAL', 'sentry.origin': 'auto.db.otel.prisma', 'db.system': 'prisma', 'sentry.op': 'db', }, - /* description: expect.stringContaining( + description: expect.stringContaining( 'INSERT INTO "public"."User" ("createdAt","email","name") VALUES ($1,$2,$3) RETURNING "public"."User"."id", "public"."User"."createdAt", "public"."User"."email", "public"."User"."name" /* traceparent', - ), */ + ), status: 'ok', }), expect.objectContaining({