dd-trace 6.9.0 → 6.10.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/ci/vitest-no-worker-init-setup.mjs +112 -10
- package/index.d.ts +42 -0
- package/package.json +4 -3
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +111 -6
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +629 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +81 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
- package/packages/datadog-instrumentations/src/jest.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/utils.js +2 -8
- package/packages/datadog-instrumentations/src/mysql.js +45 -15
- package/packages/datadog-instrumentations/src/mysql2.js +119 -18
- package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
- package/packages/datadog-instrumentations/src/pg.js +116 -92
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -4
- package/packages/datadog-instrumentations/src/vitest-main.js +169 -17
- package/packages/datadog-instrumentations/src/vitest-util.js +64 -5
- package/packages/datadog-instrumentations/src/vitest-worker.js +150 -25
- package/packages/datadog-instrumentations/src/webdriverio.js +31 -17
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -5
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +1 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +31 -17
- package/packages/datadog-plugin-jest/src/index.js +5 -4
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
- package/packages/datadog-plugin-kafkajs/src/producer.js +3 -2
- package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
- package/packages/datadog-plugin-mocha/src/index.js +550 -39
- package/packages/datadog-plugin-mysql/src/index.js +37 -0
- package/packages/datadog-plugin-next/src/index.js +1 -19
- package/packages/datadog-plugin-openai-agents/src/integration.js +278 -69
- package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
- package/packages/datadog-plugin-pg/src/index.js +1 -0
- package/packages/datadog-plugin-vitest/src/index.js +8 -20
- package/packages/datadog-plugin-ws/src/util.js +2 -1
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +453 -0
- package/packages/dd-trace/src/appsec/handlers/http-request.js +13 -0
- package/packages/dd-trace/src/carrier.js +356 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +3 -0
- package/packages/dd-trace/src/config/supported-configurations.json +10 -0
- package/packages/dd-trace/src/datastreams/context.js +6 -1
- package/packages/dd-trace/src/datastreams/manager.js +5 -1
- package/packages/dd-trace/src/datastreams/pathway.js +23 -22
- package/packages/dd-trace/src/datastreams/processor.js +3 -2
- package/packages/dd-trace/src/llmobs/constants/tags.js +5 -0
- package/packages/dd-trace/src/llmobs/index.js +34 -2
- package/packages/dd-trace/src/llmobs/span_processor.js +16 -0
- package/packages/dd-trace/src/llmobs/tagger.js +96 -0
- package/packages/dd-trace/src/llmobs/util.js +78 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +126 -120
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +1 -62
- package/packages/dd-trace/src/plugins/util/test.js +84 -5
- package/packages/dd-trace/src/plugins/util/web.js +53 -2
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/** @typedef {Record<string, unknown>} Carrier */
|
|
4
|
+
/** @typedef {string | Buffer} PathwayValue */
|
|
5
|
+
|
|
6
|
+
const FIELD_READ = 1
|
|
7
|
+
const FIELD_WRITE = 2
|
|
8
|
+
const FIELD_HAS = 4
|
|
9
|
+
const FIELD_DELETE = 8
|
|
10
|
+
const FIELD_NAME_LENGTH = 16
|
|
11
|
+
|
|
12
|
+
/** @type {Record<string, unknown>} */
|
|
13
|
+
const carrierOperations = {}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {object} CarrierCapability
|
|
17
|
+
* @property {unknown} [read]
|
|
18
|
+
* @property {unknown} [write]
|
|
19
|
+
* @property {unknown} [has]
|
|
20
|
+
* @property {unknown} [delete]
|
|
21
|
+
* @property {unknown} [nameLength]
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @template Value
|
|
26
|
+
* @typedef {object} CarrierField
|
|
27
|
+
* @property {(carrier: Carrier) => Value | undefined} read
|
|
28
|
+
* @property {(carrier: Carrier, value: Value) => void} write
|
|
29
|
+
* @property {(carrier: Carrier) => boolean} has
|
|
30
|
+
* @property {(carrier: Carrier) => void} delete
|
|
31
|
+
* @property {(carrier: Carrier, target: Carrier) => void} copy
|
|
32
|
+
* @property {number} nameLength
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @template {CarrierCapability} Capability
|
|
37
|
+
* @param {string} name
|
|
38
|
+
* @param {number} operations
|
|
39
|
+
* @param {Capability} capability
|
|
40
|
+
* @returns {Capability}
|
|
41
|
+
*/
|
|
42
|
+
function defineCapability (name, operations, capability) {
|
|
43
|
+
const suffix = name[0].toUpperCase() + name.slice(1)
|
|
44
|
+
if (operations & FIELD_READ) carrierOperations[`read${suffix}`] = capability.read
|
|
45
|
+
if (operations & FIELD_WRITE) carrierOperations[`write${suffix}`] = capability.write
|
|
46
|
+
if (operations & FIELD_HAS) carrierOperations[`has${suffix}`] = capability.has
|
|
47
|
+
if (operations & FIELD_DELETE) carrierOperations[`delete${suffix}`] = capability.delete
|
|
48
|
+
if (operations & FIELD_NAME_LENGTH) carrierOperations[`${name}NameLength`] = capability.nameLength
|
|
49
|
+
return capability
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @template Value
|
|
54
|
+
* @param {string} fieldName
|
|
55
|
+
* @param {string} headerName
|
|
56
|
+
* @param {(value: unknown) => Value | undefined} resolve
|
|
57
|
+
* @param {number} operations
|
|
58
|
+
* @returns {CarrierField<Value>}
|
|
59
|
+
*/
|
|
60
|
+
function defineField (fieldName, headerName, resolve, operations) {
|
|
61
|
+
return defineCapability(fieldName, operations, {
|
|
62
|
+
/**
|
|
63
|
+
* @param {Carrier} carrier
|
|
64
|
+
* @returns {Value | undefined}
|
|
65
|
+
*/
|
|
66
|
+
read (carrier) {
|
|
67
|
+
return resolve(carrier[headerName])
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @param {Carrier} carrier
|
|
72
|
+
* @param {Value} value
|
|
73
|
+
* @returns {void}
|
|
74
|
+
*/
|
|
75
|
+
write (carrier, value) {
|
|
76
|
+
carrier[headerName] = value
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @param {Carrier} carrier
|
|
81
|
+
* @returns {boolean}
|
|
82
|
+
*/
|
|
83
|
+
has (carrier) {
|
|
84
|
+
return headerName in carrier
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @param {Carrier} carrier
|
|
89
|
+
* @returns {void}
|
|
90
|
+
*/
|
|
91
|
+
delete (carrier) {
|
|
92
|
+
delete carrier[headerName]
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @param {Carrier} carrier
|
|
97
|
+
* @param {Carrier} target
|
|
98
|
+
* @returns {void}
|
|
99
|
+
*/
|
|
100
|
+
copy (carrier, target) {
|
|
101
|
+
if (Object.hasOwn(carrier, headerName)) target[headerName] = carrier[headerName]
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
nameLength: headerName.length,
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Skipping non-strings keeps a Symbol or a throwing `toString` from aborting
|
|
110
|
+
* extraction, which discards the whole context rather than one field.
|
|
111
|
+
*
|
|
112
|
+
* @param {unknown} value
|
|
113
|
+
* @returns {string | undefined}
|
|
114
|
+
*/
|
|
115
|
+
function readListText (value) {
|
|
116
|
+
if (typeof value === 'string') return value
|
|
117
|
+
if (!Array.isArray(value)) return
|
|
118
|
+
|
|
119
|
+
let joined = ''
|
|
120
|
+
let first = true
|
|
121
|
+
|
|
122
|
+
for (const item of value) {
|
|
123
|
+
if (typeof item !== 'string') continue
|
|
124
|
+
if (first) {
|
|
125
|
+
joined = item
|
|
126
|
+
first = false
|
|
127
|
+
} else {
|
|
128
|
+
joined += `,${item}`
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return joined
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @param {unknown} value
|
|
137
|
+
* @returns {string | undefined}
|
|
138
|
+
*/
|
|
139
|
+
function readLastText (value) {
|
|
140
|
+
if (typeof value === 'string') return value
|
|
141
|
+
if (!Array.isArray(value)) return
|
|
142
|
+
|
|
143
|
+
let result
|
|
144
|
+
for (let i = value.length - 1; i >= 0; i--) {
|
|
145
|
+
if (typeof value[i] === 'string') {
|
|
146
|
+
result = value[i]
|
|
147
|
+
break
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return result
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @param {unknown} value
|
|
155
|
+
* @returns {string | undefined}
|
|
156
|
+
*/
|
|
157
|
+
function readUniqueText (value) {
|
|
158
|
+
let result
|
|
159
|
+
if (typeof value === 'string') {
|
|
160
|
+
result = value
|
|
161
|
+
} else if (Array.isArray(value) && value.length === 1 && typeof value[0] === 'string') {
|
|
162
|
+
result = value[0]
|
|
163
|
+
}
|
|
164
|
+
return result
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @param {unknown} value
|
|
169
|
+
* @returns {PathwayValue | undefined}
|
|
170
|
+
*/
|
|
171
|
+
function readLastPathway (value) {
|
|
172
|
+
if (typeof value === 'string' || Buffer.isBuffer(value)) return value
|
|
173
|
+
if (!Array.isArray(value)) return
|
|
174
|
+
|
|
175
|
+
let result
|
|
176
|
+
for (let i = value.length - 1; i >= 0; i--) {
|
|
177
|
+
const item = value[i]
|
|
178
|
+
if (typeof item === 'string' || Buffer.isBuffer(item)) {
|
|
179
|
+
result = item
|
|
180
|
+
break
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return result
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const datadogTraceId = defineField(
|
|
187
|
+
'datadogTraceId',
|
|
188
|
+
'x-datadog-trace-id',
|
|
189
|
+
readLastText,
|
|
190
|
+
FIELD_READ | FIELD_WRITE
|
|
191
|
+
)
|
|
192
|
+
const datadogParentId = defineField(
|
|
193
|
+
'datadogParentId',
|
|
194
|
+
'x-datadog-parent-id',
|
|
195
|
+
readLastText,
|
|
196
|
+
FIELD_READ | FIELD_WRITE | FIELD_DELETE
|
|
197
|
+
)
|
|
198
|
+
const datadogOrigin = defineField(
|
|
199
|
+
'datadogOrigin',
|
|
200
|
+
'x-datadog-origin',
|
|
201
|
+
readLastText,
|
|
202
|
+
FIELD_READ | FIELD_WRITE
|
|
203
|
+
)
|
|
204
|
+
const datadogSamplingPriority = defineField(
|
|
205
|
+
'datadogSamplingPriority',
|
|
206
|
+
'x-datadog-sampling-priority',
|
|
207
|
+
readLastText,
|
|
208
|
+
FIELD_READ | FIELD_WRITE
|
|
209
|
+
)
|
|
210
|
+
defineField('datadogTags', 'x-datadog-tags', readListText, FIELD_READ | FIELD_WRITE)
|
|
211
|
+
defineField('baggage', 'baggage', readListText, FIELD_READ | FIELD_WRITE)
|
|
212
|
+
const b3TraceId = defineField('b3TraceId', 'x-b3-traceid', readLastText, FIELD_READ | FIELD_WRITE)
|
|
213
|
+
const b3SpanId = defineField('b3SpanId', 'x-b3-spanid', readLastText, FIELD_READ | FIELD_WRITE)
|
|
214
|
+
const b3ParentId = defineField('b3ParentId', 'x-b3-parentspanid', readLastText, FIELD_WRITE)
|
|
215
|
+
const b3Sampled = defineField('b3Sampled', 'x-b3-sampled', readLastText, FIELD_READ | FIELD_WRITE)
|
|
216
|
+
const b3Flags = defineField('b3Flags', 'x-b3-flags', readLastText, FIELD_READ | FIELD_WRITE)
|
|
217
|
+
const b3 = defineField('b3', 'b3', readLastText, FIELD_READ | FIELD_WRITE)
|
|
218
|
+
defineField('sqsd', 'x-aws-sqsd-attr-_datadog', readLastText, FIELD_READ)
|
|
219
|
+
const traceparent = defineField('traceparent', 'traceparent', readUniqueText, FIELD_READ | FIELD_WRITE)
|
|
220
|
+
const tracestate = defineField('tracestate', 'tracestate', readListText, FIELD_READ | FIELD_WRITE)
|
|
221
|
+
const dsmBase64 = defineField(
|
|
222
|
+
'dsmBase64',
|
|
223
|
+
'dd-pathway-ctx-base64',
|
|
224
|
+
readLastPathway,
|
|
225
|
+
FIELD_READ | FIELD_WRITE | FIELD_HAS | FIELD_NAME_LENGTH
|
|
226
|
+
)
|
|
227
|
+
const dsmBinary = defineField('dsmBinary', 'dd-pathway-ctx', readLastPathway, FIELD_READ | FIELD_HAS)
|
|
228
|
+
|
|
229
|
+
const legacyBaggagePrefix = 'ot-baggage-'
|
|
230
|
+
// RFC 7230 `token`, compatible with Node.js header-name validation.
|
|
231
|
+
const httpHeaderNameExpr = /^[0-9A-Za-z!#$%&'*+\-.^_`|~]+$/
|
|
232
|
+
|
|
233
|
+
defineCapability('legacyBaggage', FIELD_READ | FIELD_WRITE, {
|
|
234
|
+
/**
|
|
235
|
+
* @param {Record<string, string> | undefined} carrier
|
|
236
|
+
* @param {string} key
|
|
237
|
+
* @param {string} value
|
|
238
|
+
* @returns {Record<string, string> | undefined}
|
|
239
|
+
*/
|
|
240
|
+
write (carrier, key, value) {
|
|
241
|
+
const name = legacyBaggagePrefix + key
|
|
242
|
+
if (!httpHeaderNameExpr.test(name)) return
|
|
243
|
+
|
|
244
|
+
carrier ??= {}
|
|
245
|
+
carrier[name] = value
|
|
246
|
+
return carrier
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* @param {Carrier} carrier
|
|
251
|
+
* @param {Record<string, string>} target
|
|
252
|
+
* @returns {void}
|
|
253
|
+
*/
|
|
254
|
+
read (carrier, target) {
|
|
255
|
+
for (const name of Object.keys(carrier)) {
|
|
256
|
+
if (!name.startsWith(legacyBaggagePrefix)) continue
|
|
257
|
+
const key = name.slice(legacyBaggagePrefix.length)
|
|
258
|
+
if (!key) continue
|
|
259
|
+
const value = readLastText(carrier[name])
|
|
260
|
+
if (value !== undefined) target[key] = value
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
const textMapLogFields = [
|
|
266
|
+
datadogTraceId,
|
|
267
|
+
datadogParentId,
|
|
268
|
+
datadogSamplingPriority,
|
|
269
|
+
datadogOrigin,
|
|
270
|
+
b3TraceId,
|
|
271
|
+
b3SpanId,
|
|
272
|
+
b3ParentId,
|
|
273
|
+
b3Sampled,
|
|
274
|
+
b3Flags,
|
|
275
|
+
b3,
|
|
276
|
+
traceparent,
|
|
277
|
+
tracestate,
|
|
278
|
+
]
|
|
279
|
+
const dsmLogFields = [dsmBinary, dsmBase64]
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* @param {Carrier} carrier
|
|
283
|
+
* @param {Array<Pick<CarrierField<unknown>, 'copy'>>} selectedFields
|
|
284
|
+
* @returns {Carrier}
|
|
285
|
+
*/
|
|
286
|
+
function pick (carrier, selectedFields) {
|
|
287
|
+
/** @type {Carrier} */
|
|
288
|
+
const result = {}
|
|
289
|
+
for (const field of selectedFields) field.copy(carrier, result)
|
|
290
|
+
return result
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* @param {Carrier} carrier
|
|
295
|
+
* @returns {Carrier}
|
|
296
|
+
*/
|
|
297
|
+
function pickTextMap (carrier) {
|
|
298
|
+
return pick(carrier, textMapLogFields)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* @param {Carrier} carrier
|
|
303
|
+
* @returns {Carrier}
|
|
304
|
+
*/
|
|
305
|
+
function pickDsm (carrier) {
|
|
306
|
+
return pick(carrier, dsmLogFields)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* @typedef {object} CarrierOperations
|
|
311
|
+
* @property {typeof datadogParentId.delete} deleteDatadogParentId
|
|
312
|
+
* @property {typeof dsmBase64.nameLength} dsmBase64NameLength
|
|
313
|
+
* @property {typeof dsmBase64.has} hasDsmBase64
|
|
314
|
+
* @property {typeof dsmBinary.has} hasDsmBinary
|
|
315
|
+
* @property {typeof pickDsm} pickDsm
|
|
316
|
+
* @property {typeof pickTextMap} pickTextMap
|
|
317
|
+
* @property {(carrier: Carrier) => string | undefined} readBaggage
|
|
318
|
+
* @property {typeof b3.read} readB3
|
|
319
|
+
* @property {typeof b3Flags.read} readB3Flags
|
|
320
|
+
* @property {typeof b3Sampled.read} readB3Sampled
|
|
321
|
+
* @property {typeof b3SpanId.read} readB3SpanId
|
|
322
|
+
* @property {typeof b3TraceId.read} readB3TraceId
|
|
323
|
+
* @property {typeof datadogOrigin.read} readDatadogOrigin
|
|
324
|
+
* @property {typeof datadogParentId.read} readDatadogParentId
|
|
325
|
+
* @property {typeof datadogSamplingPriority.read} readDatadogSamplingPriority
|
|
326
|
+
* @property {(carrier: Carrier) => string | undefined} readDatadogTags
|
|
327
|
+
* @property {typeof datadogTraceId.read} readDatadogTraceId
|
|
328
|
+
* @property {typeof dsmBase64.read} readDsmBase64
|
|
329
|
+
* @property {typeof dsmBinary.read} readDsmBinary
|
|
330
|
+
* @property {(carrier: Carrier, target: Record<string, string>) => void} readLegacyBaggage
|
|
331
|
+
* @property {(carrier: Carrier) => string | undefined} readSqsd
|
|
332
|
+
* @property {typeof traceparent.read} readTraceparent
|
|
333
|
+
* @property {typeof tracestate.read} readTracestate
|
|
334
|
+
* @property {(carrier: Carrier, value: string) => void} writeBaggage
|
|
335
|
+
* @property {typeof b3.write} writeB3
|
|
336
|
+
* @property {typeof b3Flags.write} writeB3Flags
|
|
337
|
+
* @property {typeof b3ParentId.write} writeB3ParentId
|
|
338
|
+
* @property {typeof b3Sampled.write} writeB3Sampled
|
|
339
|
+
* @property {typeof b3SpanId.write} writeB3SpanId
|
|
340
|
+
* @property {typeof b3TraceId.write} writeB3TraceId
|
|
341
|
+
* @property {typeof datadogOrigin.write} writeDatadogOrigin
|
|
342
|
+
* @property {typeof datadogParentId.write} writeDatadogParentId
|
|
343
|
+
* @property {typeof datadogSamplingPriority.write} writeDatadogSamplingPriority
|
|
344
|
+
* @property {(carrier: Carrier, value: string) => void} writeDatadogTags
|
|
345
|
+
* @property {typeof datadogTraceId.write} writeDatadogTraceId
|
|
346
|
+
* @property {typeof dsmBase64.write} writeDsmBase64
|
|
347
|
+
* @property {(carrier: Record<string, string> | undefined, key: string, value: string) =>
|
|
348
|
+
* Record<string, string> | undefined} writeLegacyBaggage
|
|
349
|
+
* @property {typeof traceparent.write} writeTraceparent
|
|
350
|
+
* @property {typeof tracestate.write} writeTracestate
|
|
351
|
+
*/
|
|
352
|
+
|
|
353
|
+
carrierOperations.pickDsm = pickDsm
|
|
354
|
+
carrierOperations.pickTextMap = pickTextMap
|
|
355
|
+
|
|
356
|
+
module.exports = /** @type {CarrierOperations} */ (carrierOperations)
|
|
@@ -272,6 +272,7 @@ export interface GeneratedConfig {
|
|
|
272
272
|
DD_TRACE_HAPI_HAPI_ENABLED: boolean;
|
|
273
273
|
DD_TRACE_HONO_ENABLED: boolean;
|
|
274
274
|
DD_TRACE_HTTP_ENABLED: boolean;
|
|
275
|
+
DD_TRACE_HTTP_SERVER_ERROR_STATUSES: string;
|
|
275
276
|
DD_TRACE_HTTP2_ENABLED: boolean;
|
|
276
277
|
DD_TRACE_HTTPS_ENABLED: boolean;
|
|
277
278
|
DD_TRACE_IOREDIS_ENABLED: boolean;
|
|
@@ -727,6 +728,7 @@ export interface GeneratedEnvVarConfig {
|
|
|
727
728
|
DD_HEAP_SNAPSHOT_COUNT: number;
|
|
728
729
|
DD_HEAP_SNAPSHOT_DESTINATION: string;
|
|
729
730
|
DD_HEAP_SNAPSHOT_INTERVAL: number;
|
|
731
|
+
DD_HTTP_SERVER_ERROR_STATUSES: string;
|
|
730
732
|
DD_IAST_DB_ROWS_TO_TAINT: number;
|
|
731
733
|
DD_IAST_DEDUPLICATION_ENABLED: boolean;
|
|
732
734
|
DD_IAST_ENABLED: boolean;
|
|
@@ -966,6 +968,7 @@ export interface GeneratedEnvVarConfig {
|
|
|
966
968
|
DD_TRACE_HEADER_TAGS: string[];
|
|
967
969
|
DD_TRACE_HONO_ENABLED: boolean;
|
|
968
970
|
DD_TRACE_HTTP_ENABLED: boolean;
|
|
971
|
+
DD_TRACE_HTTP_SERVER_ERROR_STATUSES: string;
|
|
969
972
|
DD_TRACE_HTTP2_ENABLED: boolean;
|
|
970
973
|
DD_TRACE_HTTPS_ENABLED: boolean;
|
|
971
974
|
DD_TRACE_INFERRED_PROXY_SERVICES_ENABLED: boolean;
|
|
@@ -2999,6 +2999,16 @@
|
|
|
2999
2999
|
"default": "true"
|
|
3000
3000
|
}
|
|
3001
3001
|
],
|
|
3002
|
+
"DD_TRACE_HTTP_SERVER_ERROR_STATUSES": [
|
|
3003
|
+
{
|
|
3004
|
+
"implementation": "A",
|
|
3005
|
+
"type": "string",
|
|
3006
|
+
"default": "500-599",
|
|
3007
|
+
"aliases": [
|
|
3008
|
+
"DD_HTTP_SERVER_ERROR_STATUSES"
|
|
3009
|
+
]
|
|
3010
|
+
}
|
|
3011
|
+
],
|
|
3002
3012
|
"DD_TRACE_INFERRED_PROXY_SERVICES_ENABLED": [
|
|
3003
3013
|
{
|
|
3004
3014
|
"implementation": "A",
|
|
@@ -13,7 +13,12 @@ function getDataStreamsContext () {
|
|
|
13
13
|
function setDataStreamsContext (dataStreamsContext) {
|
|
14
14
|
log.debug('Setting new DSM Context: %j.', dataStreamsContext)
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
// `undefined` clears: a message that carries no context must not inherit the previous message's.
|
|
17
|
+
// The identity check keeps the common no-context case from allocating a store per message.
|
|
18
|
+
const store = legacyStorage.getStore()
|
|
19
|
+
if (store?.dataStreamsContext !== dataStreamsContext) {
|
|
20
|
+
legacyStorage.enterWith({ ...store, dataStreamsContext })
|
|
21
|
+
}
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
module.exports = {
|
|
@@ -16,9 +16,13 @@ class DataStreamsManager {
|
|
|
16
16
|
return ctx
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* @param {Record<string, string>|undefined} carrier Pass every message's carrier, including the
|
|
21
|
+
* ones without a context: that is what starts a new pathway instead of extending the previous
|
|
22
|
+
* message's.
|
|
23
|
+
*/
|
|
19
24
|
decodeDataStreamsContext (carrier) {
|
|
20
25
|
const ctx = DsmPathwayCodec.decode(carrier)
|
|
21
|
-
// we erase the previous context everytime we decode a new one
|
|
22
26
|
DataStreamsContext.setDataStreamsContext(ctx)
|
|
23
27
|
return ctx
|
|
24
28
|
}
|
|
@@ -5,23 +5,25 @@
|
|
|
5
5
|
// this inconsistency is ok because hashes do not need to be consistent across services
|
|
6
6
|
const crypto = require('crypto')
|
|
7
7
|
const { LRUCache } = require('../../../../vendor/dist/lru-cache')
|
|
8
|
+
const {
|
|
9
|
+
hasDsmBase64,
|
|
10
|
+
hasDsmBinary,
|
|
11
|
+
pickDsm,
|
|
12
|
+
readDsmBase64,
|
|
13
|
+
readDsmBinary,
|
|
14
|
+
writeDsmBase64,
|
|
15
|
+
} = require('../carrier')
|
|
8
16
|
const log = require('../log')
|
|
9
|
-
const pick = require('../../../datadog-core/src/utils/src/pick')
|
|
10
17
|
const { encodeVarintInto, decodeVarint } = require('./encoding')
|
|
11
18
|
|
|
12
19
|
const cache = new LRUCache({ max: 500 })
|
|
13
20
|
|
|
14
|
-
const CONTEXT_PROPAGATION_KEY = 'dd-pathway-ctx'
|
|
15
|
-
const CONTEXT_PROPAGATION_KEY_BASE64 = 'dd-pathway-ctx-base64'
|
|
16
|
-
|
|
17
21
|
const PATHWAY_CONTEXT_BYTES = 20
|
|
18
22
|
|
|
19
23
|
// Reused across `encodePathwayContext` calls; the buffer is fully rewritten before each
|
|
20
24
|
// `Buffer.from(...)` copy-out so callers never observe mutation between checkpoints.
|
|
21
25
|
const pathwayScratch = Buffer.allocUnsafe(PATHWAY_CONTEXT_BYTES)
|
|
22
26
|
|
|
23
|
-
const logKeys = [CONTEXT_PROPAGATION_KEY, CONTEXT_PROPAGATION_KEY_BASE64]
|
|
24
|
-
|
|
25
27
|
function shaHash (checkpointString) {
|
|
26
28
|
// Copy out of the 32-byte digest so the LRU cache doesn't retain it.
|
|
27
29
|
return Buffer.from(crypto.createHash('sha256').update(checkpointString).digest().subarray(0, 8))
|
|
@@ -114,7 +116,7 @@ function decodePathwayContext (pathwayContext) {
|
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
/**
|
|
117
|
-
* @param {string} pathwayContext
|
|
119
|
+
* @param {string | Buffer} pathwayContext
|
|
118
120
|
* @returns {ReturnType<typeof decodePathwayContext>|undefined}
|
|
119
121
|
*/
|
|
120
122
|
function decodePathwayContextBase64 (pathwayContext) {
|
|
@@ -142,10 +144,10 @@ const DsmPathwayCodec = {
|
|
|
142
144
|
encode (dataStreamsContext, carrier) {
|
|
143
145
|
if (!dataStreamsContext || !dataStreamsContext.hash) return
|
|
144
146
|
carrier ??= {}
|
|
145
|
-
carrier
|
|
147
|
+
writeDsmBase64(carrier, encodePathwayContextBase64(dataStreamsContext))
|
|
146
148
|
|
|
147
149
|
// eslint-disable-next-line eslint-rules/eslint-log-printf-style
|
|
148
|
-
log.debug(() => `Injected into DSM carrier: ${JSON.stringify(
|
|
150
|
+
log.debug(() => `Injected into DSM carrier: ${JSON.stringify(pickDsm(carrier))}.`)
|
|
149
151
|
|
|
150
152
|
return carrier
|
|
151
153
|
},
|
|
@@ -155,25 +157,25 @@ const DsmPathwayCodec = {
|
|
|
155
157
|
* @returns {ReturnType<typeof decodePathwayContext>|undefined}
|
|
156
158
|
*/
|
|
157
159
|
decode (carrier) {
|
|
158
|
-
// eslint-disable-next-line eslint-rules/eslint-log-printf-style
|
|
159
|
-
log.debug(() => `Attempting extract from DSM carrier: ${JSON.stringify(pick(carrier, logKeys))}.`)
|
|
160
|
-
|
|
161
160
|
if (carrier == null) return
|
|
162
161
|
|
|
162
|
+
// eslint-disable-next-line eslint-rules/eslint-log-printf-style
|
|
163
|
+
log.debug(() => `Attempting extract from DSM carrier: ${JSON.stringify(pickDsm(carrier))}.`)
|
|
164
|
+
|
|
163
165
|
let ctx
|
|
164
|
-
if (
|
|
166
|
+
if (hasDsmBase64(carrier)) {
|
|
165
167
|
// decode v2 encoding of base64
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
const encoded = readDsmBase64(carrier)
|
|
169
|
+
if (encoded !== undefined) ctx = decodePathwayContextBase64(encoded)
|
|
170
|
+
} else if (hasDsmBinary(carrier)) {
|
|
171
|
+
const encoded = readDsmBinary(carrier)
|
|
172
|
+
if (Buffer.isBuffer(encoded)) {
|
|
169
173
|
// decode v1 encoding
|
|
170
|
-
ctx = decodePathwayContext(
|
|
171
|
-
} catch {
|
|
172
|
-
// pass
|
|
174
|
+
ctx = decodePathwayContext(encoded)
|
|
173
175
|
}
|
|
174
176
|
// cover case where base64 context was received under wrong key
|
|
175
|
-
if (!ctx &&
|
|
176
|
-
ctx = decodePathwayContextBase64(
|
|
177
|
+
if (!ctx && encoded !== undefined) {
|
|
178
|
+
ctx = decodePathwayContextBase64(encoded)
|
|
177
179
|
}
|
|
178
180
|
}
|
|
179
181
|
|
|
@@ -182,7 +184,6 @@ const DsmPathwayCodec = {
|
|
|
182
184
|
}
|
|
183
185
|
|
|
184
186
|
module.exports = {
|
|
185
|
-
CONTEXT_PROPAGATION_KEY_BASE64,
|
|
186
187
|
computePathwayHash: computeHash,
|
|
187
188
|
encodePathwayContext,
|
|
188
189
|
decodePathwayContext,
|
|
@@ -5,10 +5,11 @@ const pkg = require('../../../../package.json')
|
|
|
5
5
|
|
|
6
6
|
const { LogCollapsingLowestDenseDDSketch } = require('../../../../vendor/dist/@datadog/sketches-js')
|
|
7
7
|
const { PATHWAY_HASH, DSM_TRANSACTION_ID, DSM_TRANSACTION_CHECKPOINT } = require('../../../../ext/tags')
|
|
8
|
+
const { dsmBase64NameLength } = require('../carrier')
|
|
8
9
|
const log = require('../log')
|
|
9
10
|
const processTags = require('../process-tags')
|
|
10
11
|
const propagationHash = require('../propagation-hash')
|
|
11
|
-
const {
|
|
12
|
+
const { computePathwayHash } = require('./pathway')
|
|
12
13
|
const { DataStreamsWriter } = require('./writer')
|
|
13
14
|
const { getAmqpMessageSize, getHeadersSize, getMessageSize, getSizeOrZero } = require('./size')
|
|
14
15
|
const { SchemaBuilder } = require('./schemas/schema_builder')
|
|
@@ -21,7 +22,7 @@ const ENTRY_PARENT_HASH = Buffer.from('0000000000000000', 'hex')
|
|
|
21
22
|
// bytes, encoded as 28 base64 chars; together with the header key and
|
|
22
23
|
// JSON framing (matching the prior `JSON.stringify({key: value})` byte
|
|
23
24
|
// count minus 1), this is a fixed value.
|
|
24
|
-
const PATHWAY_HEADER_BYTES =
|
|
25
|
+
const PATHWAY_HEADER_BYTES = dsmBase64NameLength + 28 + 6
|
|
25
26
|
|
|
26
27
|
class StatsPoint {
|
|
27
28
|
constructor (hash, parentHash, edgeTags) {
|
|
@@ -16,6 +16,11 @@ module.exports = {
|
|
|
16
16
|
PROPAGATED_ML_APP_KEY: '_dd.p.llmobs_ml_app',
|
|
17
17
|
PROPAGATED_SESSION_ID_KEY: '_dd.p.llmobs_sid',
|
|
18
18
|
PARENT_ID_KEY: '_ml_obs.llmobs_parent_id',
|
|
19
|
+
|
|
20
|
+
PARENT_AGENT_NAME: '_ml_obs.parent_agent_name',
|
|
21
|
+
PARENT_AGENT_SPAN_ID: '_ml_obs.parent_agent_span_id',
|
|
22
|
+
PROPAGATED_PARENT_AGENT_ID_KEY: '_dd.p.llmobs_pagent_span_id',
|
|
23
|
+
PROPAGATED_PARENT_AGENT_NAME_KEY: '_dd.p.llmobs_pagent_name',
|
|
19
24
|
PROPAGATED_SAMPLE_RATE_KEY: '_dd.p.llmobs_sr',
|
|
20
25
|
PROPAGATED_SAMPLING_DECISION_KEY: '_dd.p.llmobs_sd',
|
|
21
26
|
SAMPLE_RATE: '_ml_obs.sample_rate',
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { channel } = require('dc-polyfill')
|
|
4
4
|
|
|
5
|
+
const { readDatadogTags, writeDatadogTags } = require('../carrier')
|
|
5
6
|
const log = require('../log')
|
|
6
7
|
const { DD_MAJOR } = require('../../../../version')
|
|
7
8
|
const startupLogs = require('../startup-log')
|
|
@@ -12,6 +13,8 @@ const {
|
|
|
12
13
|
PROPAGATED_ML_APP_KEY,
|
|
13
14
|
PROPAGATED_PARENT_ID_KEY,
|
|
14
15
|
PROPAGATED_SESSION_ID_KEY,
|
|
16
|
+
PROPAGATED_PARENT_AGENT_ID_KEY,
|
|
17
|
+
PROPAGATED_PARENT_AGENT_NAME_KEY,
|
|
15
18
|
SAMPLE_RATE,
|
|
16
19
|
SAMPLING_DECISION,
|
|
17
20
|
PROPAGATED_SAMPLE_RATE_KEY,
|
|
@@ -20,6 +23,7 @@ const {
|
|
|
20
23
|
PROPAGATED_TRACE_ID_KEY,
|
|
21
24
|
} = require('./constants/tags')
|
|
22
25
|
const { storage } = require('./storage')
|
|
26
|
+
const { agentNameWireSafe, appendOptionalPropagatedTag, resolveAgentAttribution, stripTagsetEntry } = require('./util')
|
|
23
27
|
const telemetry = require('./telemetry')
|
|
24
28
|
const LLMObsSpanProcessor = require('./span_processor')
|
|
25
29
|
const LLMObsEvalMetricsWriter = require('./writers/evaluations')
|
|
@@ -147,9 +151,17 @@ function handleLLMObsInjection ({ carrier }) {
|
|
|
147
151
|
|
|
148
152
|
if (!parentId && !mlApp && samplingDecision == null && !sessionId && !propagatedTraceId) return
|
|
149
153
|
|
|
154
|
+
// Propagate the nearest agent so spans in the downstream process attribute correctly. When the
|
|
155
|
+
// active span sits under a distributed agent, `resolveAgentAttribution` inherits the propagated
|
|
156
|
+
// id/name already on the parent's registry entry, so the chain survives multiple hops. Resolved
|
|
157
|
+
// after the bail-out above so we don't allocate when there is nothing to inject.
|
|
158
|
+
const { name: parentAgentName, spanId: parentAgentSpanId } = resolveAgentAttribution(
|
|
159
|
+
mlObsSpanTags, parent
|
|
160
|
+
)
|
|
161
|
+
|
|
150
162
|
// `_injectTags` only writes `x-datadog-tags` when the trace has `_dd.p.*`
|
|
151
163
|
// tags, so it may be undefined here — coalesce before appending.
|
|
152
|
-
const existing = carrier
|
|
164
|
+
const existing = readDatadogTags(carrier)
|
|
153
165
|
let tags = existing || ''
|
|
154
166
|
if (parentId) tags += `${tags ? ',' : ''}${PROPAGATED_PARENT_ID_KEY}=${parentId}`
|
|
155
167
|
if (mlApp) tags += `${tags ? ',' : ''}${PROPAGATED_ML_APP_KEY}=${mlApp}`
|
|
@@ -157,7 +169,27 @@ function handleLLMObsInjection ({ carrier }) {
|
|
|
157
169
|
if (sampleRate != null) tags += `${tags ? ',' : ''}${PROPAGATED_SAMPLE_RATE_KEY}=${sampleRate}`
|
|
158
170
|
if (samplingDecision != null) tags += `${tags ? ',' : ''}${PROPAGATED_SAMPLING_DECISION_KEY}=${samplingDecision}`
|
|
159
171
|
if (propagatedTraceId != null) tags += `${tags ? ',' : ''}${PROPAGATED_TRACE_ID_KEY}=${propagatedTraceId}`
|
|
160
|
-
|
|
172
|
+
// When a local agent attribution is resolved, strip any stale upstream pagent entries that
|
|
173
|
+
// `_injectTags` may have already written into the carrier (it propagates all `_dd.p.*` from
|
|
174
|
+
// `_trace.tags`). This ensures the downstream sees a consistent id-only or id+name pair
|
|
175
|
+
// rather than a mix from different hops. The id is always digit-safe; an unsafe name is wiped.
|
|
176
|
+
if (parentAgentSpanId) {
|
|
177
|
+
tags = stripTagsetEntry(tags, PROPAGATED_PARENT_AGENT_ID_KEY)
|
|
178
|
+
tags = stripTagsetEntry(tags, PROPAGATED_PARENT_AGENT_NAME_KEY)
|
|
179
|
+
}
|
|
180
|
+
const maxLength = globalTracerConfig.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH
|
|
181
|
+
const tagsWithId = appendOptionalPropagatedTag(
|
|
182
|
+
tags, PROPAGATED_PARENT_AGENT_ID_KEY, parentAgentSpanId, null, maxLength
|
|
183
|
+
)
|
|
184
|
+
// Only append the name when the id fit: a name without an id is unresolvable by the backend.
|
|
185
|
+
if (tagsWithId === tags) {
|
|
186
|
+
tags = tagsWithId
|
|
187
|
+
} else {
|
|
188
|
+
tags = appendOptionalPropagatedTag(
|
|
189
|
+
tagsWithId, PROPAGATED_PARENT_AGENT_NAME_KEY, parentAgentName, agentNameWireSafe, maxLength
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
if (tags !== existing) writeDatadogTags(carrier, tags)
|
|
161
193
|
}
|
|
162
194
|
|
|
163
195
|
function handleFlush () {
|
|
@@ -27,6 +27,8 @@ const {
|
|
|
27
27
|
ML_APP,
|
|
28
28
|
TAGS,
|
|
29
29
|
PARENT_ID_KEY,
|
|
30
|
+
PARENT_AGENT_NAME,
|
|
31
|
+
PARENT_AGENT_SPAN_ID,
|
|
30
32
|
SESSION_ID,
|
|
31
33
|
NAME,
|
|
32
34
|
INPUT_PROMPT,
|
|
@@ -157,6 +159,20 @@ class LLMObsSpanProcessor {
|
|
|
157
159
|
this.#addObject(mlObsTags[TOOL_DEFINITIONS], meta.tool_definitions)
|
|
158
160
|
}
|
|
159
161
|
|
|
162
|
+
// Surface the agent attribution resolved at registration, but only on spans that actually
|
|
163
|
+
// have an agent ancestor. The id is always present in that case; the name may be missing when
|
|
164
|
+
// it arrived id-only over distributed propagation (older/unsafe upstream). Emit the name as
|
|
165
|
+
// explicit `null` then, matching the cross-language wire shape (dd-trace-py sends null, not an
|
|
166
|
+
// absent key) so the shared backend and system-tests see the same payload.
|
|
167
|
+
const parentAgentName = mlObsTags[PARENT_AGENT_NAME]
|
|
168
|
+
const parentAgentSpanId = mlObsTags[PARENT_AGENT_SPAN_ID]
|
|
169
|
+
if (parentAgentName != null || parentAgentSpanId != null) {
|
|
170
|
+
meta.agent_attribution = {
|
|
171
|
+
pagent_name: parentAgentName ?? null,
|
|
172
|
+
pagent_span_id: parentAgentSpanId,
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
160
176
|
const llmObsSpan = new LLMObservabilitySpan(spanKind)
|
|
161
177
|
|
|
162
178
|
if (spanKind === 'llm' && mlObsTags[INPUT_MESSAGES]) {
|