-
Notifications
You must be signed in to change notification settings - Fork 407
feat(otel): otel sampling support in distributed tracing #10117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
49eb625
5785a9b
9a7dfdd
af0fa51
1186c76
30ab81c
42005a6
660cb44
46548fb
e8d4fd6
9c18613
f9ff0ad
8fa5940
9dc3f5d
cf02963
6c0513a
3e895ea
59c8dd7
c0614ce
3447697
abf2703
fa1dad8
d7deb7d
d5c0f55
1f726e5
57d5657
5af8a45
df787d4
c13b5f1
0af916c
6e6f7f0
a8937f5
3237de8
ddf0605
875ae2e
eef8dcd
dd8c192
77ef7a5
e74e7cc
b99215d
8444a9a
fade28a
ca108b1
754e47f
2ea52be
79fa291
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| 'use strict' | ||
|
|
||
| const UINT64_MODULO = 2n ** 64n | ||
|
|
||
| // Knuth's factor for the sampling algorithm shared across Datadog tracers. | ||
| const SAMPLING_KNUTH_FACTOR = 1_111_111_111_111_111_111n | ||
|
|
||
| /** | ||
| * Hashes the lower 64 bits of a trace ID for deterministic trace sampling. | ||
| * | ||
| * @param {bigint} traceId | ||
| * @returns {bigint} | ||
| */ | ||
| function knuthHash (traceId) { | ||
| return (traceId * SAMPLING_KNUTH_FACTOR) % UINT64_MODULO | ||
| } | ||
|
|
||
| module.exports = knuthHash | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -77,6 +77,8 @@ const zeroTraceId = '0000000000000000' | |
| const hex16 = /^[0-9A-Fa-f]{16}$/ | ||
| const percentByte = /%([0-9A-Fa-f]{2})/g | ||
|
|
||
| let updateOtelTraceState | ||
|
|
||
| /** | ||
| * @typedef {object} B3Context | ||
| * @property {string} [flags] | ||
|
|
@@ -151,20 +153,23 @@ function getB3Priority (sampled, debug) { | |
| * @returns {DatadogSpanContext | undefined} | ||
| */ | ||
| function extractB3Context (b3) { | ||
| const priority = getB3Priority(b3.sampled, b3.flags === '1') | ||
| const spanContext = extractGenericContext(b3.traceId, b3.spanId, 16) | ||
| const debug = b3.flags === '1' | ||
| const priority = getB3Priority(b3.sampled, debug) | ||
| let spanContext = extractGenericContext(b3.traceId, b3.spanId, 16) | ||
|
|
||
| if (priority !== undefined) { | ||
| if (!spanContext) { | ||
| return new DatadogSpanContext({ | ||
| if (spanContext) { | ||
| spanContext._sampling.priority = priority | ||
| } else { | ||
| spanContext = new DatadogSpanContext({ | ||
| traceId: id(), | ||
| spanId: null, | ||
| sampling: { priority }, | ||
| isRemote: true, | ||
| }) | ||
| } | ||
|
|
||
| spanContext._sampling.priority = priority | ||
| if (debug) spanContext._sampling.isProbabilityDecision = false | ||
| } | ||
|
|
||
| if (spanContext && b3.traceId) extract128BitTraceId(b3.traceId, spanContext) | ||
|
|
@@ -619,6 +624,9 @@ class TextMapPropagator { | |
|
|
||
| writeTraceparent(carrier, spanContext.toTraceparent()) | ||
|
|
||
| updateOtelTraceState ??= require('../../otel-sampling').updateOtelTraceState | ||
| updateOtelTraceState(spanContext, ts) | ||
|
|
||
| ts.forVendor('dd', state => { | ||
| if (!spanContext._isRemote) { | ||
| // SpanContext was created by a ddtrace span. | ||
|
|
@@ -694,6 +702,33 @@ class TextMapPropagator { | |
| return this.#config.tracePropagationStyle[mode].includes(name) | ||
| } | ||
|
|
||
| /** | ||
| * Merges W3C sampling state into the context selected by propagation-style precedence. | ||
| * | ||
| * @param {DatadogSpanContext} w3cSpanContext | ||
| * @param {DatadogSpanContext} selectedSpanContext | ||
| * @returns {void} | ||
| */ | ||
| #mergeTraceContextState (w3cSpanContext, selectedSpanContext) { | ||
| const selectedPriority = selectedSpanContext._sampling.priority | ||
| if (selectedPriority === undefined) { | ||
| selectedSpanContext._sampling.priority = w3cSpanContext._sampling.priority | ||
| selectedSpanContext._sampling.mechanism = w3cSpanContext._sampling.mechanism | ||
| selectedSpanContext._trace.tags['_dd.p.dm'] = w3cSpanContext._trace.tags['_dd.p.dm'] | ||
|
Comment on lines
+714
to
+717
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When B3 wins extraction and supplies Useful? React with 👍 / 👎.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed this follow-up because agreeing B3/W3C contexts already retain the raw |
||
| } else if ( | ||
| selectedSpanContext._sampling.isProbabilityDecision === false || | ||
| (selectedPriority >= AUTO_KEEP) !== (w3cSpanContext._sampling.priority >= AUTO_KEEP) | ||
| ) { | ||
| // Copied sampling metadata must not describe a conflicting or non-probabilistic selected decision. | ||
| selectedSpanContext._sampling.isProbabilityDecision = false | ||
| w3cSpanContext._tracestate.forVendor('dd', state => { | ||
| if (state.get('t.dm') !== undefined) state.delete('t.dm') | ||
| }) | ||
| } | ||
|
|
||
| selectedSpanContext._tracestate = w3cSpanContext._tracestate | ||
| } | ||
|
|
||
| /** | ||
| * @param {DatadogSpanContext | undefined} w3cSpanContext | ||
| * @param {DatadogSpanContext} firstSpanContext | ||
|
|
@@ -703,10 +738,13 @@ class TextMapPropagator { | |
| */ | ||
| #resolveTraceContextConflicts (w3cSpanContext, firstSpanContext, carrier, datadogContext) { | ||
| if (w3cSpanContext === undefined || | ||
| firstSpanContext.toTraceId(true) !== w3cSpanContext.toTraceId(true) || | ||
| firstSpanContext.toSpanId() === w3cSpanContext.toSpanId()) { | ||
| firstSpanContext.toTraceId(true) !== w3cSpanContext.toTraceId(true)) { | ||
| return firstSpanContext | ||
| } | ||
|
|
||
| this.#mergeTraceContextState(w3cSpanContext, firstSpanContext) | ||
| if (firstSpanContext.toSpanId() === w3cSpanContext.toSpanId()) return firstSpanContext | ||
|
|
||
| if (tags.DD_PARENT_ID in w3cSpanContext._trace.tags) { | ||
| // tracecontext headers contain a p value, ensure this value is sent to backend | ||
| firstSpanContext._trace.tags[tags.DD_PARENT_ID] = w3cSpanContext._trace.tags[tags.DD_PARENT_ID] | ||
|
|
@@ -744,7 +782,8 @@ class TextMapPropagator { | |
| traceContext = this.#extractTraceparentContext(carrier) | ||
| traceContextExtracted = true | ||
| } | ||
| this.#addTraceContextState(extractedContext, traceContext) | ||
| const selectedContext = context && context !== traceContext ? context : extractedContext | ||
| this.#addTraceContextState(selectedContext, traceContext) | ||
| } | ||
| break | ||
| case 'tracecontext': | ||
|
|
@@ -846,7 +885,14 @@ class TextMapPropagator { | |
| } | ||
|
|
||
| const traceTags = this.#extractTags(carrier) | ||
| if (traceTags) spanContext._trace.tags = traceTags | ||
| if (traceTags) { | ||
| spanContext._trace.tags = traceTags | ||
| const decisionMaker = traceTags['_dd.p.dm'] | ||
| if (decisionMaker !== undefined) { | ||
| const mechanism = Math.abs(Number.parseInt(decisionMaker, 10)) | ||
| if (Number.isInteger(mechanism)) spanContext._sampling.mechanism = mechanism | ||
| } | ||
| } | ||
|
|
||
| return spanContext | ||
| } | ||
|
|
@@ -883,7 +929,7 @@ class TextMapPropagator { | |
| #addTraceContextState (datadogContext, traceContext) { | ||
| if (traceContext && datadogContext._traceId.equals(traceContext._traceId)) { | ||
| datadogContext._traceparent = traceContext._traceparent | ||
| datadogContext._tracestate = traceContext._tracestate | ||
| this.#mergeTraceContextState(traceContext, datadogContext) | ||
|
vpellan marked this conversation as resolved.
|
||
| } | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.