dd-trace 5.99.1 → 5.100.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-3rdparty.csv +0 -1
- package/package.json +4 -4
- package/packages/datadog-instrumentations/src/cucumber.js +69 -5
- package/packages/datadog-instrumentations/src/express.js +3 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/hono.js +15 -4
- package/packages/datadog-instrumentations/src/jest.js +84 -58
- package/packages/datadog-instrumentations/src/mocha/main.js +18 -22
- package/packages/datadog-instrumentations/src/mocha/utils.js +114 -96
- package/packages/datadog-instrumentations/src/mocha/worker.js +2 -2
- package/packages/datadog-instrumentations/src/path-to-regexp.js +44 -0
- package/packages/datadog-instrumentations/src/playwright.js +108 -18
- package/packages/datadog-instrumentations/src/router.js +53 -33
- package/packages/datadog-instrumentations/src/vitest.js +76 -30
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -1
- package/packages/datadog-plugin-bullmq/src/consumer.js +3 -2
- package/packages/datadog-plugin-bullmq/src/producer.js +25 -11
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +32 -9
- package/packages/datadog-plugin-cypress/src/support.js +22 -21
- package/packages/datadog-plugin-grpc/src/client.js +1 -1
- package/packages/datadog-plugin-grpc/src/server.js +1 -1
- package/packages/datadog-plugin-mongodb-core/src/index.js +2 -3
- package/packages/datadog-plugin-playwright/src/index.js +6 -0
- package/packages/datadog-plugin-router/src/index.js +13 -0
- package/packages/dd-trace/index.js +4 -3
- package/packages/dd-trace/src/aiguard/sdk.js +2 -2
- package/packages/dd-trace/src/baggage.js +10 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +17 -41
- package/packages/dd-trace/src/config/index.js +6 -5
- package/packages/dd-trace/src/config/normalize-service.js +31 -0
- package/packages/dd-trace/src/config/supported-configurations.json +15 -32
- package/packages/dd-trace/src/debugger/config.js +1 -1
- package/packages/dd-trace/src/encode/0.4.js +1 -1
- package/packages/dd-trace/src/encode/tags-processors.js +3 -3
- package/packages/dd-trace/src/heap_snapshots.js +4 -4
- package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +2 -2
- package/packages/dd-trace/src/opentelemetry/context_manager.js +11 -8
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +170 -0
- package/packages/dd-trace/src/opentelemetry/span.js +14 -42
- package/packages/dd-trace/src/opentelemetry/tracer.js +11 -36
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +31 -10
- package/packages/dd-trace/src/opentracing/propagation/tracestate.js +42 -12
- package/packages/dd-trace/src/opentracing/span.js +3 -2
- package/packages/dd-trace/src/plugins/util/ci.js +119 -32
- package/packages/dd-trace/src/plugins/util/test.js +293 -27
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +2 -2
- package/packages/dd-trace/src/propagation-hash/index.js +1 -1
- package/packages/dd-trace/src/proxy.js +3 -3
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +1 -1
- package/packages/dd-trace/src/span_processor.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +7 -5
- package/packages/dd-trace/src/tracer_metadata.js +1 -1
- package/vendor/dist/path-to-regexp/LICENSE +0 -21
- package/vendor/dist/path-to-regexp/index.js +0 -1
|
@@ -8,11 +8,11 @@ const { threadId } = require('worker_threads')
|
|
|
8
8
|
const log = require('./log')
|
|
9
9
|
|
|
10
10
|
async function scheduleSnapshot (config, total) {
|
|
11
|
-
if (total > config.
|
|
11
|
+
if (total > config.DD_HEAP_SNAPSHOT_COUNT) return
|
|
12
12
|
|
|
13
|
-
await setTimeout(config.
|
|
13
|
+
await setTimeout(config.DD_HEAP_SNAPSHOT_INTERVAL * 1000, null, { ref: false })
|
|
14
14
|
await clearMemory()
|
|
15
|
-
writeHeapSnapshot(getName(config.
|
|
15
|
+
writeHeapSnapshot(getName(config.DD_HEAP_SNAPSHOT_DESTINATION))
|
|
16
16
|
await scheduleSnapshot(config, total + 1)
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -49,7 +49,7 @@ module.exports = {
|
|
|
49
49
|
* @param {import('./config/config-base')} config - Tracer configuration
|
|
50
50
|
*/
|
|
51
51
|
async start (config) {
|
|
52
|
-
const destination = config.
|
|
52
|
+
const destination = config.DD_HEAP_SNAPSHOT_DESTINATION
|
|
53
53
|
|
|
54
54
|
try {
|
|
55
55
|
await scheduleSnapshot(config, 1)
|
|
@@ -30,7 +30,7 @@ const COUNTER_UNIT = '{evaluation}'
|
|
|
30
30
|
* If counter creation fails (e.g. the OTel API is not yet available), the call
|
|
31
31
|
* is silently skipped and retried on the next `finally()` invocation.
|
|
32
32
|
*
|
|
33
|
-
* When `config.
|
|
33
|
+
* When `config.DD_METRICS_OTEL_ENABLED` is false, `finally()` is always a no-op.
|
|
34
34
|
*/
|
|
35
35
|
class EvalMetricsHook {
|
|
36
36
|
#enabled = false
|
|
@@ -40,7 +40,7 @@ class EvalMetricsHook {
|
|
|
40
40
|
* @param {import('../config')} config - Tracer configuration object
|
|
41
41
|
*/
|
|
42
42
|
constructor (config) {
|
|
43
|
-
this.#enabled = config.
|
|
43
|
+
this.#enabled = config.DD_METRICS_OTEL_ENABLED === true
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { trace, ROOT_CONTEXT, propagation } = require('@opentelemetry/api')
|
|
4
4
|
const { storage } = require('../../../datadog-core')
|
|
5
|
-
const { getAllBaggageItems,
|
|
5
|
+
const { getAllBaggageItems, setAllBaggageItems, removeAllBaggageItems } = require('../baggage')
|
|
6
6
|
|
|
7
7
|
const SpanContext = require('./span_context')
|
|
8
8
|
|
|
@@ -64,13 +64,16 @@ class ContextManager {
|
|
|
64
64
|
return this._store.run(context, cb, ...args)
|
|
65
65
|
}
|
|
66
66
|
const baggages = propagation.getBaggage(context)
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
const baggageItems = baggages ? baggages.getAllEntries() : []
|
|
68
|
+
if (baggageItems.length > 0) {
|
|
69
|
+
/** @type {Record<string, string>} */
|
|
70
|
+
const items = {}
|
|
71
|
+
for (const [key, entry] of baggageItems) {
|
|
72
|
+
items[key] = entry.value
|
|
73
|
+
}
|
|
74
|
+
setAllBaggageItems(items)
|
|
75
|
+
} else {
|
|
76
|
+
removeAllBaggageItems()
|
|
74
77
|
}
|
|
75
78
|
if (span && span._ddSpan) {
|
|
76
79
|
const ddSpan = span._ddSpan
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { ERROR_MESSAGE, ERROR_STACK, ERROR_TYPE, IGNORE_OTEL_ERROR } = require('../constants')
|
|
4
|
+
const DatadogSpanContext = require('../opentracing/span_context')
|
|
5
|
+
const TraceState = require('../opentracing/propagation/tracestate')
|
|
6
|
+
|
|
7
|
+
const id = require('../id')
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {{ toTraceId: (get128?: boolean) => string, toSpanId: (get128?: boolean) => string }} DatadogContextLike
|
|
11
|
+
* @typedef {{ _ddContext: import('../opentracing/span_context') }} OtelBridgeSpanContextLike
|
|
12
|
+
* @typedef {{
|
|
13
|
+
* traceId: string,
|
|
14
|
+
* spanId: string,
|
|
15
|
+
* traceFlags?: number,
|
|
16
|
+
* traceState?: { serialize: () => string }
|
|
17
|
+
* }} OtelSpanContextLike
|
|
18
|
+
* @typedef {DatadogContextLike | OtelBridgeSpanContextLike | OtelSpanContextLike} LinkContextLike
|
|
19
|
+
* @typedef {{ context: LinkContextLike, attributes?: Record<string, unknown> }} OtelLink
|
|
20
|
+
* @typedef {{
|
|
21
|
+
* name?: string,
|
|
22
|
+
* message?: string,
|
|
23
|
+
* stack?: string,
|
|
24
|
+
* type?: string,
|
|
25
|
+
* escaped?: unknown
|
|
26
|
+
* }} ExceptionLike
|
|
27
|
+
* @typedef {{
|
|
28
|
+
* addEvent: (name: string, attributes: Record<string, unknown>, timeInput?: unknown) => unknown
|
|
29
|
+
* }} EventTarget
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Normalize any Datadog/OTel span-context shape to a `DatadogSpanContext`.
|
|
34
|
+
*
|
|
35
|
+
* @param {LinkContextLike | undefined | null} context
|
|
36
|
+
* @returns {import('../opentracing/span_context') | undefined}
|
|
37
|
+
*/
|
|
38
|
+
function normalizeLinkContext (context) {
|
|
39
|
+
if (!context) return
|
|
40
|
+
|
|
41
|
+
const bridgeCtx = /** @type {OtelBridgeSpanContextLike} */ (context)
|
|
42
|
+
if (bridgeCtx._ddContext) return bridgeCtx._ddContext
|
|
43
|
+
|
|
44
|
+
const ddCtx = /** @type {DatadogContextLike} */ (context)
|
|
45
|
+
if (typeof ddCtx.toTraceId === 'function' && typeof ddCtx.toSpanId === 'function') {
|
|
46
|
+
return /** @type {import('../opentracing/span_context')} */ (/** @type {unknown} */ (context))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const otelCtx = /** @type {OtelSpanContextLike} */ (context)
|
|
50
|
+
if (typeof otelCtx.traceId !== 'string' || typeof otelCtx.spanId !== 'string') return
|
|
51
|
+
|
|
52
|
+
let sampling
|
|
53
|
+
if (typeof otelCtx.traceFlags === 'number') {
|
|
54
|
+
sampling = { priority: otelCtx.traceFlags & 1 }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let tracestate
|
|
58
|
+
if (otelCtx.traceState?.serialize) {
|
|
59
|
+
tracestate = TraceState.fromString(otelCtx.traceState.serialize())
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return new DatadogSpanContext({
|
|
63
|
+
traceId: id(otelCtx.traceId, 16),
|
|
64
|
+
spanId: id(otelCtx.spanId, 16),
|
|
65
|
+
sampling,
|
|
66
|
+
tracestate,
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @param {import('../opentracing/span')} ddSpan
|
|
72
|
+
* @param {string} key
|
|
73
|
+
* @param {unknown} value
|
|
74
|
+
* @returns {void}
|
|
75
|
+
*/
|
|
76
|
+
function setOtelAttribute (ddSpan, key, value) {
|
|
77
|
+
if (key === 'http.response.status_code') {
|
|
78
|
+
ddSpan.setTag('http.status_code', String(value))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
ddSpan.setTag(key, value)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param {import('../opentracing/span')} ddSpan
|
|
86
|
+
* @param {Record<string, unknown>} attributes
|
|
87
|
+
* @returns {void}
|
|
88
|
+
*/
|
|
89
|
+
function setOtelAttributes (ddSpan, attributes) {
|
|
90
|
+
if ('http.response.status_code' in attributes) {
|
|
91
|
+
attributes['http.status_code'] = String(attributes['http.response.status_code'])
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
ddSpan.addTags(attributes)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Accepts both `{ context, attributes }` and the deprecated `(context, attrs)` form.
|
|
99
|
+
*
|
|
100
|
+
* @param {import('../opentracing/span')} ddSpan
|
|
101
|
+
* @param {LinkContextLike | OtelLink} link
|
|
102
|
+
* @param {Record<string, unknown>} [attrs]
|
|
103
|
+
* @returns {void}
|
|
104
|
+
*/
|
|
105
|
+
function addOtelLink (ddSpan, link, attrs) {
|
|
106
|
+
// TODO: Drop the (context, attrs) form in v6.0.0.
|
|
107
|
+
const linkObj = link && typeof link === 'object' && 'context' in link
|
|
108
|
+
? /** @type {OtelLink} */ (link)
|
|
109
|
+
: { context: /** @type {LinkContextLike} */ (link), attributes: attrs ?? {} }
|
|
110
|
+
|
|
111
|
+
const ddSpanContext = normalizeLinkContext(linkObj.context)
|
|
112
|
+
if (!ddSpanContext) return
|
|
113
|
+
|
|
114
|
+
ddSpan.addLink({ context: ddSpanContext, attributes: linkObj.attributes })
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @param {import('../opentracing/span')} ddSpan
|
|
119
|
+
* @param {EventTarget} eventTarget
|
|
120
|
+
* @param {ExceptionLike} exception
|
|
121
|
+
* @param {unknown} [timeInput]
|
|
122
|
+
* @returns {void}
|
|
123
|
+
*/
|
|
124
|
+
function recordException (ddSpan, eventTarget, exception, timeInput) {
|
|
125
|
+
ddSpan.addTags({
|
|
126
|
+
[ERROR_TYPE]: exception.name,
|
|
127
|
+
[ERROR_MESSAGE]: exception.message,
|
|
128
|
+
[ERROR_STACK]: exception.stack,
|
|
129
|
+
[IGNORE_OTEL_ERROR]: ddSpan.context()._tags[IGNORE_OTEL_ERROR] ?? true,
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
/** @type {Record<string, unknown>} */
|
|
133
|
+
const attributes = {}
|
|
134
|
+
if (exception.message) attributes['exception.message'] = exception.message
|
|
135
|
+
if (exception.type) attributes['exception.type'] = exception.type
|
|
136
|
+
if (exception.escaped) attributes['exception.escaped'] = exception.escaped
|
|
137
|
+
if (exception.stack) attributes['exception.stacktrace'] = exception.stack
|
|
138
|
+
|
|
139
|
+
eventTarget.addEvent(exception.name ?? 'Error', attributes, timeInput)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* First-call-wins; no-op on ended spans. Only `code === 2` emits Datadog error tags.
|
|
144
|
+
*
|
|
145
|
+
* @param {import('../opentracing/span')} ddSpan
|
|
146
|
+
* @param {{ ended: boolean, _hasStatus: boolean }} bridgeSpan
|
|
147
|
+
* @param {{ code?: number, message?: string }} [status]
|
|
148
|
+
* @returns {void}
|
|
149
|
+
*/
|
|
150
|
+
function setStatus (ddSpan, bridgeSpan, { code, message } = {}) {
|
|
151
|
+
if (bridgeSpan.ended || bridgeSpan._hasStatus || !code) return
|
|
152
|
+
|
|
153
|
+
bridgeSpan._hasStatus = true
|
|
154
|
+
|
|
155
|
+
if (code === 2) {
|
|
156
|
+
ddSpan.addTags({
|
|
157
|
+
[ERROR_MESSAGE]: message,
|
|
158
|
+
[IGNORE_OTEL_ERROR]: false,
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
module.exports = {
|
|
164
|
+
addOtelLink,
|
|
165
|
+
normalizeLinkContext,
|
|
166
|
+
recordException,
|
|
167
|
+
setOtelAttribute,
|
|
168
|
+
setOtelAttributes,
|
|
169
|
+
setStatus,
|
|
170
|
+
}
|
|
@@ -9,12 +9,18 @@ const { timeInputToHrTime } = require('../../../../vendor/dist/@opentelemetry/co
|
|
|
9
9
|
|
|
10
10
|
const tracer = require('../../')
|
|
11
11
|
const DatadogSpan = require('../opentracing/span')
|
|
12
|
-
const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK, IGNORE_OTEL_ERROR } = require('../constants')
|
|
13
12
|
const { SERVICE_NAME, RESOURCE_NAME, SPAN_KIND } = require('../../../../ext/tags')
|
|
14
13
|
const kinds = require('../../../../ext/kinds')
|
|
15
14
|
|
|
16
15
|
const id = require('../id')
|
|
17
16
|
const SpanContext = require('./span_context')
|
|
17
|
+
const {
|
|
18
|
+
addOtelLink,
|
|
19
|
+
recordException,
|
|
20
|
+
setOtelAttribute,
|
|
21
|
+
setOtelAttributes,
|
|
22
|
+
setStatus,
|
|
23
|
+
} = require('./span-helpers')
|
|
18
24
|
|
|
19
25
|
// The one built into OTel rounds so we lose sub-millisecond precision.
|
|
20
26
|
function hrTimeToMilliseconds (time) {
|
|
@@ -195,33 +201,17 @@ class Span {
|
|
|
195
201
|
}
|
|
196
202
|
|
|
197
203
|
setAttribute (key, value) {
|
|
198
|
-
|
|
199
|
-
this._ddSpan.setTag('http.status_code', value.toString())
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
this._ddSpan.setTag(key, value)
|
|
204
|
+
setOtelAttribute(this._ddSpan, key, value)
|
|
203
205
|
return this
|
|
204
206
|
}
|
|
205
207
|
|
|
206
208
|
setAttributes (attributes) {
|
|
207
|
-
|
|
208
|
-
attributes['http.status_code'] = attributes['http.response.status_code'].toString()
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
this._ddSpan.addTags(attributes)
|
|
209
|
+
setOtelAttributes(this._ddSpan, attributes)
|
|
212
210
|
return this
|
|
213
211
|
}
|
|
214
212
|
|
|
215
213
|
addLink (link, attrs) {
|
|
216
|
-
|
|
217
|
-
if (link instanceof SpanContext) {
|
|
218
|
-
link = { context: link, attributes: attrs ?? {} }
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const { context, attributes } = link
|
|
222
|
-
// Extract dd context
|
|
223
|
-
const ddSpanContext = context._ddContext
|
|
224
|
-
this._ddSpan.addLink({ context: ddSpanContext, attributes })
|
|
214
|
+
addOtelLink(this._ddSpan, link, attrs)
|
|
225
215
|
return this
|
|
226
216
|
}
|
|
227
217
|
|
|
@@ -244,16 +234,8 @@ class Span {
|
|
|
244
234
|
return this.addLink(zeroContext, attributes)
|
|
245
235
|
}
|
|
246
236
|
|
|
247
|
-
setStatus (
|
|
248
|
-
|
|
249
|
-
this._hasStatus = true
|
|
250
|
-
if (code === 2) {
|
|
251
|
-
this._ddSpan.addTags({
|
|
252
|
-
[ERROR_MESSAGE]: message,
|
|
253
|
-
[IGNORE_OTEL_ERROR]: false,
|
|
254
|
-
})
|
|
255
|
-
}
|
|
256
|
-
}
|
|
237
|
+
setStatus (status) {
|
|
238
|
+
setStatus(this._ddSpan, this, status)
|
|
257
239
|
return this
|
|
258
240
|
}
|
|
259
241
|
|
|
@@ -291,18 +273,8 @@ class Span {
|
|
|
291
273
|
}
|
|
292
274
|
|
|
293
275
|
recordException (exception, timeInput) {
|
|
294
|
-
this.
|
|
295
|
-
|
|
296
|
-
[ERROR_MESSAGE]: exception.message,
|
|
297
|
-
[ERROR_STACK]: exception.stack,
|
|
298
|
-
[IGNORE_OTEL_ERROR]: this._ddSpan.context()._tags[IGNORE_OTEL_ERROR] ?? true,
|
|
299
|
-
})
|
|
300
|
-
const attributes = {}
|
|
301
|
-
if (exception.message) attributes['exception.message'] = exception.message
|
|
302
|
-
if (exception.type) attributes['exception.type'] = exception.type
|
|
303
|
-
if (exception.escaped) attributes['exception.escaped'] = exception.escaped
|
|
304
|
-
if (exception.stack) attributes['exception.stacktrace'] = exception.stack
|
|
305
|
-
this.addEvent(exception.name, attributes, timeInput)
|
|
276
|
+
// Route through `this.addEvent` so its time-input conversion applies.
|
|
277
|
+
recordException(this._ddSpan, this, exception, timeInput)
|
|
306
278
|
}
|
|
307
279
|
|
|
308
280
|
get duration () {
|
|
@@ -3,49 +3,16 @@
|
|
|
3
3
|
const api = require('@opentelemetry/api')
|
|
4
4
|
const { sanitizeAttributes } = require('../../../../vendor/dist/@opentelemetry/core')
|
|
5
5
|
|
|
6
|
+
const tracer = require('../../')
|
|
7
|
+
|
|
6
8
|
const id = require('../id')
|
|
7
9
|
const log = require('../log')
|
|
8
|
-
const DatadogSpanContext = require('../opentracing/span_context')
|
|
9
10
|
const TextMapPropagator = require('../opentracing/propagation/text_map')
|
|
10
11
|
const TraceState = require('../opentracing/propagation/tracestate')
|
|
11
12
|
const SpanContext = require('./span_context')
|
|
12
13
|
const Span = require('./span')
|
|
13
14
|
const Sampler = require('./sampler')
|
|
14
|
-
|
|
15
|
-
function normalizeLinkContext (context) {
|
|
16
|
-
if (!context) return
|
|
17
|
-
|
|
18
|
-
// OTel API bridge SpanContext wrapper
|
|
19
|
-
if (context._ddContext) return context._ddContext
|
|
20
|
-
|
|
21
|
-
// Datadog span context
|
|
22
|
-
if (typeof context.toTraceId === 'function' && typeof context.toSpanId === 'function') {
|
|
23
|
-
return context
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Standard OTel SpanContext (traceId/spanId)
|
|
27
|
-
if (typeof context.traceId !== 'string' || typeof context.spanId !== 'string') {
|
|
28
|
-
// Invalid
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
let sampling
|
|
33
|
-
if (typeof context.traceFlags === 'number') {
|
|
34
|
-
sampling = { priority: context.traceFlags & 1 }
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
let tracestate
|
|
38
|
-
if (context.traceState?.serialize) {
|
|
39
|
-
tracestate = TraceState.fromString(context.traceState.serialize())
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return new DatadogSpanContext({
|
|
43
|
-
traceId: id(context.traceId, 16),
|
|
44
|
-
spanId: id(context.spanId, 16),
|
|
45
|
-
sampling,
|
|
46
|
-
tracestate,
|
|
47
|
-
})
|
|
48
|
-
}
|
|
15
|
+
const { normalizeLinkContext } = require('./span-helpers')
|
|
49
16
|
|
|
50
17
|
class Tracer {
|
|
51
18
|
constructor (library, config, tracerProvider) {
|
|
@@ -140,6 +107,14 @@ class Tracer {
|
|
|
140
107
|
spanContext = new SpanContext()
|
|
141
108
|
}
|
|
142
109
|
|
|
110
|
+
// init() didn't finish setting up real tracing (e.g. DD_TRACE_ENABLED=false,
|
|
111
|
+
// or init() was never called), so the inner tracer is still the noop.
|
|
112
|
+
// DatadogSpan can't construct without a processor + prioritySampler, so fall
|
|
113
|
+
// through to a non-recording span; the SpanContext still propagates.
|
|
114
|
+
if (!tracer._tracingInitialized) {
|
|
115
|
+
return api.trace.wrapSpanContext(spanContext)
|
|
116
|
+
}
|
|
117
|
+
|
|
143
118
|
const spanKind = options.kind || api.SpanKind.INTERNAL
|
|
144
119
|
const links = []
|
|
145
120
|
if (options.links?.length) {
|
|
@@ -7,7 +7,7 @@ const DatadogSpanContext = require('../span_context')
|
|
|
7
7
|
const log = require('../../log')
|
|
8
8
|
const tags = require('../../../../../ext/tags')
|
|
9
9
|
const { getConfiguredEnvName } = require('../../config/helper')
|
|
10
|
-
const {
|
|
10
|
+
const { setAllBaggageItems, getAllBaggageItems, removeAllBaggageItems } = require('../../baggage')
|
|
11
11
|
const telemetryMetrics = require('../../telemetry/metrics')
|
|
12
12
|
|
|
13
13
|
const { AUTO_KEEP, AUTO_REJECT, USER_KEEP } = require('../../../../../ext/priority')
|
|
@@ -166,6 +166,7 @@ class TextMapPropagator {
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
|
+
|
|
169
170
|
if (this._hasPropagationStyle('inject', 'baggage')) {
|
|
170
171
|
let baggage = ''
|
|
171
172
|
let itemCounter = 0
|
|
@@ -408,10 +409,10 @@ class TextMapPropagator {
|
|
|
408
409
|
}
|
|
409
410
|
}
|
|
410
411
|
|
|
411
|
-
if (this._config.
|
|
412
|
+
if (this._config.DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT === 'ignore') {
|
|
412
413
|
context._links = []
|
|
413
414
|
} else {
|
|
414
|
-
if (this._config.
|
|
415
|
+
if (this._config.DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT === 'restart') {
|
|
415
416
|
context._links = []
|
|
416
417
|
context._links.push({
|
|
417
418
|
context,
|
|
@@ -675,11 +676,29 @@ class TextMapPropagator {
|
|
|
675
676
|
_extractBaggageItems (carrier, spanContext) {
|
|
676
677
|
removeAllBaggageItems()
|
|
677
678
|
if (!this._hasPropagationStyle('extract', 'baggage')) return
|
|
678
|
-
|
|
679
|
-
const
|
|
679
|
+
const baggageHeader = carrier?.baggage
|
|
680
|
+
const header = Array.isArray(baggageHeader) ? baggageHeader.join(',') : baggageHeader
|
|
681
|
+
if (!header) return
|
|
682
|
+
|
|
683
|
+
const baggages = header.split(',')
|
|
680
684
|
const baggageTagKeys = new Set(this._config.baggageTagKeys)
|
|
681
685
|
const tagAllKeys = baggageTagKeys.has('*')
|
|
686
|
+
/** @type {Record<string, string> | undefined} */
|
|
687
|
+
let items
|
|
688
|
+
let itemCount = 0
|
|
689
|
+
let byteCount = 0
|
|
690
|
+
|
|
682
691
|
for (const keyValue of baggages) {
|
|
692
|
+
if (itemCount >= this._config.baggageMaxItems) {
|
|
693
|
+
tracerMetrics.count('context_header.truncated', ['truncation_reason:baggage_item_count_exceeded']).inc()
|
|
694
|
+
break
|
|
695
|
+
}
|
|
696
|
+
// Charge the comma slot before the empty-entry skip so a `,,,,,foo=bar` can't iterate for free.
|
|
697
|
+
byteCount += keyValue.length + 1
|
|
698
|
+
if (byteCount > this._config.baggageMaxBytes) {
|
|
699
|
+
tracerMetrics.count('context_header.truncated', ['truncation_reason:baggage_byte_count_exceeded']).inc()
|
|
700
|
+
break
|
|
701
|
+
}
|
|
683
702
|
if (!keyValue) continue
|
|
684
703
|
|
|
685
704
|
// Per W3C baggage, list-members can contain optional properties after `;`.
|
|
@@ -692,7 +711,6 @@ class TextMapPropagator {
|
|
|
692
711
|
const eqIdx = member.indexOf('=')
|
|
693
712
|
if (eqIdx === -1) {
|
|
694
713
|
tracerMetrics.count('context_header_style.malformed', ['header_style:baggage']).inc()
|
|
695
|
-
removeAllBaggageItems()
|
|
696
714
|
return
|
|
697
715
|
}
|
|
698
716
|
|
|
@@ -701,7 +719,6 @@ class TextMapPropagator {
|
|
|
701
719
|
|
|
702
720
|
if (!baggageTokenExpr.test(key) || !value) {
|
|
703
721
|
tracerMetrics.count('context_header_style.malformed', ['header_style:baggage']).inc()
|
|
704
|
-
removeAllBaggageItems()
|
|
705
722
|
return
|
|
706
723
|
}
|
|
707
724
|
try {
|
|
@@ -710,15 +727,19 @@ class TextMapPropagator {
|
|
|
710
727
|
const bytes = value.replaceAll(percentByte, (_, hex) => String.fromCharCode(Number.parseInt(hex, 16)))
|
|
711
728
|
value = Buffer.from(bytes, 'binary').toString('utf8')
|
|
712
729
|
}
|
|
730
|
+
items ??= {}
|
|
731
|
+
items[key] = value
|
|
732
|
+
itemCount++
|
|
713
733
|
|
|
714
734
|
if (spanContext && (tagAllKeys || baggageTagKeys.has(key))) {
|
|
715
735
|
spanContext._trace.tags['baggage.' + key] = value
|
|
716
736
|
}
|
|
717
|
-
setBaggageItem(key, value)
|
|
718
737
|
}
|
|
719
738
|
|
|
720
|
-
|
|
721
|
-
|
|
739
|
+
if (items) {
|
|
740
|
+
setAllBaggageItems(items)
|
|
741
|
+
tracerMetrics.count('context_header_style.extracted', ['header_style:baggage']).inc()
|
|
742
|
+
}
|
|
722
743
|
}
|
|
723
744
|
|
|
724
745
|
_extractSamplingPriority (carrier, spanContext) {
|
|
@@ -1,19 +1,49 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
// W3C Trace Context §3.3.1.2: max 32 list-members.
|
|
4
|
+
// https://www.w3.org/TR/trace-context/#tracestate-header-field-values
|
|
5
|
+
const MAX_LIST_MEMBERS = 32
|
|
6
|
+
const WHITESPACE = /[ \t]/
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Parse a separator-delimited string into key/value entries.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} value
|
|
12
|
+
* @param {string} fieldSeparator Between entries.
|
|
13
|
+
* @param {string} pairSeparator Between key and value within an entry.
|
|
14
|
+
* @param {boolean} rejectValueTabs Drop entries whose value contains an internal tab.
|
|
15
|
+
* @returns {[string, string][]} Entries in reverse of wire order.
|
|
16
|
+
*/
|
|
17
|
+
function parseEntries (value, fieldSeparator, pairSeparator, rejectValueTabs) {
|
|
18
|
+
const segments = value.split(fieldSeparator)
|
|
19
|
+
segments.length = Math.min(segments.length, MAX_LIST_MEMBERS)
|
|
20
|
+
|
|
21
|
+
// TODO: We should extract dd no matter at what position and move it to the front of the list.
|
|
22
|
+
// Extract up 31 additional entries.
|
|
23
|
+
const entries = []
|
|
24
|
+
for (let index = 0; index < segments.length; index++) {
|
|
25
|
+
const segment = segments[index]
|
|
26
|
+
const splitIndex = segment.indexOf(pairSeparator)
|
|
27
|
+
if (splitIndex === -1) continue
|
|
28
|
+
const key = segment.slice(0, splitIndex).trim()
|
|
29
|
+
if (!key || WHITESPACE.test(key)) continue
|
|
30
|
+
// W3C §3.3.1.3.2: value = 0*255(chr) nblk-chr; chr = %x20 / nblk-chr (no tab).
|
|
31
|
+
// Leading 0x20 is part of value; trailing whitespace is OWS.
|
|
32
|
+
const entryValue = segment.slice(splitIndex + 1).trimEnd()
|
|
33
|
+
if (!entryValue || rejectValueTabs && entryValue.includes('\t')) continue
|
|
34
|
+
entries.push([key, entryValue])
|
|
9
35
|
}
|
|
36
|
+
// Reverse so the Map's insertion order is reverse of wire order. `toString`
|
|
37
|
+
// prepends as it iterates, which yields the original wire order back.
|
|
38
|
+
entries.reverse()
|
|
39
|
+
return entries
|
|
40
|
+
}
|
|
10
41
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
42
|
+
function fromString (Type, value, fieldSeparator, pairSeparator, rejectValueTabs) {
|
|
43
|
+
if (typeof value !== 'string' || !value.length) {
|
|
44
|
+
return new Type()
|
|
14
45
|
}
|
|
15
|
-
|
|
16
|
-
return new Type(values)
|
|
46
|
+
return new Type(parseEntries(value, fieldSeparator, pairSeparator, rejectValueTabs))
|
|
17
47
|
}
|
|
18
48
|
|
|
19
49
|
function toString (map, pairSeparator, fieldSeparator) {
|
|
@@ -52,7 +82,7 @@ class TraceStateData extends Map {
|
|
|
52
82
|
}
|
|
53
83
|
|
|
54
84
|
static fromString (value) {
|
|
55
|
-
return fromString(TraceStateData,
|
|
85
|
+
return fromString(TraceStateData, value, ';', ':', false)
|
|
56
86
|
}
|
|
57
87
|
|
|
58
88
|
toString () {
|
|
@@ -92,7 +122,7 @@ class TraceState extends Map {
|
|
|
92
122
|
}
|
|
93
123
|
|
|
94
124
|
static fromString (value) {
|
|
95
|
-
return fromString(TraceState,
|
|
125
|
+
return fromString(TraceState, value, ',', '=', true)
|
|
96
126
|
}
|
|
97
127
|
|
|
98
128
|
toString () {
|
|
@@ -336,7 +336,8 @@ class DatadogSpan {
|
|
|
336
336
|
let startTime
|
|
337
337
|
|
|
338
338
|
let baggage = {}
|
|
339
|
-
|
|
339
|
+
const propagationBehavior = this._parentTracer?._config?.DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT
|
|
340
|
+
if (parent && parent._isRemote && propagationBehavior !== 'continue') {
|
|
340
341
|
baggage = parent._baggageItems
|
|
341
342
|
parent = null
|
|
342
343
|
}
|
|
@@ -375,7 +376,7 @@ class DatadogSpan {
|
|
|
375
376
|
.padEnd(16, '0')
|
|
376
377
|
}
|
|
377
378
|
|
|
378
|
-
if (
|
|
379
|
+
if (propagationBehavior === 'restart') {
|
|
379
380
|
spanContext._baggageItems = baggage
|
|
380
381
|
}
|
|
381
382
|
}
|