dd-trace 5.105.0 → 5.107.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/index.d.ts +20 -1
- package/package.json +5 -7
- package/packages/datadog-core/src/storage.js +47 -48
- package/packages/datadog-esbuild/index.js +6 -1
- package/packages/datadog-instrumentations/src/ai.js +12 -3
- package/packages/datadog-instrumentations/src/aws-sdk.js +3 -2
- package/packages/datadog-instrumentations/src/body-parser.js +5 -2
- package/packages/datadog-instrumentations/src/connect.js +3 -2
- package/packages/datadog-instrumentations/src/cookie-parser.js +3 -2
- package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +19 -0
- package/packages/datadog-instrumentations/src/cucumber.js +319 -152
- package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +7 -5
- package/packages/datadog-instrumentations/src/express-session.js +12 -11
- package/packages/datadog-instrumentations/src/express.js +24 -20
- package/packages/datadog-instrumentations/src/fastify.js +18 -6
- package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +27 -12
- package/packages/datadog-instrumentations/src/http/client.js +9 -12
- package/packages/datadog-instrumentations/src/http/server.js +30 -16
- package/packages/datadog-instrumentations/src/http2/client.js +15 -12
- package/packages/datadog-instrumentations/src/http2/server.js +15 -8
- package/packages/datadog-instrumentations/src/jest/bail-reporter.js +42 -0
- package/packages/datadog-instrumentations/src/jest.js +143 -73
- package/packages/datadog-instrumentations/src/mocha/main.js +43 -8
- package/packages/datadog-instrumentations/src/mocha/utils.js +128 -17
- package/packages/datadog-instrumentations/src/multer.js +3 -2
- package/packages/datadog-instrumentations/src/mysql2.js +34 -0
- package/packages/datadog-instrumentations/src/net.js +8 -6
- package/packages/datadog-instrumentations/src/openai.js +19 -7
- package/packages/datadog-instrumentations/src/pg.js +19 -0
- package/packages/datadog-instrumentations/src/router.js +12 -10
- package/packages/datadog-instrumentations/src/vitest.js +29 -4
- package/packages/datadog-plugin-aws-sdk/src/base.js +0 -3
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +218 -4
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +62 -11
- package/packages/datadog-plugin-cucumber/src/index.js +2 -0
- package/packages/datadog-plugin-cypress/src/support.js +31 -1
- package/packages/datadog-plugin-http/src/client.js +0 -3
- package/packages/datadog-plugin-http/src/server.js +11 -1
- package/packages/datadog-plugin-mocha/src/index.js +2 -0
- package/packages/datadog-plugin-pg/src/index.js +10 -0
- package/packages/dd-trace/src/aiguard/index.js +34 -15
- package/packages/dd-trace/src/aiguard/sdk.js +34 -3
- package/packages/dd-trace/src/aiguard/tags.js +6 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +1 -1
- package/packages/dd-trace/src/config/defaults.js +14 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +1 -1
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +5 -9
- package/packages/dd-trace/src/config/parsers.js +8 -0
- package/packages/dd-trace/src/config/supported-configurations.json +13 -6
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
- package/packages/dd-trace/src/datastreams/writer.js +1 -2
- package/packages/dd-trace/src/debugger/config.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/config.js +3 -2
- package/packages/dd-trace/src/debugger/index.js +1 -2
- package/packages/dd-trace/src/dogstatsd.js +2 -3
- package/packages/dd-trace/src/encode/0.4.js +49 -41
- package/packages/dd-trace/src/encode/agentless-json.js +5 -1
- package/packages/dd-trace/src/encode/tags-processors.js +14 -0
- package/packages/dd-trace/src/exporters/agent/index.js +1 -2
- package/packages/dd-trace/src/exporters/agentless/index.js +6 -10
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +1 -2
- package/packages/dd-trace/src/exporters/common/request.js +26 -0
- package/packages/dd-trace/src/exporters/span-stats/index.js +1 -2
- package/packages/dd-trace/src/id.js +15 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +91 -5
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +43 -21
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +4 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +45 -0
- package/packages/dd-trace/src/llmobs/sdk.js +4 -1
- package/packages/dd-trace/src/llmobs/span_processor.js +17 -1
- package/packages/dd-trace/src/llmobs/tagger.js +5 -3
- package/packages/dd-trace/src/llmobs/util.js +54 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -2
- package/packages/dd-trace/src/llmobs/writers/util.js +1 -2
- package/packages/dd-trace/src/openfeature/writers/base.js +1 -10
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -2
- package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +26 -13
- package/packages/dd-trace/src/opentelemetry/metrics/meter.js +7 -10
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +92 -0
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +25 -5
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +2 -10
- package/packages/dd-trace/src/opentracing/span.js +23 -18
- package/packages/dd-trace/src/opentracing/span_context.js +1 -3
- package/packages/dd-trace/src/opentracing/tracer.js +16 -12
- package/packages/dd-trace/src/plugins/ci_plugin.js +131 -46
- package/packages/dd-trace/src/priority_sampler.js +6 -5
- package/packages/dd-trace/src/profiling/config.js +11 -25
- package/packages/dd-trace/src/profiling/exporters/agent.js +11 -10
- package/packages/dd-trace/src/profiling/profiler.js +19 -9
- package/packages/dd-trace/src/profiling/profilers/wall.js +2 -3
- package/packages/dd-trace/src/proxy.js +13 -10
- package/packages/dd-trace/src/remote_config/index.js +1 -2
- package/packages/dd-trace/src/runtime_metrics/client.js +30 -0
- package/packages/dd-trace/src/runtime_metrics/index.js +12 -2
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +284 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +2 -11
- package/packages/dd-trace/src/service-naming/source-resolver.js +5 -1
- package/packages/dd-trace/src/span_format.js +33 -25
- package/packages/dd-trace/src/span_stats.js +1 -1
- package/packages/dd-trace/src/startup-log.js +1 -2
- package/packages/dd-trace/src/telemetry/send-data.js +1 -1
- package/packages/dd-trace/src/tracer.js +1 -1
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +2 -2
- package/vendor/dist/shell-quote/index.js +1 -1
- package/packages/dd-trace/src/agent/url.js +0 -28
- package/scripts/preinstall.js +0 -34
|
@@ -284,7 +284,7 @@ class TextMapPropagator {
|
|
|
284
284
|
(DD_MAJOR < 6 && this._hasPropagationStyle('inject', 'b3'))
|
|
285
285
|
if (!hasB3multi) return
|
|
286
286
|
|
|
287
|
-
carrier[b3TraceKey] =
|
|
287
|
+
carrier[b3TraceKey] = spanContext._traceId.toTraceIdHex(spanContext._trace.tags['_dd.p.tid'])
|
|
288
288
|
carrier[b3SpanKey] = spanContext._spanId.toString(16)
|
|
289
289
|
carrier[b3SampledKey] = spanContext._sampling.priority >= AUTO_KEEP ? '1' : '0'
|
|
290
290
|
|
|
@@ -303,7 +303,7 @@ class TextMapPropagator {
|
|
|
303
303
|
(DD_MAJOR >= 6 && this._hasPropagationStyle('inject', 'b3'))
|
|
304
304
|
if (!hasB3SingleHeader) return
|
|
305
305
|
|
|
306
|
-
const traceId =
|
|
306
|
+
const traceId = spanContext._traceId.toTraceIdHex(spanContext._trace.tags['_dd.p.tid'])
|
|
307
307
|
const spanId = spanContext._spanId.toString(16)
|
|
308
308
|
const sampled = spanContext._sampling.priority >= AUTO_KEEP ? '1' : '0'
|
|
309
309
|
|
|
@@ -859,14 +859,6 @@ class TextMapPropagator {
|
|
|
859
859
|
}
|
|
860
860
|
}
|
|
861
861
|
|
|
862
|
-
_getB3TraceId (spanContext) {
|
|
863
|
-
if (spanContext._traceId.toBuffer().length <= 8 && spanContext._trace.tags['_dd.p.tid']) {
|
|
864
|
-
return spanContext._trace.tags['_dd.p.tid'] + spanContext._traceId.toString(16)
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
return spanContext._traceId.toString(16)
|
|
868
|
-
}
|
|
869
|
-
|
|
870
862
|
/**
|
|
871
863
|
* @param {number} traceparentSampled
|
|
872
864
|
* @param {number|undefined} tracestateSamplingPriority
|
|
@@ -38,20 +38,26 @@ const tagsUpdateCh = channel('dd-trace:span:tags:update')
|
|
|
38
38
|
// Module-scope so we don't allocate a fresh recursive closure on every
|
|
39
39
|
// `addLink` / `addEvent`.
|
|
40
40
|
/**
|
|
41
|
-
* @param {Record<string, string>} out
|
|
41
|
+
* @param {Record<string, string> | undefined} out Accumulator, created lazily
|
|
42
|
+
* on the first surviving entry so an all-dropped set stays `undefined`.
|
|
42
43
|
* @param {string} key
|
|
43
44
|
* @param {unknown} value
|
|
45
|
+
* @returns {Record<string, string> | undefined}
|
|
44
46
|
*/
|
|
45
47
|
function addArrayOrScalarAttribute (out, key, value) {
|
|
46
48
|
if (Array.isArray(value)) {
|
|
47
49
|
for (let i = 0; i < value.length; i++) {
|
|
48
|
-
addArrayOrScalarAttribute(out, `${key}.${i}`, value[i])
|
|
50
|
+
out = addArrayOrScalarAttribute(out, `${key}.${i}`, value[i])
|
|
49
51
|
}
|
|
50
|
-
|
|
52
|
+
return out
|
|
53
|
+
}
|
|
54
|
+
if (ALLOWED.has(typeof value)) {
|
|
55
|
+
out ??= {}
|
|
51
56
|
out[key] = typeof value === 'string' ? value : String(value)
|
|
52
|
-
|
|
53
|
-
log.warn('Dropping span link attribute. It is not of an allowed type')
|
|
57
|
+
return out
|
|
54
58
|
}
|
|
59
|
+
log.warn('Dropping span link attribute. It is not of an allowed type')
|
|
60
|
+
return out
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
function getIntegrationCounter (event, integration) {
|
|
@@ -79,12 +85,6 @@ class DatadogSpan {
|
|
|
79
85
|
|
|
80
86
|
const operationName = fields.operationName
|
|
81
87
|
const parent = fields.parent || null
|
|
82
|
-
// Stay on `Object.assign({}, src)` for backportability: V8 12+ (Node 22 /
|
|
83
|
-
// 24) inlines `{ ...src }` and beats `Object.assign` here, but on V8 10.2
|
|
84
|
-
// / 11.3 (Node 18 / 20) the spread takes a generic runtime path and slows
|
|
85
|
-
// `spans-finish-*` by ~140%. Revisit once those LTS lines drop.
|
|
86
|
-
// eslint-disable-next-line prefer-object-spread
|
|
87
|
-
const tags = Object.assign({}, fields.tags)
|
|
88
88
|
const hostname = fields.hostname
|
|
89
89
|
|
|
90
90
|
this.#parentTracer = tracer
|
|
@@ -106,7 +106,7 @@ class DatadogSpan {
|
|
|
106
106
|
|
|
107
107
|
this._spanContext = this._createContext(parent, fields)
|
|
108
108
|
this._spanContext._name = operationName
|
|
109
|
-
Object.assign(this._spanContext.getTags(), tags)
|
|
109
|
+
if (fields.tags) Object.assign(this._spanContext.getTags(), fields.tags)
|
|
110
110
|
this._spanContext._hostname = hostname
|
|
111
111
|
|
|
112
112
|
this._spanContext._trace.started.push(this)
|
|
@@ -348,21 +348,24 @@ class DatadogSpan {
|
|
|
348
348
|
|
|
349
349
|
/**
|
|
350
350
|
* @param {Record<string, unknown>} [attributes]
|
|
351
|
+
* @returns {Record<string, string> | undefined} `undefined` when nothing
|
|
352
|
+
* survives, so `extractSpanLinks` omits the slot without an emptiness probe.
|
|
351
353
|
*/
|
|
352
354
|
_sanitizeAttributes (attributes = {}) {
|
|
353
|
-
|
|
354
|
-
const out = {}
|
|
355
|
+
let out
|
|
355
356
|
for (const key of Object.keys(attributes)) {
|
|
356
|
-
addArrayOrScalarAttribute(out, key, attributes[key])
|
|
357
|
+
out = addArrayOrScalarAttribute(out, key, attributes[key])
|
|
357
358
|
}
|
|
358
359
|
return out
|
|
359
360
|
}
|
|
360
361
|
|
|
361
362
|
/**
|
|
362
363
|
* @param {Record<string, unknown>} [attributes]
|
|
364
|
+
* @returns {Record<string, unknown> | undefined} `undefined` when nothing
|
|
365
|
+
* survives, so the encoders skip the slot without an emptiness probe.
|
|
363
366
|
*/
|
|
364
367
|
_sanitizeEventAttributes (attributes = {}) {
|
|
365
|
-
|
|
368
|
+
let sanitizedAttributes
|
|
366
369
|
|
|
367
370
|
for (const key of Object.keys(attributes)) {
|
|
368
371
|
const value = attributes[key]
|
|
@@ -375,8 +378,10 @@ class DatadogSpan {
|
|
|
375
378
|
log.warn('Dropping span event attribute. It is not of an allowed type')
|
|
376
379
|
}
|
|
377
380
|
}
|
|
381
|
+
sanitizedAttributes ??= {}
|
|
378
382
|
sanitizedAttributes[key] = newArray
|
|
379
383
|
} else if (ALLOWED.has(typeof value)) {
|
|
384
|
+
sanitizedAttributes ??= {}
|
|
380
385
|
sanitizedAttributes[key] = value
|
|
381
386
|
} else {
|
|
382
387
|
log.warn('Dropping span event attribute. It is not of an allowed type')
|
|
@@ -389,7 +394,7 @@ class DatadogSpan {
|
|
|
389
394
|
let spanContext
|
|
390
395
|
let startTime
|
|
391
396
|
|
|
392
|
-
let baggage
|
|
397
|
+
let baggage
|
|
393
398
|
const propagationBehavior = this.#parentTracer._config.DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT
|
|
394
399
|
if (parent && parent._isRemote && propagationBehavior !== 'continue') {
|
|
395
400
|
baggage = parent._baggageItems
|
|
@@ -431,7 +436,7 @@ class DatadogSpan {
|
|
|
431
436
|
}
|
|
432
437
|
|
|
433
438
|
if (propagationBehavior === 'restart') {
|
|
434
|
-
spanContext._baggageItems = baggage
|
|
439
|
+
spanContext._baggageItems = baggage ?? {}
|
|
435
440
|
}
|
|
436
441
|
}
|
|
437
442
|
|
|
@@ -46,9 +46,7 @@ class DatadogSpanContext {
|
|
|
46
46
|
|
|
47
47
|
toTraceId (get128bitId = false) {
|
|
48
48
|
if (get128bitId) {
|
|
49
|
-
return this._traceId.
|
|
50
|
-
? this._trace.tags[TRACE_ID_128] + this._traceId.toString(16).padStart(16, '0')
|
|
51
|
-
: this._traceId.toString(16).padStart(32, '0')
|
|
49
|
+
return this._traceId.toTraceIdHex(this._trace.tags[TRACE_ID_128]).padStart(32, '0')
|
|
52
50
|
}
|
|
53
51
|
return this._traceId.toString(10)
|
|
54
52
|
}
|
|
@@ -23,6 +23,8 @@ class DatadogTracer {
|
|
|
23
23
|
constructor (config, prioritySampler) {
|
|
24
24
|
this._config = config
|
|
25
25
|
this._service = config.service
|
|
26
|
+
// Lowercased once for span_format's per-span base-service comparison.
|
|
27
|
+
this.serviceLower = typeof config.service === 'string' ? config.service.toLowerCase() : ''
|
|
26
28
|
this._version = config.version
|
|
27
29
|
this._env = config.env
|
|
28
30
|
this._logInjection = config.logInjection
|
|
@@ -64,21 +66,9 @@ class DatadogTracer {
|
|
|
64
66
|
? getContext(options.childOf)
|
|
65
67
|
: getParent(options.references)
|
|
66
68
|
|
|
67
|
-
// as per spec, allow the setting of service name through options
|
|
68
|
-
const tags = {
|
|
69
|
-
'service.name': options?.tags?.service ? String(options.tags.service) : this._service,
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// As per unified service tagging spec if a span is created with a service name different from the global
|
|
73
|
-
// service name it will not inherit the global version value
|
|
74
|
-
if (options?.tags?.service && options.tags.service !== this._service) {
|
|
75
|
-
options.tags.version = undefined
|
|
76
|
-
}
|
|
77
|
-
|
|
78
69
|
const span = new Span(this, this._processor, this._prioritySampler, {
|
|
79
70
|
operationName: options.operationName || name,
|
|
80
71
|
parent,
|
|
81
|
-
tags,
|
|
82
72
|
startTime: options.startTime,
|
|
83
73
|
hostname: this._hostname,
|
|
84
74
|
traceId128BitGenerationEnabled: this._traceId128BitGenerationEnabled,
|
|
@@ -86,6 +76,20 @@ class DatadogTracer {
|
|
|
86
76
|
links: options.links,
|
|
87
77
|
}, this._debug)
|
|
88
78
|
|
|
79
|
+
// As per unified service tagging spec if a span is created with a service name different from the global
|
|
80
|
+
// service name it will not inherit the global version value
|
|
81
|
+
const ctx = span.context()
|
|
82
|
+
if (options.tags?.service) {
|
|
83
|
+
if (options.tags.service !== this._service) options.tags.version = undefined
|
|
84
|
+
// as per spec, allow the setting of service name through options; set it
|
|
85
|
+
// after all tags are merged so config/options values take precedence
|
|
86
|
+
// eslint-disable-next-line eslint-rules/eslint-prefer-set-service-name
|
|
87
|
+
ctx.setTag('service.name', String(options.tags.service))
|
|
88
|
+
} else {
|
|
89
|
+
// eslint-disable-next-line eslint-rules/eslint-prefer-set-service-name
|
|
90
|
+
ctx.setTag('service.name', this._service)
|
|
91
|
+
}
|
|
92
|
+
|
|
89
93
|
span.addTags(this._config.tags)
|
|
90
94
|
span.addTags(options.tags)
|
|
91
95
|
|
|
@@ -143,6 +143,7 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
143
143
|
this.fileLineToProbeId = new Map()
|
|
144
144
|
this.rootDir = process.cwd() // fallback in case :session:start events are not emitted
|
|
145
145
|
this._testSuiteSpansByTestSuite = new Map()
|
|
146
|
+
this._pendingWorkerTracesByTestSuite = new Map()
|
|
146
147
|
this._pendingRequestErrorTags = []
|
|
147
148
|
|
|
148
149
|
this.addSub(`ci:${this.constructor.id}:library-configuration`, (ctx) => {
|
|
@@ -352,52 +353,8 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
352
353
|
const formattedTraces = JSON.parse(traces)
|
|
353
354
|
|
|
354
355
|
for (const trace of formattedTraces) {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
span.trace_id = id(span.trace_id)
|
|
358
|
-
span.parent_id = id(span.parent_id)
|
|
359
|
-
|
|
360
|
-
if (span.name?.startsWith(`${this.constructor.id}.`)) {
|
|
361
|
-
span.meta[TEST_IS_TEST_FRAMEWORK_WORKER] = 'true'
|
|
362
|
-
if (span.name === `${this.constructor.id}.test` || span.name === `${this.constructor.id}.test_suite`) {
|
|
363
|
-
Object.assign(span.meta, getSessionItrSkippingEnabledTags(this.testSessionSpan))
|
|
364
|
-
}
|
|
365
|
-
// augment with git information (since it will not be available in the worker)
|
|
366
|
-
for (const key in this.testEnvironmentMetadata) {
|
|
367
|
-
// CAREFUL: this bypasses the metadata/metrics distinction
|
|
368
|
-
// Be careful not to pass numbers in `meta`
|
|
369
|
-
if (key.startsWith('git.')) {
|
|
370
|
-
span.meta[key] = this.testEnvironmentMetadata[key]
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Only test hooks run in the cucumber worker, so the test events do not have the
|
|
376
|
-
// test session, test module and test suite ids. We have to update them here.
|
|
377
|
-
if (span.name === 'cucumber.test' || span.name === 'mocha.test') {
|
|
378
|
-
const testSuite = span.meta[TEST_SUITE]
|
|
379
|
-
const testSuiteSpan = this._testSuiteSpansByTestSuite.get(testSuite)
|
|
380
|
-
if (!testSuiteSpan) {
|
|
381
|
-
log.warn('Test suite span not found for test span with test suite %s', testSuite)
|
|
382
|
-
continue
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
const testSuiteTags = getTestSuiteLevelVisibilityTags(testSuiteSpan, this.constructor.id)
|
|
386
|
-
span.meta = {
|
|
387
|
-
...span.meta,
|
|
388
|
-
...testSuiteTags,
|
|
389
|
-
...getSessionRequestErrorTags(this.testSessionSpan),
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// Jest and Vitest worker test spans are serialized in the worker and may not include
|
|
394
|
-
// request error tags; add them from the session span in the main process.
|
|
395
|
-
if ((span.name === 'jest.test' || span.name === 'vitest.test' || span.name === 'vitest.test_suite') &&
|
|
396
|
-
this.testSessionSpan) {
|
|
397
|
-
Object.assign(span.meta, getSessionRequestErrorTags(this.testSessionSpan))
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
this.tracer._exporter.export(trace)
|
|
356
|
+
this._prepareWorkerTrace(trace)
|
|
357
|
+
this._exportWorkerTraceOrBuffer(trace)
|
|
401
358
|
}
|
|
402
359
|
})
|
|
403
360
|
|
|
@@ -502,6 +459,134 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
502
459
|
return getSessionItrSkippingEnabledTags(this.testSessionSpan)
|
|
503
460
|
}
|
|
504
461
|
|
|
462
|
+
/**
|
|
463
|
+
* Normalizes worker trace identifiers and adds process-level metadata before exporting or buffering.
|
|
464
|
+
*
|
|
465
|
+
* @param {object[]} trace - Worker trace spans decoded from the worker payload.
|
|
466
|
+
*/
|
|
467
|
+
_prepareWorkerTrace (trace) {
|
|
468
|
+
for (const span of trace) {
|
|
469
|
+
span.span_id = id(span.span_id)
|
|
470
|
+
span.trace_id = id(span.trace_id)
|
|
471
|
+
span.parent_id = id(span.parent_id)
|
|
472
|
+
|
|
473
|
+
if (span.name?.startsWith(`${this.constructor.id}.`)) {
|
|
474
|
+
span.meta[TEST_IS_TEST_FRAMEWORK_WORKER] = 'true'
|
|
475
|
+
if (span.name === `${this.constructor.id}.test` || span.name === `${this.constructor.id}.test_suite`) {
|
|
476
|
+
Object.assign(span.meta, getSessionItrSkippingEnabledTags(this.testSessionSpan))
|
|
477
|
+
}
|
|
478
|
+
// augment with git information (since it will not be available in the worker)
|
|
479
|
+
for (const key in this.testEnvironmentMetadata) {
|
|
480
|
+
// CAREFUL: this bypasses the metadata/metrics distinction
|
|
481
|
+
// Be careful not to pass numbers in `meta`
|
|
482
|
+
if (key.startsWith('git.')) {
|
|
483
|
+
span.meta[key] = this.testEnvironmentMetadata[key]
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Jest and Vitest worker test spans are serialized in the worker and may not include
|
|
489
|
+
// request error tags; add them from the session span in the main process.
|
|
490
|
+
if ((span.name === 'jest.test' || span.name === 'vitest.test' || span.name === 'vitest.test_suite') &&
|
|
491
|
+
this.testSessionSpan) {
|
|
492
|
+
Object.assign(span.meta, getSessionRequestErrorTags(this.testSessionSpan))
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Adds suite-level CI Visibility tags to worker test spans when their suite span is available.
|
|
499
|
+
*
|
|
500
|
+
* @param {object[]} trace - Worker trace spans.
|
|
501
|
+
* @returns {string|undefined} Missing test suite name, if the trace cannot be exported yet.
|
|
502
|
+
*/
|
|
503
|
+
_addSuiteTagsToWorkerTrace (trace) {
|
|
504
|
+
for (const span of trace) {
|
|
505
|
+
// Only test hooks run in Cucumber and Mocha workers, so the test events do not have the
|
|
506
|
+
// test session, test module and test suite ids. We have to update them in the main process.
|
|
507
|
+
if (span.name !== 'cucumber.test' && span.name !== 'mocha.test') continue
|
|
508
|
+
|
|
509
|
+
const testSuite = span.meta[TEST_SUITE]
|
|
510
|
+
const testSuiteSpan = this._testSuiteSpansByTestSuite.get(testSuite)
|
|
511
|
+
if (!testSuiteSpan) return testSuite
|
|
512
|
+
|
|
513
|
+
const testSuiteTags = getTestSuiteLevelVisibilityTags(testSuiteSpan, this.constructor.id)
|
|
514
|
+
span.meta = {
|
|
515
|
+
...span.meta,
|
|
516
|
+
...testSuiteTags,
|
|
517
|
+
...getSessionRequestErrorTags(this.testSessionSpan),
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Stores a worker trace until the matching test suite span exists in the main process.
|
|
524
|
+
*
|
|
525
|
+
* @param {string} testSuite - Test suite path used as the pending trace key.
|
|
526
|
+
* @param {object[]} trace - Worker trace spans.
|
|
527
|
+
*/
|
|
528
|
+
_bufferWorkerTrace (testSuite, trace) {
|
|
529
|
+
let pendingTraces = this._pendingWorkerTracesByTestSuite.get(testSuite)
|
|
530
|
+
if (!pendingTraces) {
|
|
531
|
+
pendingTraces = []
|
|
532
|
+
this._pendingWorkerTracesByTestSuite.set(testSuite, pendingTraces)
|
|
533
|
+
}
|
|
534
|
+
pendingTraces.push(trace)
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Exports a worker trace immediately, or buffers it if suite-level tags cannot be added yet.
|
|
539
|
+
*
|
|
540
|
+
* @param {object[]} trace - Worker trace spans.
|
|
541
|
+
*/
|
|
542
|
+
_exportWorkerTraceOrBuffer (trace) {
|
|
543
|
+
const missingTestSuite = this._addSuiteTagsToWorkerTrace(trace)
|
|
544
|
+
if (missingTestSuite) {
|
|
545
|
+
this._bufferWorkerTrace(missingTestSuite, trace)
|
|
546
|
+
return
|
|
547
|
+
}
|
|
548
|
+
this.tracer._exporter.export(trace)
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Exports buffered worker traces for a suite after its suite span has been created.
|
|
553
|
+
*
|
|
554
|
+
* @param {string} testSuite - Test suite path that may now have pending worker traces.
|
|
555
|
+
*/
|
|
556
|
+
_exportPendingWorkerTracesForTestSuite (testSuite) {
|
|
557
|
+
const pendingTraces = this._pendingWorkerTracesByTestSuite.get(testSuite)
|
|
558
|
+
if (!pendingTraces) return
|
|
559
|
+
|
|
560
|
+
this._pendingWorkerTracesByTestSuite.delete(testSuite)
|
|
561
|
+
for (const trace of pendingTraces) {
|
|
562
|
+
this._exportWorkerTraceOrBuffer(trace)
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Drains all buffered worker traces, falling back to the previous unaugmented export behavior
|
|
568
|
+
* if a matching suite span never appears.
|
|
569
|
+
*/
|
|
570
|
+
_exportPendingWorkerTraces () {
|
|
571
|
+
if (!this._pendingWorkerTracesByTestSuite.size) return
|
|
572
|
+
|
|
573
|
+
const pendingTraces = new Set()
|
|
574
|
+
for (const traces of this._pendingWorkerTracesByTestSuite.values()) {
|
|
575
|
+
for (const trace of traces) {
|
|
576
|
+
pendingTraces.add(trace)
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
this._pendingWorkerTracesByTestSuite.clear()
|
|
580
|
+
|
|
581
|
+
for (const trace of pendingTraces) {
|
|
582
|
+
const missingTestSuite = this._addSuiteTagsToWorkerTrace(trace)
|
|
583
|
+
if (missingTestSuite) {
|
|
584
|
+
log.warn('Test suite span not found for test span with test suite %s', missingTestSuite)
|
|
585
|
+
}
|
|
586
|
+
this.tracer._exporter.export(trace)
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
505
590
|
/**
|
|
506
591
|
* @param {import('../config/config-base')} config - Tracer configuration
|
|
507
592
|
* @param {boolean} shouldGetEnvironmentData - Whether to get environment data
|
|
@@ -333,11 +333,12 @@ class PrioritySampler {
|
|
|
333
333
|
if (!trace.tags[DECISION_MAKER_KEY]) {
|
|
334
334
|
trace.tags[DECISION_MAKER_KEY] = `-${mechanism}`
|
|
335
335
|
}
|
|
336
|
-
} else if (DECISION_MAKER_KEY
|
|
337
|
-
//
|
|
338
|
-
// dictionary
|
|
339
|
-
//
|
|
340
|
-
|
|
336
|
+
} else if (trace.tags[DECISION_MAKER_KEY] !== undefined) {
|
|
337
|
+
// Clear by assigning undefined rather than deleting: `delete` drops
|
|
338
|
+
// trace.tags into V8 dictionary (slow) mode for the per-trace extract
|
|
339
|
+
// and propagation scans that follow. Both skip undefined values, so the
|
|
340
|
+
// emitted meta and injected headers are unchanged.
|
|
341
|
+
trace.tags[DECISION_MAKER_KEY] = undefined
|
|
341
342
|
}
|
|
342
343
|
}
|
|
343
344
|
|
|
@@ -5,16 +5,15 @@ const { pathToFileURL } = require('url')
|
|
|
5
5
|
|
|
6
6
|
const satisfies = require('../../../../vendor/dist/semifies')
|
|
7
7
|
const getGitMetadata = require('../git_metadata')
|
|
8
|
+
const log = require('../log')
|
|
8
9
|
const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../plugins/util/tags')
|
|
9
10
|
const { getIsAzureFunction } = require('../serverless')
|
|
10
11
|
const { getAzureTagsFromMetadata, getAzureAppMetadata, getAzureFunctionMetadata } = require('../azure_metadata')
|
|
11
12
|
const { getEnvironmentVariable } = require('../config/helper')
|
|
12
|
-
const { getAgentUrl } = require('../agent/url')
|
|
13
13
|
const { isACFActive } = require('../../../datadog-core/src/storage')
|
|
14
14
|
|
|
15
15
|
const { AgentExporter } = require('./exporters/agent')
|
|
16
16
|
const { FileExporter } = require('./exporters/file')
|
|
17
|
-
const { ConsoleLogger } = require('./loggers/console')
|
|
18
17
|
const WallProfiler = require('./profilers/wall')
|
|
19
18
|
const SpaceProfiler = require('./profilers/space')
|
|
20
19
|
const EventsProfiler = require('./profilers/events')
|
|
@@ -54,8 +53,7 @@ class Config {
|
|
|
54
53
|
this.pprofPrefix = options.DD_PROFILING_PPROF_PREFIX
|
|
55
54
|
this.v8ProfilerBugWorkaroundEnabled = options.DD_PROFILING_V8_PROFILER_BUG_WORKAROUND
|
|
56
55
|
|
|
57
|
-
this.
|
|
58
|
-
this.url = getAgentUrl(options)
|
|
56
|
+
this.url = options.url
|
|
59
57
|
|
|
60
58
|
this.libraryInjected = !!options.DD_INJECTION_ENABLED
|
|
61
59
|
|
|
@@ -73,7 +71,7 @@ class Config {
|
|
|
73
71
|
const heapLimitExtensionSize = options.DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE
|
|
74
72
|
const maxHeapExtensionCount = options.DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT
|
|
75
73
|
const exportStrategies = oomMonitoringEnabled
|
|
76
|
-
? ensureOOMExportStrategies(options.DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES
|
|
74
|
+
? ensureOOMExportStrategies(options.DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES)
|
|
77
75
|
: []
|
|
78
76
|
const exportCommand = oomMonitoringEnabled ? buildExportCommand(this) : undefined
|
|
79
77
|
this.oomMonitoring = {
|
|
@@ -102,7 +100,7 @@ class Config {
|
|
|
102
100
|
if (level !== undefined) {
|
|
103
101
|
const maxLevel = { gzip: 9, zstd: 22 }[uploadCompression]
|
|
104
102
|
if (level > maxLevel) {
|
|
105
|
-
|
|
103
|
+
log.warn('Invalid compression level %d. Will use %d.', level, maxLevel)
|
|
106
104
|
level = maxLevel
|
|
107
105
|
}
|
|
108
106
|
}
|
|
@@ -119,8 +117,7 @@ class Config {
|
|
|
119
117
|
|
|
120
118
|
const that = this
|
|
121
119
|
function turnOffAsyncContextFrame (msg) {
|
|
122
|
-
|
|
123
|
-
`DD_PROFILING_ASYNC_CONTEXT_FRAME_ENABLED was set ${msg}, it will have no effect.`)
|
|
120
|
+
log.warn('DD_PROFILING_ASYNC_CONTEXT_FRAME_ENABLED was set %s, it will have no effect.', msg)
|
|
124
121
|
that.asyncContextFrameEnabled = false
|
|
125
122
|
}
|
|
126
123
|
|
|
@@ -215,18 +212,18 @@ function getProfilers ({
|
|
|
215
212
|
return profilersArray
|
|
216
213
|
}
|
|
217
214
|
|
|
218
|
-
function getExportStrategy (name
|
|
215
|
+
function getExportStrategy (name) {
|
|
219
216
|
const strategy = Object.values(oomExportStrategies).find(value => value === name)
|
|
220
217
|
if (strategy === undefined) {
|
|
221
|
-
|
|
218
|
+
log.error('Unknown oom export strategy "%s"', name)
|
|
222
219
|
}
|
|
223
220
|
return strategy
|
|
224
221
|
}
|
|
225
222
|
|
|
226
|
-
function ensureOOMExportStrategies (strategies
|
|
223
|
+
function ensureOOMExportStrategies (strategies) {
|
|
227
224
|
const set = new Set()
|
|
228
225
|
for (const strategy of strategies) {
|
|
229
|
-
set.add(getExportStrategy(strategy
|
|
226
|
+
set.add(getExportStrategy(strategy))
|
|
230
227
|
}
|
|
231
228
|
|
|
232
229
|
return [...set]
|
|
@@ -239,7 +236,7 @@ function getExporter (name, options) {
|
|
|
239
236
|
case 'file':
|
|
240
237
|
return new FileExporter(options)
|
|
241
238
|
default:
|
|
242
|
-
|
|
239
|
+
log.error('Unknown exporter "%s"', name)
|
|
243
240
|
}
|
|
244
241
|
}
|
|
245
242
|
|
|
@@ -255,7 +252,7 @@ function getProfiler (name, options) {
|
|
|
255
252
|
case 'space':
|
|
256
253
|
return new SpaceProfiler(options)
|
|
257
254
|
default:
|
|
258
|
-
|
|
255
|
+
log.error('Unknown profiler "%s"', name)
|
|
259
256
|
}
|
|
260
257
|
}
|
|
261
258
|
|
|
@@ -278,17 +275,6 @@ function ensureProfilers (profilers, options) {
|
|
|
278
275
|
return filteredProfilers
|
|
279
276
|
}
|
|
280
277
|
|
|
281
|
-
function ensureLogger (logger) {
|
|
282
|
-
if (typeof logger?.debug !== 'function' ||
|
|
283
|
-
typeof logger.info !== 'function' ||
|
|
284
|
-
typeof logger.warn !== 'function' ||
|
|
285
|
-
typeof logger.error !== 'function') {
|
|
286
|
-
return new ConsoleLogger()
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
return logger
|
|
290
|
-
}
|
|
291
|
-
|
|
292
278
|
function buildExportCommand (options) {
|
|
293
279
|
const tags = [...Object.entries(options.tags),
|
|
294
280
|
['snapshot', snapshotKinds.ON_OUT_OF_MEMORY]].map(([key, value]) => `${key}:${value}`).join(',')
|
|
@@ -9,6 +9,7 @@ const retry = require('../../../../../vendor/dist/retry')
|
|
|
9
9
|
// TODO: avoid using dd-trace internals. Make this a separate module?
|
|
10
10
|
const docker = require('../../exporters/common/docker')
|
|
11
11
|
const FormData = require('../../exporters/common/form-data')
|
|
12
|
+
const log = require('../../log')
|
|
12
13
|
const { storage } = require('../../../../datadog-core')
|
|
13
14
|
const version = require('../../../../../package.json').version
|
|
14
15
|
const telemetryMetrics = require('../../telemetry/metrics')
|
|
@@ -89,9 +90,8 @@ function computeRetries (uploadTimeout) {
|
|
|
89
90
|
class AgentExporter extends EventSerializer {
|
|
90
91
|
constructor (config = {}) {
|
|
91
92
|
super(config)
|
|
92
|
-
const { url,
|
|
93
|
+
const { url, uploadTimeout } = config
|
|
93
94
|
this._url = url
|
|
94
|
-
this._logger = logger
|
|
95
95
|
|
|
96
96
|
const [backoffTries, backoffTime] = computeRetries(uploadTimeout)
|
|
97
97
|
|
|
@@ -109,12 +109,11 @@ class AgentExporter extends EventSerializer {
|
|
|
109
109
|
contentType: 'application/json',
|
|
110
110
|
}])
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
return `Building agent export report:\n${event}`
|
|
114
|
-
})
|
|
112
|
+
log.debug('Building agent export report:\n%s', event)
|
|
115
113
|
|
|
116
114
|
for (const [type, buffer] of Object.entries(profiles)) {
|
|
117
|
-
|
|
115
|
+
// eslint-disable-next-line eslint-rules/eslint-log-printf-style
|
|
116
|
+
log.debug(() => {
|
|
118
117
|
const bytes = buffer.toString('hex').match(/../g).join(' ')
|
|
119
118
|
return `Adding ${type} profile to agent export: ` + bytes
|
|
120
119
|
})
|
|
@@ -163,7 +162,8 @@ class AgentExporter extends EventSerializer {
|
|
|
163
162
|
options.port = httpOptions.port
|
|
164
163
|
}
|
|
165
164
|
|
|
166
|
-
|
|
165
|
+
// eslint-disable-next-line eslint-rules/eslint-log-printf-style
|
|
166
|
+
log.debug(() => {
|
|
167
167
|
return `Submitting profiler agent report attempt #${attempt} to: ${JSON.stringify(options)}`
|
|
168
168
|
})
|
|
169
169
|
|
|
@@ -171,7 +171,7 @@ class AgentExporter extends EventSerializer {
|
|
|
171
171
|
if (err) {
|
|
172
172
|
const { status } = err
|
|
173
173
|
if ((typeof status !== 'number' || status >= 500 || status === 429) && operation.retry(err)) {
|
|
174
|
-
|
|
174
|
+
log.warn('Error from the agent: %s', err.message)
|
|
175
175
|
} else {
|
|
176
176
|
reject(err)
|
|
177
177
|
}
|
|
@@ -180,9 +180,10 @@ class AgentExporter extends EventSerializer {
|
|
|
180
180
|
|
|
181
181
|
getBody(response, (err, body) => {
|
|
182
182
|
if (err) {
|
|
183
|
-
|
|
183
|
+
log.warn('Error reading agent response: %s', err.message)
|
|
184
184
|
} else {
|
|
185
|
-
|
|
185
|
+
// eslint-disable-next-line eslint-rules/eslint-log-printf-style
|
|
186
|
+
log.debug(() => {
|
|
186
187
|
const bytes = (body.toString('hex').match(/../g) || []).join(' ')
|
|
187
188
|
return `Agent export response: ${bytes}`
|
|
188
189
|
})
|