dd-trace 5.104.0 → 5.105.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 (151) hide show
  1. package/LICENSE-3rdparty.csv +90 -102
  2. package/index.d.ts +82 -3
  3. package/package.json +15 -15
  4. package/packages/datadog-core/src/storage.js +1 -1
  5. package/packages/datadog-instrumentations/src/aerospike.js +1 -1
  6. package/packages/datadog-instrumentations/src/ai.js +8 -7
  7. package/packages/datadog-instrumentations/src/aws-sdk.js +13 -0
  8. package/packages/datadog-instrumentations/src/azure-cosmos.js +7 -0
  9. package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
  10. package/packages/datadog-instrumentations/src/cucumber.js +78 -5
  11. package/packages/datadog-instrumentations/src/dns.js +54 -18
  12. package/packages/datadog-instrumentations/src/fastify.js +142 -82
  13. package/packages/datadog-instrumentations/src/graphql.js +188 -62
  14. package/packages/datadog-instrumentations/src/helpers/ai-messages.js +322 -14
  15. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  16. package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -1
  17. package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +269 -0
  18. package/packages/datadog-instrumentations/src/helpers/promise-instrumentor.js +42 -0
  19. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  20. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -3
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-cosmos.js +50 -0
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +4 -2
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +85 -0
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +37 -236
  26. package/packages/datadog-instrumentations/src/hono.js +54 -3
  27. package/packages/datadog-instrumentations/src/http/server.js +9 -4
  28. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +163 -0
  29. package/packages/datadog-instrumentations/src/jest.js +360 -150
  30. package/packages/datadog-instrumentations/src/kafkajs.js +120 -16
  31. package/packages/datadog-instrumentations/src/mocha/main.js +128 -17
  32. package/packages/datadog-instrumentations/src/nats.js +182 -0
  33. package/packages/datadog-instrumentations/src/nyc.js +38 -1
  34. package/packages/datadog-instrumentations/src/openai.js +33 -18
  35. package/packages/datadog-instrumentations/src/oracledb.js +6 -1
  36. package/packages/datadog-instrumentations/src/pino.js +17 -5
  37. package/packages/datadog-instrumentations/src/playwright.js +515 -292
  38. package/packages/datadog-instrumentations/src/router.js +76 -32
  39. package/packages/datadog-instrumentations/src/stripe.js +1 -1
  40. package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -1
  41. package/packages/datadog-plugin-azure-cosmos/src/index.js +144 -0
  42. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -1
  43. package/packages/datadog-plugin-azure-functions/src/index.js +5 -2
  44. package/packages/datadog-plugin-azure-service-bus/src/producer.js +1 -1
  45. package/packages/datadog-plugin-bunyan/src/index.js +28 -0
  46. package/packages/datadog-plugin-cucumber/src/index.js +17 -3
  47. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +199 -28
  48. package/packages/datadog-plugin-cypress/src/support.js +69 -1
  49. package/packages/datadog-plugin-dns/src/lookup.js +8 -6
  50. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +1 -1
  51. package/packages/datadog-plugin-graphql/src/execute.js +2 -0
  52. package/packages/datadog-plugin-graphql/src/resolve.js +64 -67
  53. package/packages/datadog-plugin-http/src/server.js +40 -15
  54. package/packages/datadog-plugin-jest/src/index.js +11 -3
  55. package/packages/datadog-plugin-jest/src/util.js +15 -8
  56. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +1 -1
  57. package/packages/datadog-plugin-kafkajs/src/producer.js +3 -0
  58. package/packages/datadog-plugin-langgraph/src/stream.js +1 -1
  59. package/packages/datadog-plugin-mocha/src/index.js +19 -4
  60. package/packages/datadog-plugin-mongodb-core/src/index.js +281 -40
  61. package/packages/datadog-plugin-nats/src/consumer.js +43 -0
  62. package/packages/datadog-plugin-nats/src/index.js +20 -0
  63. package/packages/datadog-plugin-nats/src/producer.js +62 -0
  64. package/packages/datadog-plugin-nats/src/util.js +33 -0
  65. package/packages/datadog-plugin-next/src/index.js +5 -3
  66. package/packages/datadog-plugin-openai/src/tracing.js +15 -2
  67. package/packages/datadog-plugin-oracledb/src/index.js +13 -2
  68. package/packages/datadog-plugin-pino/src/index.js +42 -0
  69. package/packages/datadog-plugin-playwright/src/index.js +4 -4
  70. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
  71. package/packages/datadog-plugin-rhea/src/producer.js +1 -1
  72. package/packages/datadog-plugin-router/src/index.js +33 -44
  73. package/packages/datadog-plugin-selenium/src/index.js +1 -1
  74. package/packages/datadog-plugin-vitest/src/index.js +5 -13
  75. package/packages/datadog-plugin-winston/src/index.js +30 -0
  76. package/packages/datadog-shimmer/src/shimmer.js +33 -40
  77. package/packages/dd-trace/src/aiguard/index.js +1 -1
  78. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  79. package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
  80. package/packages/dd-trace/src/appsec/index.js +1 -1
  81. package/packages/dd-trace/src/appsec/reporter.js +5 -6
  82. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  83. package/packages/dd-trace/src/appsec/sdk/utils.js +1 -1
  84. package/packages/dd-trace/src/appsec/user_tracking.js +5 -4
  85. package/packages/dd-trace/src/baggage.js +7 -1
  86. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +0 -1
  87. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +25 -13
  88. package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +70 -6
  89. package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -2
  90. package/packages/dd-trace/src/config/supported-configurations.json +27 -8
  91. package/packages/dd-trace/src/datastreams/writer.js +2 -4
  92. package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -8
  93. package/packages/dd-trace/src/encode/0.4.js +124 -108
  94. package/packages/dd-trace/src/encode/0.5.js +114 -26
  95. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +31 -23
  96. package/packages/dd-trace/src/encode/agentless-json.js +4 -2
  97. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -13
  98. package/packages/dd-trace/src/encode/span-stats.js +16 -16
  99. package/packages/dd-trace/src/encode/tags-processors.js +16 -0
  100. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
  101. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
  102. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
  103. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +9 -7
  104. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  105. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +1 -1
  106. package/packages/dd-trace/src/llmobs/sdk.js +0 -16
  107. package/packages/dd-trace/src/llmobs/span_processor.js +3 -3
  108. package/packages/dd-trace/src/llmobs/tagger.js +9 -1
  109. package/packages/dd-trace/src/llmobs/telemetry.js +1 -1
  110. package/packages/dd-trace/src/llmobs/util.js +66 -3
  111. package/packages/dd-trace/src/log/index.js +1 -1
  112. package/packages/dd-trace/src/msgpack/chunk.js +394 -10
  113. package/packages/dd-trace/src/msgpack/index.js +96 -2
  114. package/packages/dd-trace/src/openfeature/encoding.js +70 -0
  115. package/packages/dd-trace/src/openfeature/flagging_provider.js +20 -0
  116. package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +143 -0
  117. package/packages/dd-trace/src/openfeature/span-enrichment.js +149 -0
  118. package/packages/dd-trace/src/opentelemetry/span-helpers.js +4 -3
  119. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  120. package/packages/dd-trace/src/opentracing/propagation/log.js +18 -7
  121. package/packages/dd-trace/src/opentracing/propagation/text_map.js +62 -67
  122. package/packages/dd-trace/src/opentracing/span.js +59 -19
  123. package/packages/dd-trace/src/opentracing/span_context.js +49 -0
  124. package/packages/dd-trace/src/plugins/ci_plugin.js +20 -20
  125. package/packages/dd-trace/src/plugins/database.js +7 -6
  126. package/packages/dd-trace/src/plugins/index.js +4 -0
  127. package/packages/dd-trace/src/plugins/log_injection.js +56 -0
  128. package/packages/dd-trace/src/plugins/log_plugin.js +3 -48
  129. package/packages/dd-trace/src/plugins/outbound.js +1 -1
  130. package/packages/dd-trace/src/plugins/plugin.js +15 -17
  131. package/packages/dd-trace/src/plugins/tracing.js +43 -5
  132. package/packages/dd-trace/src/plugins/util/test.js +236 -13
  133. package/packages/dd-trace/src/plugins/util/web.js +79 -65
  134. package/packages/dd-trace/src/priority_sampler.js +2 -2
  135. package/packages/dd-trace/src/profiling/profiler.js +2 -2
  136. package/packages/dd-trace/src/profiling/profilers/wall.js +10 -4
  137. package/packages/dd-trace/src/sampling_rule.js +7 -7
  138. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +10 -0
  139. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
  140. package/packages/dd-trace/src/service-naming/source-resolver.js +46 -0
  141. package/packages/dd-trace/src/span_format.js +190 -58
  142. package/packages/dd-trace/src/spanleak.js +1 -1
  143. package/packages/dd-trace/src/standalone/index.js +3 -3
  144. package/packages/dd-trace/src/tagger.js +0 -2
  145. package/vendor/dist/@apm-js-collab/code-transformer/index.js +70 -39
  146. package/vendor/dist/@datadog/sketches-js/LICENSE +10 -36
  147. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  148. package/vendor/dist/protobufjs/index.js +1 -1
  149. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  150. package/packages/dd-trace/src/msgpack/encoder.js +0 -308
  151. package/packages/dd-trace/src/plugins/structured_log_plugin.js +0 -9
