dd-trace 6.14.0 → 6.16.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 (98) hide show
  1. package/README.md +15 -0
  2. package/ci/test-optimization-validation/framework-adapters/cucumber.js +4 -2
  3. package/ci/test-optimization-validation/package-check.js +4 -1
  4. package/ci/test-optimization-validation/scenarios/ci-wiring.js +1 -1
  5. package/index.d.ts +12 -0
  6. package/next.d.ts +27 -0
  7. package/next.js +3 -0
  8. package/package.json +17 -13
  9. package/packages/datadog-esbuild/index.js +14 -1
  10. package/packages/datadog-esbuild/src/resolver.js +284 -0
  11. package/packages/datadog-esbuild/src/utils.js +163 -119
  12. package/packages/datadog-instrumentations/src/ai.js +39 -97
  13. package/packages/datadog-instrumentations/src/anthropic.js +76 -134
  14. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +1 -1
  15. package/packages/datadog-instrumentations/src/azure-cosmos.js +1 -1
  16. package/packages/datadog-instrumentations/src/azure-durable-functions.js +23 -9
  17. package/packages/datadog-instrumentations/src/azure-functions.js +48 -0
  18. package/packages/datadog-instrumentations/src/bullmq.js +1 -1
  19. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -1
  20. package/packages/datadog-instrumentations/src/dd-trace-api.js +2 -2
  21. package/packages/datadog-instrumentations/src/graphql.js +1 -1
  22. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +39 -14
  23. package/packages/datadog-instrumentations/src/helpers/instrument.js +25 -8
  24. package/packages/datadog-instrumentations/src/helpers/instrumentation-utils.js +72 -0
  25. package/packages/datadog-instrumentations/src/helpers/register.js +9 -64
  26. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +46 -14
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-durable-functions.js +17 -0
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +1 -5
  30. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +6 -7
  31. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
  32. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +99 -70
  33. package/packages/datadog-instrumentations/src/jest.js +141 -25
  34. package/packages/datadog-instrumentations/src/langchain.js +1 -1
  35. package/packages/datadog-instrumentations/src/langgraph.js +1 -1
  36. package/packages/datadog-instrumentations/src/mercurius.js +1 -1
  37. package/packages/datadog-instrumentations/src/mocha/main.js +41 -5
  38. package/packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js +1 -1
  39. package/packages/datadog-instrumentations/src/openai-agents.js +1 -1
  40. package/packages/datadog-instrumentations/src/openai.js +54 -76
  41. package/packages/datadog-instrumentations/src/playwright.js +4 -0
  42. package/packages/datadog-instrumentations/src/vitest-worker.js +7 -10
  43. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +7 -1
  44. package/packages/datadog-plugin-azure-cosmos/src/index.js +1 -1
  45. package/packages/datadog-plugin-azure-durable-functions/src/index.js +96 -1
  46. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -24
  47. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -36
  48. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-request-context.js +31 -0
  49. package/packages/datadog-plugin-graphql/src/resolve-error.js +33 -11
  50. package/packages/datadog-turbopack/index.js +212 -0
  51. package/packages/datadog-turbopack/src/constants.js +5 -0
  52. package/packages/datadog-turbopack/src/loader.js +500 -0
  53. package/packages/dd-trace/src/aiguard/index.js +2 -1
  54. package/packages/dd-trace/src/aiguard/integrations/anthropic.js +53 -25
  55. package/packages/dd-trace/src/aiguard/integrations/evaluate.js +49 -25
  56. package/packages/dd-trace/src/aiguard/integrations/index.js +3 -2
  57. package/packages/dd-trace/src/aiguard/integrations/openai.js +59 -30
  58. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +116 -42
  59. package/packages/dd-trace/src/aiguard/messages/utils.js +29 -1
  60. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +25 -0
  61. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +3 -1
  62. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +531 -70
  63. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +2 -2
  64. package/packages/dd-trace/src/appsec/telemetry/api_security.js +1 -3
  65. package/packages/dd-trace/src/appsec/telemetry/common.js +4 -0
  66. package/packages/dd-trace/src/appsec/telemetry/index.js +23 -5
  67. package/packages/dd-trace/src/appsec/telemetry/rasp.js +1 -20
  68. package/packages/dd-trace/src/appsec/telemetry/user.js +1 -3
  69. package/packages/dd-trace/src/appsec/telemetry/waf.js +1 -20
  70. package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -0
  71. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +4 -0
  72. package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -0
  73. package/packages/dd-trace/src/config/helper.js +10 -2
  74. package/packages/dd-trace/src/config/index.js +20 -5
  75. package/packages/dd-trace/src/config/supported-configurations.json +10 -0
  76. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +14 -7
  77. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +14 -1
  78. package/packages/dd-trace/src/debugger/devtools_client/request-options.js +1 -6
  79. package/packages/dd-trace/src/evp_proxy/direct.js +2 -28
  80. package/packages/dd-trace/src/evp_proxy/discovery.js +3 -4
  81. package/packages/dd-trace/src/evp_proxy/path.js +12 -3
  82. package/packages/dd-trace/src/exporters/agentless/writer.js +3 -1
  83. package/packages/dd-trace/src/exporters/common/docker.js +4 -1
  84. package/packages/dd-trace/src/exporters/common/proxy.js +55 -0
  85. package/packages/dd-trace/src/exporters/common/request.js +11 -3
  86. package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
  87. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +5 -4
  88. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +106 -25
  89. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +12 -1
  90. package/packages/dd-trace/src/opentracing/propagation/text_map.js +8 -2
  91. package/packages/dd-trace/src/plugin_manager.js +9 -3
  92. package/packages/dd-trace/src/plugins/index.js +1 -0
  93. package/packages/dd-trace/src/plugins/util/test.js +4 -1
  94. package/packages/dd-trace/src/priority_sampler.js +4 -0
  95. package/packages/dd-trace/src/sampling_rule.js +3 -1
  96. package/packages/dd-trace/src/span_processor.js +18 -3
  97. package/packages/dd-trace/src/telemetry/send-data.js +5 -4
  98. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +0 -10
