dd-trace 5.99.0 → 5.100.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 (74) hide show
  1. package/LICENSE-3rdparty.csv +0 -1
  2. package/package.json +24 -5
  3. package/packages/datadog-instrumentations/src/cucumber.js +69 -5
  4. package/packages/datadog-instrumentations/src/express.js +3 -2
  5. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  6. package/packages/datadog-instrumentations/src/hono.js +15 -4
  7. package/packages/datadog-instrumentations/src/jest.js +89 -63
  8. package/packages/datadog-instrumentations/src/mocha/main.js +18 -22
  9. package/packages/datadog-instrumentations/src/mocha/utils.js +114 -96
  10. package/packages/datadog-instrumentations/src/mocha/worker.js +2 -2
  11. package/packages/datadog-instrumentations/src/path-to-regexp.js +44 -0
  12. package/packages/datadog-instrumentations/src/playwright.js +108 -18
  13. package/packages/datadog-instrumentations/src/router.js +53 -33
  14. package/packages/datadog-instrumentations/src/vitest.js +76 -30
  15. package/packages/datadog-plugin-aws-sdk/src/base.js +1 -1
  16. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -1
  17. package/packages/datadog-plugin-bullmq/src/consumer.js +3 -2
  18. package/packages/datadog-plugin-bullmq/src/producer.js +25 -11
  19. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +32 -9
  20. package/packages/datadog-plugin-cypress/src/support.js +22 -21
  21. package/packages/datadog-plugin-dd-trace-api/src/index.js +1 -1
  22. package/packages/datadog-plugin-graphql/src/utils.js +2 -2
  23. package/packages/datadog-plugin-grpc/src/client.js +1 -1
  24. package/packages/datadog-plugin-grpc/src/server.js +1 -1
  25. package/packages/datadog-plugin-memcached/src/index.js +1 -1
  26. package/packages/datadog-plugin-mongodb-core/src/index.js +2 -3
  27. package/packages/datadog-plugin-playwright/src/index.js +6 -0
  28. package/packages/datadog-plugin-router/src/index.js +13 -0
  29. package/packages/dd-trace/index.js +4 -3
  30. package/packages/dd-trace/src/aiguard/sdk.js +2 -2
  31. package/packages/dd-trace/src/appsec/blocking.js +18 -6
  32. package/packages/dd-trace/src/appsec/graphql.js +1 -1
  33. package/packages/dd-trace/src/baggage.js +26 -13
  34. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +1 -1
  35. package/packages/dd-trace/src/config/generated-config-types.d.ts +45 -69
  36. package/packages/dd-trace/src/config/index.js +13 -12
  37. package/packages/dd-trace/src/config/normalize-service.js +31 -0
  38. package/packages/dd-trace/src/config/supported-configurations.json +31 -76
  39. package/packages/dd-trace/src/debugger/config.js +1 -1
  40. package/packages/dd-trace/src/dogstatsd.js +5 -8
  41. package/packages/dd-trace/src/encode/0.4.js +1 -1
  42. package/packages/dd-trace/src/encode/tags-processors.js +3 -3
  43. package/packages/dd-trace/src/exporter.js +1 -1
  44. package/packages/dd-trace/src/git_metadata_tagger.js +1 -1
  45. package/packages/dd-trace/src/heap_snapshots.js +4 -4
  46. package/packages/dd-trace/src/llmobs/constants/tags.js +3 -0
  47. package/packages/dd-trace/src/llmobs/sdk.js +21 -1
  48. package/packages/dd-trace/src/llmobs/span_processor.js +14 -1
  49. package/packages/dd-trace/src/llmobs/writers/base.js +7 -1
  50. package/packages/dd-trace/src/llmobs/writers/spans.js +1 -1
  51. package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +2 -2
  52. package/packages/dd-trace/src/opentelemetry/context_manager.js +11 -8
  53. package/packages/dd-trace/src/opentelemetry/logs/index.js +5 -5
  54. package/packages/dd-trace/src/opentelemetry/metrics/index.js +6 -6
  55. package/packages/dd-trace/src/opentelemetry/span-helpers.js +170 -0
  56. package/packages/dd-trace/src/opentelemetry/span.js +14 -42
  57. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +1 -1
  58. package/packages/dd-trace/src/opentelemetry/tracer.js +11 -36
  59. package/packages/dd-trace/src/opentracing/propagation/text_map.js +44 -23
  60. package/packages/dd-trace/src/opentracing/propagation/tracestate.js +42 -12
  61. package/packages/dd-trace/src/opentracing/span.js +4 -3
  62. package/packages/dd-trace/src/plugin_manager.js +6 -6
  63. package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
  64. package/packages/dd-trace/src/plugins/util/ci.js +119 -32
  65. package/packages/dd-trace/src/plugins/util/test.js +295 -29
  66. package/packages/dd-trace/src/profiling/ssi-heuristics.js +2 -2
  67. package/packages/dd-trace/src/propagation-hash/index.js +1 -1
  68. package/packages/dd-trace/src/proxy.js +9 -9
  69. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +1 -1
  70. package/packages/dd-trace/src/span_processor.js +1 -1
  71. package/packages/dd-trace/src/telemetry/telemetry.js +7 -5
  72. package/packages/dd-trace/src/tracer_metadata.js +1 -1
  73. package/vendor/dist/path-to-regexp/LICENSE +0 -21
  74. package/vendor/dist/path-to-regexp/index.js +0 -1