@@ -10,7 +10,10 @@ const tagger = require('../tagger')
10
10
  const runtimeMetrics = require('../runtime_metrics')
11
11
  const log = require('../log')
12
12
  const { storage } = require('../../../datadog-core')
13
+ const { resolveServiceSource } = require('../service-naming/source-resolver')
13
14
  const telemetryMetrics = require('../telemetry/metrics')
15
+ const { MANUAL_DROP, MANUAL_KEEP, SAMPLING_PRIORITY } = require('../../../../ext/tags')
16
+ const { DD_MAJOR } = require('../../../../version')
14
17
  const SpanContext = require('./span_context')
15
18
 
16
19
  const dateNow = Date.now
@@ -103,7 +106,7 @@ class DatadogSpan {
103
106
 
104
107
  this._spanContext = this._createContext(parent, fields)
105
108
  this._spanContext._name = operationName
106
- this._spanContext._tags = tags
109
+ Object.assign(this._spanContext.getTags(), tags)
107
110
  this._spanContext._hostname = hostname
108
111
 
109
112
  this._spanContext._trace.started.push(this)
@@ -145,7 +148,7 @@ class DatadogSpan {
145
148
 
146
149
  toString () {
147
150
  const spanContext = this.context()
148
- const resourceName = spanContext._tags['resource.name'] || ''
151
+ const resourceName = spanContext.getTag('resource.name') || ''
149
152
  const resource = resourceName.length > 100
150
153
  ? `${resourceName.slice(0, 97)}...`
151
154
  : resourceName
@@ -153,7 +156,7 @@ class DatadogSpan {
153
156
  traceId: spanContext._traceId,
154
157
  spanId: spanContext._spanId,
155
158
  parentId: spanContext._parentId,
156
- service: spanContext._tags['service.name'],
159
+ service: spanContext.getTag('service.name'),
157
160
  name: spanContext._name,
158
161
  resource,
159
162
  })
@@ -199,12 +202,52 @@ class DatadogSpan {
199
202
  }
200
203
 
201
204
  setTag (key, value) {
202
- this._addTags({ [key]: value })
205
+ this._spanContext.setTag(key, value)
206
+
207
+ if (isSamplingPriorityTag(key) && this._spanContext._sampling.priority === undefined) {
208
+ this._prioritySampler.sample(this, false)
209
+ }
210
+
211
+ if (tagsUpdateCh.hasSubscribers) {
212
+ tagsUpdateCh.publish(this)
213
+ }
214
+
203
215
  return this
204
216
  }
205
217
 
206
218
  addTags (keyValueMap) {
207
- this._addTags(keyValueMap)
219
+ // v6 hot path: `Object.assign` straight onto the live tag map. The
220
+ // string and array shapes never appeared in the public TypeScript
221
+ // surface, and no internal v6 caller passes one (see MIGRATING.md).
222
+ // v5 still accepts both via `tagger.add` for `config.tags` /
223
+ // `options.tags` callers that pass `'key:val,key:val'` strings.
224
+ const tags = this._spanContext.getTags()
225
+ let mayChangeSamplingPriority
226
+
227
+ if (keyValueMap !== null && typeof keyValueMap === 'object' && !Array.isArray(keyValueMap)) {
228
+ Object.assign(tags, keyValueMap)
229
+ mayChangeSamplingPriority =
230
+ MANUAL_KEEP in keyValueMap ||
231
+ MANUAL_DROP in keyValueMap ||
232
+ SAMPLING_PRIORITY in keyValueMap
233
+ } else {
234
+ /* istanbul ignore if: v5 fallback, master ships 6.0.0-pre */
235
+ if (DD_MAJOR < 6 && (typeof keyValueMap === 'string' || Array.isArray(keyValueMap))) {
236
+ tagger.add(tags, keyValueMap)
237
+ mayChangeSamplingPriority = true
238
+ } else {
239
+ return this
240
+ }
241
+ }
242
+
243
+ if (mayChangeSamplingPriority && this._spanContext._sampling.priority === undefined) {
244
+ this._prioritySampler.sample(this, false)
245
+ }
246
+
247
+ if (tagsUpdateCh.hasSubscribers) {
248
+ tagsUpdateCh.publish(this)
249
+ }
250
+
208
251
  return this
209
252
  }
210
253
 
@@ -215,8 +258,9 @@ class DatadogSpan {
215
258
  logEvent () {}
216
259
 
217
260
  addLink (link, attrs) {
218
- // TODO: Remove this once we remove addLink(context, attrs) in v6.0.0
219
- if (link instanceof SpanContext) {
261
+ // v5 still accepts the legacy `addLink(spanContext, attrs)` shape; v6 only takes
262
+ // `addLink({ context, attributes })`.
263
+ if (DD_MAJOR < 6 && link instanceof SpanContext) {
220
264
  link = { context: link, attributes: attrs ?? {} }
221
265
  }
222
266
 
@@ -267,12 +311,14 @@ class DatadogSpan {
267
311
  return
268
312
  }
269
313
 
270
- if (this.#parentTracer._config.DD_TRACE_EXPERIMENTAL_STATE_TRACKING && !this._spanContext._tags['service.name']) {
314
+ if (this.#parentTracer._config.DD_TRACE_EXPERIMENTAL_STATE_TRACKING && !this._spanContext.getTag('service.name')) {
271
315
  log.error('Finishing invalid span: %s', this)
272
316
  }
273
317
 
274
318
  getIntegrationCounter('spans_finished', this._integrationName).inc()
275
- this._spanContext._tags['_dd.integration'] = this._integrationName
319
+ this._spanContext.setTag('_dd.integration', this._integrationName)
320
+
321
+ resolveServiceSource(this, this.#parentTracer._service)
276
322
 
277
323
  if (this.#parentTracer._config.DD_TRACE_EXPERIMENTAL_SPAN_COUNTS && finishedRegistry) {
278
324
  runtimeMetrics.decrement('runtime.node.spans.unfinished')
@@ -404,16 +450,6 @@ class DatadogSpan {
404
450
 
405
451
  return startTime + now() - ticks
406
452
  }
407
-
408
- _addTags (keyValuePairs) {
409
- tagger.add(this._spanContext._tags, keyValuePairs)
410
-
411
- this._prioritySampler.sample(this, false)
412
-
413
- if (tagsUpdateCh.hasSubscribers) {
414
- tagsUpdateCh.publish(this)
415
- }
416
- }
417
453
  }
418
454
 
419
455
  function createRegistry (type) {
@@ -423,4 +459,8 @@ function createRegistry (type) {
423
459
  })
424
460
  }
425
461
 
462
+ function isSamplingPriorityTag (key) {
463
+ return key === MANUAL_KEEP || key === MANUAL_DROP || key === SAMPLING_PRIORITY
464
+ }
465
+
426
466
  module.exports = DatadogSpan
@@ -71,6 +71,55 @@ class DatadogSpanContext {
71
71
  const version = (this._traceparent && this._traceparent.version) || '00'
72
72
  return `${version}-${traceId}-${spanId}-${flags}`
73
73
  }
74
+
75
+ /**
76
+ * Set a tag value.
77
+ * @param {string} key - Tag key
78
+ * @param {unknown} value - Tag value
79
+ */
80
+ setTag (key, value) {
81
+ this._tags[key] = value
82
+ }
83
+
84
+ /**
85
+ * Get a tag value.
86
+ * @param {string} key - Tag key
87
+ * @returns {unknown} Tag value or undefined
88
+ */
89
+ getTag (key) {
90
+ return this._tags[key]
91
+ }
92
+
93
+ /**
94
+ * Check if a tag exists.
95
+ * @param {string} key - Tag key
96
+ * @returns {boolean}
97
+ */
98
+ hasTag (key) { return Object.hasOwn(this._tags, key) }
99
+
100
+ /**
101
+ * Delete a tag.
102
+ * @param {string} key - Tag key
103
+ */
104
+ deleteTag (key) { delete this._tags[key] }
105
+
106
+ /**
107
+ * Get the live internal tags map. The returned reference is mutable;
108
+ * callers may assign or delete keys directly (e.g.
109
+ * `Object.assign(getTags(), tags)` in span.js). Subclasses may have
110
+ * additional sync side effects on the individual `setTag` / `deleteTag`
111
+ * setters; mutating the returned map bypasses those.
112
+ *
113
+ * @returns {object}
114
+ */
115
+ getTags () {
116
+ return this._tags
117
+ }
118
+
119
+ /**
120
+ * Clear all tags.
121
+ */
122
+ clearTags () { this._tags = Object.create(null) }
74
123
  }
75
124
 
76
125
  module.exports = DatadogSpanContext
@@ -52,7 +52,6 @@ const {
52
52
  TEST_ITR_SKIPPING_ENABLED,
53
53
  ITR_CORRELATION_ID,
54
54
  TEST_SOURCE_FILE,
55
- TEST_LEVEL_EVENT_TYPES,
56
55
  TEST_SUITE,
57
56
  getFileAndLineNumberFromError,
58
57
  DI_ERROR_DEBUG_INFO_CAPTURED,
@@ -128,7 +127,6 @@ function getTestSuiteLevelVisibilityTags (testSuiteSpan, testFramework) {
128
127
  const suiteTags = {
129
128
  [TEST_SUITE_ID]: testSuiteSpanContext.toSpanId(),
130
129
  [TEST_SESSION_ID]: testSuiteSpanContext.toTraceId(),
131
- [TEST_COMMAND]: testSuiteSpanContext._tags[TEST_COMMAND],
132
130
  [TEST_MODULE]: testFramework,
133
131
  }
134
132
 
@@ -174,7 +172,7 @@ module.exports = class CiPlugin extends Plugin {
174
172
  },
175
173
  }
176
174
  this.tracer._exporter.addMetadataTags(metadataTags)
177
- onDone({ err, libraryConfig, requestErrorTags })
175
+ onDone({ err, libraryConfig, repositoryRoot: this.repositoryRoot, requestErrorTags })
178
176
  })
179
177
  })
180
178
 
@@ -186,15 +184,22 @@ module.exports = class CiPlugin extends Plugin {
186
184
  if (!this.tracer._exporter?.getSkippableSuites) {
187
185
  return onDone({ err: new Error('Test optimization was not initialized correctly') })
188
186
  }
189
- this.tracer._exporter.getSkippableSuites(this.testConfiguration, (err, skippableSuites, itrCorrelationId) => {
190
- if (err) {
191
- log.error('Skippable suites could not be fetched. %s', err.message)
192
- this._addRequestErrorTag(DD_CI_LIBRARY_CONFIGURATION_ERROR_SKIPPABLE_TESTS, err)
193
- } else {
194
- this.itrCorrelationId = itrCorrelationId
187
+ this.tracer._exporter.getSkippableSuites(
188
+ {
189
+ ...this.testConfiguration,
190
+ isCoverageReportUploadEnabled: this.libraryConfig?.isCoverageReportUploadEnabled,
191
+ },
192
+ (err, skippableSuites, itrCorrelationId, skippableSuitesCoverage) => {
193
+ if (err) {
194
+ log.error('Skippable suites could not be fetched. %s', err.message)
195
+ this._addRequestErrorTag(DD_CI_LIBRARY_CONFIGURATION_ERROR_SKIPPABLE_TESTS, err)
196
+ } else {
197
+ this.itrCorrelationId = itrCorrelationId
198
+ this.skippableSuitesCoverage = skippableSuitesCoverage
199
+ }
200
+ onDone({ err, skippableSuites, itrCorrelationId, skippableSuitesCoverage })
195
201
  }
196
- onDone({ err, skippableSuites, itrCorrelationId })
197
- })
202
+ )
198
203
  })
199
204
 
200
205
  this.addSub(`ci:${this.constructor.id}:session:start`, ({ command, frameworkVersion, rootDir }) => {
@@ -213,12 +218,7 @@ module.exports = class CiPlugin extends Plugin {
213
218
  this.testEnvironmentMetadata
214
219
  )
215
220
 
216
- const metadataTags = {}
217
- for (const testLevel of TEST_LEVEL_EVENT_TYPES) {
218
- metadataTags[testLevel] = {
219
- [TEST_SESSION_NAME]: testSessionName,
220
- }
221
- }
221
+ const metadataTags = { '*': { [TEST_COMMAND]: command, [TEST_SESSION_NAME]: testSessionName } }
222
222
  // tracer might not be initialized correctly
223
223
  if (this.tracer._exporter.addMetadataTags) {
224
224
  this.tracer._exporter.addMetadataTags(metadataTags)
@@ -255,7 +255,7 @@ module.exports = class CiPlugin extends Plugin {
255
255
  })
256
256
 
257
257
  this.addSub(`ci:${this.constructor.id}:itr:skipped-suites`, ({ skippedSuites, frameworkVersion }) => {
258
- const testCommand = this.testSessionSpan.context()._tags[TEST_COMMAND]
258
+ const testCommand = this.command
259
259
  for (const testSuite of skippedSuites) {
260
260
  const testSuiteMetadata = {
261
261
  ...getTestSuiteCommonTags(testCommand, frameworkVersion, testSuite, this.constructor.id),
@@ -615,7 +615,7 @@ module.exports = class CiPlugin extends Plugin {
615
615
  const suiteTags = {
616
616
  [TEST_SUITE_ID]: testSuiteSpan.context().toSpanId(),
617
617
  [TEST_SESSION_ID]: testSuiteSpan.context().toTraceId(),
618
- [TEST_COMMAND]: testSuiteSpan.context()._tags[TEST_COMMAND],
618
+ [TEST_COMMAND]: testSuiteSpan.context().getTag(TEST_COMMAND),
619
619
  [TEST_MODULE]: this.constructor.id,
620
620
  ...getSessionRequestErrorTags(this.testSessionSpan),
621
621
  }
@@ -808,7 +808,7 @@ module.exports = class CiPlugin extends Plugin {
808
808
  }
809
809
 
810
810
  getTestTelemetryTags (testSpan) {
811
- const activeSpanTags = testSpan.context()._tags
811
+ const activeSpanTags = testSpan.context().getTags()
812
812
  return {
813
813
  hasCodeOwners: !!activeSpanTags[TEST_CODE_OWNERS] || undefined,
814
814
  isNew: activeSpanTags[TEST_IS_NEW] === 'true' || undefined,
@@ -49,7 +49,7 @@ class DatabasePlugin extends StoragePlugin {
49
49
  * @returns {string}
50
50
  */
51
51
  #createDBMPropagationCommentService (serviceName, span, peerData) {
52
- const spanTags = span.context()._tags
52
+ const spanTags = span.context().getTags()
53
53
  const dddb = spanTags['db.name']
54
54
  const ddh = spanTags['out.host']
55
55
  const cacheKey = `${dddb ?? ''}\0${ddh ?? ''}\0${serviceName ?? ''}`
@@ -91,23 +91,24 @@ class DatabasePlugin extends StoragePlugin {
91
91
  return null
92
92
  }
93
93
 
94
- const peerData = this.getPeerService(span.context()._tags)
94
+ const peerData = this.getPeerService(span.context().getTags())
95
95
  const dbmService = this.#getDbmServiceName(serviceName, peerData)
96
96
  const servicePropagation = this.#createDBMPropagationCommentService(dbmService, span, peerData)
97
97
 
98
98
  let dbmComment = servicePropagation
99
99
 
100
- // Add propagation hash if both process tags and SQL base hash injection are enabled
101
- if (propagationHash.isEnabled() && this.config['dbm.injectSqlBaseHash']) {
100
+ // Add propagation hash if process tags are enabled and either SQL base hash injection is enabled
101
+ // or dynamic_service mode implicitly enables it
102
+ if (propagationHash.isEnabled() && (this.config['dbm.injectSqlBaseHash'] || mode === 'dynamic_service')) {
102
103
  const hashBase64 = propagationHash.getHashBase64()
103
104
  if (hashBase64) {
104
105
  dbmComment += `,ddsh='${hashBase64}'`
105
106
  // Add hash to span meta as a tag
106
- span.setTag('_dd.dbm.propagation_hash', hashBase64)
107
+ span.setTag('_dd.propagated_hash', hashBase64)
107
108
  }
108
109
  }
109
110
 
110
- if (disableFullMode || mode === 'service') {
111
+ if (disableFullMode || mode === 'service' || mode === 'dynamic_service') {
111
112
  return dbmComment
112
113
  } else if (mode === 'full') {
113
114
  span.setTag('_dd.dbm_trace_injected', 'true')
@@ -4,6 +4,7 @@ const plugins = {
4
4
  get '@anthropic-ai/sdk' () { return require('../../../datadog-plugin-anthropic/src') },
5
5
  get '@apollo/gateway' () { return require('../../../datadog-plugin-apollo/src') },
6
6
  get '@aws-sdk/smithy-client' () { return require('../../../datadog-plugin-aws-sdk/src') },
7
+ get '@azure/cosmos' () { return require('../../../datadog-plugin-azure-cosmos/src') },
7
8
  get '@azure/event-hubs' () { return require('../../../datadog-plugin-azure-event-hubs/src') },
8
9
  get '@azure/functions' () { return require('../../../datadog-plugin-azure-functions/src') },
9
10
  get '@modelcontextprotocol/sdk' () { return require('../../../datadog-plugin-modelcontextprotocol-sdk/src') },
@@ -30,6 +31,7 @@ const plugins = {
30
31
  get '@prisma/client' () { return require('../../../datadog-plugin-prisma/src') },
31
32
  get './runtime/library.js' () { return require('../../../datadog-plugin-prisma/src') },
32
33
  get '@redis/client' () { return require('../../../datadog-plugin-redis/src') },
34
+ get '@smithy/core' () { return require('../../../datadog-plugin-aws-sdk/src') },
33
35
  get '@smithy/smithy-client' () { return require('../../../datadog-plugin-aws-sdk/src') },
34
36
  get '@vitest/runner' () { return require('../../../datadog-plugin-vitest/src') },
35
37
  get '@langchain/langgraph' () { return require('../../../datadog-plugin-langgraph/src') },
@@ -90,6 +92,8 @@ const plugins = {
90
92
  get mongoose () { return require('../../../datadog-plugin-mongoose/src') },
91
93
  get mysql () { return require('../../../datadog-plugin-mysql/src') },
92
94
  get mysql2 () { return require('../../../datadog-plugin-mysql2/src') },
95
+ get '@nats-io/nats-core' () { return require('../../../datadog-plugin-nats/src') },
96
+ get '@nats-io/transport-node' () { return require('../../../datadog-plugin-nats/src') },
93
97
  get net () { return require('../../../datadog-plugin-net/src') },
94
98
  get next () { return require('../../../datadog-plugin-next/src') },
95
99
  get 'node:dns' () { return require('../../../datadog-plugin-dns/src') },
@@ -0,0 +1,56 @@
1
+ 'use strict'
2
+
3
+ const { LOG } = require('../../../../ext/formats')
4
+ const { storage } = require('../../../datadog-core')
5
+
6
+ const legacyStorage = storage('legacy')
7
+
8
+ /**
9
+ * Runs the tracer's log injector and returns the populated log holder, or
10
+ * `undefined` when the propagator emitted no `dd` field (no span, no
11
+ * service / version / env). Hot-path callers gate on the return.
12
+ *
13
+ * @param {object} tracer
14
+ * @returns {{ dd: object } | undefined}
15
+ */
16
+ function buildLogHolder (tracer) {
17
+ const logHolder = {}
18
+ tracer.inject(legacyStorage.getStore()?.span, LOG, logHolder)
19
+ return logHolder.dd ? logHolder : undefined
20
+ }
21
+
22
+ /**
23
+ * @param {object} message Caller-owned log record; never mutated.
24
+ * @param {{ dd: object }} logHolder Holds the dd fields injected by the tracer.
25
+ */
26
+ function messageProxy (message, logHolder) {
27
+ return new Proxy(message, {
28
+ get (target, key) {
29
+ if (shouldOverride(target, key)) return logHolder.dd
30
+ return target[key]
31
+ },
32
+ set (target, key, value) {
33
+ return Reflect.set(target, key, value)
34
+ },
35
+ ownKeys (target) {
36
+ const ownKeys = Reflect.ownKeys(target)
37
+ if (!Object.hasOwn(target, 'dd') && Reflect.isExtensible(target)) {
38
+ ownKeys.push('dd')
39
+ }
40
+ return ownKeys
41
+ },
42
+ getOwnPropertyDescriptor (target, p) {
43
+ return Reflect.getOwnPropertyDescriptor(shouldOverride(target, p) ? logHolder : target, p)
44
+ },
45
+ })
46
+ }
47
+
48
+ /**
49
+ * @param {object} target
50
+ * @param {string | symbol} p
51
+ */
52
+ function shouldOverride (target, p) {
53
+ return p === 'dd' && !Object.hasOwn(target, p) && Reflect.isExtensible(target)
54
+ }
55
+
56
+ module.exports = { buildLogHolder, messageProxy }
@@ -1,55 +1,8 @@
1
1
  'use strict'
2
2
 
3
- const { LOG } = require('../../../../ext/formats')
4
- const { storage } = require('../../../datadog-core')
5
3
  const Plugin = require('./plugin')
6
4
 
7
- const legacyStorage = storage('legacy')
8
-
9
- function messageProxy (message, holder) {
10
- return new Proxy(message, {
11
- get (target, key) {
12
- if (shouldOverride(target, key)) {
13
- return holder.dd
14
- }
15
-
16
- return target[key]
17
- },
18
- set (target, key, value) {
19
- return Reflect.set(target, key, value)
20
- },
21
- ownKeys (target) {
22
- const ownKeys = Reflect.ownKeys(target)
23
- if (!Object.hasOwn(target, 'dd') && Reflect.isExtensible(target)) {
24
- ownKeys.push('dd')
25
- }
26
- return ownKeys
27
- },
28
- getOwnPropertyDescriptor (target, p) {
29
- return Reflect.getOwnPropertyDescriptor(shouldOverride(target, p) ? holder : target, p)
30
- },
31
- })
32
- }
33
-
34
- function shouldOverride (target, p) {
35
- return p === 'dd' && !Object.hasOwn(target, p) && Reflect.isExtensible(target)
36
- }
37
-
38
- module.exports = class LogPlugin extends Plugin {
39
- constructor (...args) {
40
- super(...args)
41
-
42
- this.addSub(`apm:${this.constructor.id}:log`, (arg) => {
43
- const span = legacyStorage.getStore()?.span
44
-
45
- // NOTE: This needs to run whether or not there is a span
46
- // so service, version, and env will always get injected.
47
- const holder = {}
48
- this.tracer.inject(span, LOG, holder)
49
- arg.message = messageProxy(arg.message, holder)
50
- })
51
- }
52
-
5
+ class LogPlugin extends Plugin {
53
6
  configure (config) {
54
7
  return super.configure({
55
8
  ...config,
@@ -57,3 +10,5 @@ module.exports = class LogPlugin extends Plugin {
57
10
  })
58
11
  }
59
12
  }
13
+
14
+ module.exports = LogPlugin
@@ -125,7 +125,7 @@ class OutboundPlugin extends TracingPlugin {
125
125
  */
126
126
  tagPeerService (span) {
127
127
  if (this._tracerConfig.spanComputePeerService) {
128
- const peerData = this.getPeerService(span.context()._tags)
128
+ const peerData = this.getPeerService(span.context().getTags())
129
129
  if (peerData !== undefined) {
130
130
  span.addTags(this.getPeerServiceRemap(peerData))
131
131
  }
@@ -6,6 +6,8 @@ const dc = require('dc-polyfill')
6
6
  const logger = require('../log')
7
7
  const { storage } = require('../../../datadog-core')
8
8
 
9
+ const legacyStorage = storage('legacy')
10
+
9
11
  /**
10
12
  * Base class for all Datadog plugins.
11
13
  *
@@ -28,8 +30,7 @@ class Subscription {
28
30
  constructor (event, handler) {
29
31
  this._channel = dc.channel(event)
30
32
  this._handler = (message, name) => {
31
- const store = storage('legacy').getStore()
32
- if (!store || !store.noop) {
33
+ if (!legacyStorage.getHandle()?.noop) {
33
34
  handler(message, name)
34
35
  }
35
36
  }
@@ -50,20 +51,20 @@ class StoreBinding {
50
51
  constructor (event, transform) {
51
52
  this._channel = dc.channel(event)
52
53
  this._transform = data => {
53
- const store = storage('legacy').getStore()
54
+ const handle = legacyStorage.getHandle()
54
55
 
55
- return !store || !store.noop || (data && Object.hasOwn(data, 'currentStore'))
56
+ return !handle?.noop || (data && Object.hasOwn(data, 'currentStore'))
56
57
  ? transform(data)
57
- : store
58
+ : legacyStorage.getStore()
58
59
  }
59
60
  }
60
61
 
61
62
  enable () {
62
- this._channel.bindStore(storage('legacy'), this._transform)
63
+ this._channel.bindStore(legacyStorage, this._transform)
63
64
  }
64
65
 
65
66
  disable () {
66
- this._channel.unbindStore(storage('legacy'))
67
+ this._channel.unbindStore(legacyStorage)
67
68
  }
68
69
  }
69
70
 
@@ -102,24 +103,21 @@ module.exports = class Plugin {
102
103
  * @returns {void}
103
104
  */
104
105
  enter (span, store) {
105
- store = store || storage('legacy').getStore()
106
- storage('legacy').enterWith({ ...store, span })
106
+ store = store || legacyStorage.getStore()
107
+ legacyStorage.enterWith({ ...store, span })
107
108
  }
108
109
 
109
110
  /**
110
111
  * Subscribe to a diagnostic channel with automatic error handling and enable/disable lifecycle.
111
112
  *
112
113
  * @param {string} channelName Diagnostic channel name.
113
- * @param {(...args: unknown[]) => unknown} handler Handler invoked on messages.
114
+ * @param {(message: unknown, name: string) => unknown} handler Handler invoked on messages.
114
115
  * @returns {void}
115
116
  */
116
117
  addSub (channelName, handler) {
117
- /**
118
- * @type {typeof handler}
119
- */
120
- const wrappedHandler = (...args) => {
118
+ const wrappedHandler = (message, name) => {
121
119
  try {
122
- return handler.apply(this, args)
120
+ return handler.call(this, message, name)
123
121
  } catch (error) {
124
122
  logger.error('Error in plugin handler:', error)
125
123
  logger.info('Disabling plugin: %s', this.constructor.name)
@@ -147,12 +145,12 @@ module.exports = class Plugin {
147
145
  * @returns {void}
148
146
  */
149
147
  addError (error) {
150
- const store = storage('legacy').getStore()
148
+ const store = legacyStorage.getStore()
151
149
 
152
150
  if (!store || !store.span) return
153
151
 
154
152
  const span = /** @type {import('../opentracing/span')} */ (store.span)
155
- if (!span._spanContext._tags.error) {
153
+ if (!span.context().getTag('error')) {
156
154
  span.setTag('error', error || 1)
157
155
  }
158
156
  }
@@ -3,6 +3,7 @@
3
3
  const { storage } = require('../../../datadog-core')
4
4
  const analyticsSampler = require('../analytics_sampler')
5
5
  const { COMPONENT, SVC_SRC_KEY } = require('../constants')
6
+ const { INTEGRATION_SERVICE } = require('../service-naming/source-resolver')
6
7
  const Plugin = require('./plugin')
7
8
 
8
9
  const legacyStorage = storage('legacy')
@@ -99,13 +100,11 @@ class TracingPlugin extends Plugin {
99
100
  const bindName = `bind${event.charAt(0).toUpperCase()}${event.slice(1)}`
100
101
 
101
102
  if (this[event]) {
102
- this.addTraceSub(event, message => {
103
- this[event](message)
104
- })
103
+ this.addTraceSub(event, this[event].bind(this))
105
104
  }
106
105
 
107
106
  if (this[bindName]) {
108
- this.addTraceBind(event, message => this[bindName](message))
107
+ this.addTraceBind(event, this[bindName].bind(this))
109
108
  }
110
109
  }
111
110
  }
@@ -128,12 +127,49 @@ class TracingPlugin extends Plugin {
128
127
  this.addBind(`${prefix}:${eventName}`, transform)
129
128
  }
130
129
 
130
+ /**
131
+ * Record the integration's intended `service.name` on a span without writing the tag.
132
+ *
133
+ * Use this when the plugin has already set `service.name` directly on the span (e.g. via
134
+ * the `tracer.startSpan` tags object) and only needs to stamp the marker so
135
+ * `Span#finish` can later detect user overrides and re-attribute the source.
136
+ *
137
+ * Prefer {@link TracingPlugin#setServiceName} when the tag itself also needs to be written.
138
+ *
139
+ * No-op when there is nothing meaningful to record
140
+ *
141
+ * @param {import('../opentracing/span')} span Internal DatadogSpan instance.
142
+ * @param {string|undefined} name Service name the integration is claiming.
143
+ */
144
+ stampIntegrationService (span, name) {
145
+ if (name === undefined) return
146
+ span[INTEGRATION_SERVICE] = name
147
+ }
148
+
149
+ /**
150
+ * Set `service.name` on a span on behalf of this integration and stamp the marker.
151
+ *
152
+ * Use this for late-binding cases where the service is not known at startSpan time
153
+ * (e.g. web framework config applied after the span is already open).
154
+ *
155
+ * For spans started via {@link TracingPlugin#startSpan}, pass `service` as an option
156
+ * instead — it sets the tag and stamps the marker in one step.
157
+ *
158
+ * @param {import('../opentracing/span')} span Internal DatadogSpan instance.
159
+ * @param {string} name Service name the integration is claiming.
160
+ */
161
+ setServiceName (span, name) {
162
+ // eslint-disable-next-line eslint-rules/eslint-prefer-set-service-name -- this is the implementation
163
+ span._spanContext.setTag('service.name', name)
164
+ this.stampIntegrationService(span, name)
165
+ }
166
+
131
167
  /**
132
168
  * @param {unknown} error
133
169
  * @param {import('../../../..').Span} [span]
134
170
  */
135
171
  addError (error, span = this.activeSpan) {
136
- if (span && !span._spanContext._tags.error) {
172
+ if (span && !span.context().getTag('error')) {
137
173
  // Errors may be wrapped in a context.
138
174
  span.setTag('error', error?.error || error || 1)
139
175
  }
@@ -224,6 +260,8 @@ class TracingPlugin extends Plugin {
224
260
  links: childOf?._links,
225
261
  })
226
262
 
263
+ this.stampIntegrationService(span, serviceName)
264
+
227
265
  analyticsSampler.sample(span, config.measured)
228
266
 
229
267
  // TODO: Remove this after migration to TracingChannel is done.