From 80d612a2513cbaf1c9ef50e322351f5f1c3d1133 Mon Sep 17 00:00:00 2001 From: tristal Date: Mon, 15 Dec 2025 19:39:53 +1000 Subject: [PATCH] fix(prisma): use real trace context in getTraceParent for DBM correlation This fixes the Prisma instrumentation to return actual trace IDs instead of hardcoded placeholder values, enabling proper Database Monitoring (DBM) correlation. The implementation: - Retrieves active span context from async local storage - Calls toTraceparent() to get the W3C traceparent with real trace IDs - Forces sampled flag to ensure Prisma generates engine spans - Falls back to placeholder when no active span exists Fixes database monitoring correlation issues where Prisma was injecting fake trace IDs into SQL comments. --- .../datadog-instrumentations/src/prisma.js | 25 ++++++++++- .../datadog-plugin-prisma/test/index.spec.js | 45 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/packages/datadog-instrumentations/src/prisma.js b/packages/datadog-instrumentations/src/prisma.js index ab2b79e0323..80c815335e6 100644 --- a/packages/datadog-instrumentations/src/prisma.js +++ b/packages/datadog-instrumentations/src/prisma.js @@ -4,6 +4,7 @@ const { channel, addHook } = require('./helpers/instrument') +const { storage } = require('../../datadog-core') const prismaEngineStart = channel('apm:prisma:engine:start') const tracingChannel = require('dc-polyfill').tracingChannel @@ -23,7 +24,29 @@ class TracingHelper { // needs a sampled tracecontext to generate engine spans getTraceParent (context) { - return '00-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-01' // valid sampled traceparent + // try to get span from current async storage context + const store = storage('legacy').getStore() + const span = store && store.span + + if (span && typeof span.context === 'function') { + const spanContext = span.context() + if (spanContext && typeof spanContext.toTraceparent === 'function') { + let traceparent = spanContext.toTraceparent() + + // force the sampled flag to '01' for Prisma's engine span generation + // prisma only generates engine spans when the traceparent indicates the trace is sampled + // this ensures engine spans are created and dispatched to dd-trace, which will then + // apply its own sampling decision when recording/sending spans + // the trace IDs and span IDs remain correct for DBM correlation + traceparent = traceparent.replace(/-0[01]$/, '-01') + + return traceparent + } + } + + // fallback to a valid sampled traceparent if no active span. + // this ensures Prisma can generate engine spans even when there's no active dd-trace context + return '00-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-01' } dispatchEngineSpans (spans) { diff --git a/packages/datadog-plugin-prisma/test/index.spec.js b/packages/datadog-plugin-prisma/test/index.spec.js index 969ee6d5e34..7d713bda034 100644 --- a/packages/datadog-plugin-prisma/test/index.spec.js +++ b/packages/datadog-plugin-prisma/test/index.spec.js @@ -202,6 +202,51 @@ describe('Plugin', () => { tracingPromise ]) }) + + it('should return real traceparent from active span context', async () => { + let capturedTraceparent + const tracingPromise = agent.assertSomeTraces(traces => { + // Get the trace ID and span ID from the created span + const clientSpan = traces[0].find(span => span.meta['prisma.type'] === 'client') + assert.ok(clientSpan != null) + + // Verify the traceparent is not the hardcoded fallback + assert.notEqual(capturedTraceparent, '00-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-01') + + // Verify it matches W3C traceparent format + const traceparentRegex = /^00-[a-f0-9]{32}-[a-f0-9]{16}-[0-9]{2}$/ + assert.ok(traceparentRegex.test(capturedTraceparent), + `Invalid traceparent format: ${capturedTraceparent}`) + + // Extract trace ID from traceparent (format: version-traceid-spanid-flags) + const [, traceparentTraceId] = capturedTraceparent.split('-') + + // Convert the span's trace_id to hex for comparison + const spanTraceIdHex = clientSpan.trace_id.toString(16).padStart(32, '0') + + // The last 16 characters of the traceparent trace ID should match + // the span's trace ID (dd-trace uses 64-bit trace IDs by default) + assert.ok(traceparentTraceId.endsWith(spanTraceIdHex.slice(-16)), + `Traceparent trace ID ${traceparentTraceId} should contain span trace ID ${spanTraceIdHex}`) + }) + + // Capture the traceparent during the query + await prismaClient.$queryRaw`SELECT 1`.then(() => { + // Get traceparent while span is still active + capturedTraceparent = tracingHelper.getTraceParent() + }) + + await tracingPromise + }) + + it('should return fallback traceparent when no active span', () => { + // Get traceparent outside of a traced operation + // This should return the fallback value since there's no active span + const result = tracingHelper.getTraceParent() + + // Should be the valid sampled fallback traceparent + assert.strictEqual(result, '00-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-01') + }) }) describe('with configuration', () => {