dd-trace 5.100.0 → 5.101.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 +14 -0
- package/package.json +5 -5
- package/packages/datadog-instrumentations/src/cypress.js +5 -3
- package/packages/datadog-instrumentations/src/http/client.js +20 -3
- package/packages/datadog-instrumentations/src/jest.js +62 -32
- package/packages/datadog-instrumentations/src/mocha/common.js +4 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +25 -4
- package/packages/datadog-instrumentations/src/mocha/worker.js +5 -2
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +11 -6
- package/packages/datadog-plugin-bullmq/src/consumer.js +2 -2
- package/packages/datadog-plugin-bullmq/src/producer.js +14 -20
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +17 -0
- package/packages/datadog-plugin-cypress/src/plugin.js +5 -14
- package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -9
- package/packages/datadog-plugin-kafkajs/src/producer.js +2 -8
- package/packages/dd-trace/src/appsec/reporter.js +4 -1
- package/packages/dd-trace/src/ci-visibility/lage.js +2 -1
- package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -33
- package/packages/dd-trace/src/config/config-types.d.ts +0 -2
- package/packages/dd-trace/src/config/index.js +1 -55
- package/packages/dd-trace/src/datastreams/checkpointer.js +4 -10
- package/packages/dd-trace/src/datastreams/encoding.js +39 -28
- package/packages/dd-trace/src/datastreams/pathway.js +29 -26
- package/packages/dd-trace/src/datastreams/processor.js +17 -15
- package/packages/dd-trace/src/datastreams/size.js +6 -2
- package/packages/dd-trace/src/debugger/config.js +5 -2
- package/packages/dd-trace/src/debugger/devtools_client/index.js +2 -5
- package/packages/dd-trace/src/debugger/devtools_client/send.js +2 -1
- package/packages/dd-trace/src/dogstatsd.js +10 -7
- package/packages/dd-trace/src/encode/0.4.js +2 -2
- package/packages/dd-trace/src/encode/0.5.js +2 -2
- package/packages/dd-trace/src/encode/agentless-json.js +2 -2
- package/packages/dd-trace/src/encode/tags-processors.js +2 -27
- package/packages/dd-trace/src/exporters/common/request.js +22 -11
- package/packages/dd-trace/src/exporters/common/retry.js +104 -0
- package/packages/dd-trace/src/git_metadata.js +66 -0
- package/packages/dd-trace/src/git_metadata_tagger.js +13 -5
- package/packages/dd-trace/src/id.js +15 -26
- package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +27 -16
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +3 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +30 -13
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +20 -50
- package/packages/dd-trace/src/llmobs/sdk.js +5 -1
- package/packages/dd-trace/src/llmobs/span_processor.js +28 -2
- package/packages/dd-trace/src/llmobs/tagger.js +42 -0
- package/packages/dd-trace/src/llmobs/telemetry.js +29 -0
- package/packages/dd-trace/src/llmobs/util.js +80 -5
- package/packages/dd-trace/src/opentelemetry/active-span-proxy.js +42 -0
- package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +106 -0
- package/packages/dd-trace/src/opentelemetry/context_manager.js +11 -2
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +188 -50
- package/packages/dd-trace/src/opentelemetry/span.js +42 -80
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +65 -27
- package/packages/dd-trace/src/opentracing/propagation/tracestate.js +58 -22
- package/packages/dd-trace/src/opentracing/span.js +56 -48
- package/packages/dd-trace/src/opentracing/span_context.js +1 -0
- package/packages/dd-trace/src/priority_sampler.js +6 -4
- package/packages/dd-trace/src/profiling/config.js +5 -4
- package/packages/dd-trace/src/remote_config/index.js +5 -3
- package/packages/dd-trace/src/span_format.js +52 -5
- package/packages/dd-trace/src/span_processor.js +0 -4
- package/packages/dd-trace/src/spanleak.js +0 -1
- package/packages/dd-trace/src/util.js +17 -0
|
@@ -4,6 +4,7 @@ const { trace, ROOT_CONTEXT, propagation } = require('@opentelemetry/api')
|
|
|
4
4
|
const { storage } = require('../../../datadog-core')
|
|
5
5
|
const { getAllBaggageItems, setAllBaggageItems, removeAllBaggageItems } = require('../baggage')
|
|
6
6
|
|
|
7
|
+
const ActiveSpanProxy = require('./active-span-proxy')
|
|
7
8
|
const SpanContext = require('./span_context')
|
|
8
9
|
|
|
9
10
|
class ContextManager {
|
|
@@ -48,11 +49,19 @@ class ContextManager {
|
|
|
48
49
|
ddContext._otelSpanContext = new SpanContext(ddContext)
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
// Cache the active-span proxy next to the bridge span context. This lets
|
|
53
|
+
// `trace.getActiveSpan()` forward attribute/status/link/exception writes
|
|
54
|
+
// onto the active Datadog span rather than returning a NonRecordingSpan
|
|
55
|
+
// whose mutation methods are silent no-ops.
|
|
56
|
+
if (!ddContext._otelActiveSpan) {
|
|
57
|
+
ddContext._otelActiveSpan = new ActiveSpanProxy(activeSpan, ddContext._otelSpanContext)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (store && trace.getSpan(store) === ddContext._otelActiveSpan) {
|
|
52
61
|
return otelBaggages ? propagation.setBaggage(store, otelBaggages) : store
|
|
53
62
|
}
|
|
54
63
|
|
|
55
|
-
const wrappedContext = trace.
|
|
64
|
+
const wrappedContext = trace.setSpan(baseContext, ddContext._otelActiveSpan)
|
|
56
65
|
return otelBaggages ? propagation.setBaggage(wrappedContext, otelBaggages) : wrappedContext
|
|
57
66
|
}
|
|
58
67
|
|
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { performance } = require('node:perf_hooks')
|
|
4
|
+
|
|
5
|
+
const { timeInputToHrTime } = require('../../../../vendor/dist/@opentelemetry/core')
|
|
6
|
+
|
|
3
7
|
const { ERROR_MESSAGE, ERROR_STACK, ERROR_TYPE, IGNORE_OTEL_ERROR } = require('../constants')
|
|
4
8
|
const DatadogSpanContext = require('../opentracing/span_context')
|
|
5
9
|
const TraceState = require('../opentracing/propagation/tracestate')
|
|
6
10
|
|
|
7
11
|
const id = require('../id')
|
|
8
12
|
|
|
13
|
+
const { timeOrigin } = performance
|
|
14
|
+
|
|
9
15
|
/**
|
|
10
|
-
* @typedef {{ toTraceId: (get128?: boolean) => string, toSpanId: (get128?: boolean) => string }} DatadogContextLike
|
|
11
|
-
* @typedef {{ _ddContext: import('../opentracing/span_context') }} OtelBridgeSpanContextLike
|
|
12
16
|
* @typedef {{
|
|
13
|
-
*
|
|
14
|
-
*
|
|
17
|
+
* _ddContext?: import('../opentracing/span_context'),
|
|
18
|
+
* toTraceId?: (get128?: boolean) => string,
|
|
19
|
+
* toSpanId?: (get128?: boolean) => string,
|
|
20
|
+
* traceId?: string,
|
|
21
|
+
* spanId?: string,
|
|
15
22
|
* traceFlags?: number,
|
|
16
23
|
* traceState?: { serialize: () => string }
|
|
17
|
-
* }}
|
|
18
|
-
* @typedef {DatadogContextLike | OtelBridgeSpanContextLike | OtelSpanContextLike} LinkContextLike
|
|
24
|
+
* }} LinkContextLike
|
|
19
25
|
* @typedef {{ context: LinkContextLike, attributes?: Record<string, unknown> }} OtelLink
|
|
20
26
|
* @typedef {{
|
|
21
27
|
* name?: string,
|
|
@@ -24,56 +30,119 @@ const id = require('../id')
|
|
|
24
30
|
* type?: string,
|
|
25
31
|
* escaped?: unknown
|
|
26
32
|
* }} ExceptionLike
|
|
27
|
-
* @typedef {
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
* @typedef {number | Date | [number, number]} TimeInput
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {import('../opentracing/span')} ddSpan
|
|
30
38
|
*/
|
|
39
|
+
function isWritable (ddSpan) {
|
|
40
|
+
return ddSpan._duration === undefined
|
|
41
|
+
}
|
|
31
42
|
|
|
32
43
|
/**
|
|
33
|
-
*
|
|
44
|
+
* @param {unknown} value
|
|
45
|
+
* @returns {value is TimeInput}
|
|
46
|
+
*/
|
|
47
|
+
function isTimeInput (value) {
|
|
48
|
+
return typeof value === 'number' ||
|
|
49
|
+
value instanceof Date ||
|
|
50
|
+
(Array.isArray(value) && value.length === 2 &&
|
|
51
|
+
typeof value[0] === 'number' && typeof value[1] === 'number')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {LinkContextLike} ctx
|
|
56
|
+
* @returns {ctx is import('../opentracing/span_context')}
|
|
57
|
+
*/
|
|
58
|
+
function isDatadogSpanContext (ctx) {
|
|
59
|
+
return typeof ctx.toTraceId === 'function' && typeof ctx.toSpanId === 'function'
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {unknown} link
|
|
64
|
+
* @returns {link is OtelLink}
|
|
65
|
+
*/
|
|
66
|
+
function isOtelLink (link) {
|
|
67
|
+
return typeof link === 'object' && link !== null && 'context' in link
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The OTel-shipped `hrTimeToMilliseconds` rounds, which drops sub-millisecond precision.
|
|
72
|
+
*
|
|
73
|
+
* @param {TimeInput} [timeInput]
|
|
74
|
+
*/
|
|
75
|
+
function timeInputToMilliseconds (timeInput) {
|
|
76
|
+
const hrTime = timeInputToHrTime(timeInput || (performance.now() + timeOrigin))
|
|
77
|
+
return hrTime[0] * 1e3 + hrTime[1] / 1e6
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Resolves the OTel `addEvent(name, attributesOrStartTime, startTime)` overloads into a
|
|
82
|
+
* `{ attributes, startTime }` pair where `startTime` is always a millisecond number, so the
|
|
83
|
+
* caller can hand the OTel inputs straight to `DatadogSpan.addEvent` without `Date`/hrTime
|
|
84
|
+
* confusion.
|
|
85
|
+
*
|
|
86
|
+
* @param {Record<string, unknown> | TimeInput | undefined} attributesOrStartTime
|
|
87
|
+
* @param {TimeInput} [startTime]
|
|
88
|
+
*/
|
|
89
|
+
function normalizeOtelEvent (attributesOrStartTime, startTime) {
|
|
90
|
+
let attributes
|
|
91
|
+
if (attributesOrStartTime) {
|
|
92
|
+
if (isTimeInput(attributesOrStartTime)) {
|
|
93
|
+
startTime = attributesOrStartTime
|
|
94
|
+
} else if (typeof attributesOrStartTime === 'object') {
|
|
95
|
+
attributes = attributesOrStartTime
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { attributes, startTime: timeInputToMilliseconds(startTime) }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Accepts the native `DatadogSpanContext` (`toTraceId`/`toSpanId`), the bridge wrapper
|
|
103
|
+
* (`_ddContext`), or a standard OTel `SpanContext` (`traceId`/`spanId` strings); returns
|
|
104
|
+
* a `DatadogSpanContext` or `undefined` when nothing usable is present.
|
|
34
105
|
*
|
|
35
106
|
* @param {LinkContextLike | undefined | null} context
|
|
36
|
-
* @returns {import('../opentracing/span_context') | undefined}
|
|
37
107
|
*/
|
|
38
108
|
function normalizeLinkContext (context) {
|
|
39
109
|
if (!context) return
|
|
40
110
|
|
|
41
|
-
|
|
42
|
-
if (bridgeCtx._ddContext) return bridgeCtx._ddContext
|
|
111
|
+
if (context._ddContext) return context._ddContext
|
|
43
112
|
|
|
44
|
-
|
|
45
|
-
if (typeof ddCtx.toTraceId === 'function' && typeof ddCtx.toSpanId === 'function') {
|
|
46
|
-
return /** @type {import('../opentracing/span_context')} */ (/** @type {unknown} */ (context))
|
|
47
|
-
}
|
|
113
|
+
if (isDatadogSpanContext(context)) return context
|
|
48
114
|
|
|
49
|
-
|
|
50
|
-
if (typeof otelCtx.traceId !== 'string' || typeof otelCtx.spanId !== 'string') return
|
|
115
|
+
if (typeof context.traceId !== 'string' || typeof context.spanId !== 'string') return
|
|
51
116
|
|
|
52
117
|
let sampling
|
|
53
|
-
if (typeof
|
|
54
|
-
sampling = { priority:
|
|
118
|
+
if (typeof context.traceFlags === 'number') {
|
|
119
|
+
sampling = { priority: context.traceFlags & 1 }
|
|
55
120
|
}
|
|
56
121
|
|
|
57
122
|
let tracestate
|
|
58
|
-
if (
|
|
59
|
-
tracestate = TraceState.fromString(
|
|
123
|
+
if (context.traceState?.serialize) {
|
|
124
|
+
tracestate = TraceState.fromString(context.traceState.serialize())
|
|
60
125
|
}
|
|
61
126
|
|
|
62
127
|
return new DatadogSpanContext({
|
|
63
|
-
traceId: id(
|
|
64
|
-
spanId: id(
|
|
128
|
+
traceId: id(context.traceId, 16),
|
|
129
|
+
spanId: id(context.spanId, 16),
|
|
65
130
|
sampling,
|
|
66
131
|
tracestate,
|
|
67
132
|
})
|
|
68
133
|
}
|
|
69
134
|
|
|
70
135
|
/**
|
|
136
|
+
* Mirrors `http.response.status_code` onto `http.status_code` (DD's special tag used by APM
|
|
137
|
+
* trace metrics and client-side stats); both names end up on the span.
|
|
138
|
+
*
|
|
71
139
|
* @param {import('../opentracing/span')} ddSpan
|
|
72
140
|
* @param {string} key
|
|
73
141
|
* @param {unknown} value
|
|
74
|
-
* @returns {void}
|
|
75
142
|
*/
|
|
76
143
|
function setOtelAttribute (ddSpan, key, value) {
|
|
144
|
+
if (!isWritable(ddSpan)) return
|
|
145
|
+
|
|
77
146
|
if (key === 'http.response.status_code') {
|
|
78
147
|
ddSpan.setTag('http.status_code', String(value))
|
|
79
148
|
}
|
|
@@ -82,16 +151,19 @@ function setOtelAttribute (ddSpan, key, value) {
|
|
|
82
151
|
}
|
|
83
152
|
|
|
84
153
|
/**
|
|
154
|
+
* Same `http.status_code` mirror as `setOtelAttribute`; does not mutate the caller's
|
|
155
|
+
* `attributes` object.
|
|
156
|
+
*
|
|
85
157
|
* @param {import('../opentracing/span')} ddSpan
|
|
86
158
|
* @param {Record<string, unknown>} attributes
|
|
87
|
-
* @returns {void}
|
|
88
159
|
*/
|
|
89
160
|
function setOtelAttributes (ddSpan, attributes) {
|
|
90
|
-
if (
|
|
91
|
-
attributes['http.status_code'] = String(attributes['http.response.status_code'])
|
|
92
|
-
}
|
|
161
|
+
if (!isWritable(ddSpan)) return
|
|
93
162
|
|
|
94
163
|
ddSpan.addTags(attributes)
|
|
164
|
+
if ('http.response.status_code' in attributes) {
|
|
165
|
+
ddSpan.setTag('http.status_code', String(attributes['http.response.status_code']))
|
|
166
|
+
}
|
|
95
167
|
}
|
|
96
168
|
|
|
97
169
|
/**
|
|
@@ -100,28 +172,60 @@ function setOtelAttributes (ddSpan, attributes) {
|
|
|
100
172
|
* @param {import('../opentracing/span')} ddSpan
|
|
101
173
|
* @param {LinkContextLike | OtelLink} link
|
|
102
174
|
* @param {Record<string, unknown>} [attrs]
|
|
103
|
-
* @returns {void}
|
|
104
175
|
*/
|
|
105
176
|
function addOtelLink (ddSpan, link, attrs) {
|
|
177
|
+
if (!isWritable(ddSpan) || !link) return
|
|
178
|
+
|
|
106
179
|
// TODO: Drop the (context, attrs) form in v6.0.0.
|
|
107
|
-
const
|
|
108
|
-
?
|
|
109
|
-
: { context:
|
|
180
|
+
const { context, attributes } = isOtelLink(link)
|
|
181
|
+
? link
|
|
182
|
+
: { context: link, attributes: attrs ?? {} }
|
|
110
183
|
|
|
111
|
-
const ddSpanContext = normalizeLinkContext(
|
|
184
|
+
const ddSpanContext = normalizeLinkContext(context)
|
|
112
185
|
if (!ddSpanContext) return
|
|
113
186
|
|
|
114
|
-
ddSpan.addLink({ context: ddSpanContext, attributes
|
|
187
|
+
ddSpan.addLink({ context: ddSpanContext, attributes })
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Forwards the array-form `addLinks` overload; non-array inputs are silently ignored to
|
|
192
|
+
* match the OTel API's lenient handling.
|
|
193
|
+
*
|
|
194
|
+
* @param {import('../opentracing/span')} ddSpan
|
|
195
|
+
* @param {Array<LinkContextLike | OtelLink>} links
|
|
196
|
+
*/
|
|
197
|
+
function addOtelLinks (ddSpan, links) {
|
|
198
|
+
if (!isWritable(ddSpan) || !Array.isArray(links)) return
|
|
199
|
+
|
|
200
|
+
for (const link of links) {
|
|
201
|
+
addOtelLink(ddSpan, link)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Owns the OTel `addEvent(name, attributesOrStartTime, startTime)` overload normalization so
|
|
207
|
+
* the bridge classes can delegate without touching `Date`/hrTime conversion.
|
|
208
|
+
*
|
|
209
|
+
* @param {import('../opentracing/span')} ddSpan
|
|
210
|
+
* @param {string} name
|
|
211
|
+
* @param {Record<string, unknown> | TimeInput | undefined} [attributesOrStartTime]
|
|
212
|
+
* @param {TimeInput} [startTime]
|
|
213
|
+
*/
|
|
214
|
+
function addOtelEvent (ddSpan, name, attributesOrStartTime, startTime) {
|
|
215
|
+
if (!isWritable(ddSpan)) return
|
|
216
|
+
|
|
217
|
+
const event = normalizeOtelEvent(attributesOrStartTime, startTime)
|
|
218
|
+
ddSpan.addEvent(name, event.attributes, event.startTime)
|
|
115
219
|
}
|
|
116
220
|
|
|
117
221
|
/**
|
|
118
222
|
* @param {import('../opentracing/span')} ddSpan
|
|
119
|
-
* @param {EventTarget} eventTarget
|
|
120
223
|
* @param {ExceptionLike} exception
|
|
121
|
-
* @param {
|
|
122
|
-
* @returns {void}
|
|
224
|
+
* @param {TimeInput} [timeInput]
|
|
123
225
|
*/
|
|
124
|
-
function recordException (ddSpan,
|
|
226
|
+
function recordException (ddSpan, exception, timeInput) {
|
|
227
|
+
if (!isWritable(ddSpan)) return
|
|
228
|
+
|
|
125
229
|
ddSpan.addTags({
|
|
126
230
|
[ERROR_TYPE]: exception.name,
|
|
127
231
|
[ERROR_MESSAGE]: exception.message,
|
|
@@ -129,42 +233,76 @@ function recordException (ddSpan, eventTarget, exception, timeInput) {
|
|
|
129
233
|
[IGNORE_OTEL_ERROR]: ddSpan.context()._tags[IGNORE_OTEL_ERROR] ?? true,
|
|
130
234
|
})
|
|
131
235
|
|
|
132
|
-
/** @type {Record<string, unknown>} */
|
|
133
236
|
const attributes = {}
|
|
134
237
|
if (exception.message) attributes['exception.message'] = exception.message
|
|
135
238
|
if (exception.type) attributes['exception.type'] = exception.type
|
|
136
239
|
if (exception.escaped) attributes['exception.escaped'] = exception.escaped
|
|
137
240
|
if (exception.stack) attributes['exception.stacktrace'] = exception.stack
|
|
138
241
|
|
|
139
|
-
|
|
242
|
+
ddSpan.addEvent(exception.name ?? 'Error', attributes, timeInputToMilliseconds(timeInput))
|
|
140
243
|
}
|
|
141
244
|
|
|
142
245
|
/**
|
|
143
|
-
*
|
|
246
|
+
* Applies OTel `setStatus({ code, message })` per spec: UNSET / missing is a no-op, OK is
|
|
247
|
+
* final, ERROR is replaceable. Only ERROR writes tags; the returned code is the one the
|
|
248
|
+
* caller must store for the next call.
|
|
144
249
|
*
|
|
145
250
|
* @param {import('../opentracing/span')} ddSpan
|
|
146
|
-
* @param {
|
|
251
|
+
* @param {number} currentCode 0 = UNSET, 1 = OK, 2 = ERROR.
|
|
147
252
|
* @param {{ code?: number, message?: string }} [status]
|
|
148
|
-
* @returns {
|
|
253
|
+
* @returns {number} The new status code to track on the caller.
|
|
149
254
|
*/
|
|
150
|
-
function
|
|
151
|
-
if (
|
|
255
|
+
function applyOtelStatus (ddSpan, currentCode, status) {
|
|
256
|
+
if (!isWritable(ddSpan)) return currentCode
|
|
152
257
|
|
|
153
|
-
|
|
258
|
+
const code = status?.code
|
|
259
|
+
if (!code || currentCode === 1) return currentCode
|
|
154
260
|
|
|
155
261
|
if (code === 2) {
|
|
156
262
|
ddSpan.addTags({
|
|
157
|
-
[ERROR_MESSAGE]: message,
|
|
263
|
+
[ERROR_MESSAGE]: status.message,
|
|
158
264
|
[IGNORE_OTEL_ERROR]: false,
|
|
159
265
|
})
|
|
160
266
|
}
|
|
267
|
+
|
|
268
|
+
return code
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* OTel `updateName` for OTel-created bridge spans: writes the DD operation name, matching
|
|
273
|
+
* the OTel SDK semantic that `updateName` updates the canonical span identifier.
|
|
274
|
+
*
|
|
275
|
+
* @param {import('../opentracing/span')} ddSpan
|
|
276
|
+
* @param {string} name
|
|
277
|
+
*/
|
|
278
|
+
function setOtelOperationName (ddSpan, name) {
|
|
279
|
+
if (!isWritable(ddSpan)) return
|
|
280
|
+
|
|
281
|
+
ddSpan.setOperationName(name)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* OTel `updateName` for DD-native spans the bridge did not create: writes `resource.name`
|
|
286
|
+
* so the operation name (and the backend metric aggregation it drives) stays stable.
|
|
287
|
+
*
|
|
288
|
+
* @param {import('../opentracing/span')} ddSpan
|
|
289
|
+
* @param {string} name
|
|
290
|
+
*/
|
|
291
|
+
function setOtelResource (ddSpan, name) {
|
|
292
|
+
if (!isWritable(ddSpan)) return
|
|
293
|
+
|
|
294
|
+
ddSpan.setTag('resource.name', name)
|
|
161
295
|
}
|
|
162
296
|
|
|
163
297
|
module.exports = {
|
|
298
|
+
addOtelEvent,
|
|
164
299
|
addOtelLink,
|
|
300
|
+
addOtelLinks,
|
|
301
|
+
applyOtelStatus,
|
|
165
302
|
normalizeLinkContext,
|
|
166
303
|
recordException,
|
|
167
304
|
setOtelAttribute,
|
|
168
305
|
setOtelAttributes,
|
|
169
|
-
|
|
306
|
+
setOtelOperationName,
|
|
307
|
+
setOtelResource,
|
|
170
308
|
}
|
|
@@ -13,33 +13,9 @@ const { SERVICE_NAME, RESOURCE_NAME, SPAN_KIND } = require('../../../../ext/tags
|
|
|
13
13
|
const kinds = require('../../../../ext/kinds')
|
|
14
14
|
|
|
15
15
|
const id = require('../id')
|
|
16
|
+
const BridgeSpanBase = require('./bridge-span-base')
|
|
16
17
|
const SpanContext = require('./span_context')
|
|
17
|
-
const {
|
|
18
|
-
addOtelLink,
|
|
19
|
-
recordException,
|
|
20
|
-
setOtelAttribute,
|
|
21
|
-
setOtelAttributes,
|
|
22
|
-
setStatus,
|
|
23
|
-
} = require('./span-helpers')
|
|
24
|
-
|
|
25
|
-
// The one built into OTel rounds so we lose sub-millisecond precision.
|
|
26
|
-
function hrTimeToMilliseconds (time) {
|
|
27
|
-
return time[0] * 1e3 + time[1] / 1e6
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function isTimeInput (startTime) {
|
|
31
|
-
if (typeof startTime === 'number') {
|
|
32
|
-
return true
|
|
33
|
-
}
|
|
34
|
-
if (startTime instanceof Date) {
|
|
35
|
-
return true
|
|
36
|
-
}
|
|
37
|
-
if (Array.isArray(startTime) && startTime.length === 2 &&
|
|
38
|
-
typeof startTime[0] === 'number' && typeof startTime[1] === 'number') {
|
|
39
|
-
return true
|
|
40
|
-
}
|
|
41
|
-
return false
|
|
42
|
-
}
|
|
18
|
+
const { setOtelOperationName } = require('./span-helpers')
|
|
43
19
|
|
|
44
20
|
const spanKindNames = {
|
|
45
21
|
[api.SpanKind.INTERNAL]: kinds.INTERNAL,
|
|
@@ -49,6 +25,15 @@ const spanKindNames = {
|
|
|
49
25
|
[api.SpanKind.CONSUMER]: kinds.CONSUMER,
|
|
50
26
|
}
|
|
51
27
|
|
|
28
|
+
/**
|
|
29
|
+
* The OTel-shipped `hrTimeToMilliseconds` rounds, dropping sub-millisecond precision we want.
|
|
30
|
+
*
|
|
31
|
+
* @param {[number, number]} hrTime
|
|
32
|
+
*/
|
|
33
|
+
function hrTimeToMilliseconds (hrTime) {
|
|
34
|
+
return hrTime[0] * 1e3 + hrTime[1] / 1e6
|
|
35
|
+
}
|
|
36
|
+
|
|
52
37
|
/**
|
|
53
38
|
* Several of these attributes are not yet supported by the Node.js OTel API.
|
|
54
39
|
* We check for old equivalents where we can, but not all had equivalents.
|
|
@@ -128,7 +113,21 @@ function spanNameMapper (spanName, kind, attributes) {
|
|
|
128
113
|
return spanKindNames[kind]
|
|
129
114
|
}
|
|
130
115
|
|
|
131
|
-
|
|
116
|
+
/**
|
|
117
|
+
* OTel-bridge span backed by a `DatadogSpan`. `Tracer` constructs these on the OTel API
|
|
118
|
+
* surface; the underlying DD span carries the lifecycle.
|
|
119
|
+
*/
|
|
120
|
+
class Span extends BridgeSpanBase {
|
|
121
|
+
/**
|
|
122
|
+
* @param {import('./tracer')} parentTracer
|
|
123
|
+
* @param {import('@opentelemetry/api').Context} context
|
|
124
|
+
* @param {string | undefined} spanName
|
|
125
|
+
* @param {import('./span_context')} spanContext
|
|
126
|
+
* @param {import('@opentelemetry/api').SpanKind} kind
|
|
127
|
+
* @param {Array<import('@opentelemetry/api').Link>} [links]
|
|
128
|
+
* @param {import('@opentelemetry/api').TimeInput} [timeInput]
|
|
129
|
+
* @param {import('@opentelemetry/api').Attributes} [attributes]
|
|
130
|
+
*/
|
|
132
131
|
constructor (
|
|
133
132
|
parentTracer,
|
|
134
133
|
context,
|
|
@@ -144,7 +143,7 @@ class Span {
|
|
|
144
143
|
const hrStartTime = timeInputToHrTime(timeInput || (performance.now() + timeOrigin))
|
|
145
144
|
const startTime = hrTimeToMilliseconds(hrStartTime)
|
|
146
145
|
|
|
147
|
-
|
|
146
|
+
const ddSpan = new DatadogSpan(_tracer, _tracer._processor, _tracer._prioritySampler, {
|
|
148
147
|
operationName: spanNameMapper(spanName, kind, attributes),
|
|
149
148
|
context: spanContext._ddContext,
|
|
150
149
|
startTime,
|
|
@@ -158,6 +157,8 @@ class Span {
|
|
|
158
157
|
links,
|
|
159
158
|
}, _tracer._debug)
|
|
160
159
|
|
|
160
|
+
super(ddSpan)
|
|
161
|
+
|
|
161
162
|
if (attributes) {
|
|
162
163
|
this.setAttributes(attributes)
|
|
163
164
|
}
|
|
@@ -165,8 +166,6 @@ class Span {
|
|
|
165
166
|
this._parentTracer = parentTracer
|
|
166
167
|
this._context = context
|
|
167
168
|
|
|
168
|
-
this._hasStatus = false
|
|
169
|
-
|
|
170
169
|
// NOTE: Need to grab the value before setting it on the span because the
|
|
171
170
|
// math for computing opentracing timestamps is apparently lossy...
|
|
172
171
|
this.startTime = hrStartTime
|
|
@@ -200,27 +199,13 @@ class Span {
|
|
|
200
199
|
return new SpanContext(this._ddSpan.context())
|
|
201
200
|
}
|
|
202
201
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
setAttributes (attributes) {
|
|
209
|
-
setOtelAttributes(this._ddSpan, attributes)
|
|
210
|
-
return this
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
addLink (link, attrs) {
|
|
214
|
-
addOtelLink(this._ddSpan, link, attrs)
|
|
215
|
-
return this
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
addLinks (links) {
|
|
219
|
-
for (const link of links) this.addLink(link)
|
|
220
|
-
return this
|
|
221
|
-
}
|
|
222
|
-
|
|
202
|
+
/**
|
|
203
|
+
* @param {string} ptrKind
|
|
204
|
+
* @param {string} ptrDir
|
|
205
|
+
* @param {string} ptrHash
|
|
206
|
+
*/
|
|
223
207
|
addSpanPointer (ptrKind, ptrDir, ptrHash) {
|
|
208
|
+
if (this.ended) return this
|
|
224
209
|
const zeroContext = new SpanContext({
|
|
225
210
|
traceId: id('0'),
|
|
226
211
|
spanId: id('0'),
|
|
@@ -234,18 +219,17 @@ class Span {
|
|
|
234
219
|
return this.addLink(zeroContext, attributes)
|
|
235
220
|
}
|
|
236
221
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
241
|
-
|
|
222
|
+
/**
|
|
223
|
+
* @param {string} name
|
|
224
|
+
*/
|
|
242
225
|
updateName (name) {
|
|
243
|
-
|
|
244
|
-
this._ddSpan.setOperationName(name)
|
|
245
|
-
}
|
|
226
|
+
setOtelOperationName(this._ddSpan, name)
|
|
246
227
|
return this
|
|
247
228
|
}
|
|
248
229
|
|
|
230
|
+
/**
|
|
231
|
+
* @param {import('@opentelemetry/api').TimeInput} [timeInput]
|
|
232
|
+
*/
|
|
249
233
|
end (timeInput) {
|
|
250
234
|
if (this.ended) {
|
|
251
235
|
api.diag.error('You can only call end() on a span once.')
|
|
@@ -259,31 +243,9 @@ class Span {
|
|
|
259
243
|
this._spanProcessor.onEnd(this)
|
|
260
244
|
}
|
|
261
245
|
|
|
262
|
-
isRecording () {
|
|
263
|
-
return this.ended === false
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
addEvent (name, attributesOrStartTime, startTime) {
|
|
267
|
-
startTime = attributesOrStartTime && isTimeInput(attributesOrStartTime) ? attributesOrStartTime : startTime
|
|
268
|
-
const hrStartTime = timeInputToHrTime(startTime || (performance.now() + timeOrigin))
|
|
269
|
-
startTime = hrTimeToMilliseconds(hrStartTime)
|
|
270
|
-
|
|
271
|
-
this._ddSpan.addEvent(name, attributesOrStartTime, startTime)
|
|
272
|
-
return this
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
recordException (exception, timeInput) {
|
|
276
|
-
// Route through `this.addEvent` so its time-input conversion applies.
|
|
277
|
-
recordException(this._ddSpan, this, exception, timeInput)
|
|
278
|
-
}
|
|
279
|
-
|
|
280
246
|
get duration () {
|
|
281
247
|
return this._ddSpan._duration
|
|
282
248
|
}
|
|
283
|
-
|
|
284
|
-
get ended () {
|
|
285
|
-
return this.duration !== undefined
|
|
286
|
-
}
|
|
287
249
|
}
|
|
288
250
|
|
|
289
251
|
module.exports = Span
|