UNPKG

@sentry/node

Version:

Sentry Node SDK using OpenTelemetry for performance instrumentation

107 lines (104 loc) 4.14 kB
import { LRUMap, defineIntegration, spanToJSON, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, debug, startInactiveSpan, SPAN_KIND } from '@sentry/core'; import { generateInstrumentOnce } from '@sentry/node-core'; import { DEBUG_BUILD } from '../../../debug-build.js'; import { PrismaInstrumentation } from './vendored/instrumentation.js'; const INTEGRATION_NAME = "Prisma"; function isPrismaV6TracingHelper(helper) { return !!helper && typeof helper === "object" && "dispatchEngineSpans" in helper; } function getPrismaTracingHelper() { const prismaInstrumentationObject = globalThis.PRISMA_INSTRUMENTATION; const prismaTracingHelper = prismaInstrumentationObject && typeof prismaInstrumentationObject === "object" && "helper" in prismaInstrumentationObject ? prismaInstrumentationObject.helper : void 0; return prismaTracingHelper; } const MAX_TRACKED_PRISMA_SPANS = 1e3; const prismaSpanRegistry = new LRUMap(MAX_TRACKED_PRISMA_SPANS); const pendingEngineSpans = []; function registerPrismaSpan(id, span) { prismaSpanRegistry.set(id, span); } function createResolvedEngineSpans() { let createdSpan = true; while (createdSpan) { createdSpan = false; for (let i = pendingEngineSpans.length - 1; i >= 0; i--) { const engineSpan = pendingEngineSpans[i]; const parentSpan = prismaSpanRegistry.get(engineSpan.parent_span_id); if (!parentSpan) { continue; } const span = startInactiveSpan({ name: engineSpan.name, attributes: engineSpan.attributes, kind: engineSpan.kind === "client" ? SPAN_KIND.CLIENT : SPAN_KIND.INTERNAL, startTime: engineSpan.start_time, parentSpan }); registerPrismaSpan(engineSpan.span_id, span); if (engineSpan.links) { span.addLinks( engineSpan.links.flatMap((link) => { const linkedSpan = prismaSpanRegistry.get(link.span_id); return linkedSpan ? [{ context: linkedSpan.spanContext() }] : []; }) ); } span.end(engineSpan.end_time); pendingEngineSpans.splice(i, 1); createdSpan = true; } } } class SentryPrismaInteropInstrumentation extends PrismaInstrumentation { constructor(options) { super(options?.instrumentationConfig); } enable() { super.enable(); const prismaTracingHelper = getPrismaTracingHelper(); if (isPrismaV6TracingHelper(prismaTracingHelper)) { prismaTracingHelper.createEngineSpan = (engineSpanEvent) => { pendingEngineSpans.push(...engineSpanEvent.spans); createResolvedEngineSpans(); const overflow = pendingEngineSpans.length - MAX_TRACKED_PRISMA_SPANS; if (overflow > 0) { DEBUG_BUILD && debug.log(`[Prisma] Dropping ${overflow} unresolved v5 engine span(s) whose parent was never registered.`); pendingEngineSpans.splice(0, overflow); } }; } } } const instrumentPrisma = generateInstrumentOnce(INTEGRATION_NAME, (options) => { return new SentryPrismaInteropInstrumentation(options); }); const prismaIntegration = defineIntegration((options) => { return { name: INTEGRATION_NAME, setupOnce() { instrumentPrisma(options); }, setup(client) { if (!getPrismaTracingHelper()) { return; } 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.startsWith("prisma:client:")) { registerPrismaSpan(span.spanContext().spanId, span); } } if ((spanJSON.description === "prisma:engine:db_query" || spanJSON.description === "prisma:client:db_query") && spanJSON.data["db.query.text"]) { span.updateName(spanJSON.data["db.query.text"]); } if (spanJSON.description === "prisma:engine:db_query" && !spanJSON.data["db.system"]) { span.setAttribute("db.system", "prisma"); } }); } }; }); export { instrumentPrisma, prismaIntegration }; //# sourceMappingURL=index.js.map