@@ -1,19 +1,49 @@
1
1
  'use strict'
2
2
 
3
- const traceStateRegex = /[ \t]*([^=]+)=([ \t]*[^, \t]+)[ \t]*(,|$)/gim
4
- const traceStateDataRegex = /([^:]+):([^;]+)(;|$)/gim
3
+ // W3C Trace Context §3.3.1.2: max 32 list-members.
4
+ // https://www.w3.org/TR/trace-context/#tracestate-header-field-values
5
+ const MAX_LIST_MEMBERS = 32
6
+ const WHITESPACE = /[ \t]/
5
7
 
6
- function fromString (Type, regex, value) {
7
- if (typeof value !== 'string' || !value.length) {
8
- return new Type()
8
+ /**
9
+ * Parse a separator-delimited string into key/value entries.
10
+ *
11
+ * @param {string} value
12
+ * @param {string} fieldSeparator Between entries.
13
+ * @param {string} pairSeparator Between key and value within an entry.
14
+ * @param {boolean} rejectValueTabs Drop entries whose value contains an internal tab.
15
+ * @returns {[string, string][]} Entries in reverse of wire order.
16
+ */
17
+ function parseEntries (value, fieldSeparator, pairSeparator, rejectValueTabs) {
18
+ const segments = value.split(fieldSeparator)
19
+ segments.length = Math.min(segments.length, MAX_LIST_MEMBERS)
20
+
21
+ // TODO: We should extract dd no matter at what position and move it to the front of the list.
22
+ // Extract up 31 additional entries.
23
+ const entries = []
24
+ for (let index = 0; index < segments.length; index++) {
25
+ const segment = segments[index]
26
+ const splitIndex = segment.indexOf(pairSeparator)
27
+ if (splitIndex === -1) continue
28
+ const key = segment.slice(0, splitIndex).trim()
29
+ if (!key || WHITESPACE.test(key)) continue
30
+ // W3C §3.3.1.3.2: value = 0*255(chr) nblk-chr; chr = %x20 / nblk-chr (no tab).
31
+ // Leading 0x20 is part of value; trailing whitespace is OWS.
32
+ const entryValue = segment.slice(splitIndex + 1).trimEnd()
33
+ if (!entryValue || rejectValueTabs && entryValue.includes('\t')) continue
34
+ entries.push([key, entryValue])
9
35
  }
36
+ // Reverse so the Map's insertion order is reverse of wire order. `toString`
37
+ // prepends as it iterates, which yields the original wire order back.
38
+ entries.reverse()
39
+ return entries
40
+ }
10
41
 
11
- const values = []
12
- for (const row of value.matchAll(regex)) {
13
- values.unshift(row.slice(1, 3))
42
+ function fromString (Type, value, fieldSeparator, pairSeparator, rejectValueTabs) {
43
+ if (typeof value !== 'string' || !value.length) {
44
+ return new Type()
14
45
  }
15
-
16
- return new Type(values)
46
+ return new Type(parseEntries(value, fieldSeparator, pairSeparator, rejectValueTabs))
17
47
  }
18
48
 
19
49
  function toString (map, pairSeparator, fieldSeparator) {
@@ -52,7 +82,7 @@ class TraceStateData extends Map {
52
82
  }
53
83
 
54
84
  static fromString (value) {
55
- return fromString(TraceStateData, traceStateDataRegex, value)
85
+ return fromString(TraceStateData, value, ';', ':', false)
56
86
  }
57
87
 
58
88
  toString () {
@@ -92,7 +122,7 @@ class TraceState extends Map {
92
122
  }
93
123
 
94
124
  static fromString (value) {
95
- return fromString(TraceState, traceStateRegex, value)
125
+ return fromString(TraceState, value, ',', '=', true)
96
126
  }
97
127
 
98
128
  toString () {
@@ -112,7 +112,7 @@ class DatadogSpan {
112
112
  // even `Span` itself in this case.
113
113
  //
114
114
  // TODO: Refactor Tracer/Span + tests to avoid having to do nullish checks.
115
- if (tracer?._config?.spanLeakDebug > 0) {
115
+ if (tracer?._config?.DD_TRACE_SPAN_LEAK_DEBUG > 0) {
116
116
  require('../spanleak').addSpan(this, operationName)
117
117
  }
118
118
 
@@ -336,7 +336,8 @@ class DatadogSpan {
336
336
  let startTime
337
337
 
338
338
  let baggage = {}
339
- if (parent && parent._isRemote && this._parentTracer?._config?.tracePropagationBehaviorExtract !== 'continue') {
339
+ const propagationBehavior = this._parentTracer?._config?.DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT
340
+ if (parent && parent._isRemote && propagationBehavior !== 'continue') {
340
341
  baggage = parent._baggageItems
341
342
  parent = null
342
343
  }
@@ -375,7 +376,7 @@ class DatadogSpan {
375
376
  .padEnd(16, '0')
376
377
  }
377
378
 
378
- if (this._parentTracer?._config?.tracePropagationBehaviorExtract === 'restart') {
379
+ if (propagationBehavior === 'restart') {
379
380
  spanContext._baggageItems = baggage
380
381
  }
381
382
  }
@@ -163,9 +163,9 @@ module.exports = class PluginManager {
163
163
  dsmEnabled,
164
164
  clientIpEnabled,
165
165
  clientIpHeader,
166
- memcachedCommandEnabled,
167
- ciVisibilityTestSessionName,
168
- ciVisAgentlessLogSubmissionEnabled,
166
+ DD_TRACE_MEMCACHED_COMMAND_ENABLED,
167
+ DD_TEST_SESSION_NAME,
168
+ DD_AGENTLESS_LOG_SUBMISSION_ENABLED,
169
169
  isTestDynamicInstrumentationEnabled,
170
170
  isServiceUserProvided,
171
171
  middlewareTracingEnabled,
@@ -180,13 +180,13 @@ module.exports = class PluginManager {
180
180
  codeOriginForSpans,
181
181
  dbmPropagationMode,
182
182
  dsmEnabled,
183
- memcachedCommandEnabled,
183
+ DD_TRACE_MEMCACHED_COMMAND_ENABLED,
184
184
  site,
185
185
  url,
186
186
  headers: headerTags || [],
187
187
  clientIpHeader,
188
- ciVisibilityTestSessionName,
189
- ciVisAgentlessLogSubmissionEnabled,
188
+ DD_TEST_SESSION_NAME,
189
+ DD_AGENTLESS_LOG_SUBMISSION_ENABLED,
190
190
  isTestDynamicInstrumentationEnabled,
191
191
  isServiceUserProvided,
192
192
  traceWebsocketMessagesEnabled,
@@ -51,7 +51,7 @@ module.exports = class LogPlugin extends Plugin {
51
51
  configure (config) {
52
52
  return super.configure({
53
53
  ...config,
54
- enabled: config.enabled && (config.logInjection || config.ciVisAgentlessLogSubmissionEnabled),
54
+ enabled: config.enabled && (config.logInjection || config.DD_AGENTLESS_LOG_SUBMISSION_ENABLED),
55
55
  })
56
56
  }
57
57
  }
@@ -103,42 +103,129 @@ function getGitHubEventPayload () {
103
103
  return JSON.parse(readFileSync(path, 'utf8'))
104
104
  }
105
105
 
106
- function getJobIDFromDiagFile (runnerTemp) {
107
- if (!runnerTemp || !existsSync(runnerTemp)) { return null }
106
+ const uniq = (items) => [...new Set(items)]
107
+
108
+ /**
109
+ * GitHub runner diagnostic logs live under the runner installation directory in `_diag`.
110
+ * On many runners, we can derive the installation directory from RUNNER_TEMP:
111
+ * <runnerRoot>/_work/_temp -> <runnerRoot>/_diag
112
+ *
113
+ * This is much more robust than relying on hardcoded paths, especially on self-hosted runners
114
+ * and GHES environments where the runner may be installed under arbitrary directories/users.
115
+ */
116
+ function getGithubDiagnosticDirsFromEnv (runnerTemp) {
117
+ const dirs = []
118
+
119
+ if (runnerTemp) {
120
+ // RUNNER_TEMP is typically: <runnerRoot>/_work/_temp
121
+ const runnerRoot = path.resolve(runnerTemp, '..', '..').replaceAll(path.sep, '/')
122
+ // Bounded-depth patterns cover every runner layout we've observed
123
+ // (including cached/<version>/_diag) without assuming a `cached` wrapper
124
+ // and without walking the whole tree.
125
+ dirs.push(
126
+ path.posix.join(runnerRoot, 'actions-runner', '_diag'),
127
+ `${runnerRoot}/actions-runner/*/_diag`,
128
+ `${runnerRoot}/actions-runner/*/*/_diag`,
129
+ path.posix.join(runnerRoot, '_diag'),
130
+ `${runnerRoot}/*/_diag`,
131
+ `${runnerRoot}/*/*/_diag`
132
+ )
133
+ }
108
134
 
109
- // RUNNER_TEMP usually looks like:
110
- // Linux/mac hosted: /home/runner/work/_temp
111
- // Windows hosted: C:\actions-runner\_work\_temp
112
- // Self-hosted (unix): /opt/actions-runner/_work/_temp
135
+ return uniq(dirs.filter(Boolean))
136
+ }
113
137
 
114
- const workDir = path.dirname(runnerTemp) // .../work or .../_work
115
- const runnerRoot = path.dirname(workDir) // /home/runner/ (runner root)
138
+ function hasMagicChars (str) {
139
+ return str.includes('*') || str.includes('?')
140
+ }
116
141
 
117
- const dirs = [
118
- path.join(runnerRoot, 'cached', '_diag'),
119
- path.join(runnerRoot, '_diag'),
120
- path.join(runnerRoot, 'actions-runner', 'cached', '_diag'),
121
- path.join(runnerRoot, 'actions-runner', '_diag'),
122
- ]
142
+ // Expands a glob pattern with only `*`/`?` at path-segment boundaries (no `**`)
143
+ // into matching concrete paths using readdirSync — no external dependency needed.
144
+ function expandGlobPattern (pattern) {
145
+ const parts = pattern.split(/[/\\]/)
146
+ const wildcardIdx = parts.findIndex(p => hasMagicChars(p))
147
+ if (wildcardIdx === -1) return [pattern]
123
148
 
124
- const isWin = process.platform === 'win32'
149
+ const prefix = parts.slice(0, wildcardIdx).join('/')
150
+ const results = []
125
151
 
126
- // Hardcoded fallbacks
127
- if (isWin) {
128
- dirs.push(
129
- 'C:/actions-runner/cached/_diag',
130
- 'C:/actions-runner/_diag',
131
- )
132
- } else {
133
- dirs.push(
134
- '/home/runner/actions-runner/cached/_diag',
135
- '/home/runner/actions-runner/_diag',
136
- '/opt/actions-runner/_diag',
137
- )
152
+ function walk (dir, segIdx) {
153
+ if (segIdx === parts.length) {
154
+ results.push(dir)
155
+ return
156
+ }
157
+ const seg = parts[segIdx]
158
+ if (!hasMagicChars(seg)) {
159
+ walk(`${dir}/${seg}`, segIdx + 1)
160
+ return
161
+ }
162
+ try {
163
+ const re = new RegExp(
164
+ '^' + seg.replaceAll(/[.+^${}()|[\]\\]/g, String.raw`\$&`).replaceAll('*', String.raw`[^/\\]*`).replaceAll('?', String.raw`[^/\\]`) + '$'
165
+ )
166
+ for (const entry of readdirSync(dir)) {
167
+ if (re.test(entry)) {
168
+ walk(`${dir}/${entry}`, segIdx + 1)
169
+ }
170
+ }
171
+ } catch {
172
+ // directory doesn't exist or isn't accessible
173
+ }
138
174
  }
139
175
 
140
- // Remove duplicates
141
- const possibleDiagsPaths = [...new Set(dirs)]
176
+ walk(prefix, wildcardIdx)
177
+ return results
178
+ }
179
+
180
+ /**
181
+ * Expands a mixed list of literal directories and glob patterns into concrete
182
+ * directories. Literals pass through unchanged (existence is checked later).
183
+ */
184
+ function expandDiagnosticDirCandidates (candidates) {
185
+ const expanded = []
186
+ for (const candidate of candidates) {
187
+ if (hasMagicChars(candidate)) {
188
+ expanded.push(...expandGlobPattern(candidate))
189
+ } else {
190
+ expanded.push(candidate)
191
+ }
192
+ }
193
+
194
+ return uniq(expanded)
195
+ }
196
+
197
+ const githubWellKnownDiagnosticDirsUnix = [
198
+ '/home/runner/actions-runner/_diag',
199
+ '/opt/actions-runner/_diag',
200
+ ]
201
+ const githubWellKnownDiagnosticDirsWin = [
202
+ 'C:/actions-runner/_diag',
203
+ ]
204
+
205
+ // Glob patterns covering layouts that namespace `_diag` under one or two
206
+ // intermediate directories. This includes both observed SaaS layouts
207
+ // (<runnerRoot>/cached/_diag pre-2.334.0, <runnerRoot>/cached/<version>/_diag
208
+ // since v2.334.0) and hypothetical future layouts that follow the same shape
209
+ // without a `cached` wrapper (e.g. <runnerRoot>/<version>/_diag). Depth is
210
+ // bounded on purpose: `*` matches a single segment, so no filesystem walk.
211
+ const githubWellKnownDiagnosticDirPatternsUnix = [
212
+ '/home/runner/actions-runner/*/_diag',
213
+ '/home/runner/actions-runner/*/*/_diag',
214
+ ]
215
+ const githubWellKnownDiagnosticDirPatternsWin = ['C:/actions-runner/*/_diag', 'C:/actions-runner/*/*/_diag']
216
+
217
+ const githubJobIDRegex = /"job":\s*{[\s\S]*?"v"\s*:\s*(\d+)(?:\.0)?/
218
+
219
+ function getJobIDFromDiagFile () {
220
+ const runnerTemp = getValueFromEnvSources('RUNNER_TEMP')
221
+ if (!runnerTemp || !existsSync(runnerTemp)) { return null }
222
+
223
+ const isWin = process.platform === 'win32'
224
+ const patterns = isWin ? githubWellKnownDiagnosticDirPatternsWin : githubWellKnownDiagnosticDirPatternsUnix
225
+ const literals = isWin ? githubWellKnownDiagnosticDirsWin : githubWellKnownDiagnosticDirsUnix
226
+ const possibleDiagsPaths = expandDiagnosticDirCandidates([
227
+ ...getGithubDiagnosticDirsFromEnv(runnerTemp), ...patterns, ...literals,
228
+ ])
142
229
 
143
230
  // This will hold the names of the worker log files that (potentially) contain the Job ID
144
231
  let workerLogFiles = []
@@ -177,7 +264,7 @@ function getJobIDFromDiagFile (runnerTemp) {
177
264
  const filePath = path.posix.join(chosenDiagPath, logFile)
178
265
  const content = readFileSync(filePath, 'utf8')
179
266
 
180
- const match = content.match(/"job":\s*{[\s\S]*?"v"\s*:\s*(\d+)(?:\.0)?/)
267
+ const match = content.match(githubJobIDRegex)
181
268
 
182
269
  // match[1] is the captured group with the display name
183
270
  if (match && match[1]) { return match[1] }
@@ -188,6 +275,7 @@ function getJobIDFromDiagFile (runnerTemp) {
188
275
 
189
276
  module.exports = {
190
277
  normalizeRef,
278
+ expandGlobPattern,
191
279
  getJobIDFromDiagFile,
192
280
  getCIMetadata () {
193
281
  const env = getEnvironmentVariables()
@@ -366,7 +454,6 @@ module.exports = {
366
454
  GITHUB_RUN_ATTEMPT,
367
455
  GITHUB_JOB,
368
456
  GITHUB_BASE_REF,
369
- RUNNER_TEMP,
370
457
  JOB_CHECK_RUN_ID,
371
458
  } = env
372
459
 
@@ -378,7 +465,7 @@ module.exports = {
378
465
  }
379
466
 
380
467
  // Build the job url extracting the job ID. If extraction fails, job url is constructed as a generalized url
381
- const GITHUB_JOB_ID = JOB_CHECK_RUN_ID ?? getJobIDFromDiagFile(RUNNER_TEMP)
468
+ const GITHUB_JOB_ID = JOB_CHECK_RUN_ID ?? getJobIDFromDiagFile()
382
469
  const jobUrl =
383
470
  GITHUB_JOB_ID === null
384
471
  ? `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/${GITHUB_SHA}/checks`