dd-trace 5.67.0 → 5.69.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 (132) hide show
  1. package/LICENSE-3rdparty.csv +6 -4
  2. package/README.md +0 -2
  3. package/ci/init.js +52 -54
  4. package/ext/exporters.d.ts +2 -1
  5. package/ext/exporters.js +2 -1
  6. package/index.d.ts +240 -3
  7. package/initialize.mjs +1 -1
  8. package/package.json +17 -11
  9. package/packages/datadog-core/src/storage.js +14 -13
  10. package/packages/datadog-esbuild/index.js +118 -26
  11. package/packages/datadog-instrumentations/src/aws-sdk.js +42 -4
  12. package/packages/datadog-instrumentations/src/azure-functions.js +1 -1
  13. package/packages/datadog-instrumentations/src/azure-service-bus.js +1 -1
  14. package/packages/datadog-instrumentations/src/cassandra-driver.js +2 -2
  15. package/packages/datadog-instrumentations/src/connect.js +6 -2
  16. package/packages/datadog-instrumentations/src/cucumber.js +31 -6
  17. package/packages/datadog-instrumentations/src/express.js +5 -6
  18. package/packages/datadog-instrumentations/src/fastify.js +3 -3
  19. package/packages/datadog-instrumentations/src/helpers/hook.js +28 -15
  20. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  21. package/packages/datadog-instrumentations/src/helpers/instrument.js +15 -5
  22. package/packages/datadog-instrumentations/src/helpers/register.js +10 -3
  23. package/packages/datadog-instrumentations/src/http2/client.js +1 -0
  24. package/packages/datadog-instrumentations/src/http2/server.js +0 -1
  25. package/packages/datadog-instrumentations/src/ioredis.js +12 -1
  26. package/packages/datadog-instrumentations/src/jest.js +48 -36
  27. package/packages/datadog-instrumentations/src/limitd-client.js +2 -1
  28. package/packages/datadog-instrumentations/src/mocha/main.js +15 -7
  29. package/packages/datadog-instrumentations/src/mocha/utils.js +3 -0
  30. package/packages/datadog-instrumentations/src/mongoose.js +2 -1
  31. package/packages/datadog-instrumentations/src/oracledb.js +19 -13
  32. package/packages/datadog-instrumentations/src/pg.js +9 -5
  33. package/packages/datadog-instrumentations/src/pino.js +18 -6
  34. package/packages/datadog-instrumentations/src/playwright.js +15 -1
  35. package/packages/datadog-instrumentations/src/sequelize.js +1 -1
  36. package/packages/datadog-instrumentations/src/vitest.js +155 -62
  37. package/packages/datadog-plugin-ai/src/tracing.js +3 -3
  38. package/packages/datadog-plugin-aws-sdk/src/base.js +23 -8
  39. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +2 -2
  40. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +101 -2
  41. package/packages/datadog-plugin-aws-sdk/src/util.js +1 -1
  42. package/packages/datadog-plugin-confluentinc-kafka-javascript/src/index.js +6 -0
  43. package/packages/datadog-plugin-cucumber/src/index.js +4 -56
  44. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +6 -3
  45. package/packages/datadog-plugin-cypress/src/support.js +4 -0
  46. package/packages/datadog-plugin-express/src/code_origin.js +2 -2
  47. package/packages/datadog-plugin-fastify/src/code_origin.js +1 -2
  48. package/packages/datadog-plugin-jest/src/index.js +0 -21
  49. package/packages/datadog-plugin-mocha/src/index.js +3 -57
  50. package/packages/datadog-plugin-mongodb-core/src/index.js +38 -12
  51. package/packages/datadog-plugin-playwright/src/index.js +11 -5
  52. package/packages/datadog-plugin-vitest/src/index.js +5 -1
  53. package/packages/datadog-plugin-ws/src/close.js +1 -1
  54. package/packages/datadog-plugin-ws/src/producer.js +6 -1
  55. package/packages/datadog-plugin-ws/src/receiver.js +6 -1
  56. package/packages/dd-trace/src/aiguard/client.js +25 -0
  57. package/packages/dd-trace/src/aiguard/noop.js +9 -0
  58. package/packages/dd-trace/src/aiguard/sdk.js +173 -0
  59. package/packages/dd-trace/src/aiguard/tags.js +11 -0
  60. package/packages/dd-trace/src/appsec/iast/path-line.js +21 -4
  61. package/packages/dd-trace/src/appsec/iast/security-controls/parser.js +1 -1
  62. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +6 -3
  63. package/packages/dd-trace/src/appsec/stack_trace.js +20 -1
  64. package/packages/dd-trace/src/appsec/telemetry/waf.js +2 -2
  65. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -4
  66. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +11 -3
  67. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +10 -1
  68. package/packages/dd-trace/src/config-helper.js +8 -1
  69. package/packages/dd-trace/src/config.js +92 -304
  70. package/packages/dd-trace/src/config_defaults.js +191 -0
  71. package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -1
  72. package/packages/dd-trace/src/datastreams/fnv.js +2 -2
  73. package/packages/dd-trace/src/datastreams/index.js +23 -1
  74. package/packages/dd-trace/src/datastreams/writer.js +3 -2
  75. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -1
  76. package/packages/dd-trace/src/dogstatsd.js +4 -3
  77. package/packages/dd-trace/src/encode/0.4.js +1 -5
  78. package/packages/dd-trace/src/exporter.js +1 -0
  79. package/packages/dd-trace/src/exporters/agent/index.js +3 -2
  80. package/packages/dd-trace/src/exporters/agent/writer.js +1 -1
  81. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +3 -2
  82. package/packages/dd-trace/src/exporters/common/request.js +2 -1
  83. package/packages/dd-trace/src/exporters/span-stats/index.js +3 -2
  84. package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
  85. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +15 -4
  86. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +20 -7
  87. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +40 -13
  88. package/packages/dd-trace/src/llmobs/plugins/openai.js +7 -1
  89. package/packages/dd-trace/src/llmobs/tagger.js +8 -0
  90. package/packages/dd-trace/src/llmobs/telemetry.js +2 -1
  91. package/packages/dd-trace/src/log/index.js +27 -16
  92. package/packages/dd-trace/src/log/log.js +29 -5
  93. package/packages/dd-trace/src/log/writer.js +5 -5
  94. package/packages/dd-trace/src/noop/proxy.js +4 -0
  95. package/packages/dd-trace/src/noop/span.js +1 -0
  96. package/packages/dd-trace/src/opentelemetry/span.js +14 -3
  97. package/packages/dd-trace/src/opentracing/span.js +19 -5
  98. package/packages/dd-trace/src/payload-tagging/config/index.js +16 -0
  99. package/packages/dd-trace/src/payload-tagging/index.js +26 -15
  100. package/packages/dd-trace/src/payload-tagging/tagging.js +17 -8
  101. package/packages/dd-trace/src/pkg.js +3 -1
  102. package/packages/dd-trace/src/plugin_manager.js +20 -2
  103. package/packages/dd-trace/src/plugins/ci_plugin.js +97 -3
  104. package/packages/dd-trace/src/plugins/composite.js +3 -0
  105. package/packages/dd-trace/src/plugins/index.js +2 -0
  106. package/packages/dd-trace/src/plugins/plugin.js +67 -0
  107. package/packages/dd-trace/src/plugins/util/git-cache.js +129 -0
  108. package/packages/dd-trace/src/plugins/util/git.js +41 -27
  109. package/packages/dd-trace/src/plugins/util/test.js +56 -27
  110. package/packages/dd-trace/src/plugins/util/web.js +1 -1
  111. package/packages/dd-trace/src/priority_sampler.js +70 -46
  112. package/packages/dd-trace/src/profiler.js +4 -1
  113. package/packages/dd-trace/src/profiling/config.js +73 -42
  114. package/packages/dd-trace/src/profiling/profiler.js +3 -1
  115. package/packages/dd-trace/src/profiling/profilers/events.js +3 -8
  116. package/packages/dd-trace/src/profiling/profilers/space.js +1 -0
  117. package/packages/dd-trace/src/profiling/profilers/wall.js +196 -117
  118. package/packages/dd-trace/src/proxy.js +15 -0
  119. package/packages/dd-trace/src/rate_limiter.js +26 -1
  120. package/packages/dd-trace/src/remote_config/capabilities.js +5 -0
  121. package/packages/dd-trace/src/remote_config/manager.js +3 -2
  122. package/packages/dd-trace/src/sampling_rule.js +124 -2
  123. package/packages/dd-trace/src/span_sampler.js +19 -0
  124. package/packages/dd-trace/src/standalone/product.js +9 -0
  125. package/packages/dd-trace/src/standalone/tracesource.js +16 -1
  126. package/packages/dd-trace/src/standalone/tracesource_priority_sampler.js +13 -0
  127. package/packages/dd-trace/src/startup-log.js +21 -2
  128. package/packages/dd-trace/src/supported-configurations.json +9 -0
  129. package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
  130. package/packages/dd-trace/src/util.js +1 -1
  131. package/register.js +1 -1
  132. package/version.js +4 -2
