Skip to content
Open
Show file tree
Hide file tree
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
25 changes: 24 additions & 1 deletion packages/datadog-instrumentations/src/prisma.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down
45 changes: 45 additions & 0 deletions packages/datadog-plugin-prisma/test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,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', () => {
Expand Down
Loading