dd-trace 5.99.0 → 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 +24 -5
- 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 +89 -63
- 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-dd-trace-api/src/index.js +1 -1
- package/packages/datadog-plugin-graphql/src/utils.js +2 -2
- package/packages/datadog-plugin-grpc/src/client.js +1 -1
- package/packages/datadog-plugin-grpc/src/server.js +1 -1
- package/packages/datadog-plugin-memcached/src/index.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/appsec/blocking.js +18 -6
- package/packages/dd-trace/src/appsec/graphql.js +1 -1
- package/packages/dd-trace/src/baggage.js +26 -13
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +1 -1
- package/packages/dd-trace/src/config/generated-config-types.d.ts +45 -69
- package/packages/dd-trace/src/config/index.js +13 -12
- package/packages/dd-trace/src/config/normalize-service.js +31 -0
- package/packages/dd-trace/src/config/supported-configurations.json +31 -76
- package/packages/dd-trace/src/debugger/config.js +1 -1
- package/packages/dd-trace/src/dogstatsd.js +5 -8
- 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/exporter.js +1 -1
- package/packages/dd-trace/src/git_metadata_tagger.js +1 -1
- package/packages/dd-trace/src/heap_snapshots.js +4 -4
- package/packages/dd-trace/src/llmobs/constants/tags.js +3 -0
- package/packages/dd-trace/src/llmobs/sdk.js +21 -1
- package/packages/dd-trace/src/llmobs/span_processor.js +14 -1
- package/packages/dd-trace/src/llmobs/writers/base.js +7 -1
- package/packages/dd-trace/src/llmobs/writers/spans.js +1 -1
- 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/logs/index.js +5 -5
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +6 -6
- 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/trace/otlp_http_trace_exporter.js +1 -1
- package/packages/dd-trace/src/opentelemetry/tracer.js +11 -36
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +44 -23
- package/packages/dd-trace/src/opentracing/propagation/tracestate.js +42 -12
- package/packages/dd-trace/src/opentracing/span.js +4 -3
- package/packages/dd-trace/src/plugin_manager.js +6 -6
- package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
- package/packages/dd-trace/src/plugins/util/ci.js +119 -32
- package/packages/dd-trace/src/plugins/util/test.js +295 -29
- 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 +9 -9
- 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
|
@@ -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
|
|
@@ -60,18 +60,18 @@ function initializeOpenTelemetryLogs (config) {
|
|
|
60
60
|
|
|
61
61
|
// Create OTLP exporter using resolved config values
|
|
62
62
|
const exporter = new OtlpHttpLogExporter(
|
|
63
|
-
config.
|
|
63
|
+
config.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
|
|
64
64
|
config.OTEL_EXPORTER_OTLP_LOGS_HEADERS,
|
|
65
|
-
config.
|
|
66
|
-
config.
|
|
65
|
+
config.OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
|
|
66
|
+
config.OTEL_EXPORTER_OTLP_LOGS_PROTOCOL,
|
|
67
67
|
resourceAttributes
|
|
68
68
|
)
|
|
69
69
|
|
|
70
70
|
// Create batch processor for exporting logs to Datadog Agent
|
|
71
71
|
const processor = new BatchLogRecordProcessor(
|
|
72
72
|
exporter,
|
|
73
|
-
config.
|
|
74
|
-
config.
|
|
73
|
+
config.OTEL_BSP_SCHEDULE_DELAY,
|
|
74
|
+
config.OTEL_BSP_MAX_EXPORT_BATCH_SIZE
|
|
75
75
|
)
|
|
76
76
|
|
|
77
77
|
// Create logger provider with processor for Datadog Agent export
|
|
@@ -57,18 +57,18 @@ function initializeOpenTelemetryMetrics (config) {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
const exporter = new OtlpHttpMetricExporter(
|
|
60
|
-
config.
|
|
60
|
+
config.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
|
|
61
61
|
config.OTEL_EXPORTER_OTLP_METRICS_HEADERS,
|
|
62
|
-
config.
|
|
63
|
-
config.
|
|
62
|
+
config.OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
|
|
63
|
+
config.OTEL_EXPORTER_OTLP_METRICS_PROTOCOL,
|
|
64
64
|
resourceAttributes
|
|
65
65
|
)
|
|
66
66
|
|
|
67
67
|
const reader = new PeriodicMetricReader(
|
|
68
68
|
exporter,
|
|
69
|
-
config.
|
|
70
|
-
config.
|
|
71
|
-
config.
|
|
69
|
+
config.OTEL_METRIC_EXPORT_INTERVAL,
|
|
70
|
+
config.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE,
|
|
71
|
+
config.OTEL_BSP_MAX_QUEUE_SIZE
|
|
72
72
|
)
|
|
73
73
|
|
|
74
74
|
const meterProvider = new MeterProvider({ reader })
|
|
@@ -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 () {
|
|
@@ -19,7 +19,7 @@ const OtlpTraceTransformer = require('./otlp_transformer')
|
|
|
19
19
|
* (https://opentelemetry.io/docs/specs/otel/trace/sdk/#batching-processor).
|
|
20
20
|
* Currently each finished trace is sent as its own HTTP request, which is
|
|
21
21
|
* unsuitable for high-traffic production environments. The config values
|
|
22
|
-
* `
|
|
22
|
+
* `OTEL_BSP_SCHEDULE_DELAY`, `OTEL_BSP_MAX_EXPORT_BATCH_SIZE`, and `OTEL_BSP_MAX_QUEUE_SIZE`
|
|
23
23
|
* (OTEL_BSP_*) are already defined and should drive that implementation.
|
|
24
24
|
*
|
|
25
25
|
* @class OtlpHttpTraceExporter
|
|
@@ -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')
|
|
@@ -64,6 +64,7 @@ const tracestateTagValueFilter = /[^\x20-\x2B\x2D-\x3A\x3C-\x7D]/g
|
|
|
64
64
|
const invalidSegment = /^0+$/
|
|
65
65
|
const zeroTraceId = '0000000000000000'
|
|
66
66
|
const hex16 = /^[0-9A-Fa-f]{16}$/
|
|
67
|
+
const percentByte = /%([0-9A-Fa-f]{2})/g
|
|
67
68
|
|
|
68
69
|
class TextMapPropagator {
|
|
69
70
|
#extractB3Context
|
|
@@ -165,6 +166,7 @@ class TextMapPropagator {
|
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
168
|
}
|
|
169
|
+
|
|
168
170
|
if (this._hasPropagationStyle('inject', 'baggage')) {
|
|
169
171
|
let baggage = ''
|
|
170
172
|
let itemCounter = 0
|
|
@@ -173,15 +175,15 @@ class TextMapPropagator {
|
|
|
173
175
|
const baggageItems = getAllBaggageItems()
|
|
174
176
|
if (!baggageItems) return
|
|
175
177
|
for (const [key, value] of Object.entries(baggageItems)) {
|
|
176
|
-
const baggageKey =
|
|
177
|
-
if (!
|
|
178
|
+
const baggageKey = key.trim()
|
|
179
|
+
if (!baggageTokenExpr.test(baggageKey)) continue
|
|
178
180
|
|
|
179
181
|
// Do not trim values. If callers include leading/trailing whitespace, it must be percent-encoded.
|
|
180
182
|
// W3C list-member allows optional properties after ';'.
|
|
181
183
|
// https://www.w3.org/TR/baggage/#header-content
|
|
182
|
-
const item = `${baggageKey}=${encodeURIComponent(
|
|
184
|
+
const item = `${baggageKey}=${encodeURIComponent(value)},`
|
|
183
185
|
itemCounter += 1
|
|
184
|
-
byteCounter +=
|
|
186
|
+
byteCounter += item.length
|
|
185
187
|
|
|
186
188
|
// Check for item count limit exceeded
|
|
187
189
|
if (itemCounter > this._config.baggageMaxItems) {
|
|
@@ -209,7 +211,7 @@ class TextMapPropagator {
|
|
|
209
211
|
_injectTags (spanContext, carrier) {
|
|
210
212
|
const trace = spanContext._trace
|
|
211
213
|
|
|
212
|
-
if (this._config.
|
|
214
|
+
if (this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH === 0) {
|
|
213
215
|
log.debug('Trace tag propagation is disabled, skipping injection.')
|
|
214
216
|
return
|
|
215
217
|
}
|
|
@@ -228,7 +230,7 @@ class TextMapPropagator {
|
|
|
228
230
|
|
|
229
231
|
const header = tags.join(',')
|
|
230
232
|
|
|
231
|
-
if (header.length > this._config.
|
|
233
|
+
if (header.length > this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH) {
|
|
232
234
|
log.error('Trace tags from span are too large, skipping injection.')
|
|
233
235
|
} else if (header) {
|
|
234
236
|
carrier[tagsKey] = header
|
|
@@ -387,7 +389,7 @@ class TextMapPropagator {
|
|
|
387
389
|
if (context === null) {
|
|
388
390
|
context = extractedContext
|
|
389
391
|
style = extractor
|
|
390
|
-
if (this._config.
|
|
392
|
+
if (this._config.DD_TRACE_PROPAGATION_EXTRACT_FIRST) {
|
|
391
393
|
break
|
|
392
394
|
}
|
|
393
395
|
} else {
|
|
@@ -407,10 +409,10 @@ class TextMapPropagator {
|
|
|
407
409
|
}
|
|
408
410
|
}
|
|
409
411
|
|
|
410
|
-
if (this._config.
|
|
412
|
+
if (this._config.DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT === 'ignore') {
|
|
411
413
|
context._links = []
|
|
412
414
|
} else {
|
|
413
|
-
if (this._config.
|
|
415
|
+
if (this._config.DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT === 'restart') {
|
|
414
416
|
context._links = []
|
|
415
417
|
context._links.push({
|
|
416
418
|
context,
|
|
@@ -436,7 +438,7 @@ class TextMapPropagator {
|
|
|
436
438
|
this._extractSamplingPriority(carrier, spanContext)
|
|
437
439
|
this._extractTags(carrier, spanContext)
|
|
438
440
|
|
|
439
|
-
if (this._config.
|
|
441
|
+
if (this._config.DD_TRACE_PROPAGATION_EXTRACT_FIRST) return spanContext
|
|
440
442
|
|
|
441
443
|
const tc = this._extractTraceparentContext(carrier)
|
|
442
444
|
|
|
@@ -674,11 +676,29 @@ class TextMapPropagator {
|
|
|
674
676
|
_extractBaggageItems (carrier, spanContext) {
|
|
675
677
|
removeAllBaggageItems()
|
|
676
678
|
if (!this._hasPropagationStyle('extract', 'baggage')) return
|
|
677
|
-
|
|
678
|
-
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(',')
|
|
679
684
|
const baggageTagKeys = new Set(this._config.baggageTagKeys)
|
|
680
685
|
const tagAllKeys = baggageTagKeys.has('*')
|
|
686
|
+
/** @type {Record<string, string> | undefined} */
|
|
687
|
+
let items
|
|
688
|
+
let itemCount = 0
|
|
689
|
+
let byteCount = 0
|
|
690
|
+
|
|
681
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
|
+
}
|
|
682
702
|
if (!keyValue) continue
|
|
683
703
|
|
|
684
704
|
// Per W3C baggage, list-members can contain optional properties after `;`.
|
|
@@ -691,7 +711,6 @@ class TextMapPropagator {
|
|
|
691
711
|
const eqIdx = member.indexOf('=')
|
|
692
712
|
if (eqIdx === -1) {
|
|
693
713
|
tracerMetrics.count('context_header_style.malformed', ['header_style:baggage']).inc()
|
|
694
|
-
removeAllBaggageItems()
|
|
695
714
|
return
|
|
696
715
|
}
|
|
697
716
|
|
|
@@ -700,25 +719,27 @@ class TextMapPropagator {
|
|
|
700
719
|
|
|
701
720
|
if (!baggageTokenExpr.test(key) || !value) {
|
|
702
721
|
tracerMetrics.count('context_header_style.malformed', ['header_style:baggage']).inc()
|
|
703
|
-
removeAllBaggageItems()
|
|
704
722
|
return
|
|
705
723
|
}
|
|
706
724
|
try {
|
|
707
725
|
value = decodeURIComponent(value)
|
|
708
726
|
} catch {
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
return
|
|
727
|
+
const bytes = value.replaceAll(percentByte, (_, hex) => String.fromCharCode(Number.parseInt(hex, 16)))
|
|
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) {
|
|
@@ -734,9 +755,9 @@ class TextMapPropagator {
|
|
|
734
755
|
|
|
735
756
|
const trace = spanContext._trace
|
|
736
757
|
|
|
737
|
-
if (this._config.
|
|
758
|
+
if (this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH === 0) {
|
|
738
759
|
log.debug('Trace tag propagation is disabled, skipping extraction.')
|
|
739
|
-
} else if (carrier[tagsKey].length > this._config.
|
|
760
|
+
} else if (carrier[tagsKey].length > this._config.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH) {
|
|
740
761
|
log.error('Trace tags from carrier are too large, skipping extraction.')
|
|
741
762
|
} else {
|
|
742
763
|
const pairs = carrier[tagsKey].split(',')
|