@@ -51,11 +51,12 @@ class MeterProvider {
51
51
  }
52
52
 
53
53
  /**
54
- * @param {Function} [done] Called after the metric export completes
54
+ * @param {(error: Error | null) => void} [done] Called after shutdown completes
55
+ * @returns {void}
55
56
  */
56
- forceFlush (done) {
57
- if (this.reader) this.reader.forceFlush(done)
58
- else done?.()
57
+ shutdown (done) {
58
+ if (this.reader) this.reader.shutdown(done)
59
+ else done?.(null)
59
60
  }
60
61
  }
61
62
 
@@ -8,6 +8,19 @@ const {
8
8
  const { ObservableInstrument } = require('./instruments')
9
9
  const { nowUnixNano } = require('./time')
10
10
 
11
+ /**
12
+ * @param {(error: Error | null) => void} [done]
13
+ * @param {Error | null} [error]
14
+ */
15
+ function callDone (done, error) {
16
+ if (!done) return
17
+ try {
18
+ done(error ?? null)
19
+ } catch (callbackError) {
20
+ log.error('Error completing OTLP metrics lifecycle callback:', callbackError)
21
+ }
22
+ }
23
+
11
24
  /**
12
25
  * @typedef {import('@opentelemetry/api').Attributes} Attributes
13
26
  * @typedef {import('@opentelemetry/core').InstrumentationScope} InstrumentationScope
@@ -105,6 +118,10 @@ class PeriodicMetricReader {
105
118
  #exportInterval
106
119
  #aggregator
107
120
  #batchCallbacks = []
121
+ #exportQueue = []
122
+ #isExporting = false
123
+ #shutdownCallbacks = []
124
+ #shutdownComplete = false
108
125
 
109
126
  /**
110
127
  * Creates a new PeriodicMetricReader instance.
@@ -197,47 +214,36 @@ class PeriodicMetricReader {
197
214
 
198
215
  /**
199
216
  * Forces an immediate collection and export of all metrics.
200
- * @param {Function} [done] Called after the metric export completes
217
+ * @param {(error: Error | null) => void} [done] Called after the metric export completes
218
+ * @returns {void}
201
219
  */
202
220
  forceFlush (done) {
203
221
  if (this.#isShutdown) {
204
222
  log.warn('PeriodicMetricReader is shutdown. %d measurement(s) were dropped', this.#droppedCount)
205
- done?.()
223
+ if (this.#shutdownComplete) callDone(done)
224
+ else if (done) this.#shutdownCallbacks.push(done)
206
225
  return
207
226
  }
208
- let pending = 2
209
- const complete = () => {
210
- if (--pending === 0) done?.()
211
- }
212
-
213
- // Snapshot requests already active before starting this flush's export.
214
- try {
215
- if (typeof this.exporter.flush === 'function') this.exporter.flush(complete)
216
- else complete()
217
- } catch (error) {
218
- log.error('Error flushing OTLP metrics:', error)
219
- complete()
220
- }
221
- try {
222
- this.#collectAndExport(complete)
223
- } catch (error) {
224
- log.error('Error exporting OTLP metrics:', error)
225
- complete()
226
- }
227
+ this.#enqueueExport(true, done)
227
228
  }
228
229
 
229
230
  /**
230
231
  * Shuts down the reader and stops periodic collection.
232
+ * @param {(error: Error | null) => void} [done] Called after the final export and exporter shutdown complete
231
233
  * @returns {void}
232
234
  */
233
- shutdown () {
235
+ shutdown (done) {
234
236
  if (this.#isShutdown) {
235
237
  log.warn('PeriodicMetricReader is already shutdown')
238
+ if (this.#shutdownComplete) callDone(done)
239
+ else if (done) this.#shutdownCallbacks.push(done)
236
240
  return
237
241
  }
242
+ if (done) this.#shutdownCallbacks.push(done)
243
+
238
244
  this.#isShutdown = true
239
245
  this.#clearTimer()
240
- this.forceFlush()
246
+ this.#enqueueExport(true, error => this.#shutdownExporter(error))
241
247
  }
242
248
 
243
249
  /**
@@ -248,7 +254,7 @@ class PeriodicMetricReader {
248
254
  if (this.#timer) return
249
255
 
250
256
  this.#timer = setInterval(() => {
251
- this.#collectAndExport()
257
+ if (!this.#isExporting && this.#exportQueue.length === 0) this.#enqueueExport(false)
252
258
  }, this.#exportInterval)
253
259
  this.#timer.unref?.()
254
260
  }
@@ -264,6 +270,75 @@ class PeriodicMetricReader {
264
270
  }
265
271
  }
266
272
 
273
+ /**
274
+ * @param {boolean} flushExporter Whether to flush the exporter after export
275
+ * @param {(error: Error | null) => void} [done] Called when the queued export completes
276
+ * @returns {void}
277
+ */
278
+ #enqueueExport (flushExporter, done) {
279
+ this.#exportQueue.push({ flushExporter, done })
280
+ this.#drainExportQueue()
281
+ }
282
+
283
+ /** @returns {void} */
284
+ #drainExportQueue () {
285
+ if (this.#isExporting || this.#exportQueue.length === 0) return
286
+
287
+ this.#isExporting = true
288
+ const { flushExporter, done } = this.#exportQueue.shift()
289
+ let completed = false
290
+ const complete = error => {
291
+ if (completed) return
292
+ completed = true
293
+ this.#isExporting = false
294
+ queueMicrotask(() => this.#drainExportQueue())
295
+ callDone(done, error)
296
+ }
297
+ let exportCompleted = false
298
+ const afterExport = error => {
299
+ if (exportCompleted) return
300
+ exportCompleted = true
301
+ if (!flushExporter || typeof this.exporter.flush !== 'function') return complete(error)
302
+
303
+ try {
304
+ this.exporter.flush(flushError => complete(error || flushError))
305
+ } catch (flushError) {
306
+ log.error('Error flushing OTLP metrics:', flushError)
307
+ complete(error || flushError)
308
+ }
309
+ }
310
+
311
+ try {
312
+ this.#collectAndExport(afterExport)
313
+ } catch (error) {
314
+ log.error('Error exporting OTLP metrics:', error)
315
+ afterExport(error)
316
+ }
317
+ }
318
+
319
+ /**
320
+ * @param {Error} [exportError] Error from the final export
321
+ * @returns {void}
322
+ */
323
+ #shutdownExporter (exportError) {
324
+ let completed = false
325
+ const complete = shutdownError => {
326
+ if (completed) return
327
+ completed = true
328
+ this.#shutdownComplete = true
329
+ const callbacks = this.#shutdownCallbacks.splice(0)
330
+ for (const callback of callbacks) callDone(callback, exportError || shutdownError)
331
+ }
332
+
333
+ try {
334
+ if (typeof this.exporter.shutdown === 'function') this.exporter.shutdown(complete)
335
+ else complete()
336
+ } catch (error) {
337
+ log.error('Error shutting down OTLP metrics exporter:', error)
338
+ complete(error)
339
+ }
340
+ }
341
+
267
342
  /**
268
343
  * Collects measurements and exports metrics.
269
344
  *
@@ -322,7 +397,13 @@ class PeriodicMetricReader {
322
397
  this.#lastExportedState
323
398
  )
324
399
 
325
- this.exporter.export(metrics, callback)
400
+ this.exporter.export(metrics, result => {
401
+ if (result?.code === 1) {
402
+ callback?.(result.error || new Error('OTLP metrics export failed'))
403
+ } else {
404
+ callback?.()
405
+ }
406
+ })
326
407
  }
327
408
  }
328
409
 
@@ -6,7 +6,9 @@ const { URL } = require('node:url')
6
6
  const { storage } = require('../../../../datadog-core')
7
7
  const log = require('../../log')
8
8
  const { createServerlessDeliveryTracker } = require('../../serverless')
9
+ const { getHttpsProxyAgent } = require('../../exporters/common/proxy')
9
10
  const telemetryMetrics = require('../../telemetry/metrics')
11
+ const { version: tracerVersion } = require('../../../../../package.json')
10
12
 
11
13
  const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
12
14
  const legacyStorage = storage('legacy')
@@ -48,8 +50,10 @@ class OtlpHttpExporterBase {
48
50
  hostname: parsedUrl.hostname,
49
51
  port: parsedUrl.port,
50
52
  path: parsedUrl.pathname + parsedUrl.search,
53
+ agent: parsedUrl.protocol === 'https:' ? getHttpsProxyAgent(parsedUrl) : undefined,
51
54
  headers: {
52
55
  'Content-Type': isJson ? 'application/json' : 'application/x-protobuf',
56
+ 'User-Agent': `dd-trace-js/${tracerVersion}`,
53
57
  ...headers,
54
58
  },
55
59
  }
@@ -169,10 +173,17 @@ class OtlpHttpExporterBase {
169
173
  this.options.hostname = parsedUrl.hostname
170
174
  this.options.port = parsedUrl.port
171
175
  this.options.path = parsedUrl.pathname + parsedUrl.search
176
+ this.options.agent = parsedUrl.protocol === 'https:' ? getHttpsProxyAgent(parsedUrl) : undefined
172
177
  this.telemetryTags[0] = `protocol:${this.#transport === https ? 'https' : 'http'}`
173
178
  }
174
179
 
175
- shutdown () {}
180
+ /**
181
+ * @param {() => void} [done]
182
+ * @returns {void}
183
+ */
184
+ shutdown (done) {
185
+ done?.()
186
+ }
176
187
  }
177
188
 
178
189
  module.exports = OtlpHttpExporterBase
@@ -363,6 +363,11 @@ class TextMapPropagator {
363
363
  * @returns {DatadogSpanContext | null}
364
364
  */
365
365
  extract (carrier) {
366
+ if (!carrier || typeof carrier !== 'object') {
367
+ if (this.#config.DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT !== 'ignore') removeAllBaggageItems()
368
+ return null
369
+ }
370
+
366
371
  const spanContext = this.#extractSpanContext(carrier)
367
372
  if (spanContext === undefined) return null
368
373
 
@@ -825,7 +830,6 @@ class TextMapPropagator {
825
830
  * @returns {DatadogSpanContext | undefined}
826
831
  */
827
832
  #extractDatadogContext (carrier) {
828
- if (!carrier) return
829
833
  const traceId = readDatadogTraceId(carrier)
830
834
  if (!traceId) return
831
835
  const spanContext = extractGenericContext(traceId, readDatadogParentId(carrier), 10)
@@ -860,6 +864,8 @@ class TextMapPropagator {
860
864
  } catch {
861
865
  return
862
866
  }
867
+ if (!parsed || typeof parsed !== 'object') return
868
+
863
869
  const spanContext = this.#extractDatadogContext(parsed)
864
870
  if (!spanContext) return
865
871
 
@@ -983,7 +989,7 @@ class TextMapPropagator {
983
989
  */
984
990
  #extractBaggageItems (carrier, spanContext, extractBaggage) {
985
991
  removeAllBaggageItems()
986
- if (!carrier || !extractBaggage) return
992
+ if (!extractBaggage) return
987
993
  const header = readBaggage(carrier)
988
994
  if (!header) return
989
995
 
@@ -2,7 +2,11 @@
2
2
 
3
3
  const { channel } = require('dc-polyfill')
4
4
 
5
- const { getEnvironmentVariable, getValueFromEnvSources } = require('./config/helper')
5
+ const {
6
+ getEnvironmentVariable,
7
+ getValueFromEnvSources,
8
+ isSupportedConfiguration,
9
+ } = require('./config/helper')
6
10
  const { isFalse, isTrue, normalizePluginEnvName } = require('./util')
7
11
  const plugins = require('./plugins')
8
12
  const log = require('./log')
@@ -59,12 +63,14 @@ function maybeEnable (Plugin) {
59
63
  }
60
64
 
61
65
  function getEnabled (Plugin) {
62
- const envName = `DD_TRACE_${Plugin.id.toUpperCase()}_ENABLED`
66
+ const envName = normalizePluginEnvName(`DD_TRACE_${Plugin.id.toUpperCase()}_ENABLED`)
67
+ if (!isSupportedConfiguration(envName)) return
68
+
63
69
  // skipDefault: only an explicitly configured value should drive enablement here. A registered
64
70
  // default of `false` (e.g. an opt-in plugin like `nats`) must not be read as an explicit
65
71
  // "disabled via configuration option" — that path both logs a misleading line and nulls the
66
72
  // plugin class, bypassing the opt-in handled by `loadPlugin`.
67
- return getValueFromEnvSources(normalizePluginEnvName(envName), true)
73
+ return getValueFromEnvSources(envName, true)
68
74
  }
69
75
 
70
76
  // TODO this must always be a singleton.
@@ -19,6 +19,7 @@ const plugins = {
19
19
  get '@google-cloud/pubsub' () { return require('../../../datadog-plugin-google-cloud-pubsub/src') },
20
20
  get '@google-cloud/vertexai' () { return require('../../../datadog-plugin-google-cloud-vertexai/src') },
21
21
  get '@google/genai' () { return require('../../../datadog-plugin-google-genai/src') },
22
+ get '@graphql-tools/executor' () { return require('../../../datadog-plugin-graphql/src') },
22
23
  get '@grpc/grpc-js' () { return require('../../../datadog-plugin-grpc/src') },
23
24
  get '@hapi/hapi' () { return require('../../../datadog-plugin-hapi/src') },
24
25
  get '@happy-dom/jest-environment' () { return require('../../../datadog-plugin-jest/src') },
@@ -1135,7 +1135,10 @@ function getCodeOwnersPatternRegex (pattern) {
1135
1135
  }
1136
1136
 
1137
1137
  const directoryOnly = pattern.endsWith('/')
1138
- const normalizedPattern = pattern.replace(/^\/+/, '').replace(/\/+$/, '')
1138
+ let normalizedPattern = pattern.replace(/^\/+/, '')
1139
+ let normalizedPatternEnd = normalizedPattern.length
1140
+ while (normalizedPatternEnd > 0 && normalizedPattern[normalizedPatternEnd - 1] === '/') normalizedPatternEnd--
1141
+ normalizedPattern = normalizedPattern.slice(0, normalizedPatternEnd)
1139
1142
  const anchored = pattern.startsWith('/') || normalizedPattern.includes('/')
1140
1143
 
1141
1144
  if (!normalizedPattern) {
@@ -263,6 +263,10 @@ class PrioritySampler {
263
263
  context._sampling.mechanism = SAMPLING_MECHANISM_REMOTE_DYNAMIC
264
264
  }
265
265
 
266
+ if (rule.discard) {
267
+ context._sampling.discard = true
268
+ }
269
+
266
270
  return rule.sample(context) && this._isSampledByRateLimit(context)
267
271
  ? USER_KEEP
268
272
  : USER_REJECT
@@ -185,6 +185,7 @@ function resourceLocator (span) {
185
185
  * @property {number} [sampleRate=1] - Deterministic sampling rate in [0, 1].
186
186
  * @property {string} [provenance] - Optional provenance/metadata for this rule.
187
187
  * @property {number} [maxPerSecond] - Maximum samples per second (rate limit).
188
+ * @property {boolean} [discard=false] - Whether to fully drop a trace if not kept.
188
189
  */
189
190
 
190
191
  /**
@@ -195,7 +196,7 @@ class SamplingRule {
195
196
  /**
196
197
  * @param {SamplingRuleConfig} [config]
197
198
  */
198
- constructor ({ name, service, resource, tags, sampleRate = 1, provenance, maxPerSecond } = {}) {
199
+ constructor ({ name, service, resource, tags, sampleRate = 1, provenance, maxPerSecond, discard = false } = {}) {
199
200
  this.matchers = []
200
201
 
201
202
  if (name !== undefined) {
@@ -216,6 +217,7 @@ class SamplingRule {
216
217
  this._sampler = new Sampler(sampleRate)
217
218
  this._limiter = undefined
218
219
  this.provenance = provenance
220
+ this.discard = !!discard
219
221
 
220
222
  if (Number.isFinite(maxPerSecond)) {
221
223
  this._limiter = new RateLimiter(maxPerSecond)
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
 
3
+ const { AUTO_REJECT } = require('../../../ext/priority')
3
4
  const log = require('./log')
4
5
  const spanFormat = require('./span_format')
5
6
  const SpanSampler = require('./span_sampler')
@@ -34,7 +35,20 @@ class SpanProcessor {
34
35
  sample (span) {
35
36
  const spanContext = span.context()
36
37
  this._prioritySampler.sample(spanContext)
37
- this._spanSampler.sample(spanContext)
38
+ if (!this.#isDiscarded(spanContext)) {
39
+ this._spanSampler.sample(spanContext)
40
+ }
41
+ }
42
+
43
+ /**
44
+ * A rule's reject decision can later be overridden (e.g. a product force-keeping the trace via
45
+ * `PrioritySampler.keepTrace()`), so `discard` only applies while the priority is still a reject.
46
+ *
47
+ * @param {import('./opentracing/span_context')} spanContext
48
+ * @returns {boolean}
49
+ */
50
+ #isDiscarded (spanContext) {
51
+ return spanContext._sampling.discard && spanContext._sampling.priority <= AUTO_REJECT
38
52
  }
39
53
 
40
54
  process (span) {
@@ -56,11 +70,12 @@ class SpanProcessor {
56
70
 
57
71
  let isFirstSpanInChunk = true
58
72
  const stampApmDisabled = this._config.apmTracingEnabled === false
73
+ const discard = this.#isDiscarded(spanContext)
59
74
 
60
75
  for (const span of started) {
61
76
  if (span._duration === undefined) {
62
77
  active.push(span)
63
- } else {
78
+ } else if (!discard) {
64
79
  const formattedSpan = spanFormat(span, isFirstSpanInChunk, this._processTags)
65
80
  if (stampApmDisabled) {
66
81
  formattedSpan.metrics[APM_TRACING_ENABLED_KEY] = 0
@@ -76,7 +91,7 @@ class SpanProcessor {
76
91
  }
77
92
  }
78
93
 
79
- if (formatted.length !== 0 && trace.isRecording !== false) {
94
+ if (!discard && formatted.length !== 0 && trace.isRecording !== false) {
80
95
  this._exporter.export(formatted)
81
96
  }
82
97
 
@@ -76,9 +76,10 @@ let getTestOptimizationAgent
76
76
  * @param {import('../config/config-base')} config
77
77
  * @param {TelemetryApplication} application
78
78
  * @param {TelemetryRequestType} reqType
79
+ * @param {string} [apiKey]
79
80
  * @returns {Record<string, string>}
80
81
  */
81
- function getHeaders (config, application, reqType) {
82
+ function getHeaders (config, application, reqType, apiKey) {
82
83
  const headers = {
83
84
  'DD-Client-Library-Language': application.language_name,
84
85
  'DD-Client-Library-Version': application.tracer_version,
@@ -94,8 +95,8 @@ function getHeaders (config, application, reqType) {
94
95
  if (debug) {
95
96
  headers['dd-telemetry-debug-enabled'] = 'true'
96
97
  }
97
- if (config.DD_API_KEY) {
98
- headers['dd-api-key'] = config.DD_API_KEY
98
+ if (apiKey) {
99
+ headers['dd-api-key'] = apiKey
99
100
  }
100
101
  return headers
101
102
  }
@@ -161,7 +162,7 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
161
162
  port,
162
163
  method: 'POST',
163
164
  path: isAgentlessMode ? '/api/v2/apmtelemetry' : '/telemetry/proxy/api/v2/apmtelemetry',
164
- headers: getHeaders(config, application, reqType),
165
+ headers: getHeaders(config, application, reqType, isAgentlessMode ? config.DD_API_KEY : undefined),
165
166
  }
166
167
  if (isCiVisibility) options.agent = getTestOptimizationAgent(url)
167
168
 
@@ -1,10 +0,0 @@
1
- 'use strict'
2
-
3
- function splitModel (model) {
4
- if (!model) return { modelName: undefined, modelProvider: 'anthropic' }
5
- const idx = model.indexOf('/')
6
- if (idx === -1) return { modelName: model, modelProvider: 'anthropic' }
7
- return { modelName: model.slice(idx + 1), modelProvider: model.slice(0, idx) }
8
- }
9
-
10
- module.exports = { splitModel }