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.
Files changed (64) hide show
  1. package/index.d.ts +14 -0
  2. package/package.json +5 -5
  3. package/packages/datadog-instrumentations/src/cypress.js +5 -3
  4. package/packages/datadog-instrumentations/src/http/client.js +20 -3
  5. package/packages/datadog-instrumentations/src/jest.js +62 -32
  6. package/packages/datadog-instrumentations/src/mocha/common.js +4 -1
  7. package/packages/datadog-instrumentations/src/mocha/main.js +25 -4
  8. package/packages/datadog-instrumentations/src/mocha/worker.js +5 -2
  9. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +11 -6
  10. package/packages/datadog-plugin-bullmq/src/consumer.js +2 -2
  11. package/packages/datadog-plugin-bullmq/src/producer.js +14 -20
  12. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +17 -0
  13. package/packages/datadog-plugin-cypress/src/plugin.js +5 -14
  14. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -9
  15. package/packages/datadog-plugin-kafkajs/src/producer.js +2 -8
  16. package/packages/dd-trace/src/appsec/reporter.js +4 -1
  17. package/packages/dd-trace/src/ci-visibility/lage.js +2 -1
  18. package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -33
  19. package/packages/dd-trace/src/config/config-types.d.ts +0 -2
  20. package/packages/dd-trace/src/config/index.js +1 -55
  21. package/packages/dd-trace/src/datastreams/checkpointer.js +4 -10
  22. package/packages/dd-trace/src/datastreams/encoding.js +39 -28
  23. package/packages/dd-trace/src/datastreams/pathway.js +29 -26
  24. package/packages/dd-trace/src/datastreams/processor.js +17 -15
  25. package/packages/dd-trace/src/datastreams/size.js +6 -2
  26. package/packages/dd-trace/src/debugger/config.js +5 -2
  27. package/packages/dd-trace/src/debugger/devtools_client/index.js +2 -5
  28. package/packages/dd-trace/src/debugger/devtools_client/send.js +2 -1
  29. package/packages/dd-trace/src/dogstatsd.js +10 -7
  30. package/packages/dd-trace/src/encode/0.4.js +2 -2
  31. package/packages/dd-trace/src/encode/0.5.js +2 -2
  32. package/packages/dd-trace/src/encode/agentless-json.js +2 -2
  33. package/packages/dd-trace/src/encode/tags-processors.js +2 -27
  34. package/packages/dd-trace/src/exporters/common/request.js +22 -11
  35. package/packages/dd-trace/src/exporters/common/retry.js +104 -0
  36. package/packages/dd-trace/src/git_metadata.js +66 -0
  37. package/packages/dd-trace/src/git_metadata_tagger.js +13 -5
  38. package/packages/dd-trace/src/id.js +15 -26
  39. package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
  40. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +27 -16
  41. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +3 -0
  42. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +30 -13
  43. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +20 -50
  44. package/packages/dd-trace/src/llmobs/sdk.js +5 -1
  45. package/packages/dd-trace/src/llmobs/span_processor.js +28 -2
  46. package/packages/dd-trace/src/llmobs/tagger.js +42 -0
  47. package/packages/dd-trace/src/llmobs/telemetry.js +29 -0
  48. package/packages/dd-trace/src/llmobs/util.js +80 -5
  49. package/packages/dd-trace/src/opentelemetry/active-span-proxy.js +42 -0
  50. package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +106 -0
  51. package/packages/dd-trace/src/opentelemetry/context_manager.js +11 -2
  52. package/packages/dd-trace/src/opentelemetry/span-helpers.js +188 -50
  53. package/packages/dd-trace/src/opentelemetry/span.js +42 -80
  54. package/packages/dd-trace/src/opentracing/propagation/text_map.js +65 -27
  55. package/packages/dd-trace/src/opentracing/propagation/tracestate.js +58 -22
  56. package/packages/dd-trace/src/opentracing/span.js +56 -48
  57. package/packages/dd-trace/src/opentracing/span_context.js +1 -0
  58. package/packages/dd-trace/src/priority_sampler.js +6 -4
  59. package/packages/dd-trace/src/profiling/config.js +5 -4
  60. package/packages/dd-trace/src/remote_config/index.js +5 -3
  61. package/packages/dd-trace/src/span_format.js +52 -5
  62. package/packages/dd-trace/src/span_processor.js +0 -4
  63. package/packages/dd-trace/src/spanleak.js +0 -1
  64. 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
