dd-trace 5.104.0 → 5.105.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-3rdparty.csv +90 -102
- package/index.d.ts +82 -3
- package/package.json +15 -15
- package/packages/datadog-core/src/storage.js +1 -1
- package/packages/datadog-instrumentations/src/aerospike.js +1 -1
- package/packages/datadog-instrumentations/src/ai.js +8 -7
- package/packages/datadog-instrumentations/src/aws-sdk.js +13 -0
- package/packages/datadog-instrumentations/src/azure-cosmos.js +7 -0
- package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
- package/packages/datadog-instrumentations/src/cucumber.js +78 -5
- package/packages/datadog-instrumentations/src/dns.js +54 -18
- package/packages/datadog-instrumentations/src/fastify.js +142 -82
- package/packages/datadog-instrumentations/src/graphql.js +188 -62
- package/packages/datadog-instrumentations/src/helpers/ai-messages.js +322 -14
- package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +269 -0
- package/packages/datadog-instrumentations/src/helpers/promise-instrumentor.js +42 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -3
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-cosmos.js +50 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +85 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +37 -236
- package/packages/datadog-instrumentations/src/hono.js +54 -3
- package/packages/datadog-instrumentations/src/http/server.js +9 -4
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +163 -0
- package/packages/datadog-instrumentations/src/jest.js +360 -150
- package/packages/datadog-instrumentations/src/kafkajs.js +120 -16
- package/packages/datadog-instrumentations/src/mocha/main.js +128 -17
- package/packages/datadog-instrumentations/src/nats.js +182 -0
- package/packages/datadog-instrumentations/src/nyc.js +38 -1
- package/packages/datadog-instrumentations/src/openai.js +33 -18
- package/packages/datadog-instrumentations/src/oracledb.js +6 -1
- package/packages/datadog-instrumentations/src/pino.js +17 -5
- package/packages/datadog-instrumentations/src/playwright.js +515 -292
- package/packages/datadog-instrumentations/src/router.js +76 -32
- package/packages/datadog-instrumentations/src/stripe.js +1 -1
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -1
- package/packages/datadog-plugin-azure-cosmos/src/index.js +144 -0
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -1
- package/packages/datadog-plugin-azure-functions/src/index.js +5 -2
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +1 -1
- package/packages/datadog-plugin-bunyan/src/index.js +28 -0
- package/packages/datadog-plugin-cucumber/src/index.js +17 -3
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +199 -28
- package/packages/datadog-plugin-cypress/src/support.js +69 -1
- package/packages/datadog-plugin-dns/src/lookup.js +8 -6
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +2 -0
- package/packages/datadog-plugin-graphql/src/resolve.js +64 -67
- package/packages/datadog-plugin-http/src/server.js +40 -15
- package/packages/datadog-plugin-jest/src/index.js +11 -3
- package/packages/datadog-plugin-jest/src/util.js +15 -8
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +1 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +3 -0
- package/packages/datadog-plugin-langgraph/src/stream.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +19 -4
- package/packages/datadog-plugin-mongodb-core/src/index.js +281 -40
- package/packages/datadog-plugin-nats/src/consumer.js +43 -0
- package/packages/datadog-plugin-nats/src/index.js +20 -0
- package/packages/datadog-plugin-nats/src/producer.js +62 -0
- package/packages/datadog-plugin-nats/src/util.js +33 -0
- package/packages/datadog-plugin-next/src/index.js +5 -3
- package/packages/datadog-plugin-openai/src/tracing.js +15 -2
- package/packages/datadog-plugin-oracledb/src/index.js +13 -2
- package/packages/datadog-plugin-pino/src/index.js +42 -0
- package/packages/datadog-plugin-playwright/src/index.js +4 -4
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
- package/packages/datadog-plugin-rhea/src/producer.js +1 -1
- package/packages/datadog-plugin-router/src/index.js +33 -44
- package/packages/datadog-plugin-selenium/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +5 -13
- package/packages/datadog-plugin-winston/src/index.js +30 -0
- package/packages/datadog-shimmer/src/shimmer.js +33 -40
- package/packages/dd-trace/src/aiguard/index.js +1 -1
- package/packages/dd-trace/src/aiguard/sdk.js +1 -1
- package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +1 -1
- package/packages/dd-trace/src/appsec/reporter.js +5 -6
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
- package/packages/dd-trace/src/appsec/sdk/utils.js +1 -1
- package/packages/dd-trace/src/appsec/user_tracking.js +5 -4
- package/packages/dd-trace/src/baggage.js +7 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +0 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +25 -13
- package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +70 -6
- package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -2
- package/packages/dd-trace/src/config/supported-configurations.json +27 -8
- package/packages/dd-trace/src/datastreams/writer.js +2 -4
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -8
- package/packages/dd-trace/src/encode/0.4.js +124 -108
- package/packages/dd-trace/src/encode/0.5.js +114 -26
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +31 -23
- package/packages/dd-trace/src/encode/agentless-json.js +4 -2
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -13
- package/packages/dd-trace/src/encode/span-stats.js +16 -16
- package/packages/dd-trace/src/encode/tags-processors.js +16 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +9 -7
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +0 -16
- package/packages/dd-trace/src/llmobs/span_processor.js +3 -3
- package/packages/dd-trace/src/llmobs/tagger.js +9 -1
- package/packages/dd-trace/src/llmobs/telemetry.js +1 -1
- package/packages/dd-trace/src/llmobs/util.js +66 -3
- package/packages/dd-trace/src/log/index.js +1 -1
- package/packages/dd-trace/src/msgpack/chunk.js +394 -10
- package/packages/dd-trace/src/msgpack/index.js +96 -2
- package/packages/dd-trace/src/openfeature/encoding.js +70 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +20 -0
- package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +143 -0
- package/packages/dd-trace/src/openfeature/span-enrichment.js +149 -0
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +4 -3
- package/packages/dd-trace/src/opentelemetry/span.js +1 -1
- package/packages/dd-trace/src/opentracing/propagation/log.js +18 -7
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +62 -67
- package/packages/dd-trace/src/opentracing/span.js +59 -19
- package/packages/dd-trace/src/opentracing/span_context.js +49 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +20 -20
- package/packages/dd-trace/src/plugins/database.js +7 -6
- package/packages/dd-trace/src/plugins/index.js +4 -0
- package/packages/dd-trace/src/plugins/log_injection.js +56 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +3 -48
- package/packages/dd-trace/src/plugins/outbound.js +1 -1
- package/packages/dd-trace/src/plugins/plugin.js +15 -17
- package/packages/dd-trace/src/plugins/tracing.js +43 -5
- package/packages/dd-trace/src/plugins/util/test.js +236 -13
- package/packages/dd-trace/src/plugins/util/web.js +79 -65
- package/packages/dd-trace/src/priority_sampler.js +2 -2
- package/packages/dd-trace/src/profiling/profiler.js +2 -2
- package/packages/dd-trace/src/profiling/profilers/wall.js +10 -4
- package/packages/dd-trace/src/sampling_rule.js +7 -7
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +10 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
- package/packages/dd-trace/src/service-naming/source-resolver.js +46 -0
- package/packages/dd-trace/src/span_format.js +190 -58
- package/packages/dd-trace/src/spanleak.js +1 -1
- package/packages/dd-trace/src/standalone/index.js +3 -3
- package/packages/dd-trace/src/tagger.js +0 -2
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +70 -39
- package/vendor/dist/@datadog/sketches-js/LICENSE +10 -36
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/packages/dd-trace/src/msgpack/encoder.js +0 -308
- package/packages/dd-trace/src/plugins/structured_log_plugin.js +0 -9
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { channel } = require('dc-polyfill')
|
|
4
|
+
const log = require('../log')
|
|
5
|
+
const { SpanEnrichmentState } = require('./span-enrichment')
|
|
6
|
+
|
|
7
|
+
const finishCh = channel('dd-trace:span:finish')
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* OpenFeature hook that enriches APM spans with feature flag evaluation data.
|
|
11
|
+
*
|
|
12
|
+
* Implements the OpenFeature `finally` hook interface to capture flag evaluations
|
|
13
|
+
* and add span tags for observability. Tags are accumulated during the span's
|
|
14
|
+
* lifetime and applied when the root span finishes.
|
|
15
|
+
*
|
|
16
|
+
* Span tags added:
|
|
17
|
+
* - `ffe_flags_enc`: Base64 delta-varint encoded serial IDs
|
|
18
|
+
* - `ffe_subjects_enc`: JSON dict of SHA256(targeting_key) → encoded serial IDs
|
|
19
|
+
* - `ffe_runtime_defaults`: JSON dict of flag_key → default value string
|
|
20
|
+
*/
|
|
21
|
+
class SpanEnrichmentHook {
|
|
22
|
+
#tracer
|
|
23
|
+
/** @type {WeakMap<object, SpanEnrichmentState>} */
|
|
24
|
+
#spanStates = new WeakMap()
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Handler for span finish channel. Applies accumulated tags to the span.
|
|
28
|
+
* Arrow function to preserve `this` binding for channel subscription.
|
|
29
|
+
*
|
|
30
|
+
* @param {object} span - The span that is finishing
|
|
31
|
+
*/
|
|
32
|
+
#onSpanFinish = (span) => {
|
|
33
|
+
const state = this.#spanStates.get(span)
|
|
34
|
+
if (!state || !state.hasData()) return
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const tags = state.toSpanTags()
|
|
38
|
+
|
|
39
|
+
for (const [key, value] of Object.entries(tags)) {
|
|
40
|
+
if (value) {
|
|
41
|
+
span.setTag(key, value)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
} catch (err) {
|
|
45
|
+
log.warn('SpanEnrichmentHook: error applying span tags: %s', err.message)
|
|
46
|
+
} finally {
|
|
47
|
+
this.#spanStates.delete(span)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {import('../tracer')} tracer - Datadog tracer instance
|
|
53
|
+
*/
|
|
54
|
+
constructor (tracer) {
|
|
55
|
+
this.#tracer = tracer
|
|
56
|
+
finishCh.subscribe(this.#onSpanFinish)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Called by the OpenFeature SDK after every flag evaluation (success or error).
|
|
61
|
+
*
|
|
62
|
+
* @param {object} hookContext - Hook context containing the flag key and evaluation context
|
|
63
|
+
* @param {string} hookContext.flagKey - The flag key being evaluated
|
|
64
|
+
* @param {object} [hookContext.context] - Evaluation context
|
|
65
|
+
* @param {string} [hookContext.context.targetingKey] - Targeting key
|
|
66
|
+
* @param {object} evaluationDetails - Full evaluation details including flag metadata
|
|
67
|
+
* @param {object} [evaluationDetails.flagMetadata] - Metadata from the provider
|
|
68
|
+
* @param {number} [evaluationDetails.flagMetadata.__dd_split_serial_id] - Serial ID from UFC split
|
|
69
|
+
* @param {boolean} [evaluationDetails.flagMetadata.__dd_do_log] - Whether to log subject
|
|
70
|
+
* @param {string} [evaluationDetails.variant] - Variant key if flag was found in UFC
|
|
71
|
+
* @param {boolean|string|number|object} [evaluationDetails.value] - Evaluated value
|
|
72
|
+
* @returns {void}
|
|
73
|
+
*/
|
|
74
|
+
finally (hookContext, evaluationDetails) {
|
|
75
|
+
try {
|
|
76
|
+
const rootSpan = this._getRootSpan()
|
|
77
|
+
if (!rootSpan) return
|
|
78
|
+
|
|
79
|
+
const state = this._getOrCreateState(rootSpan)
|
|
80
|
+
const { flagKey, context } = hookContext || {}
|
|
81
|
+
const { flagMetadata, variant, value } = evaluationDetails || {}
|
|
82
|
+
|
|
83
|
+
const serialId = flagMetadata?.__dd_split_serial_id
|
|
84
|
+
const doLog = flagMetadata?.__dd_do_log ?? false
|
|
85
|
+
const targetingKey = context?.targetingKey
|
|
86
|
+
|
|
87
|
+
if (serialId != null) {
|
|
88
|
+
state.addSerialId(serialId)
|
|
89
|
+
|
|
90
|
+
if (doLog && targetingKey) {
|
|
91
|
+
state.addSubject(targetingKey, serialId)
|
|
92
|
+
}
|
|
93
|
+
} else if (variant === undefined) {
|
|
94
|
+
state.addDefault(flagKey, value)
|
|
95
|
+
}
|
|
96
|
+
} catch (err) {
|
|
97
|
+
log.warn('SpanEnrichmentHook: error in finally hook: %s', err.message)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get the root span for the current trace context.
|
|
103
|
+
* The root span is always the first span in trace.started since spans
|
|
104
|
+
* are added in creation order and the root is created first.
|
|
105
|
+
*
|
|
106
|
+
* @returns {object|null} The root span, or null if no active span
|
|
107
|
+
* @private
|
|
108
|
+
*/
|
|
109
|
+
_getRootSpan () {
|
|
110
|
+
const span = this.#tracer.scope().active()
|
|
111
|
+
if (!span) return null
|
|
112
|
+
|
|
113
|
+
const trace = span.context()._trace
|
|
114
|
+
|
|
115
|
+
return trace?.started?.[0] ?? span
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get or create enrichment state for a span.
|
|
120
|
+
*
|
|
121
|
+
* @param {object} span - The span to get state for
|
|
122
|
+
* @returns {SpanEnrichmentState} The enrichment state
|
|
123
|
+
* @private
|
|
124
|
+
*/
|
|
125
|
+
_getOrCreateState (span) {
|
|
126
|
+
let state = this.#spanStates.get(span)
|
|
127
|
+
if (!state) {
|
|
128
|
+
state = new SpanEnrichmentState()
|
|
129
|
+
this.#spanStates.set(span, state)
|
|
130
|
+
}
|
|
131
|
+
return state
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Cleanup method to unsubscribe from channels.
|
|
136
|
+
* Should be called when the provider is shut down.
|
|
137
|
+
*/
|
|
138
|
+
destroy () {
|
|
139
|
+
finishCh.unsubscribe(this.#onSpanFinish)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = SpanEnrichmentHook
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const log = require('../log')
|
|
4
|
+
|
|
5
|
+
const { encodeDeltaVarint, hashTargetingKey } = require('./encoding')
|
|
6
|
+
|
|
7
|
+
const MAX_SERIAL_IDS = 200
|
|
8
|
+
const MAX_SUBJECTS = 10
|
|
9
|
+
const MAX_EXPERIMENTS_PER_SUBJECT = 20
|
|
10
|
+
const MAX_DEFAULTS = 5
|
|
11
|
+
const MAX_DEFAULT_VALUE_LENGTH = 64
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Manages feature flag enrichment state for a single root span.
|
|
15
|
+
* Accumulates serial IDs, subjects, and defaults throughout the span's lifetime.
|
|
16
|
+
*/
|
|
17
|
+
class SpanEnrichmentState {
|
|
18
|
+
constructor () {
|
|
19
|
+
/** @type {Set<number>} */
|
|
20
|
+
this._serialIds = new Set()
|
|
21
|
+
|
|
22
|
+
/** @type {Map<string, Set<number>>} hashed targeting key -> serial IDs */
|
|
23
|
+
this._subjects = new Map()
|
|
24
|
+
|
|
25
|
+
/** @type {Map<string, string>} flag key -> runtime default value */
|
|
26
|
+
this._defaults = new Map()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Add a serial ID from a flag evaluation.
|
|
31
|
+
*
|
|
32
|
+
* @param {number} serialId - The serial ID to add
|
|
33
|
+
* @returns {boolean} True if added, false if limit reached
|
|
34
|
+
*/
|
|
35
|
+
addSerialId (serialId) {
|
|
36
|
+
if (this._serialIds.size >= MAX_SERIAL_IDS) {
|
|
37
|
+
log.debug('SpanEnrichment: MAX_SERIAL_IDS limit (%d) reached, dropping serialId %d', MAX_SERIAL_IDS, serialId)
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
this._serialIds.add(serialId)
|
|
41
|
+
return true
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Add a subject (targeting key) with its associated serial ID.
|
|
46
|
+
* Only called when doLog=true.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} targetingKey - The targeting key (will be hashed)
|
|
49
|
+
* @param {number} serialId - The serial ID associated with this evaluation
|
|
50
|
+
* @returns {boolean} True if added, false if limit reached
|
|
51
|
+
*/
|
|
52
|
+
addSubject (targetingKey, serialId) {
|
|
53
|
+
const hashedKey = hashTargetingKey(targetingKey)
|
|
54
|
+
|
|
55
|
+
if (this._subjects.has(hashedKey)) {
|
|
56
|
+
const subjectIds = this._subjects.get(hashedKey)
|
|
57
|
+
if (subjectIds.size >= MAX_EXPERIMENTS_PER_SUBJECT) {
|
|
58
|
+
log.debug('SpanEnrichment: MAX_EXPERIMENTS_PER_SUBJECT limit (%d) reached for subject',
|
|
59
|
+
MAX_EXPERIMENTS_PER_SUBJECT)
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
subjectIds.add(serialId)
|
|
63
|
+
return true
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (this._subjects.size >= MAX_SUBJECTS) {
|
|
67
|
+
log.debug('SpanEnrichment: MAX_SUBJECTS limit (%d) reached, dropping subject', MAX_SUBJECTS)
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this._subjects.set(hashedKey, new Set([serialId]))
|
|
72
|
+
return true
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Add a default fallback for a flag not found in UFC.
|
|
77
|
+
*
|
|
78
|
+
* @param {string} flagKey - The flag key
|
|
79
|
+
* @param {boolean|string|number|object} defaultValue - The default value used
|
|
80
|
+
* @returns {boolean} True if added, false if limit reached
|
|
81
|
+
*/
|
|
82
|
+
addDefault (flagKey, defaultValue) {
|
|
83
|
+
if (this._defaults.has(flagKey)) {
|
|
84
|
+
return true
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (this._defaults.size >= MAX_DEFAULTS) {
|
|
88
|
+
log.debug('SpanEnrichment: MAX_DEFAULTS limit (%d) reached, dropping flag %s', MAX_DEFAULTS, flagKey)
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let valueStr = typeof defaultValue === 'object' && defaultValue !== null
|
|
93
|
+
? JSON.stringify(defaultValue)
|
|
94
|
+
: String(defaultValue)
|
|
95
|
+
|
|
96
|
+
if (valueStr.length > MAX_DEFAULT_VALUE_LENGTH) {
|
|
97
|
+
valueStr = valueStr.slice(0, MAX_DEFAULT_VALUE_LENGTH)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this._defaults.set(flagKey, valueStr)
|
|
101
|
+
return true
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Check if there is any enrichment data to add to the span.
|
|
106
|
+
* Note: _subjects is not checked because addSubject() is never called without first
|
|
107
|
+
* calling addSerialId(), so _subjects having data necessitates _serialIds having data.
|
|
108
|
+
*
|
|
109
|
+
* @returns {boolean} True if there is data to add
|
|
110
|
+
*/
|
|
111
|
+
hasData () {
|
|
112
|
+
return this._serialIds.size > 0 || this._defaults.size > 0
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Convert accumulated state to span tags.
|
|
117
|
+
*
|
|
118
|
+
* @returns {object} Object with ffe_flags_enc, ffe_subjects_enc, and ffe_runtime_defaults tags
|
|
119
|
+
*/
|
|
120
|
+
toSpanTags () {
|
|
121
|
+
const tags = {}
|
|
122
|
+
|
|
123
|
+
if (this._serialIds.size > 0) {
|
|
124
|
+
tags.ffe_flags_enc = encodeDeltaVarint(this._serialIds)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (this._subjects.size > 0) {
|
|
128
|
+
const subjectsObj = Object.fromEntries(
|
|
129
|
+
[...this._subjects].map(([key, ids]) => [key, encodeDeltaVarint(ids)])
|
|
130
|
+
)
|
|
131
|
+
tags.ffe_subjects_enc = JSON.stringify(subjectsObj)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (this._defaults.size > 0) {
|
|
135
|
+
tags.ffe_runtime_defaults = JSON.stringify(Object.fromEntries(this._defaults))
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return tags
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = {
|
|
143
|
+
SpanEnrichmentState,
|
|
144
|
+
MAX_SERIAL_IDS,
|
|
145
|
+
MAX_SUBJECTS,
|
|
146
|
+
MAX_EXPERIMENTS_PER_SUBJECT,
|
|
147
|
+
MAX_DEFAULTS,
|
|
148
|
+
MAX_DEFAULT_VALUE_LENGTH,
|
|
149
|
+
}
|
|
@@ -7,6 +7,7 @@ const { timeInputToHrTime } = require('../../../../vendor/dist/@opentelemetry/co
|
|
|
7
7
|
const { ERROR_MESSAGE, ERROR_STACK, ERROR_TYPE, IGNORE_OTEL_ERROR } = require('../constants')
|
|
8
8
|
const DatadogSpanContext = require('../opentracing/span_context')
|
|
9
9
|
const TraceState = require('../opentracing/propagation/tracestate')
|
|
10
|
+
const { DD_MAJOR } = require('../../../../version')
|
|
10
11
|
|
|
11
12
|
const id = require('../id')
|
|
12
13
|
|
|
@@ -176,8 +177,8 @@ function setOtelAttributes (ddSpan, attributes) {
|
|
|
176
177
|
function addOtelLink (ddSpan, link, attrs) {
|
|
177
178
|
if (!isWritable(ddSpan) || !link) return
|
|
178
179
|
|
|
179
|
-
//
|
|
180
|
-
const { context, attributes } = isOtelLink(link)
|
|
180
|
+
// v5 still accepts the legacy `addLink(context, attrs)` shape; v6 only takes `addLink(otel.Link)`.
|
|
181
|
+
const { context, attributes } = isOtelLink(link) || DD_MAJOR >= 6
|
|
181
182
|
? link
|
|
182
183
|
: { context: link, attributes: attrs ?? {} }
|
|
183
184
|
|
|
@@ -230,7 +231,7 @@ function recordException (ddSpan, exception, timeInput) {
|
|
|
230
231
|
[ERROR_TYPE]: exception.name,
|
|
231
232
|
[ERROR_MESSAGE]: exception.message,
|
|
232
233
|
[ERROR_STACK]: exception.stack,
|
|
233
|
-
[IGNORE_OTEL_ERROR]: ddSpan.context().
|
|
234
|
+
[IGNORE_OTEL_ERROR]: ddSpan.context().getTag(IGNORE_OTEL_ERROR) ?? true,
|
|
234
235
|
})
|
|
235
236
|
|
|
236
237
|
const attributes = {}
|
|
@@ -11,20 +11,31 @@ class LogPropagator {
|
|
|
11
11
|
inject (spanContext, carrier) {
|
|
12
12
|
if (!carrier) return
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
const dd = {}
|
|
15
|
+
let hasField = false
|
|
15
16
|
|
|
16
17
|
if (spanContext) {
|
|
17
|
-
|
|
18
|
+
dd.trace_id = this._config.traceId128BitGenerationEnabled &&
|
|
18
19
|
this._config.traceId128BitLoggingEnabled && spanContext._trace.tags['_dd.p.tid']
|
|
19
20
|
? spanContext.toTraceId(true)
|
|
20
21
|
: spanContext.toTraceId()
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
dd.span_id = spanContext.toSpanId()
|
|
23
|
+
hasField = true
|
|
24
|
+
}
|
|
25
|
+
if (this._config.service) {
|
|
26
|
+
dd.service = this._config.service
|
|
27
|
+
hasField = true
|
|
28
|
+
}
|
|
29
|
+
if (this._config.version) {
|
|
30
|
+
dd.version = this._config.version
|
|
31
|
+
hasField = true
|
|
32
|
+
}
|
|
33
|
+
if (this._config.env) {
|
|
34
|
+
dd.env = this._config.env
|
|
35
|
+
hasField = true
|
|
23
36
|
}
|
|
24
37
|
|
|
25
|
-
if (
|
|
26
|
-
if (this._config.version) carrier.dd.version = this._config.version
|
|
27
|
-
if (this._config.env) carrier.dd.env = this._config.env
|
|
38
|
+
if (hasField) carrier.dd = dd
|
|
28
39
|
}
|
|
29
40
|
|
|
30
41
|
extract (carrier) {
|
|
@@ -35,7 +35,6 @@ const b3FlagsKey = 'x-b3-flags'
|
|
|
35
35
|
const b3HeaderKey = 'b3'
|
|
36
36
|
const sqsdHeaderHey = 'x-aws-sqsd-attr-_datadog'
|
|
37
37
|
const b3HeaderExpr = /^(([0-9a-f]{16}){1,2}-[0-9a-f]{16}(-[01d](-[0-9a-f]{16})?)?|[01d])$/i
|
|
38
|
-
const baggageExpr = new RegExp(`^${baggagePrefix}(.+)$`)
|
|
39
38
|
// W3C Baggage key grammar: key = token (RFC 7230).
|
|
40
39
|
// Spec (up-to-date): "Propagation format for distributed context: Baggage" §3.3.1
|
|
41
40
|
// https://www.w3.org/TR/baggage/#header-content
|
|
@@ -56,6 +55,15 @@ const ddKeys = [traceKey, spanKey, samplingKey, originKey]
|
|
|
56
55
|
const b3Keys = [b3TraceKey, b3SpanKey, b3ParentKey, b3SampledKey, b3FlagsKey, b3HeaderKey]
|
|
57
56
|
const w3cKeys = [traceparentKey, tracestateKey]
|
|
58
57
|
const logKeys = [...ddKeys, ...b3Keys, ...w3cKeys]
|
|
58
|
+
// Dispatch table for `_extractSpanContext`. `'b3'` resolves to the matching
|
|
59
|
+
// single/multi extractor per instance — see `#b3MethodName` — so it is not in
|
|
60
|
+
// this table. `'baggage'` is consumed by `_extractBaggageItems`, not the loop.
|
|
61
|
+
const EXTRACT_STYLE_METHODS = new Map([
|
|
62
|
+
['datadog', '_extractDatadogContext'],
|
|
63
|
+
['tracecontext', '_extractTraceparentContext'],
|
|
64
|
+
['b3 single header', '_extractB3SingleContext'],
|
|
65
|
+
['b3multi', '_extractB3MultiContext'],
|
|
66
|
+
])
|
|
59
67
|
// Origin value in tracestate replaces '~', ',' and ';' with '_"
|
|
60
68
|
const tracestateOriginFilter = /[^\x20-\x2B\x2D-\x3A\x3C-\x7D]/g
|
|
61
69
|
// Tag keys in tracestate replace ' ', ',' and '=' with '_'
|
|
@@ -68,27 +76,29 @@ const hex16 = /^[0-9A-Fa-f]{16}$/
|
|
|
68
76
|
const percentByte = /%([0-9A-Fa-f]{2})/g
|
|
69
77
|
|
|
70
78
|
class TextMapPropagator {
|
|
71
|
-
#extractB3Context
|
|
72
|
-
|
|
73
79
|
/** @type {Set<string> | undefined} Cached `Set` view of `_config.baggageTagKeys`. */
|
|
74
80
|
#baggageTagKeysSet
|
|
75
81
|
|
|
76
82
|
/** @type {string[] | undefined} Source array that `#baggageTagKeysSet` was built from. */
|
|
77
83
|
#baggageTagKeysSetSource
|
|
78
84
|
|
|
85
|
+
/** @type {'_extractB3SingleContext' | '_extractB3MultiContext'} */
|
|
86
|
+
#b3MethodName
|
|
87
|
+
|
|
79
88
|
constructor (config) {
|
|
80
89
|
this._config = config
|
|
81
90
|
|
|
82
|
-
// v6: `'b3'` is always single-header. v5:
|
|
83
|
-
// single,
|
|
91
|
+
// v6: `'b3'` is always single-header. v5: `OTEL_PROPAGATORS` callers
|
|
92
|
+
// expect single, legacy `DD_TRACE_PROPAGATION_STYLE` callers expect multi.
|
|
93
|
+
/* istanbul ignore else: v5 fallback, master ships 6.0.0-pre */
|
|
84
94
|
if (DD_MAJOR >= 6) {
|
|
85
|
-
this.#
|
|
95
|
+
this.#b3MethodName = '_extractB3SingleContext'
|
|
86
96
|
} else {
|
|
87
97
|
const envName = getConfiguredEnvName('DD_TRACE_PROPAGATION_STYLE')
|
|
88
98
|
// eslint-disable-next-line eslint-rules/eslint-env-aliases
|
|
89
|
-
this.#
|
|
90
|
-
?
|
|
91
|
-
:
|
|
99
|
+
this.#b3MethodName = envName === 'OTEL_PROPAGATORS'
|
|
100
|
+
? '_extractB3SingleContext'
|
|
101
|
+
: '_extractB3MultiContext'
|
|
92
102
|
}
|
|
93
103
|
}
|
|
94
104
|
|
|
@@ -129,8 +139,7 @@ class TextMapPropagator {
|
|
|
129
139
|
|
|
130
140
|
extract (carrier) {
|
|
131
141
|
const spanContext = this._extractSpanContext(carrier)
|
|
132
|
-
|
|
133
|
-
if (!spanContext) return spanContext
|
|
142
|
+
if (spanContext === undefined) return null
|
|
134
143
|
|
|
135
144
|
if (extractCh.hasSubscribers) {
|
|
136
145
|
extractCh.publish({ spanContext, carrier })
|
|
@@ -292,7 +301,7 @@ class TextMapPropagator {
|
|
|
292
301
|
// v6 keeps `'b3 single header'` as a back-compat alias for callers that bypass parser normalisation.
|
|
293
302
|
const hasB3SingleHeader = this._hasPropagationStyle('inject', 'b3 single header') ||
|
|
294
303
|
(DD_MAJOR >= 6 && this._hasPropagationStyle('inject', 'b3'))
|
|
295
|
-
if (!hasB3SingleHeader) return
|
|
304
|
+
if (!hasB3SingleHeader) return
|
|
296
305
|
|
|
297
306
|
const traceId = this._getB3TraceId(spanContext)
|
|
298
307
|
const spanId = spanContext._spanId.toString(16)
|
|
@@ -361,7 +370,7 @@ class TextMapPropagator {
|
|
|
361
370
|
}
|
|
362
371
|
|
|
363
372
|
_hasTraceIdConflict (w3cSpanContext, firstSpanContext) {
|
|
364
|
-
return w3cSpanContext !==
|
|
373
|
+
return w3cSpanContext !== undefined &&
|
|
365
374
|
firstSpanContext.toTraceId(true) === w3cSpanContext.toTraceId(true) &&
|
|
366
375
|
firstSpanContext.toSpanId() !== w3cSpanContext.toSpanId()
|
|
367
376
|
}
|
|
@@ -372,7 +381,7 @@ class TextMapPropagator {
|
|
|
372
381
|
|
|
373
382
|
_updateParentIdFromDdHeaders (carrier, firstSpanContext) {
|
|
374
383
|
const ddCtx = this._extractDatadogContext(carrier)
|
|
375
|
-
if (ddCtx !==
|
|
384
|
+
if (ddCtx !== undefined) {
|
|
376
385
|
firstSpanContext._trace.tags[tags.DD_PARENT_ID] = ddCtx._spanId.toString().padStart(16, '0')
|
|
377
386
|
}
|
|
378
387
|
}
|
|
@@ -394,35 +403,20 @@ class TextMapPropagator {
|
|
|
394
403
|
}
|
|
395
404
|
|
|
396
405
|
_extractSpanContext (carrier) {
|
|
397
|
-
let context
|
|
406
|
+
let context
|
|
398
407
|
let style = ''
|
|
399
408
|
for (const extractor of this._config.tracePropagationStyle.extract) {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
break
|
|
405
|
-
case 'tracecontext':
|
|
406
|
-
extractedContext = this._extractTraceparentContext(carrier)
|
|
407
|
-
break
|
|
408
|
-
case 'b3 single header':
|
|
409
|
-
extractedContext = this._extractB3SingleContext(carrier)
|
|
410
|
-
break
|
|
411
|
-
case 'b3':
|
|
412
|
-
extractedContext = this.#extractB3Context(carrier)
|
|
413
|
-
break
|
|
414
|
-
case 'b3multi':
|
|
415
|
-
extractedContext = this._extractB3MultiContext(carrier)
|
|
416
|
-
break
|
|
417
|
-
default:
|
|
418
|
-
if (extractor !== 'baggage') log.warn('Unknown propagation style:', extractor)
|
|
409
|
+
const method = extractor === 'b3' ? this.#b3MethodName : EXTRACT_STYLE_METHODS.get(extractor)
|
|
410
|
+
if (method === undefined) {
|
|
411
|
+
if (extractor !== 'baggage') log.warn('Unknown propagation style:', extractor)
|
|
412
|
+
continue
|
|
419
413
|
}
|
|
420
|
-
|
|
421
|
-
if (extractedContext ===
|
|
414
|
+
const extractedContext = this[method](carrier)
|
|
415
|
+
if (extractedContext === undefined) {
|
|
422
416
|
continue
|
|
423
417
|
}
|
|
424
418
|
|
|
425
|
-
if (context ===
|
|
419
|
+
if (context === undefined) {
|
|
426
420
|
context = extractedContext
|
|
427
421
|
style = extractor
|
|
428
422
|
if (this._config.DD_TRACE_PROPAGATION_EXTRACT_FIRST) {
|
|
@@ -446,9 +440,7 @@ class TextMapPropagator {
|
|
|
446
440
|
}
|
|
447
441
|
|
|
448
442
|
if (this._config.DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT === 'ignore') {
|
|
449
|
-
|
|
450
|
-
// the SQSD context if present, otherwise the request runs untraced.
|
|
451
|
-
if (context) context._links = []
|
|
443
|
+
if (context !== undefined) context._links = []
|
|
452
444
|
} else {
|
|
453
445
|
if (this._config.DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT === 'restart' && context) {
|
|
454
446
|
context._links = []
|
|
@@ -490,14 +482,17 @@ class TextMapPropagator {
|
|
|
490
482
|
|
|
491
483
|
_extractB3MultiContext (carrier) {
|
|
492
484
|
const b3 = this._extractB3MultipleHeaders(carrier)
|
|
493
|
-
if (
|
|
485
|
+
if (b3 === undefined) return
|
|
494
486
|
return this._extractB3Context(b3)
|
|
495
487
|
}
|
|
496
488
|
|
|
497
489
|
_extractB3SingleContext (carrier) {
|
|
498
|
-
|
|
490
|
+
// `typeof === 'string'` first; otherwise the regex coerces `undefined` to
|
|
491
|
+
// `'undefined'` and runs on every header-less request.
|
|
492
|
+
const header = carrier[b3HeaderKey]
|
|
493
|
+
if (typeof header !== 'string' || !b3HeaderExpr.test(header)) return
|
|
499
494
|
const b3 = this._extractB3SingleHeader(carrier)
|
|
500
|
-
if (
|
|
495
|
+
if (b3 === undefined) return
|
|
501
496
|
return this._extractB3Context(b3)
|
|
502
497
|
}
|
|
503
498
|
|
|
@@ -527,23 +522,19 @@ class TextMapPropagator {
|
|
|
527
522
|
|
|
528
523
|
_extractSqsdContext (carrier) {
|
|
529
524
|
const headerValue = carrier[sqsdHeaderHey]
|
|
530
|
-
if (!headerValue)
|
|
531
|
-
return null
|
|
532
|
-
}
|
|
525
|
+
if (!headerValue) return
|
|
533
526
|
let parsed
|
|
534
527
|
try {
|
|
535
528
|
parsed = JSON.parse(headerValue)
|
|
536
529
|
} catch {
|
|
537
|
-
return
|
|
530
|
+
return
|
|
538
531
|
}
|
|
539
532
|
return this._extractDatadogContext(parsed)
|
|
540
533
|
}
|
|
541
534
|
|
|
542
535
|
_extractTraceparentContext (carrier) {
|
|
543
536
|
const headerValue = carrier[traceparentKey]
|
|
544
|
-
if (typeof headerValue !== 'string')
|
|
545
|
-
return null
|
|
546
|
-
}
|
|
537
|
+
if (typeof headerValue !== 'string') return
|
|
547
538
|
const matches = headerValue.trim().match(traceparentExpr)
|
|
548
539
|
if (matches !== null) {
|
|
549
540
|
const [, version, traceId, spanId, flags, tail] = matches
|
|
@@ -554,14 +545,14 @@ class TextMapPropagator {
|
|
|
554
545
|
? carrier.tracestate.filter(item => typeof item === 'string').join(',')
|
|
555
546
|
: carrier.tracestate
|
|
556
547
|
const tracestate = TraceState.fromString(rawTracestate)
|
|
557
|
-
if (invalidSegment.test(traceId)) return
|
|
558
|
-
if (invalidSegment.test(spanId)) return
|
|
548
|
+
if (invalidSegment.test(traceId)) return
|
|
549
|
+
if (invalidSegment.test(spanId)) return
|
|
559
550
|
|
|
560
551
|
// Version ff is considered invalid
|
|
561
|
-
if (version === 'ff') return
|
|
552
|
+
if (version === 'ff') return
|
|
562
553
|
|
|
563
554
|
// Version 00 should have no tail, but future versions may
|
|
564
|
-
if (tail && version === '00') return
|
|
555
|
+
if (tail && version === '00') return
|
|
565
556
|
|
|
566
557
|
const spanContext = new DatadogSpanContext({
|
|
567
558
|
traceId: id(traceId, 16),
|
|
@@ -624,12 +615,11 @@ class TextMapPropagator {
|
|
|
624
615
|
this._extractLegacyBaggageItems(carrier, spanContext)
|
|
625
616
|
return spanContext
|
|
626
617
|
}
|
|
627
|
-
return null
|
|
628
618
|
}
|
|
629
619
|
|
|
630
620
|
_extractGenericContext (carrier, traceKey, spanKey, radix) {
|
|
631
621
|
if (carrier && carrier[traceKey] && carrier[spanKey]) {
|
|
632
|
-
if (invalidSegment.test(carrier[traceKey])) return
|
|
622
|
+
if (invalidSegment.test(carrier[traceKey])) return
|
|
633
623
|
|
|
634
624
|
return new DatadogSpanContext({
|
|
635
625
|
traceId: id(carrier[traceKey], radix),
|
|
@@ -637,11 +627,17 @@ class TextMapPropagator {
|
|
|
637
627
|
isRemote: true,
|
|
638
628
|
})
|
|
639
629
|
}
|
|
640
|
-
|
|
641
|
-
return null
|
|
642
630
|
}
|
|
643
631
|
|
|
644
632
|
_extractB3MultipleHeaders (carrier) {
|
|
633
|
+
// `b3ParentKey` is intentionally absent: this method never consults it,
|
|
634
|
+
// so a parent-id-only carrier should bail with the rest.
|
|
635
|
+
if (carrier[b3TraceKey] === undefined &&
|
|
636
|
+
carrier[b3SampledKey] === undefined &&
|
|
637
|
+
carrier[b3FlagsKey] === undefined) {
|
|
638
|
+
return
|
|
639
|
+
}
|
|
640
|
+
|
|
645
641
|
let empty = true
|
|
646
642
|
const b3 = {}
|
|
647
643
|
|
|
@@ -661,12 +657,12 @@ class TextMapPropagator {
|
|
|
661
657
|
empty = false
|
|
662
658
|
}
|
|
663
659
|
|
|
664
|
-
return empty ?
|
|
660
|
+
return empty ? undefined : b3
|
|
665
661
|
}
|
|
666
662
|
|
|
667
663
|
_extractB3SingleHeader (carrier) {
|
|
668
664
|
const header = carrier[b3HeaderKey]
|
|
669
|
-
if (!header) return
|
|
665
|
+
if (!header) return
|
|
670
666
|
|
|
671
667
|
const parts = header.split('-')
|
|
672
668
|
|
|
@@ -705,13 +701,12 @@ class TextMapPropagator {
|
|
|
705
701
|
}
|
|
706
702
|
|
|
707
703
|
_extractLegacyBaggageItems (carrier, spanContext) {
|
|
708
|
-
if (this._config.legacyBaggageEnabled)
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
}
|
|
704
|
+
if (!this._config.legacyBaggageEnabled) return
|
|
705
|
+
for (const key of Object.keys(carrier)) {
|
|
706
|
+
if (!key.startsWith(baggagePrefix)) continue
|
|
707
|
+
const baggageKey = key.slice(baggagePrefix.length)
|
|
708
|
+
if (baggageKey) {
|
|
709
|
+
spanContext._baggageItems[baggageKey] = carrier[key]
|
|
715
710
|
}
|
|
716
711
|
}
|
|
717
712
|
}
|