dd-trace 6.2.0 → 6.4.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/README.md +1 -1
- package/index.electron.js +3 -0
- package/package.json +8 -5
- package/packages/datadog-instrumentations/src/ai.js +29 -24
- package/packages/datadog-instrumentations/src/cookie.js +7 -1
- package/packages/datadog-instrumentations/src/cucumber.js +4 -3
- package/packages/datadog-instrumentations/src/express.js +20 -2
- package/packages/datadog-instrumentations/src/grpc/server.js +18 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/modelcontextprotocol-sdk.js +30 -54
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +8 -8
- package/packages/datadog-instrumentations/src/http/server.js +27 -0
- package/packages/datadog-instrumentations/src/http2/server.js +143 -17
- package/packages/datadog-instrumentations/src/jest.js +10 -4
- package/packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js +248 -1
- package/packages/datadog-instrumentations/src/playwright.js +40 -3
- package/packages/datadog-instrumentations/src/router.js +180 -105
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +41 -16
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +21 -21
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +14 -8
- package/packages/datadog-plugin-aws-sdk/src/util.js +0 -22
- package/packages/datadog-plugin-azure-functions/src/index.js +2 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +5 -5
- package/packages/datadog-plugin-graphql/src/execute.js +90 -14
- package/packages/datadog-plugin-graphql/src/parse.js +22 -1
- package/packages/datadog-plugin-graphql/src/request.js +33 -2
- package/packages/datadog-plugin-graphql/src/utils.js +42 -0
- package/packages/datadog-plugin-graphql/src/validate.js +13 -1
- package/packages/datadog-plugin-http2/src/server.js +38 -6
- package/packages/datadog-plugin-jest/src/util.js +21 -6
- package/packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js +144 -26
- package/packages/datadog-plugin-modelcontextprotocol-sdk/src/utils.js +30 -0
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +4 -2
- package/packages/datadog-shimmer/src/shimmer.js +9 -2
- package/packages/dd-trace/index.electron.js +3 -0
- package/packages/dd-trace/index.js +2 -37
- package/packages/dd-trace/src/appsec/channels.js +1 -0
- package/packages/dd-trace/src/appsec/index.js +6 -3
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +1 -1
- package/packages/dd-trace/src/bootstrap.js +39 -0
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +23 -16
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +14 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +22 -6
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +5 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +55 -5
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +8 -1
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
- package/packages/dd-trace/src/config/config-types.d.ts +8 -0
- package/packages/dd-trace/src/config/defaults.js +20 -15
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
- package/packages/dd-trace/src/config/index.js +13 -3
- package/packages/dd-trace/src/config/parsers.js +79 -0
- package/packages/dd-trace/src/config/supported-configurations.json +17 -0
- package/packages/dd-trace/src/constants.js +7 -0
- package/packages/dd-trace/src/datastreams/pathway.js +6 -4
- package/packages/dd-trace/src/feature-registry.js +22 -0
- package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
- package/packages/dd-trace/src/llmobs/index.js +10 -2
- package/packages/dd-trace/src/llmobs/tagger.js +19 -1
- package/packages/dd-trace/src/noop/proxy.js +5 -4
- package/packages/dd-trace/src/openfeature/register.js +46 -0
- package/packages/dd-trace/src/opentelemetry/span_processor.js +7 -5
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +32 -16
- package/packages/dd-trace/src/opentracing/propagation/log.js +11 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +80 -14
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +10 -1
- package/packages/dd-trace/src/opentracing/tracer.js +7 -1
- package/packages/dd-trace/src/plugins/util/test.js +9 -4
- package/packages/dd-trace/src/plugins/util/web.js +12 -0
- package/packages/dd-trace/src/proxy.js +21 -9
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +20 -11
- package/packages/dd-trace/src/standalone/index.js +0 -22
- 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
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { registerFeature } = require('../feature-registry')
|
|
4
|
+
|
|
5
|
+
const noop = new (require('./noop'))()
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {import('../proxy')} proxy
|
|
9
|
+
* @returns {boolean}
|
|
10
|
+
*/
|
|
11
|
+
function hasFlaggingProvider (proxy) {
|
|
12
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(proxy, 'openfeature')
|
|
13
|
+
|
|
14
|
+
return descriptor?.value !== undefined && descriptor.value !== noop
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
registerFeature({
|
|
18
|
+
name: 'openfeature',
|
|
19
|
+
noop,
|
|
20
|
+
factory: () => require('./index'),
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {object} rc - RemoteConfig instance
|
|
24
|
+
* @param {import('../config/config-base')} config
|
|
25
|
+
* @param {import('../proxy')} proxy
|
|
26
|
+
*/
|
|
27
|
+
remoteConfig (rc, config, proxy) {
|
|
28
|
+
const openfeatureRemoteConfig = require('./remote_config')
|
|
29
|
+
openfeatureRemoteConfig.enable(rc, config, () => proxy.openfeature)
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {import('../config/config-base')} config
|
|
34
|
+
* @param {import('../tracer')} tracer
|
|
35
|
+
* @param {import('../proxy')} proxy
|
|
36
|
+
* @param {Function} lazyProxy
|
|
37
|
+
*/
|
|
38
|
+
enable (config, tracer, proxy, lazyProxy) {
|
|
39
|
+
if (config.experimental.flaggingProvider.enabled) {
|
|
40
|
+
proxy._modules.openfeature.enable(config)
|
|
41
|
+
if (!hasFlaggingProvider(proxy)) {
|
|
42
|
+
lazyProxy(proxy, 'openfeature', () => require('./flagging_provider'), tracer, config)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
})
|
|
@@ -14,32 +14,34 @@ class NoopSpanProcessor {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
class MultiSpanProcessor extends NoopSpanProcessor {
|
|
17
|
+
#processors
|
|
18
|
+
|
|
17
19
|
constructor (spanProcessors) {
|
|
18
20
|
super()
|
|
19
|
-
this
|
|
21
|
+
this.#processors = spanProcessors
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
forceFlush () {
|
|
23
25
|
return Promise.all(
|
|
24
|
-
this.
|
|
26
|
+
this.#processors.map(p => p.forceFlush())
|
|
25
27
|
)
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
onStart (span, context) {
|
|
29
|
-
for (const processor of this
|
|
31
|
+
for (const processor of this.#processors) {
|
|
30
32
|
processor.onStart(span, context)
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
onEnd (span) {
|
|
35
|
-
for (const processor of this
|
|
37
|
+
for (const processor of this.#processors) {
|
|
36
38
|
processor.onEnd(span)
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
shutdown () {
|
|
41
43
|
return Promise.all(
|
|
42
|
-
this.
|
|
44
|
+
this.#processors.map(p => p.shutdown())
|
|
43
45
|
)
|
|
44
46
|
}
|
|
45
47
|
}
|
|
@@ -10,46 +10,62 @@ const { MultiSpanProcessor, NoopSpanProcessor } = require('./span_processor')
|
|
|
10
10
|
const Tracer = require('./tracer')
|
|
11
11
|
|
|
12
12
|
class TracerProvider {
|
|
13
|
+
#activeProcessor = new NoopSpanProcessor()
|
|
14
|
+
#contextManager = new ContextManager()
|
|
15
|
+
#processors = []
|
|
16
|
+
#tracers = new Map()
|
|
17
|
+
|
|
13
18
|
constructor (config = {}) {
|
|
14
19
|
this.config = config
|
|
15
20
|
this.resource = config.resource
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
// @opentelemetry/sdk-trace 2.x (used by @opentelemetry/sdk-node 0.220+)
|
|
23
|
+
// dropped `addSpanProcessor` and hands the processors to the provider
|
|
24
|
+
// constructor instead. Wire them the same way the 1.x `addSpanProcessor`
|
|
25
|
+
// path does, so a NodeSDK configured with a trace exporter or custom
|
|
26
|
+
// processors still delivers onStart/onEnd to them.
|
|
27
|
+
if (Array.isArray(config.spanProcessors)) {
|
|
28
|
+
for (const spanProcessor of config.spanProcessors) {
|
|
29
|
+
this.addSpanProcessor(spanProcessor)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
21
32
|
}
|
|
22
33
|
|
|
23
34
|
getTracer (name = 'opentelemetry', version = '0.0.0', options) {
|
|
24
35
|
const key = `${name}@${version}`
|
|
25
|
-
if (!this.
|
|
26
|
-
this.
|
|
36
|
+
if (!this.#tracers.has(key)) {
|
|
37
|
+
this.#tracers.set(key, new Tracer(
|
|
27
38
|
{ ...options, name, version },
|
|
28
39
|
this.config,
|
|
29
40
|
this
|
|
30
41
|
))
|
|
31
42
|
}
|
|
32
|
-
return this.
|
|
43
|
+
return this.#tracers.get(key)
|
|
33
44
|
}
|
|
34
45
|
|
|
46
|
+
/**
|
|
47
|
+
* @param {NoopSpanProcessor} spanProcessor
|
|
48
|
+
*/
|
|
35
49
|
addSpanProcessor (spanProcessor) {
|
|
36
|
-
if (
|
|
37
|
-
|
|
50
|
+
if (this.#processors.includes(spanProcessor)) return
|
|
51
|
+
|
|
52
|
+
if (!this.#processors.length) {
|
|
53
|
+
this.#activeProcessor.shutdown()
|
|
38
54
|
}
|
|
39
|
-
this.
|
|
40
|
-
this
|
|
41
|
-
this
|
|
55
|
+
this.#processors.push(spanProcessor)
|
|
56
|
+
this.#activeProcessor = new MultiSpanProcessor(
|
|
57
|
+
this.#processors
|
|
42
58
|
)
|
|
43
59
|
}
|
|
44
60
|
|
|
45
61
|
getActiveSpanProcessor () {
|
|
46
|
-
return this
|
|
62
|
+
return this.#activeProcessor
|
|
47
63
|
}
|
|
48
64
|
|
|
49
65
|
// Not actually required by the SDK spec, but the official Node.js SDK does
|
|
50
66
|
// this and the docs reflect that so we should do this too for familiarity.
|
|
51
67
|
register (config = {}) {
|
|
52
|
-
context.setGlobalContextManager(this
|
|
68
|
+
context.setGlobalContextManager(this.#contextManager)
|
|
53
69
|
if (!trace.setGlobalTracerProvider(this)) {
|
|
54
70
|
trace.getTracerProvider().setDelegate(this)
|
|
55
71
|
}
|
|
@@ -69,11 +85,11 @@ class TracerProvider {
|
|
|
69
85
|
}
|
|
70
86
|
|
|
71
87
|
exporter._writer?.flush()
|
|
72
|
-
return this.
|
|
88
|
+
return this.#activeProcessor.forceFlush()
|
|
73
89
|
}
|
|
74
90
|
|
|
75
91
|
shutdown () {
|
|
76
|
-
return this.
|
|
92
|
+
return this.#activeProcessor.shutdown()
|
|
77
93
|
}
|
|
78
94
|
}
|
|
79
95
|
|
|
@@ -8,8 +8,13 @@ class LogPropagator {
|
|
|
8
8
|
this._config = config
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* @param {DatadogSpanContext | null | undefined} spanContext
|
|
13
|
+
* @param {Record<string, unknown>} [carrier]
|
|
14
|
+
* @returns {Record<string, unknown> | undefined}
|
|
15
|
+
*/
|
|
11
16
|
inject (spanContext, carrier) {
|
|
12
|
-
if (
|
|
17
|
+
if (carrier === null) return
|
|
13
18
|
|
|
14
19
|
const dd = {}
|
|
15
20
|
let hasField = false
|
|
@@ -35,7 +40,11 @@ class LogPropagator {
|
|
|
35
40
|
hasField = true
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
if (hasField)
|
|
43
|
+
if (!hasField) return
|
|
44
|
+
|
|
45
|
+
carrier ??= {}
|
|
46
|
+
carrier.dd = dd
|
|
47
|
+
return carrier
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
extract (carrier) {
|
|
@@ -8,6 +8,7 @@ const log = require('../../log')
|
|
|
8
8
|
const tags = require('../../../../../ext/tags')
|
|
9
9
|
const { getConfiguredEnvName } = require('../../config/helper')
|
|
10
10
|
const { setAllBaggageItems, getAllBaggageItems, removeAllBaggageItems } = require('../../baggage')
|
|
11
|
+
const { hasTraceSourcePropagationTag } = require('../../standalone/tracesource')
|
|
11
12
|
const telemetryMetrics = require('../../telemetry/metrics')
|
|
12
13
|
const { DD_MAJOR } = require('../../../../../version')
|
|
13
14
|
|
|
@@ -119,22 +120,35 @@ class TextMapPropagator {
|
|
|
119
120
|
return this.#baggageTagKeysSet
|
|
120
121
|
}
|
|
121
122
|
|
|
123
|
+
/**
|
|
124
|
+
* @param {DatadogSpanContext | null | undefined} spanContext
|
|
125
|
+
* @param {Record<string, string>} [carrier]
|
|
126
|
+
* @returns {Record<string, string> | undefined}
|
|
127
|
+
*/
|
|
122
128
|
inject (spanContext, carrier) {
|
|
123
|
-
if (
|
|
124
|
-
this._injectBaggageItems(spanContext, carrier)
|
|
125
|
-
if (!spanContext) return
|
|
129
|
+
if (carrier === null) return
|
|
130
|
+
let injectedCarrier = this._injectBaggageItems(spanContext, carrier)
|
|
131
|
+
if (!spanContext) return injectedCarrier
|
|
126
132
|
|
|
127
|
-
this.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
injectCh.publish({ spanContext, carrier })
|
|
133
|
+
const injectTraceContext = this._config.apmTracingEnabled !== false ||
|
|
134
|
+
hasTraceSourcePropagationTag(spanContext._trace.tags)
|
|
135
|
+
if (injectTraceContext) {
|
|
136
|
+
injectedCarrier = this._injectDatadog(spanContext, injectedCarrier ?? carrier) ?? injectedCarrier
|
|
137
|
+
injectedCarrier = this._injectB3MultipleHeaders(spanContext, injectedCarrier ?? carrier) ?? injectedCarrier
|
|
138
|
+
injectedCarrier = this._injectB3SingleHeader(spanContext, injectedCarrier ?? carrier) ?? injectedCarrier
|
|
134
139
|
}
|
|
140
|
+
injectedCarrier = this
|
|
141
|
+
._injectTraceparent(spanContext, injectedCarrier ?? carrier, injectTraceContext) ?? injectedCarrier
|
|
142
|
+
|
|
143
|
+
if (injectedCarrier === undefined) return
|
|
144
|
+
|
|
145
|
+
carrier = injectedCarrier
|
|
146
|
+
if (injectCh.hasSubscribers) injectCh.publish({ spanContext, carrier })
|
|
135
147
|
|
|
136
148
|
// eslint-disable-next-line eslint-rules/eslint-log-printf-style
|
|
137
149
|
log.debug(() => `Inject into carrier: ${JSON.stringify(pick(carrier, logKeys))}.`)
|
|
150
|
+
|
|
151
|
+
return carrier
|
|
138
152
|
}
|
|
139
153
|
|
|
140
154
|
extract (carrier) {
|
|
@@ -156,15 +170,23 @@ class TextMapPropagator {
|
|
|
156
170
|
return spanContext
|
|
157
171
|
}
|
|
158
172
|
|
|
173
|
+
/**
|
|
174
|
+
* @param {DatadogSpanContext} spanContext
|
|
175
|
+
* @param {Record<string, string>} [carrier]
|
|
176
|
+
* @returns {Record<string, string> | undefined}
|
|
177
|
+
*/
|
|
159
178
|
_injectDatadog (spanContext, carrier) {
|
|
160
179
|
if (!this._hasPropagationStyle('inject', 'datadog')) return
|
|
161
180
|
|
|
181
|
+
carrier ??= {}
|
|
162
182
|
carrier[traceKey] = spanContext.toTraceId()
|
|
163
183
|
carrier[spanKey] = spanContext.toSpanId()
|
|
164
184
|
|
|
165
185
|
this._injectOrigin(spanContext, carrier)
|
|
166
186
|
this._injectSamplingPriority(spanContext, carrier)
|
|
167
187
|
this._injectTags(spanContext, carrier)
|
|
188
|
+
|
|
189
|
+
return carrier
|
|
168
190
|
}
|
|
169
191
|
|
|
170
192
|
_injectOrigin (spanContext, carrier) {
|
|
@@ -183,7 +205,13 @@ class TextMapPropagator {
|
|
|
183
205
|
}
|
|
184
206
|
}
|
|
185
207
|
|
|
208
|
+
/**
|
|
209
|
+
* @param {DatadogSpanContext | null | undefined} spanContext
|
|
210
|
+
* @param {Record<string, string>} [carrier]
|
|
211
|
+
* @returns {Record<string, string> | undefined}
|
|
212
|
+
*/
|
|
186
213
|
_injectBaggageItems (spanContext, carrier) {
|
|
214
|
+
let injectedCarrier
|
|
187
215
|
if (this._config.legacyBaggageEnabled) {
|
|
188
216
|
const baggageItems = spanContext?._baggageItems
|
|
189
217
|
if (baggageItems) {
|
|
@@ -202,7 +230,8 @@ class TextMapPropagator {
|
|
|
202
230
|
if (invalidHeaderValueCharExpr.test(headerValue)) {
|
|
203
231
|
headerValue = encodeURIComponent(headerValue)
|
|
204
232
|
}
|
|
205
|
-
carrier
|
|
233
|
+
injectedCarrier ??= carrier ?? {}
|
|
234
|
+
injectedCarrier[headerName] = headerValue
|
|
206
235
|
}
|
|
207
236
|
}
|
|
208
237
|
}
|
|
@@ -213,7 +242,6 @@ class TextMapPropagator {
|
|
|
213
242
|
let byteCounter = 0
|
|
214
243
|
|
|
215
244
|
const baggageItems = getAllBaggageItems()
|
|
216
|
-
if (!baggageItems) return
|
|
217
245
|
for (const key of Object.keys(baggageItems)) {
|
|
218
246
|
const baggageKey = key.trim()
|
|
219
247
|
if (!baggageTokenExpr.test(baggageKey)) continue
|
|
@@ -242,10 +270,13 @@ class TextMapPropagator {
|
|
|
242
270
|
|
|
243
271
|
baggage = baggage.slice(0, -1)
|
|
244
272
|
if (baggage) {
|
|
245
|
-
carrier
|
|
273
|
+
injectedCarrier ??= carrier ?? {}
|
|
274
|
+
injectedCarrier.baggage = baggage
|
|
246
275
|
tracerMetrics.count('context_header_style.injected', ['header_style:baggage']).inc()
|
|
247
276
|
}
|
|
248
277
|
}
|
|
278
|
+
|
|
279
|
+
return injectedCarrier
|
|
249
280
|
}
|
|
250
281
|
|
|
251
282
|
_injectTags (spanContext, carrier) {
|
|
@@ -278,12 +309,18 @@ class TextMapPropagator {
|
|
|
278
309
|
}
|
|
279
310
|
}
|
|
280
311
|
|
|
312
|
+
/**
|
|
313
|
+
* @param {DatadogSpanContext} spanContext
|
|
314
|
+
* @param {Record<string, string>} [carrier]
|
|
315
|
+
* @returns {Record<string, string> | undefined}
|
|
316
|
+
*/
|
|
281
317
|
_injectB3MultipleHeaders (spanContext, carrier) {
|
|
282
318
|
// v5 also accepts the legacy `'b3'` spelling for multi; v6 routes `'b3'` to single-header.
|
|
283
319
|
const hasB3multi = this._hasPropagationStyle('inject', 'b3multi') ||
|
|
284
320
|
(DD_MAJOR < 6 && this._hasPropagationStyle('inject', 'b3'))
|
|
285
321
|
if (!hasB3multi) return
|
|
286
322
|
|
|
323
|
+
carrier ??= {}
|
|
287
324
|
carrier[b3TraceKey] = spanContext._traceId.toTraceIdHex(spanContext._trace.tags['_dd.p.tid'])
|
|
288
325
|
carrier[b3SpanKey] = spanContext._spanId.toString(16)
|
|
289
326
|
carrier[b3SampledKey] = spanContext._sampling.priority >= AUTO_KEEP ? '1' : '0'
|
|
@@ -295,14 +332,22 @@ class TextMapPropagator {
|
|
|
295
332
|
if (spanContext._parentId) {
|
|
296
333
|
carrier[b3ParentKey] = spanContext._parentId.toString(16)
|
|
297
334
|
}
|
|
335
|
+
|
|
336
|
+
return carrier
|
|
298
337
|
}
|
|
299
338
|
|
|
339
|
+
/**
|
|
340
|
+
* @param {DatadogSpanContext} spanContext
|
|
341
|
+
* @param {Record<string, string>} [carrier]
|
|
342
|
+
* @returns {Record<string, string> | undefined}
|
|
343
|
+
*/
|
|
300
344
|
_injectB3SingleHeader (spanContext, carrier) {
|
|
301
345
|
// v6 keeps `'b3 single header'` as a back-compat alias for callers that bypass parser normalisation.
|
|
302
346
|
const hasB3SingleHeader = this._hasPropagationStyle('inject', 'b3 single header') ||
|
|
303
347
|
(DD_MAJOR >= 6 && this._hasPropagationStyle('inject', 'b3'))
|
|
304
348
|
if (!hasB3SingleHeader) return
|
|
305
349
|
|
|
350
|
+
carrier ??= {}
|
|
306
351
|
const traceId = spanContext._traceId.toTraceIdHex(spanContext._trace.tags['_dd.p.tid'])
|
|
307
352
|
const spanId = spanContext._spanId.toString(16)
|
|
308
353
|
const sampled = spanContext._sampling.priority >= AUTO_KEEP ? '1' : '0'
|
|
@@ -311,11 +356,30 @@ class TextMapPropagator {
|
|
|
311
356
|
if (spanContext._parentId) {
|
|
312
357
|
carrier[b3HeaderKey] += '-' + spanContext._parentId.toString(16)
|
|
313
358
|
}
|
|
359
|
+
|
|
360
|
+
return carrier
|
|
314
361
|
}
|
|
315
362
|
|
|
316
|
-
|
|
363
|
+
/**
|
|
364
|
+
* @param {DatadogSpanContext} spanContext
|
|
365
|
+
* @param {Record<string, string> | undefined} carrier
|
|
366
|
+
* @param {boolean} injectTraceContext
|
|
367
|
+
* @returns {Record<string, string> | undefined}
|
|
368
|
+
*/
|
|
369
|
+
_injectTraceparent (spanContext, carrier, injectTraceContext) {
|
|
317
370
|
if (!this._hasPropagationStyle('inject', 'tracecontext')) return
|
|
318
371
|
|
|
372
|
+
if (!injectTraceContext) {
|
|
373
|
+
const tracestate = TraceState.fromString(spanContext._tracestate?.toString())
|
|
374
|
+
tracestate.delete('dd')
|
|
375
|
+
const header = tracestate.toString()
|
|
376
|
+
if (!header) return
|
|
377
|
+
carrier ??= {}
|
|
378
|
+
carrier.tracestate = header
|
|
379
|
+
return carrier
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
carrier ??= {}
|
|
319
383
|
const {
|
|
320
384
|
_sampling: { priority, mechanism },
|
|
321
385
|
_tracestate: ts = new TraceState(),
|
|
@@ -363,6 +427,8 @@ class TextMapPropagator {
|
|
|
363
427
|
})
|
|
364
428
|
|
|
365
429
|
carrier.tracestate = ts.toString()
|
|
430
|
+
|
|
431
|
+
return carrier
|
|
366
432
|
}
|
|
367
433
|
|
|
368
434
|
_hasPropagationStyle (mode, name) {
|
|
@@ -13,13 +13,22 @@ class DSMTextMapPropagator {
|
|
|
13
13
|
this.config = config
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* @param {object} ctx DSM pathway context.
|
|
18
|
+
* @param {Record<string, string>} [carrier]
|
|
19
|
+
* @returns {Record<string, string> | undefined}
|
|
20
|
+
*/
|
|
16
21
|
inject (ctx, carrier) {
|
|
17
22
|
if (!this.config.dsmEnabled) return
|
|
18
23
|
|
|
19
|
-
DsmPathwayCodec.encode(ctx, carrier)
|
|
24
|
+
const injectedCarrier = DsmPathwayCodec.encode(ctx, carrier)
|
|
25
|
+
if (injectedCarrier === undefined) return
|
|
20
26
|
|
|
27
|
+
carrier = injectedCarrier
|
|
21
28
|
// eslint-disable-next-line eslint-rules/eslint-log-printf-style
|
|
22
29
|
log.debug(() => `Inject into carrier (DSM): ${JSON.stringify(pick(carrier, logKeys))}.`)
|
|
30
|
+
|
|
31
|
+
return carrier
|
|
23
32
|
}
|
|
24
33
|
|
|
25
34
|
extract (carrier) {
|
|
@@ -101,6 +101,12 @@ class DatadogTracer {
|
|
|
101
101
|
return span
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* @param {Span|SpanContext} context
|
|
106
|
+
* @param {string} format
|
|
107
|
+
* @param {object} [carrier]
|
|
108
|
+
* @returns {object | undefined}
|
|
109
|
+
*/
|
|
104
110
|
inject (context, format, carrier) {
|
|
105
111
|
if (context instanceof Span) {
|
|
106
112
|
context = context.context()
|
|
@@ -110,7 +116,7 @@ class DatadogTracer {
|
|
|
110
116
|
if (format !== 'text_map_dsm' && format !== formats.LOG) {
|
|
111
117
|
this._prioritySampler.sample(context)
|
|
112
118
|
}
|
|
113
|
-
this._propagators[format].inject(context, carrier)
|
|
119
|
+
return this._propagators[format].inject(context, carrier)
|
|
114
120
|
} catch (e) {
|
|
115
121
|
log.error('Error injecting trace', e)
|
|
116
122
|
runtimeMetrics.increment('datadog.tracer.node.inject.errors', true)
|
|
@@ -1454,7 +1454,7 @@ function parseAnnotations (annotations) {
|
|
|
1454
1454
|
* Returns 0 when the test is too slow to retry (≥ 5 min).
|
|
1455
1455
|
*
|
|
1456
1456
|
* @param {number} durationMs
|
|
1457
|
-
* @param {Record<string, number>} slowTestRetries
|
|
1457
|
+
* @param {Record<string, number>} slowTestRetries e.g. { '5s': 10, '10s': 5, '30s': 3, '5m': 2 }
|
|
1458
1458
|
* @returns {number}
|
|
1459
1459
|
*/
|
|
1460
1460
|
function getEfdRetryCount (durationMs, slowTestRetries) {
|
|
@@ -1469,12 +1469,17 @@ function getEfdRetryCount (durationMs, slowTestRetries) {
|
|
|
1469
1469
|
/**
|
|
1470
1470
|
* Returns the maximum retry count configured by the backend for EFD.
|
|
1471
1471
|
*
|
|
1472
|
-
* @param {Record<string, number>} slowTestRetries
|
|
1473
|
-
* @returns {number}
|
|
1472
|
+
* @param {Record<string, number> | undefined} slowTestRetries
|
|
1473
|
+
* @returns {number | undefined}
|
|
1474
1474
|
*/
|
|
1475
1475
|
function getMaxEfdRetryCount (slowTestRetries) {
|
|
1476
|
+
if (slowTestRetries === undefined) return
|
|
1477
|
+
|
|
1478
|
+
const retryCounts = Object.values(slowTestRetries)
|
|
1479
|
+
if (retryCounts.length === 0) return
|
|
1480
|
+
|
|
1476
1481
|
let maxRetries = 0
|
|
1477
|
-
for (const retryCount of
|
|
1482
|
+
for (const retryCount of retryCounts) {
|
|
1478
1483
|
if (retryCount > maxRetries) {
|
|
1479
1484
|
maxRetries = retryCount
|
|
1480
1485
|
}
|
|
@@ -185,6 +185,18 @@ const web = {
|
|
|
185
185
|
return context
|
|
186
186
|
},
|
|
187
187
|
|
|
188
|
+
// Key an existing context on the underlying `Http2Stream` so a second request
|
|
189
|
+
// backed by the same stream resolves to it via the `req.stream` lookup in
|
|
190
|
+
// `patch`. Only a mixed HTTP/2 server (raw-stream + 'request' listeners) needs
|
|
191
|
+
// this: it creates the span from a throwaway stream adapter, then the
|
|
192
|
+
// compatibility layer synthesizes the real `Http2ServerRequest` off the same
|
|
193
|
+
// stream and must reuse the adapter's span rather than open an orphan context.
|
|
194
|
+
// Confined to that path so the common single-listener request pays no extra
|
|
195
|
+
// per-request map write.
|
|
196
|
+
linkContextToStream (stream, context) {
|
|
197
|
+
contexts.set(stream, context)
|
|
198
|
+
},
|
|
199
|
+
|
|
188
200
|
// Return the request root span.
|
|
189
201
|
root (req) {
|
|
190
202
|
const context = contexts.get(req)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const NoopProxy = require('./noop/proxy')
|
|
4
|
+
const { features } = require('./feature-registry')
|
|
4
5
|
const DatadogTracer = require('./tracer')
|
|
5
6
|
const getConfig = require('./config')
|
|
6
7
|
const runtimeMetrics = require('./runtime_metrics')
|
|
@@ -91,7 +92,10 @@ class Tracer extends NoopProxy {
|
|
|
91
92
|
iast: new LazyModule(() => require('./appsec/iast')),
|
|
92
93
|
llmobs: new LazyModule(() => require('./llmobs')),
|
|
93
94
|
rewriter: new LazyModule(() => require('./appsec/iast/taint-tracking/rewriter')),
|
|
94
|
-
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (const feature of Object.values(features)) {
|
|
98
|
+
this._modules[feature.name] = new LazyModule(feature.factory)
|
|
95
99
|
}
|
|
96
100
|
}
|
|
97
101
|
|
|
@@ -114,7 +118,11 @@ class Tracer extends NoopProxy {
|
|
|
114
118
|
propagationHash.configure(config)
|
|
115
119
|
|
|
116
120
|
if (config.DD_CRASHTRACKING_ENABLED) {
|
|
117
|
-
|
|
121
|
+
try {
|
|
122
|
+
require('./crashtracking').start(config)
|
|
123
|
+
} catch (e) {
|
|
124
|
+
log.warn('Crashtracking is not available in this environment', e)
|
|
125
|
+
}
|
|
118
126
|
}
|
|
119
127
|
|
|
120
128
|
if (config.DD_HEAP_SNAPSHOT_COUNT > 0) {
|
|
@@ -176,8 +184,9 @@ class Tracer extends NoopProxy {
|
|
|
176
184
|
DynamicInstrumentation.start(config, rc)
|
|
177
185
|
}
|
|
178
186
|
|
|
179
|
-
const
|
|
180
|
-
|
|
187
|
+
for (const feature of Object.values(features)) {
|
|
188
|
+
feature.remoteConfig?.(rc, config, this)
|
|
189
|
+
}
|
|
181
190
|
}
|
|
182
191
|
|
|
183
192
|
if (config.profiling.DD_PROFILING_ENABLED === 'true') {
|
|
@@ -287,14 +296,15 @@ class Tracer extends NoopProxy {
|
|
|
287
296
|
lazyProxy(this, 'llmobs', () => require('./llmobs/sdk'), this._tracer, this._modules.llmobs, config)
|
|
288
297
|
|
|
289
298
|
if (config.experimental?.aiguard?.enabled) {
|
|
290
|
-
this._modules.aiguard.enable(this._tracer, config)
|
|
291
299
|
lazyProxy(this, 'aiguard', () => require('./aiguard/sdk'), this._tracer, config)
|
|
292
300
|
}
|
|
293
301
|
this._tracingInitialized = true
|
|
294
302
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
303
|
+
for (const feature of Object.values(features)) {
|
|
304
|
+
feature.enable?.(config, this._tracer, this, lazyProxy)
|
|
305
|
+
}
|
|
306
|
+
if (config.experimental?.aiguard?.enabled) {
|
|
307
|
+
this._modules.aiguard.enable(this._tracer, config)
|
|
298
308
|
}
|
|
299
309
|
if (config.iast.enabled) {
|
|
300
310
|
this._modules.iast.enable(config, this._tracer)
|
|
@@ -305,7 +315,9 @@ class Tracer extends NoopProxy {
|
|
|
305
315
|
this._modules.aiguard.disable()
|
|
306
316
|
this._modules.iast.disable()
|
|
307
317
|
this._modules.llmobs.disable()
|
|
308
|
-
|
|
318
|
+
for (const feature of Object.values(features)) {
|
|
319
|
+
this._modules[feature.name].disable()
|
|
320
|
+
}
|
|
309
321
|
}
|
|
310
322
|
|
|
311
323
|
if (this._tracingInitialized) {
|
|
@@ -7,10 +7,16 @@ const os = require('os')
|
|
|
7
7
|
const process = require('process')
|
|
8
8
|
const { performance, PerformanceObserver, monitorEventLoopDelay } = require('perf_hooks')
|
|
9
9
|
const log = require('../log')
|
|
10
|
-
const { NODE_MAJOR } = require('../../../../version')
|
|
10
|
+
const { NODE_MAJOR, NODE_MINOR } = require('../../../../version')
|
|
11
11
|
const { createMetricsClient } = require('./client')
|
|
12
12
|
|
|
13
13
|
const eventLoopDelayResolution = 4
|
|
14
|
+
const EVENT_LOOP_SAMPLE_PER_ITERATION_AVAILABLE = NODE_MAJOR > 26 || (NODE_MAJOR === 26 && NODE_MINOR >= 5)
|
|
15
|
+
|
|
16
|
+
// @datadog/native-metrics is only needed on Node versions where
|
|
17
|
+
// monitorEventLoopDelay's samplePerIteration option is unavailable.
|
|
18
|
+
// TODO: remove @datadog/native-metrics and this branch once Node < 26.5 is no longer supported.
|
|
19
|
+
const NATIVE_METRICS_REQUIRED = !EVENT_LOOP_SAMPLE_PER_ITERATION_AVAILABLE
|
|
14
20
|
|
|
15
21
|
let nativeMetrics = null
|
|
16
22
|
let gcObserver = null
|
|
@@ -45,7 +51,10 @@ module.exports = {
|
|
|
45
51
|
startGCObserver()
|
|
46
52
|
}
|
|
47
53
|
|
|
48
|
-
|
|
54
|
+
// When per-iteration sampling is available we prefer it over the native
|
|
55
|
+
// addon: it provides accurate event loop delay measurements and lets us
|
|
56
|
+
// collect CPU/GC/heap metrics entirely from JS APIs.
|
|
57
|
+
const useNative = NATIVE_METRICS_REQUIRED && config.runtimeMetrics.native !== false
|
|
49
58
|
|
|
50
59
|
if (useNative) {
|
|
51
60
|
// Using no-gc prevents the native gc metrics from being tracked. Not
|
|
@@ -74,7 +83,10 @@ module.exports = {
|
|
|
74
83
|
lastCpuUsage = process.cpuUsage()
|
|
75
84
|
|
|
76
85
|
if (trackEventLoop) {
|
|
77
|
-
eventLoopDelayObserver = monitorEventLoopDelay({
|
|
86
|
+
eventLoopDelayObserver = monitorEventLoopDelay({
|
|
87
|
+
resolution: eventLoopDelayResolution,
|
|
88
|
+
samplePerIteration: EVENT_LOOP_SAMPLE_PER_ITERATION_AVAILABLE,
|
|
89
|
+
})
|
|
78
90
|
eventLoopDelayObserver.enable()
|
|
79
91
|
}
|
|
80
92
|
|
|
@@ -83,11 +95,6 @@ module.exports = {
|
|
|
83
95
|
captureCommonMetrics(trackEventLoop)
|
|
84
96
|
captureHeapSpace()
|
|
85
97
|
if (trackEventLoop) {
|
|
86
|
-
// Experimental: The Node.js implementation deviates from the native metrics.
|
|
87
|
-
// We normalize the metrics to the same format but the Node.js values
|
|
88
|
-
// are that way lower than they should be, while they are still nearer
|
|
89
|
-
// to the native ones that way.
|
|
90
|
-
// We use these only as fallback values.
|
|
91
98
|
captureEventLoopDelay()
|
|
92
99
|
}
|
|
93
100
|
client.flush()
|
|
@@ -195,12 +202,14 @@ function captureEventLoopDelay () {
|
|
|
195
202
|
eventLoopDelayObserver.disable()
|
|
196
203
|
|
|
197
204
|
if (eventLoopDelayObserver.count !== 0) {
|
|
198
|
-
|
|
205
|
+
// Node.js versions without iteration-based metrics use the default sampling method,
|
|
206
|
+
// which is interval-based and requires normalization because its values are much smaller
|
|
207
|
+
// than those from the original native implementation and iteration-based metrics.
|
|
208
|
+
const minimum = EVENT_LOOP_SAMPLE_PER_ITERATION_AVAILABLE ? 0 : eventLoopDelayResolution * 1e6
|
|
199
209
|
const avg = Math.max(eventLoopDelayObserver.mean - minimum, 0)
|
|
200
210
|
const sum = Math.round(avg * eventLoopDelayObserver.count)
|
|
201
211
|
|
|
202
212
|
if (sum !== 0) {
|
|
203
|
-
// Normalize the metrics to the same format as the native metrics.
|
|
204
213
|
const stats = {
|
|
205
214
|
min: Math.max(eventLoopDelayObserver.min - minimum, 0),
|
|
206
215
|
max: Math.max(eventLoopDelayObserver.max - minimum, 0),
|
|
@@ -214,7 +223,7 @@ function captureEventLoopDelay () {
|
|
|
214
223
|
histogram('runtime.node.event_loop.delay', stats)
|
|
215
224
|
}
|
|
216
225
|
}
|
|
217
|
-
eventLoopDelayObserver
|
|
226
|
+
eventLoopDelayObserver.reset()
|
|
218
227
|
eventLoopDelayObserver.enable()
|
|
219
228
|
}
|
|
220
229
|
|