- if (store && trace.getSpanContext(store) === ddContext._otelSpanContext) {
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.setSpanContext(baseContext, ddContext._otelSpanContext)
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
- * traceId: string,
14
- * spanId: string,
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
- * }} OtelSpanContextLike
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
- * addEvent: (name: string, attributes: Record<string, unknown>, timeInput?: unknown) => unknown
29
- * }} EventTarget
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
- * Normalize any Datadog/OTel span-context shape to a `DatadogSpanContext`.
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
- const bridgeCtx = /** @type {OtelBridgeSpanContextLike} */ (context)
42
- if (bridgeCtx._ddContext) return bridgeCtx._ddContext
111
+ if (context._ddContext) return context._ddContext
43
112
 
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
- }
113
+ if (isDatadogSpanContext(context)) return context
48
114
 
49
- const otelCtx = /** @type {OtelSpanContextLike} */ (context)
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 otelCtx.traceFlags === 'number') {
54
- sampling = { priority: otelCtx.traceFlags & 1 }
118
+ if (typeof context.traceFlags === 'number') {
119
+ sampling = { priority: context.traceFlags & 1 }
55
120
  }
56
121
 
57
122
  let tracestate
58
- if (otelCtx.traceState?.serialize) {
59
- tracestate = TraceState.fromString(otelCtx.traceState.serialize())
123
+ if (context.traceState?.serialize) {
124
+ tracestate = TraceState.fromString(context.traceState.serialize())
60
125
  }
61
126
 
62
127
  return new DatadogSpanContext({
63
- traceId: id(otelCtx.traceId, 16),
64
- spanId: id(otelCtx.spanId, 16),
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 ('http.response.status_code' in attributes) {
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 linkObj = link && typeof link === 'object' && 'context' in link
108
- ? /** @type {OtelLink} */ (link)
109
- : { context: /** @type {LinkContextLike} */ (link), attributes: attrs ?? {} }
180
+ const { context, attributes } = isOtelLink(link)
181
+ ? link
182
+ : { context: link, attributes: attrs ?? {} }
110
183
 
111
- const ddSpanContext = normalizeLinkContext(linkObj.context)
184
+ const ddSpanContext = normalizeLinkContext(context)
112
185
  if (!ddSpanContext) return
113
186
 
114
- ddSpan.addLink({ context: ddSpanContext, attributes: linkObj.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 {unknown} [timeInput]
122
- * @returns {void}
224
+ * @param {TimeInput} [timeInput]
123
225
  */
124
- function recordException (ddSpan, eventTarget, exception, timeInput) {
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
- eventTarget.addEvent(exception.name ?? 'Error', attributes, timeInput)
242
+ ddSpan.addEvent(exception.name ?? 'Error', attributes, timeInputToMilliseconds(timeInput))
140
243
  }
141
244
 
142
245
  /**
143
- * First-call-wins; no-op on ended spans. Only `code === 2` emits Datadog error tags.
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 {{ ended: boolean, _hasStatus: boolean }} bridgeSpan
251
+ * @param {number} currentCode 0 = UNSET, 1 = OK, 2 = ERROR.
147
252
  * @param {{ code?: number, message?: string }} [status]
148
- * @returns {void}
253
+ * @returns {number} The new status code to track on the caller.
149
254
  */
150
- function setStatus (ddSpan, bridgeSpan, { code, message } = {}) {
151
- if (bridgeSpan.ended || bridgeSpan._hasStatus || !code) return
255
+ function applyOtelStatus (ddSpan, currentCode, status) {
256
+ if (!isWritable(ddSpan)) return currentCode
152
257
 
153
- bridgeSpan._hasStatus = true
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
- setStatus,
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
- class Span {
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
- this._ddSpan = new DatadogSpan(_tracer, _tracer._processor, _tracer._prioritySampler, {
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
- setAttribute (key, value) {
204
- setOtelAttribute(this._ddSpan, key, value)
205
- return this
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
- setStatus (status) {
238
- setStatus(this._ddSpan, this, status)
239
- return this
240
- }
241
-
222
+ /**
223
+ * @param {string} name
224
+ */
242
225
  updateName (name) {
243
- if (!this.ended) {
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