@@ -8,10 +8,11 @@ const telemetry = require('../telemetry')
8
8
  const {
9
9
  extractRequestParams,
10
10
  extractTextAndResponseReason,
11
- parseModelId
11
+ parseModelId,
12
+ extractTextAndResponseReasonFromStream
12
13
  } = require('../../../../datadog-plugin-aws-sdk/src/services/bedrockruntime/utils')
13
14
 
14
- const ENABLED_OPERATIONS = new Set(['invokeModel'])
15
+ const ENABLED_OPERATIONS = new Set(['invokeModel', 'invokeModelWithResponseStream'])
15
16
 
16
17
  const requestIdsToTokens = {}
17
18
 
@@ -19,7 +20,8 @@ class BedrockRuntimeLLMObsPlugin extends BaseLLMObsPlugin {
19
20
  constructor () {
20
21
  super(...arguments)
21
22
 
22
- this.addSub('apm:aws:request:complete:bedrockruntime', ({ response }) => {
23
+ this.addSub('apm:aws:request:complete:bedrockruntime', (ctx) => {
24
+ const { response } = ctx
23
25
  const request = response.request
24
26
  const operation = request.operation
25
27
  // avoids instrumenting other non supported runtime operations
@@ -32,23 +34,34 @@ class BedrockRuntimeLLMObsPlugin extends BaseLLMObsPlugin {
32
34
  if (modelName.includes('embed')) {
33
35
  return
34
36
  }
35
- const span = storage('legacy').getStore()?.span
36
- this.setLLMObsTags({ request, span, response, modelProvider, modelName })
37
+ const span = ctx.currentStore?.span
38
+ this.setLLMObsTags({ ctx, request, span, response, modelProvider, modelName })
37
39
  })
38
40
 
39
41
  this.addSub('apm:aws:response:deserialize:bedrockruntime', ({ headers }) => {
40
42
  const requestId = headers['x-amzn-requestid']
41
43
  const inputTokenCount = headers['x-amzn-bedrock-input-token-count']
42
44
  const outputTokenCount = headers['x-amzn-bedrock-output-token-count']
45
+ const cacheReadTokenCount = headers['x-amzn-bedrock-cache-read-input-token-count']
46
+ const cacheWriteTokenCount = headers['x-amzn-bedrock-cache-write-input-token-count']
43
47
 
44
48
  requestIdsToTokens[requestId] = {
45
49
  inputTokensFromHeaders: inputTokenCount && Number.parseInt(inputTokenCount),
46
- outputTokensFromHeaders: outputTokenCount && Number.parseInt(outputTokenCount)
50
+ outputTokensFromHeaders: outputTokenCount && Number.parseInt(outputTokenCount),
51
+ cacheReadTokensFromHeaders: cacheReadTokenCount && Number.parseInt(cacheReadTokenCount),
52
+ cacheWriteTokensFromHeaders: cacheWriteTokenCount && Number.parseInt(cacheWriteTokenCount)
47
53
  }
48
54
  })
55
+
56
+ this.addSub('apm:aws:response:streamed-chunk:bedrockruntime', ({ ctx, chunk }) => {
57
+ if (!ctx.chunks) ctx.chunks = []
58
+
59
+ if (chunk) ctx.chunks.push(chunk)
60
+ })
49
61
  }
50
62
 
51
- setLLMObsTags ({ request, span, response, modelProvider, modelName }) {
63
+ setLLMObsTags ({ ctx, request, span, response, modelProvider, modelName }) {
64
+ const isStream = request?.operation?.toLowerCase().includes('stream')
52
65
  telemetry.incrementLLMObsSpanStartCount({ autoinstrumented: true, integration: 'bedrock' })
53
66
 
54
67
  const parent = llmobsStore.getStore()?.span
@@ -62,7 +75,10 @@ class BedrockRuntimeLLMObsPlugin extends BaseLLMObsPlugin {
62
75
  })
63
76
 
64
77
  const requestParams = extractRequestParams(request.params, modelProvider)
65
- const textAndResponseReason = extractTextAndResponseReason(response, modelProvider, modelName)
78
+ // for streamed responses, we'll use the coerced response object we formed in the stream handler
79
+ const textAndResponseReason = isStream
80
+ ? extractTextAndResponseReasonFromStream(ctx.chunks, modelProvider, modelName)
81
+ : extractTextAndResponseReason(response, modelProvider, modelName)
66
82
 
67
83
  // add metadata tags
68
84
  this._tagger.tagMetadata(span, {
@@ -78,14 +94,16 @@ class BedrockRuntimeLLMObsPlugin extends BaseLLMObsPlugin {
78
94
  )
79
95
 
80
96
  // add token metrics
81
- const { inputTokens, outputTokens, totalTokens } = extractTokens({
97
+ const { inputTokens, outputTokens, totalTokens, cacheReadTokens, cacheWriteTokens } = extractTokens({
82
98
  requestId: response.$metadata.requestId,
83
99
  usage: textAndResponseReason.usage
84
100
  })
85
101
  this._tagger.tagMetrics(span, {
86
102
  inputTokens,
87
103
  outputTokens,
88
- totalTokens
104
+ totalTokens,
105
+ cacheReadTokens,
106
+ cacheWriteTokens
89
107
  })
90
108
  }
91
109
  }
@@ -93,17 +111,26 @@ class BedrockRuntimeLLMObsPlugin extends BaseLLMObsPlugin {
93
111
  function extractTokens ({ requestId, usage }) {
94
112
  const {
95
113
  inputTokensFromHeaders,
96
- outputTokensFromHeaders
114
+ outputTokensFromHeaders,
115
+ cacheReadTokensFromHeaders,
116
+ cacheWriteTokensFromHeaders
97
117
  } = requestIdsToTokens[requestId] || {}
98
118
  delete requestIdsToTokens[requestId]
99
119
 
100
120
  const inputTokens = usage.inputTokens || inputTokensFromHeaders || 0
101
121
  const outputTokens = usage.outputTokens || outputTokensFromHeaders || 0
122
+ const cacheReadTokens = usage.cacheReadTokens || cacheReadTokensFromHeaders || 0
123
+ const cacheWriteTokens = usage.cacheWriteTokens || cacheWriteTokensFromHeaders || 0
124
+
125
+ // adjust for the fact that bedrock input tokens only count non-cached tokens
126
+ const normalizedInputTokens = inputTokens + cacheReadTokens + cacheWriteTokens
102
127
 
103
128
  return {
104
- inputTokens,
129
+ inputTokens: normalizedInputTokens,
105
130
  outputTokens,
106
- totalTokens: inputTokens + outputTokens
131
+ totalTokens: normalizedInputTokens + outputTokens,
132
+ cacheReadTokens,
133
+ cacheWriteTokens
107
134
  }
108
135
  }
109
136
 
@@ -81,8 +81,14 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
81
81
  const outputTokens = tokenUsage.completion_tokens
82
82
  if (outputTokens) metrics.outputTokens = outputTokens
83
83
 
84
- const totalTokens = tokenUsage.total_toksn || (inputTokens + outputTokens)
84
+ const totalTokens = tokenUsage.total_tokens || (inputTokens + outputTokens)
85
85
  if (totalTokens) metrics.totalTokens = totalTokens
86
+
87
+ const promptTokensDetails = tokenUsage.prompt_tokens_details
88
+ if (promptTokensDetails) {
89
+ const cacheReadTokens = promptTokensDetails.cached_tokens
90
+ if (cacheReadTokens) metrics.cacheReadTokens = cacheReadTokens
91
+ }
86
92
  }
87
93
 
88
94
  return metrics
@@ -20,6 +20,8 @@ const {
20
20
  NAME,
21
21
  PROPAGATED_PARENT_ID_KEY,
22
22
  ROOT_PARENT_ID,
23
+ CACHE_READ_INPUT_TOKENS_METRIC_KEY,
24
+ CACHE_WRITE_INPUT_TOKENS_METRIC_KEY,
23
25
  INPUT_TOKENS_METRIC_KEY,
24
26
  OUTPUT_TOKENS_METRIC_KEY,
25
27
  TOTAL_TOKENS_METRIC_KEY,
@@ -144,6 +146,12 @@ class LLMObsTagger {
144
146
  case 'totalTokens':
145
147
  processedKey = TOTAL_TOKENS_METRIC_KEY
146
148
  break
149
+ case 'cacheReadTokens':
150
+ processedKey = CACHE_READ_INPUT_TOKENS_METRIC_KEY
151
+ break
152
+ case 'cacheWriteTokens':
153
+ processedKey = CACHE_WRITE_INPUT_TOKENS_METRIC_KEY
154
+ break
147
155
  }
148
156
 
149
157
  if (typeof value === 'number') {
@@ -85,7 +85,8 @@ function recordLLMObsEnabled (startTime, config, value = 1) {
85
85
  error: 0,
86
86
  agentless: Number(config.llmobs.agentlessEnabled),
87
87
  site: config.site,
88
- auto: Number(autoEnabled)
88
+ auto: Number(autoEnabled),
89
+ ml_app: config.llmobs.mlApp
89
90
  }
90
91
  llmobsMetrics.count('product_enabled', tags).inc(value)
91
92
  llmobsMetrics.distribution('init_time', tags).track(initTimeMs)
@@ -1,11 +1,9 @@
1
1
  'use strict'
2
-
3
- const coalesce = require('koalas')
4
2
  const { inspect } = require('util')
5
3
  const { isTrue } = require('../util')
6
4
  const { traceChannel, debugChannel, infoChannel, warnChannel, errorChannel } = require('./channels')
7
5
  const logWriter = require('./writer')
8
- const { Log } = require('./log')
6
+ const { Log, LogConfig, NoTransmitError } = require('./log')
9
7
  const { memoize } = require('./utils')
10
8
  const { getEnvironmentVariable } = require('../config-helper')
11
9
 
@@ -15,7 +13,14 @@ const config = {
15
13
  logLevel: 'debug'
16
14
  }
17
15
 
16
+ // in most places where we know we want to mute a log we use log.error() directly
17
+ const NO_TRANSMIT = new LogConfig(false)
18
+
18
19
  const log = {
20
+ LogConfig,
21
+ NO_TRANSMIT,
22
+ NoTransmitError,
23
+
19
24
  /**
20
25
  * @returns Read-only version of logging config. To modify config, call `log.use` and `log.toggle`
21
26
  */
@@ -92,18 +97,26 @@ const log = {
92
97
  return this
93
98
  },
94
99
 
100
+ errorWithoutTelemetry (...args) {
101
+ args.push(NO_TRANSMIT)
102
+ if (errorChannel.hasSubscribers) {
103
+ errorChannel.publish(Log.parse(...args))
104
+ }
105
+ return this
106
+ },
107
+
95
108
  deprecate (code, message) {
96
109
  return this._deprecate(code, message)
97
110
  },
98
111
 
99
112
  isEnabled (fleetStableConfigValue, localStableConfigValue) {
100
- return isTrue(coalesce(
101
- fleetStableConfigValue,
102
- getEnvironmentVariable('DD_TRACE_DEBUG'),
103
- getEnvironmentVariable('OTEL_LOG_LEVEL') === 'debug' || undefined,
104
- localStableConfigValue,
113
+ return isTrue(
114
+ fleetStableConfigValue ??
115
+ getEnvironmentVariable('DD_TRACE_DEBUG') ??
116
+ (getEnvironmentVariable('OTEL_LOG_LEVEL') === 'debug' || undefined) ??
117
+ localStableConfigValue ??
105
118
  config.enabled
106
- ))
119
+ )
107
120
  },
108
121
 
109
122
  getLogLevel (
@@ -111,14 +124,12 @@ const log = {
111
124
  fleetStableConfigValue,
112
125
  localStableConfigValue
113
126
  ) {
114
- return coalesce(
115
- optionsValue,
116
- fleetStableConfigValue,
117
- getEnvironmentVariable('DD_TRACE_LOG_LEVEL'),
118
- getEnvironmentVariable('OTEL_LOG_LEVEL'),
119
- localStableConfigValue,
127
+ return optionsValue ??
128
+ fleetStableConfigValue ??
129
+ getEnvironmentVariable('DD_TRACE_LOG_LEVEL') ??
130
+ getEnvironmentVariable('OTEL_LOG_LEVEL') ??
131
+ localStableConfigValue ??
120
132
  config.logLevel
121
- )
122
133
  }
123
134
  }
124
135
 
@@ -2,12 +2,16 @@
2
2
 
3
3
  const { format } = require('util')
4
4
 
5
+ // other times we produce an Error in a central location and log it several other places
6
+ class NoTransmitError extends Error {}
7
+
5
8
  class Log {
6
- constructor (message, args, cause, delegate) {
9
+ constructor (message, args, cause, delegate, sendViaTelemetry = true) {
7
10
  this.message = message
8
11
  this.args = args
9
12
  this.cause = cause
10
13
  this.delegate = delegate
14
+ this.sendViaTelemetry = sendViaTelemetry
11
15
  }
12
16
 
13
17
  get formatted () {
@@ -22,10 +26,18 @@ class Log {
22
26
 
23
27
  static parse (...args) {
24
28
  let message, cause, delegate
29
+ let sendViaTelemetry = true
30
+
31
+ const maybeLogConfig = args.at(-1)
32
+ if (maybeLogConfig instanceof LogConfig) {
33
+ args.pop()
34
+ sendViaTelemetry = maybeLogConfig.transmit
35
+ }
25
36
 
26
- const lastArg = args.at(-1)
27
- if (lastArg && typeof lastArg === 'object' && lastArg.stack) { // lastArg instanceof Error?
37
+ const maybeError = args.at(-1)
38
+ if (maybeError && typeof maybeError === 'object' && maybeError.stack) { // maybeError instanceof Error?
28
39
  cause = args.pop()
40
+ if (cause instanceof NoTransmitError) sendViaTelemetry = false
29
41
  }
30
42
 
31
43
  const firstArg = args.shift()
@@ -43,10 +55,22 @@ class Log {
43
55
  message = String(firstArg)
44
56
  }
45
57
 
46
- return new Log(message, args, cause, delegate)
58
+ return new Log(message, args, cause, delegate, sendViaTelemetry)
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Pass instances of this class to logger methods when fine-grain control is needed
64
+ * @property {boolean} transmit - Whether to send the log via telemetry.
65
+ */
66
+ class LogConfig {
67
+ constructor (transmit = true) {
68
+ this.transmit = transmit
47
69
  }
48
70
  }
49
71
 
50
72
  module.exports = {
51
- Log
73
+ Log,
74
+ LogConfig,
75
+ NoTransmitError,
52
76
  }
@@ -75,12 +75,12 @@ function onError (err) {
75
75
  // TODO: replace it with Error(message, { cause }) when cause has broad support
76
76
  if (formatted) {
77
77
  withNoop(() => {
78
- const l = Error.stackTraceLimit
78
+ const stackTraceLimitBackup = Error.stackTraceLimit
79
79
  Error.stackTraceLimit = 0
80
- const e = new Error(formatted)
81
- Error.stackTraceLimit = l
82
- Error.captureStackTrace(e, stackTraceLimitFunction)
83
- logger.error(e)
80
+ const newError = new Error(formatted)
81
+ Error.stackTraceLimit = stackTraceLimitBackup
82
+ Error.captureStackTrace(newError, stackTraceLimitFunction)
83
+ logger.error(newError)
84
84
  })
85
85
  }
86
86
  if (cause) withNoop(() => logger.error(cause))
@@ -4,18 +4,22 @@ const NoopTracer = require('./tracer')
4
4
  const NoopAppsecSdk = require('../appsec/sdk/noop')
5
5
  const NoopDogStatsDClient = require('./dogstatsd')
6
6
  const NoopLLMObsSDK = require('../llmobs/noop')
7
+ const NoopAIGuardSDK = require('../aiguard/noop')
7
8
 
8
9
  const noop = new NoopTracer()
9
10
  const noopAppsec = new NoopAppsecSdk()
10
11
  const noopDogStatsDClient = new NoopDogStatsDClient()
11
12
  const noopLLMObs = new NoopLLMObsSDK(noop)
13
+ const noopAIGuard = new NoopAIGuardSDK()
12
14
 
15
+ /** @type {import('../../src/index')} Proxy */
13
16
  class NoopProxy {
14
17
  constructor () {
15
18
  this._tracer = noop
16
19
  this.appsec = noopAppsec
17
20
  this.dogstatsd = noopDogStatsDClient
18
21
  this.llmobs = noopLLMObs
22
+ this.aiguard = noopAIGuard
19
23
  this.setBaggageItem = () => {}
20
24
  this.getBaggageItem = () => {}
21
25
  this.getAllBaggageItems = () => {}
@@ -22,6 +22,7 @@ class NoopSpan {
22
22
  setTag (key, value) { return this }
23
23
  addTags (keyValueMap) { return this }
24
24
  addLink (link) { return this }
25
+ addLinks (links) { return this }
25
26
  addSpanPointer (ptrKind, ptrDir, ptrHash) { return this }
26
27
  log () { return this }
27
28
  logEvent () {}
@@ -211,10 +211,21 @@ class Span {
211
211
  return this
212
212
  }
213
213
 
214
- addLink (context, attributes) {
215
- // extract dd context
214
+ addLink (link, attrs) {
215
+ // TODO: Remove this once we remove addLink(context, attrs) in v6.0.0
216
+ if (link instanceof SpanContext) {
217
+ link = { context: link, attributes: attrs ?? {} }
218
+ }
219
+
220
+ const { context, attributes } = link
221
+ // Extract dd context
216
222
  const ddSpanContext = context._ddContext
217
- this._ddSpan.addLink(ddSpanContext, attributes)
223
+ this._ddSpan.addLink({ context: ddSpanContext, attributes })
224
+ return this
225
+ }
226
+
227
+ addLinks (links) {
228
+ links.forEach(link => this.addLink(link))
218
229
  return this
219
230
  }
220
231
 
@@ -86,8 +86,10 @@ class DatadogSpan {
86
86
 
87
87
  this._startTime = fields.startTime || this._getTime()
88
88
 
89
- this._links = []
90
- fields.links && fields.links.forEach(link => this.addLink(link.context, link.attributes))
89
+ this._links = fields.links?.map(link => ({
90
+ context: link.context._ddContext ?? link.context,
91
+ attributes: this._sanitizeAttributes(link.attributes)
92
+ })) ?? []
91
93
 
92
94
  if (DD_TRACE_EXPERIMENTAL_SPAN_COUNTS && finishedRegistry) {
93
95
  runtimeMetrics.increment('runtime.node.spans.unfinished')
@@ -144,7 +146,7 @@ class DatadogSpan {
144
146
  }
145
147
 
146
148
  /**
147
- * @returns {DatadogSpanContext}
149
+ * @returns {import('../priority_sampler').DatadogSpanContext}
148
150
  */
149
151
  context () {
150
152
  return this._spanContext
@@ -196,13 +198,25 @@ class DatadogSpan {
196
198
 
197
199
  logEvent () {}
198
200
 
199
- addLink (context, attributes) {
201
+ addLink (link, attrs) {
202
+ // TODO: Remove this once we remove addLink(context, attrs) in v6.0.0
203
+ if (link instanceof SpanContext) {
204
+ link = { context: link, attributes: attrs ?? {} }
205
+ }
206
+
207
+ const { context, attributes } = link
208
+
200
209
  this._links.push({
201
210
  context: context._ddContext ?? context,
202
211
  attributes: this._sanitizeAttributes(attributes)
203
212
  })
204
213
  }
205
214
 
215
+ addLinks (links) {
216
+ links.forEach(link => this.addLink(link))
217
+ return this
218
+ }
219
+
206
220
  addSpanPointer (ptrKind, ptrDir, ptrHash) {
207
221
  const zeroContext = new SpanContext({
208
222
  traceId: id('0'),
@@ -214,7 +228,7 @@ class DatadogSpan {
214
228
  'ptr.hash': ptrHash,
215
229
  'link.kind': 'span-pointer'
216
230
  }
217
- this.addLink(zeroContext, attributes)
231
+ this.addLink({ context: zeroContext, attributes })
218
232
  }
219
233
 
220
234
  addEvent (name, attributesOrStartTime, startTime) {
@@ -3,6 +3,14 @@
3
3
  const aws = require('./aws.json')
4
4
  const sdks = { aws }
5
5
 
6
+ /**
7
+ * Builds rules per service for a given SDK, appending user-provided rules.
8
+ *
9
+ * @param {Record<string, { request: string[], response: string[], expand: string[] }>} sdk
10
+ * @param {string[]} requestInput
11
+ * @param {string[]} responseInput
12
+ * @returns {Record<string, { request: string[], response: string[], expand: string[] }>}
13
+ */
6
14
  function getSDKRules (sdk, requestInput, responseInput) {
7
15
  const sdkServiceRules = {}
8
16
  for (const [service, serviceRules] of Object.entries(sdk)) {
@@ -17,6 +25,14 @@ function getSDKRules (sdk, requestInput, responseInput) {
17
25
  return sdkServiceRules
18
26
  }
19
27
 
28
+ /**
29
+ * Appends input rules to all supported SDKs and returns a structure mapping SDK
30
+ * names to per-service rules.
31
+ *
32
+ * @param {string[]} [requestInput=[]]
33
+ * @param {string[]} [responseInput=[]]
34
+ * @returns {Record<string, Record<string, { request: string[], response: string[], expand: string[] }>>}
35
+ */
20
36
  function appendRules (requestInput = [], responseInput = []) {
21
37
  const sdkRules = {}
22
38
  for (const [name, sdk] of Object.entries(sdks)) {
@@ -14,8 +14,8 @@ const { tagsFromObject } = require('./tagging')
14
14
  /**
15
15
  * Given an identified value, attempt to parse it as JSON if relevant
16
16
  *
17
- * @param {any} value
18
- * @returns {any} the parsed object if parsing was successful, the input if not
17
+ * @param {unknown} value
18
+ * @returns {unknown} the parsed object if parsing was successful, the input if not
19
19
  */
20
20
  function maybeJSONParseValue (value) {
21
21
  if (typeof value !== 'string' || value[0] !== '{') {
@@ -32,8 +32,8 @@ function maybeJSONParseValue (value) {
32
32
  /**
33
33
  * Apply expansion to all expansion JSONPath queries
34
34
  *
35
- * @param {Object} object
36
- * @param {[String]} expansionRules list of JSONPath queries
35
+ * @param {Record<string, unknown>} object
36
+ * @param {string[]} expansionRules list of JSONPath queries
37
37
  */
38
38
  function expand (object, expansionRules) {
39
39
  for (const rule of expansionRules) {
@@ -46,8 +46,8 @@ function expand (object, expansionRules) {
46
46
  /**
47
47
  * Apply redaction to all redaction JSONPath queries
48
48
  *
49
- * @param {Object} object
50
- * @param {[String]} redactionRules
49
+ * @param {Record<string, unknown>} object
50
+ * @param {string[]} redactionRules
51
51
  */
52
52
  function redact (object, redactionRules) {
53
53
  for (const rule of redactionRules) {
@@ -65,15 +65,10 @@ function redact (object, redactionRules) {
65
65
  * as there are leaf values in the object
66
66
  * This function performs side-effects on a _copy_ of the input object.
67
67
  *
68
- * @param {Object} config sdk configuration for the service
69
- * @param {[String]} config.expand expansion rules for the service
70
- * @param {[String]} config.request redaction rules for the request
71
- * @param {[String]} config.response redaction rules for the response
72
- * @param {Object} object the input object to generate tags from
73
- * @param {Object} opts tag generation options
74
- * @param {String} opts.prefix prefix for all generated tags
75
- * @param {number} opts.maxDepth maximum depth to traverse the object
76
- * @returns
68
+ * @param {{ expand: string[], request: string[], response: string[] }} config sdk configuration for the service
69
+ * @param {Record<string, unknown>} object the input object to generate tags from
70
+ * @param {{ prefix: string, maxDepth: number }} opts tag generation options
71
+ * @returns {Record<string, string|boolean>} Tags map
77
72
  */
78
73
  function computeTags (config, object, opts) {
79
74
  const payload = rfdc(object)
@@ -84,10 +79,26 @@ function computeTags (config, object, opts) {
84
79
  return tagsFromObject(payload, opts)
85
80
  }
86
81
 
82
+ /**
83
+ * Compute request tags with the request prefix.
84
+ *
85
+ * @param {{ expand: string[], request: string[], response: string[] }} config
86
+ * @param {Record<string, unknown>} object
87
+ * @param {{ maxDepth: number }} opts
88
+ * @returns {Record<string, string|boolean>}
89
+ */
87
90
  function tagsFromRequest (config, object, opts) {
88
91
  return computeTags(config, object, { ...opts, prefix: PAYLOAD_TAG_REQUEST_PREFIX })
89
92
  }
90
93
 
94
+ /**
95
+ * Compute response tags with the response prefix.
96
+ *
97
+ * @param {{ expand: string[], request: string[], response: string[] }} config
98
+ * @param {Record<string, unknown>} object
99
+ * @param {{ maxDepth: number }} opts
100
+ * @returns {Record<string, string|boolean>}
101
+ */
91
102
  function tagsFromResponse (config, object, opts) {
92
103
  return computeTags(config, object, { ...opts, prefix: PAYLOAD_TAG_RESPONSE_PREFIX })
93
104
  }
@@ -8,24 +8,33 @@ const redactedKeys = new Set([
8
8
  const truncated = 'truncated'
9
9
  const redacted = 'redacted'
10
10
 
11
+ /**
12
+ * Escapes dots in keys to preserve hierarchy in flattened tag names.
13
+ *
14
+ * @param {string} key
15
+ * @returns {string}
16
+ */
11
17
  function escapeKey (key) {
12
18
  return key.replaceAll('.', String.raw`\.`)
13
19
  }
14
20
 
15
21
  /**
16
- * Compute normalized payload tags from any given object.
17
- *
18
- * @param {object} object
19
- * @param {import('./mask').Mask} mask
20
- * @param {number} maxDepth
21
- * @param {string} prefix
22
- * @returns
23
- */
22
+ * Compute normalized payload tags from any given object.
23
+ *
24
+ * - Limits total tag count to `PAYLOAD_TAGGING_MAX_TAGS - 1` plus the `_dd.payload_tags_incomplete` flag
25
+ * - Truncates values at max depth and for large scalars
26
+ * - Redacts known sensitive keys
27
+ *
28
+ * @param {unknown} object - Input to flatten into tags
29
+ * @param {{ maxDepth: number, prefix: string }} opts - Traversal options
30
+ * @returns {Record<string, string|boolean>} Map of tag names to values
31
+ */
24
32
  function tagsFromObject (object, opts) {
25
33
  const { maxDepth, prefix } = opts
26
34
 
27
35
  let tagCount = 0
28
36
  let abort = false
37
+ /** @type {Record<string, string|boolean>} */
29
38
  const result = {}
30
39
 
31
40
  function tagRec (prefix, object, depth = 0) {
@@ -20,8 +20,10 @@ function findPkg () {
20
20
 
21
21
  const filePath = findUp('package.json', root, directory)
22
22
 
23
+ if (filePath === undefined) return {}
24
+
23
25
  try {
24
- return JSON.parse(fs.readFileSync(filePath, 'utf8'))
26
+ return require(filePath)
25
27
  } catch {
26
28
  return {}
27
29
  }
@@ -6,6 +6,15 @@ const plugins = require('./plugins')
6
6
  const log = require('./log')
7
7
  const { getEnvironmentVariable } = require('../../dd-trace/src/config-helper')
8
8
 
9
+ // Test optimization plugins that should only be enabled when isCiVisibility is true
10
+ const TEST_OPTIMIZATION_PLUGINS = new Set([
11
+ 'jest',
12
+ 'vitest',
13
+ 'cucumber',
14
+ 'mocha',
15
+ 'playwright'
16
+ ])
17
+
9
18
  const loadChannel = channel('dd-trace:instrumentation:load')
10
19
 
11
20
  // instrument everything that needs Plugin System V2 instrumentation
@@ -74,6 +83,13 @@ module.exports = class PluginManager {
74
83
 
75
84
  if (!Plugin) return
76
85
  if (!this._tracerConfig) return // TODO: don't wait for tracer to be initialized
86
+
87
+ // Check if this is a Test Optimization plugin and Test Optimization is not enabled
88
+ if (TEST_OPTIMIZATION_PLUGINS.has(name) && !this._tracerConfig.isCiVisibility) {
89
+ log.debug('Plugin "%s" is not initialized because Test Optimization mode is not enabled.', name)
90
+ return
91
+ }
92
+
77
93
  if (!this._pluginsByName[name]) {
78
94
  this._pluginsByName[name] = new Plugin(this._tracer, this._tracerConfig)
79
95
  }
@@ -148,7 +164,8 @@ module.exports = class PluginManager {
148
164
  middlewareTracingEnabled,
149
165
  traceWebsocketMessagesEnabled,
150
166
  traceWebsocketMessagesInheritSampling,
151
- traceWebsocketMessagesSeparateTraces
167
+ traceWebsocketMessagesSeparateTraces,
168
+ experimental
152
169
  } = this._tracerConfig
153
170
 
154
171
  const sharedConfig = {
@@ -166,7 +183,8 @@ module.exports = class PluginManager {
166
183
  isServiceUserProvided,
167
184
  traceWebsocketMessagesEnabled,
168
185
  traceWebsocketMessagesInheritSampling,
169
- traceWebsocketMessagesSeparateTraces
186
+ traceWebsocketMessagesSeparateTraces,
187
+ experimental
170
188
  }
171
189
 
172
190
  if (logInjection !== undefined) {