dd-trace 5.31.0 → 5.33.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 (85) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/README.md +17 -14
  3. package/index.d.ts +11 -1
  4. package/package.json +6 -5
  5. package/packages/datadog-instrumentations/src/aws-sdk.js +4 -1
  6. package/packages/datadog-instrumentations/src/cucumber.js +31 -14
  7. package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
  8. package/packages/datadog-instrumentations/src/jest.js +105 -56
  9. package/packages/datadog-instrumentations/src/mocha/main.js +9 -4
  10. package/packages/datadog-instrumentations/src/mocha/utils.js +27 -9
  11. package/packages/datadog-instrumentations/src/mocha/worker.js +4 -2
  12. package/packages/datadog-instrumentations/src/node-serialize.js +22 -0
  13. package/packages/datadog-instrumentations/src/openai.js +2 -0
  14. package/packages/datadog-instrumentations/src/playwright.js +8 -3
  15. package/packages/datadog-instrumentations/src/vitest.js +134 -62
  16. package/packages/datadog-instrumentations/src/vm.js +49 -0
  17. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +16 -0
  18. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +63 -0
  19. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +287 -0
  20. package/packages/datadog-plugin-aws-sdk/src/services/index.js +1 -0
  21. package/packages/datadog-plugin-cucumber/src/index.js +31 -31
  22. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -8
  23. package/packages/datadog-plugin-cypress/src/support.js +6 -2
  24. package/packages/datadog-plugin-fetch/src/index.js +3 -3
  25. package/packages/datadog-plugin-http/src/client.js +5 -33
  26. package/packages/datadog-plugin-jest/src/index.js +37 -37
  27. package/packages/datadog-plugin-langchain/src/index.js +12 -80
  28. package/packages/datadog-plugin-langchain/src/tracing.js +89 -0
  29. package/packages/datadog-plugin-mocha/src/index.js +19 -35
  30. package/packages/datadog-plugin-playwright/src/index.js +3 -1
  31. package/packages/datadog-plugin-vitest/src/index.js +33 -35
  32. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  33. package/packages/dd-trace/src/appsec/iast/analyzers/code-injection-analyzer.js +2 -0
  34. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +3 -3
  35. package/packages/dd-trace/src/appsec/iast/analyzers/untrusted-deserialization-analyzer.js +16 -0
  36. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +41 -24
  37. package/packages/dd-trace/src/appsec/iast/iast-context.js +12 -0
  38. package/packages/dd-trace/src/appsec/iast/path-line.js +19 -23
  39. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +9 -8
  40. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -0
  41. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  42. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +75 -24
  43. package/packages/dd-trace/src/appsec/rasp/utils.js +10 -5
  44. package/packages/dd-trace/src/appsec/stack_trace.js +38 -28
  45. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +37 -0
  46. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +65 -28
  47. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +57 -17
  48. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -4
  49. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +18 -3
  50. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +5 -3
  51. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +20 -3
  52. package/packages/dd-trace/src/config.js +43 -3
  53. package/packages/dd-trace/src/crashtracking/crashtracker.js +9 -0
  54. package/packages/dd-trace/src/crashtracking/noop.js +3 -0
  55. package/packages/dd-trace/src/datastreams/fnv.js +1 -1
  56. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -2
  57. package/packages/dd-trace/src/debugger/devtools_client/config.js +1 -0
  58. package/packages/dd-trace/src/debugger/devtools_client/defaults.js +1 -0
  59. package/packages/dd-trace/src/debugger/devtools_client/index.js +30 -13
  60. package/packages/dd-trace/src/debugger/devtools_client/send.js +4 -8
  61. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +35 -1
  62. package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +112 -0
  63. package/packages/dd-trace/src/debugger/devtools_client/status.js +12 -10
  64. package/packages/dd-trace/src/debugger/index.js +2 -13
  65. package/packages/dd-trace/src/llmobs/plugins/base.js +40 -11
  66. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +59 -0
  67. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chain.js +24 -0
  68. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +111 -0
  69. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/embedding.js +42 -0
  70. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +102 -0
  71. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/llm.js +32 -0
  72. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +131 -0
  73. package/packages/dd-trace/src/llmobs/plugins/openai.js +1 -1
  74. package/packages/dd-trace/src/llmobs/tagger.js +11 -3
  75. package/packages/dd-trace/src/llmobs/util.js +7 -1
  76. package/packages/dd-trace/src/llmobs/writers/spans/agentProxy.js +3 -3
  77. package/packages/dd-trace/src/opentelemetry/context_manager.js +43 -3
  78. package/packages/dd-trace/src/plugins/ci_plugin.js +58 -27
  79. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +0 -2
  80. package/packages/dd-trace/src/plugins/util/test.js +44 -12
  81. package/packages/dd-trace/src/priority_sampler.js +4 -1
  82. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +21 -0
  83. package/packages/dd-trace/src/profiling/profiler.js +11 -8
  84. package/packages/dd-trace/src/profiling/profilers/events.js +17 -1
  85. package/packages/dd-trace/src/proxy.js +6 -3
@@ -1,12 +1,12 @@
1
1
  'use strict'
2
2
 
3
3
  const { join } = require('path')
4
- const { Worker } = require('worker_threads')
4
+ const { Worker, threadId: parentThreadId } = require('worker_threads')
5
5
  const { randomUUID } = require('crypto')
6
6
  const log = require('../../log')
7
7
 
8
8
  const probeIdToResolveBreakpointSet = new Map()
9
- const probeIdToResolveBreakpointHit = new Map()
9
+ const probeIdToResolveBreakpointRemove = new Map()
10
10
 
11
11
  class TestVisDynamicInstrumentation {
12
12
  constructor () {
@@ -16,28 +16,34 @@ class TestVisDynamicInstrumentation {
16
16
  })
17
17
  this.breakpointSetChannel = new MessageChannel()
18
18
  this.breakpointHitChannel = new MessageChannel()
19
+ this.breakpointRemoveChannel = new MessageChannel()
20
+ this.onHitBreakpointByProbeId = new Map()
19
21
  }
20
22
 
21
- // Return 3 elements:
22
- // 1. Snapshot ID
23
+ removeProbe (probeId) {
24
+ return new Promise(resolve => {
25
+ this.breakpointRemoveChannel.port2.postMessage(probeId)
26
+
27
+ probeIdToResolveBreakpointRemove.set(probeId, resolve)
28
+ })
29
+ }
30
+
31
+ // Return 2 elements:
32
+ // 1. Probe ID
23
33
  // 2. Promise that's resolved when the breakpoint is set
24
- // 3. Promise that's resolved when the breakpoint is hit
25
- addLineProbe ({ file, line }) {
26
- const snapshotId = randomUUID()
34
+ addLineProbe ({ file, line }, onHitBreakpoint) {
27
35
  const probeId = randomUUID()
28
36
 
29
- this.breakpointSetChannel.port2.postMessage({
30
- snapshotId,
31
- probe: { id: probeId, file, line }
32
- })
37
+ this.breakpointSetChannel.port2.postMessage(
38
+ { id: probeId, file, line }
39
+ )
40
+
41
+ this.onHitBreakpointByProbeId.set(probeId, onHitBreakpoint)
33
42
 
34
43
  return [
35
- snapshotId,
44
+ probeId,
36
45
  new Promise(resolve => {
37
46
  probeIdToResolveBreakpointSet.set(probeId, resolve)
38
- }),
39
- new Promise(resolve => {
40
- probeIdToResolveBreakpointHit.set(probeId, resolve)
41
47
  })
42
48
  ]
43
49
  }
@@ -46,23 +52,42 @@ class TestVisDynamicInstrumentation {
46
52
  return this._readyPromise
47
53
  }
48
54
 
49
- start () {
55
+ start (config) {
50
56
  if (this.worker) return
51
57
 
52
- const { NODE_OPTIONS, ...envWithoutNodeOptions } = process.env
53
-
54
58
  log.debug('Starting Test Visibility - Dynamic Instrumentation client...')
55
59
 
60
+ const rcChannel = new MessageChannel() // mock channel
61
+ const configChannel = new MessageChannel() // mock channel
62
+
56
63
  this.worker = new Worker(
57
64
  join(__dirname, 'worker', 'index.js'),
58
65
  {
59
66
  execArgv: [],
60
- env: envWithoutNodeOptions,
67
+ // Not passing `NODE_OPTIONS` results in issues with yarn, which relies on NODE_OPTIONS
68
+ // for PnP support, hence why we deviate from the DI pattern here.
69
+ // To avoid infinite initialization loops, we're disabling DI and tracing in the worker.
70
+ env: {
71
+ ...process.env,
72
+ DD_TRACE_ENABLED: 0,
73
+ DD_TEST_DYNAMIC_INSTRUMENTATION_ENABLED: 0
74
+ },
61
75
  workerData: {
76
+ config: config.serialize(),
77
+ parentThreadId,
78
+ rcPort: rcChannel.port1,
79
+ configPort: configChannel.port1,
62
80
  breakpointSetChannel: this.breakpointSetChannel.port1,
63
- breakpointHitChannel: this.breakpointHitChannel.port1
81
+ breakpointHitChannel: this.breakpointHitChannel.port1,
82
+ breakpointRemoveChannel: this.breakpointRemoveChannel.port1
64
83
  },
65
- transferList: [this.breakpointSetChannel.port1, this.breakpointHitChannel.port1]
84
+ transferList: [
85
+ rcChannel.port1,
86
+ configChannel.port1,
87
+ this.breakpointSetChannel.port1,
88
+ this.breakpointHitChannel.port1,
89
+ this.breakpointRemoveChannel.port1
90
+ ]
66
91
  }
67
92
  )
68
93
  this.worker.on('online', () => {
@@ -70,10 +95,18 @@ class TestVisDynamicInstrumentation {
70
95
  this._onReady()
71
96
  })
72
97
 
98
+ this.worker.on('error', (err) => {
99
+ log.error('Test Visibility - Dynamic Instrumentation worker error', err)
100
+ })
101
+
102
+ this.worker.on('messageerror', (err) => {
103
+ log.error('Test Visibility - Dynamic Instrumentation worker messageerror', err)
104
+ })
105
+
73
106
  // Allow the parent to exit even if the worker is still running
74
107
  this.worker.unref()
75
108
 
76
- this.breakpointSetChannel.port2.on('message', ({ probeId }) => {
109
+ this.breakpointSetChannel.port2.on('message', (probeId) => {
77
110
  const resolve = probeIdToResolveBreakpointSet.get(probeId)
78
111
  if (resolve) {
79
112
  resolve()
@@ -83,15 +116,19 @@ class TestVisDynamicInstrumentation {
83
116
 
84
117
  this.breakpointHitChannel.port2.on('message', ({ snapshot }) => {
85
118
  const { probe: { id: probeId } } = snapshot
86
- const resolve = probeIdToResolveBreakpointHit.get(probeId)
87
- if (resolve) {
88
- resolve({ snapshot })
89
- probeIdToResolveBreakpointHit.delete(probeId)
119
+ const onHit = this.onHitBreakpointByProbeId.get(probeId)
120
+ if (onHit) {
121
+ onHit({ snapshot })
90
122
  }
91
123
  }).unref()
92
124
 
93
- this.worker.on('error', (err) => log.error('ci-visibility DI worker error', err))
94
- this.worker.on('messageerror', (err) => log.error('ci-visibility DI worker messageerror', err))
125
+ this.breakpointRemoveChannel.port2.on('message', (probeId) => {
126
+ const resolve = probeIdToResolveBreakpointRemove.get(probeId)
127
+ if (resolve) {
128
+ resolve()
129
+ probeIdToResolveBreakpointRemove.delete(probeId)
130
+ }
131
+ }).unref()
95
132
  }
96
133
  }
97
134
 
@@ -1,7 +1,14 @@
1
1
  'use strict'
2
- const sourceMap = require('source-map')
3
2
  const path = require('path')
4
- const { workerData: { breakpointSetChannel, breakpointHitChannel } } = require('worker_threads')
3
+ const {
4
+ workerData: {
5
+ breakpointSetChannel,
6
+ breakpointHitChannel,
7
+ breakpointRemoveChannel
8
+ }
9
+ } = require('worker_threads')
10
+ const { randomUUID } = require('crypto')
11
+ const sourceMap = require('source-map')
5
12
 
6
13
  // TODO: move debugger/devtools_client/session to common place
7
14
  const session = require('../../../debugger/devtools_client/session')
@@ -16,8 +23,8 @@ const log = require('../../../log')
16
23
 
17
24
  let sessionStarted = false
18
25
 
19
- const breakpointIdToSnapshotId = new Map()
20
26
  const breakpointIdToProbe = new Map()
27
+ const probeIdToBreakpointId = new Map()
21
28
 
22
29
  session.on('Debugger.paused', async ({ params: { hitBreakpoints: [hitBreakpoint], callFrames } }) => {
23
30
  const probe = breakpointIdToProbe.get(hitBreakpoint)
@@ -32,13 +39,11 @@ session.on('Debugger.paused', async ({ params: { hitBreakpoints: [hitBreakpoint]
32
39
 
33
40
  await session.post('Debugger.resume')
34
41
 
35
- const snapshotId = breakpointIdToSnapshotId.get(hitBreakpoint)
36
-
37
42
  const snapshot = {
38
- id: snapshotId,
43
+ id: randomUUID(),
39
44
  timestamp: Date.now(),
40
45
  probe: {
41
- id: probe.probeId,
46
+ id: probe.id,
42
47
  version: '0',
43
48
  location: probe.location
44
49
  },
@@ -56,13 +61,32 @@ session.on('Debugger.paused', async ({ params: { hitBreakpoints: [hitBreakpoint]
56
61
  breakpointHitChannel.postMessage({ snapshot })
57
62
  })
58
63
 
59
- // TODO: add option to remove breakpoint
60
- breakpointSetChannel.on('message', async ({ snapshotId, probe: { id: probeId, file, line } }) => {
61
- await addBreakpoint(snapshotId, { probeId, file, line })
62
- breakpointSetChannel.postMessage({ probeId })
64
+ breakpointRemoveChannel.on('message', async (probeId) => {
65
+ await removeBreakpoint(probeId)
66
+ breakpointRemoveChannel.postMessage(probeId)
67
+ })
68
+
69
+ breakpointSetChannel.on('message', async (probe) => {
70
+ await addBreakpoint(probe)
71
+ breakpointSetChannel.postMessage(probe.id)
63
72
  })
64
73
 
65
- async function addBreakpoint (snapshotId, probe) {
74
+ async function removeBreakpoint (probeId) {
75
+ if (!sessionStarted) {
76
+ // We should not get in this state, but abort if we do, so the code doesn't fail unexpected
77
+ throw Error(`Cannot remove probe ${probeId}: Debugger not started`)
78
+ }
79
+
80
+ const breakpointId = probeIdToBreakpointId.get(probeId)
81
+ if (!breakpointId) {
82
+ throw Error(`Unknown probe id: ${probeId}`)
83
+ }
84
+ await session.post('Debugger.removeBreakpoint', { breakpointId })
85
+ probeIdToBreakpointId.delete(probeId)
86
+ breakpointIdToProbe.delete(breakpointId)
87
+ }
88
+
89
+ async function addBreakpoint (probe) {
66
90
  if (!sessionStarted) await start()
67
91
  const { file, line } = probe
68
92
 
@@ -81,7 +105,7 @@ async function addBreakpoint (snapshotId, probe) {
81
105
  try {
82
106
  lineNumber = await processScriptWithInlineSourceMap({ file, line, sourceMapURL })
83
107
  } catch (err) {
84
- log.error(err)
108
+ log.error('Error processing script with inline source map', err)
85
109
  }
86
110
  }
87
111
 
@@ -93,7 +117,7 @@ async function addBreakpoint (snapshotId, probe) {
93
117
  })
94
118
 
95
119
  breakpointIdToProbe.set(breakpointId, probe)
96
- breakpointIdToSnapshotId.set(breakpointId, snapshotId)
120
+ probeIdToBreakpointId.set(probe.id, breakpointId)
97
121
  }
98
122
 
99
123
  function start () {
@@ -113,14 +137,30 @@ async function processScriptWithInlineSourceMap (params) {
113
137
  // Parse the source map
114
138
  const consumer = await new sourceMap.SourceMapConsumer(decodedSourceMap)
115
139
 
116
- // Map to the generated position
117
- const generatedPosition = consumer.generatedPositionFor({
118
- source: path.basename(file), // this needs to be the file, not the filepath
140
+ let generatedPosition
141
+
142
+ // Map to the generated position. We'll attempt with the full file path first, then with the basename.
143
+ // TODO: figure out why sometimes the full path doesn't work
144
+ generatedPosition = consumer.generatedPositionFor({
145
+ source: file,
119
146
  line,
120
147
  column: 0
121
148
  })
149
+ if (generatedPosition.line === null) {
150
+ generatedPosition = consumer.generatedPositionFor({
151
+ source: path.basename(file),
152
+ line,
153
+ column: 0
154
+ })
155
+ }
122
156
 
123
157
  consumer.destroy()
124
158
 
159
+ // If we can't find the line, just return the original line
160
+ if (generatedPosition.line === null) {
161
+ log.error(`Could not find generated position for ${file}:${line}`)
162
+ return line
163
+ }
164
+
125
165
  return generatedPosition.line
126
166
  }
@@ -87,9 +87,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
87
87
 
88
88
  shouldRequestKnownTests () {
89
89
  return !!(
90
- this._config.isEarlyFlakeDetectionEnabled &&
91
90
  this._canUseCiVisProtocol &&
92
- this._libraryConfig?.isEarlyFlakeDetectionEnabled
91
+ this._libraryConfig?.isKnownTestsEnabled
93
92
  )
94
93
  }
95
94
 
@@ -197,7 +196,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
197
196
  earlyFlakeDetectionNumRetries,
198
197
  earlyFlakeDetectionFaultyThreshold,
199
198
  isFlakyTestRetriesEnabled,
200
- isDiEnabled
199
+ isDiEnabled,
200
+ isKnownTestsEnabled
201
201
  } = remoteConfiguration
202
202
  return {
203
203
  isCodeCoverageEnabled,
@@ -209,7 +209,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
209
209
  earlyFlakeDetectionFaultyThreshold,
210
210
  isFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled && this._config.isFlakyTestRetriesEnabled,
211
211
  flakyTestRetriesCount: this._config.flakyTestRetriesCount,
212
- isDiEnabled: isDiEnabled && this._config.isTestDynamicInstrumentationEnabled
212
+ isDiEnabled: isDiEnabled && this._config.isTestDynamicInstrumentationEnabled,
213
+ isKnownTestsEnabled
213
214
  }
214
215
  }
215
216
 
@@ -5,7 +5,8 @@ const {
5
5
  JEST_WORKER_COVERAGE_PAYLOAD_CODE,
6
6
  JEST_WORKER_TRACE_PAYLOAD_CODE,
7
7
  CUCUMBER_WORKER_TRACE_PAYLOAD_CODE,
8
- MOCHA_WORKER_TRACE_PAYLOAD_CODE
8
+ MOCHA_WORKER_TRACE_PAYLOAD_CODE,
9
+ JEST_WORKER_LOGS_PAYLOAD_CODE
9
10
  } = require('../../../plugins/util/test')
10
11
 
11
12
  function getInterprocessTraceCode () {
@@ -29,18 +30,27 @@ function getInterprocessCoverageCode () {
29
30
  return null
30
31
  }
31
32
 
33
+ function getInterprocessLogsCode () {
34
+ if (process.env.JEST_WORKER_ID) {
35
+ return JEST_WORKER_LOGS_PAYLOAD_CODE
36
+ }
37
+ return null
38
+ }
39
+
32
40
  /**
33
41
  * Lightweight exporter whose writers only do simple JSON serialization
34
- * of trace and coverage payloads, which they send to the test framework's main process.
35
- * Currently used by Jest and Cucumber workers.
42
+ * of trace, coverage and logs payloads, which they send to the test framework's main process.
43
+ * Currently used by Jest, Cucumber and Mocha workers.
36
44
  */
37
45
  class TestWorkerCiVisibilityExporter {
38
46
  constructor () {
39
47
  const interprocessTraceCode = getInterprocessTraceCode()
40
48
  const interprocessCoverageCode = getInterprocessCoverageCode()
49
+ const interprocessLogsCode = getInterprocessLogsCode()
41
50
 
42
51
  this._writer = new Writer(interprocessTraceCode)
43
52
  this._coverageWriter = new Writer(interprocessCoverageCode)
53
+ this._logsWriter = new Writer(interprocessLogsCode)
44
54
  }
45
55
 
46
56
  export (payload) {
@@ -51,9 +61,14 @@ class TestWorkerCiVisibilityExporter {
51
61
  this._coverageWriter.append(formattedCoverage)
52
62
  }
53
63
 
64
+ exportDiLogs (testConfiguration, logMessage) {
65
+ this._logsWriter.append({ testConfiguration, logMessage })
66
+ }
67
+
54
68
  flush () {
55
69
  this._writer.flush()
56
70
  this._coverageWriter.flush()
71
+ this._logsWriter.flush()
57
72
  }
58
73
  }
59
74
 
@@ -93,7 +93,8 @@ function getLibraryConfiguration ({
93
93
  require_git: requireGit,
94
94
  early_flake_detection: earlyFlakeDetectionConfig,
95
95
  flaky_test_retries_enabled: isFlakyTestRetriesEnabled,
96
- di_enabled: isDiEnabled
96
+ di_enabled: isDiEnabled,
97
+ known_tests_enabled: isKnownTestsEnabled
97
98
  }
98
99
  }
99
100
  } = JSON.parse(res)
@@ -103,13 +104,14 @@ function getLibraryConfiguration ({
103
104
  isSuitesSkippingEnabled,
104
105
  isItrEnabled,
105
106
  requireGit,
106
- isEarlyFlakeDetectionEnabled: earlyFlakeDetectionConfig?.enabled ?? false,
107
+ isEarlyFlakeDetectionEnabled: isKnownTestsEnabled && (earlyFlakeDetectionConfig?.enabled ?? false),
107
108
  earlyFlakeDetectionNumRetries:
108
109
  earlyFlakeDetectionConfig?.slow_test_retries?.['5s'] || DEFAULT_EARLY_FLAKE_DETECTION_NUM_RETRIES,
109
110
  earlyFlakeDetectionFaultyThreshold:
110
111
  earlyFlakeDetectionConfig?.faulty_session_threshold ?? DEFAULT_EARLY_FLAKE_DETECTION_ERROR_THRESHOLD,
111
112
  isFlakyTestRetriesEnabled,
112
- isDiEnabled: isDiEnabled && isFlakyTestRetriesEnabled
113
+ isDiEnabled: isDiEnabled && isFlakyTestRetriesEnabled,
114
+ isKnownTestsEnabled
113
115
  }
114
116
 
115
117
  log.debug(() => `Remote settings: ${JSON.stringify(settings)}`)
@@ -13,15 +13,16 @@ class TestApiManualPlugin extends CiPlugin {
13
13
 
14
14
  constructor (...args) {
15
15
  super(...args)
16
+ this._isEnvDataCalcualted = false
16
17
  this.sourceRoot = process.cwd()
17
18
 
18
- this.addSub('dd-trace:ci:manual:test:start', ({ testName, testSuite }) => {
19
+ this.unconfiguredAddSub('dd-trace:ci:manual:test:start', ({ testName, testSuite }) => {
19
20
  const store = storage.getStore()
20
21
  const testSuiteRelative = getTestSuitePath(testSuite, this.sourceRoot)
21
22
  const testSpan = this.startTestSpan(testName, testSuiteRelative)
22
23
  this.enter(testSpan, store)
23
24
  })
24
- this.addSub('dd-trace:ci:manual:test:finish', ({ status, error }) => {
25
+ this.unconfiguredAddSub('dd-trace:ci:manual:test:finish', ({ status, error }) => {
25
26
  const store = storage.getStore()
26
27
  const testSpan = store && store.span
27
28
  if (testSpan) {
@@ -33,7 +34,7 @@ class TestApiManualPlugin extends CiPlugin {
33
34
  finishAllTraceSpans(testSpan)
34
35
  }
35
36
  })
36
- this.addSub('dd-trace:ci:manual:test:addTags', (tags) => {
37
+ this.unconfiguredAddSub('dd-trace:ci:manual:test:addTags', (tags) => {
37
38
  const store = storage.getStore()
38
39
  const testSpan = store && store.span
39
40
  if (testSpan) {
@@ -41,6 +42,22 @@ class TestApiManualPlugin extends CiPlugin {
41
42
  }
42
43
  })
43
44
  }
45
+
46
+ // To lazily calculate env data.
47
+ unconfiguredAddSub (channelName, handler) {
48
+ this.addSub(channelName, (...args) => {
49
+ if (!this._isEnvDataCalcualted) {
50
+ this._isEnvDataCalcualted = true
51
+ this.configure(this._config, true)
52
+ }
53
+ return handler(...args)
54
+ })
55
+ }
56
+
57
+ configure (config, shouldGetEnvironmentData) {
58
+ this._config = config
59
+ super.configure(config, shouldGetEnvironmentData)
60
+ }
44
61
  }
45
62
 
46
63
  module.exports = TestApiManualPlugin
@@ -472,7 +472,9 @@ class Config {
472
472
  this._setValue(defaults, 'dogstatsd.hostname', '127.0.0.1')
473
473
  this._setValue(defaults, 'dogstatsd.port', '8125')
474
474
  this._setValue(defaults, 'dsmEnabled', false)
475
- this._setValue(defaults, 'dynamicInstrumentationEnabled', false)
475
+ this._setValue(defaults, 'dynamicInstrumentation.enabled', false)
476
+ this._setValue(defaults, 'dynamicInstrumentation.redactedIdentifiers', [])
477
+ this._setValue(defaults, 'dynamicInstrumentation.redactionExcludedIdentifiers', [])
476
478
  this._setValue(defaults, 'env', undefined)
477
479
  this._setValue(defaults, 'experimental.enableGetRumData', false)
478
480
  this._setValue(defaults, 'experimental.exporter', undefined)
@@ -495,6 +497,7 @@ class Config {
495
497
  this._setValue(defaults, 'iast.redactionValuePattern', null)
496
498
  this._setValue(defaults, 'iast.requestSampling', 30)
497
499
  this._setValue(defaults, 'iast.telemetryVerbosity', 'INFORMATION')
500
+ this._setValue(defaults, 'iast.stackTrace.enabled', true)
498
501
  this._setValue(defaults, 'injectionEnabled', [])
499
502
  this._setValue(defaults, 'isAzureFunction', false)
500
503
  this._setValue(defaults, 'isCiVisibility', false)
@@ -600,6 +603,8 @@ class Config {
600
603
  DD_DOGSTATSD_HOST,
601
604
  DD_DOGSTATSD_PORT,
602
605
  DD_DYNAMIC_INSTRUMENTATION_ENABLED,
606
+ DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS,
607
+ DD_DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS,
603
608
  DD_ENV,
604
609
  DD_EXPERIMENTAL_API_SECURITY_ENABLED,
605
610
  DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED,
@@ -618,6 +623,7 @@ class Config {
618
623
  DD_IAST_REDACTION_VALUE_PATTERN,
619
624
  DD_IAST_REQUEST_SAMPLING,
620
625
  DD_IAST_TELEMETRY_VERBOSITY,
626
+ DD_IAST_STACK_TRACE_ENABLED,
621
627
  DD_INJECTION_ENABLED,
622
628
  DD_INSTRUMENTATION_TELEMETRY_ENABLED,
623
629
  DD_INSTRUMENTATION_CONFIG_ID,
@@ -746,7 +752,13 @@ class Config {
746
752
  this._setString(env, 'dogstatsd.hostname', DD_DOGSTATSD_HOST || DD_DOGSTATSD_HOSTNAME)
747
753
  this._setString(env, 'dogstatsd.port', DD_DOGSTATSD_PORT)
748
754
  this._setBoolean(env, 'dsmEnabled', DD_DATA_STREAMS_ENABLED)
749
- this._setBoolean(env, 'dynamicInstrumentationEnabled', DD_DYNAMIC_INSTRUMENTATION_ENABLED)
755
+ this._setBoolean(env, 'dynamicInstrumentation.enabled', DD_DYNAMIC_INSTRUMENTATION_ENABLED)
756
+ this._setArray(env, 'dynamicInstrumentation.redactedIdentifiers', DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS)
757
+ this._setArray(
758
+ env,
759
+ 'dynamicInstrumentation.redactionExcludedIdentifiers',
760
+ DD_DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS
761
+ )
750
762
  this._setString(env, 'env', DD_ENV || tags.env)
751
763
  this._setBoolean(env, 'traceEnabled', DD_TRACE_ENABLED)
752
764
  this._setBoolean(env, 'experimental.enableGetRumData', DD_TRACE_EXPERIMENTAL_GET_RUM_DATA_ENABLED)
@@ -777,6 +789,7 @@ class Config {
777
789
  }
778
790
  this._envUnprocessed['iast.requestSampling'] = DD_IAST_REQUEST_SAMPLING
779
791
  this._setString(env, 'iast.telemetryVerbosity', DD_IAST_TELEMETRY_VERBOSITY)
792
+ this._setBoolean(env, 'iast.stackTrace.enabled', DD_IAST_STACK_TRACE_ENABLED)
780
793
  this._setArray(env, 'injectionEnabled', DD_INJECTION_ENABLED)
781
794
  this._setBoolean(env, 'isAzureFunction', getIsAzureFunction())
782
795
  this._setBoolean(env, 'isGCPFunction', getIsGCPFunction())
@@ -926,7 +939,17 @@ class Config {
926
939
  this._setString(opts, 'dogstatsd.port', options.dogstatsd.port)
927
940
  }
928
941
  this._setBoolean(opts, 'dsmEnabled', options.dsmEnabled)
929
- this._setBoolean(opts, 'dynamicInstrumentationEnabled', options.experimental?.dynamicInstrumentationEnabled)
942
+ this._setBoolean(opts, 'dynamicInstrumentation.enabled', options.dynamicInstrumentation?.enabled)
943
+ this._setArray(
944
+ opts,
945
+ 'dynamicInstrumentation.redactedIdentifiers',
946
+ options.dynamicInstrumentation?.redactedIdentifiers
947
+ )
948
+ this._setArray(
949
+ opts,
950
+ 'dynamicInstrumentation.redactionExcludedIdentifiers',
951
+ options.dynamicInstrumentation?.redactionExcludedIdentifiers
952
+ )
930
953
  this._setString(opts, 'env', options.env || tags.env)
931
954
  this._setBoolean(opts, 'experimental.enableGetRumData', options.experimental?.enableGetRumData)
932
955
  this._setString(opts, 'experimental.exporter', options.experimental?.exporter)
@@ -956,6 +979,7 @@ class Config {
956
979
  this._optsUnprocessed['iast.requestSampling'] = options.iast?.requestSampling
957
980
  }
958
981
  this._setString(opts, 'iast.telemetryVerbosity', options.iast && options.iast.telemetryVerbosity)
982
+ this._setBoolean(opts, 'iast.stackTrace.enabled', options.iast?.stackTrace?.enabled)
959
983
  this._setBoolean(opts, 'isCiVisibility', options.isCiVisibility)
960
984
  this._setBoolean(opts, 'legacyBaggageEnabled', options.legacyBaggageEnabled)
961
985
  this._setBoolean(opts, 'llmobs.agentlessEnabled', options.llmobs?.agentlessEnabled)
@@ -1312,6 +1336,22 @@ class Config {
1312
1336
  this.sampler.sampleRate = this.sampleRate
1313
1337
  updateConfig(changes, this)
1314
1338
  }
1339
+
1340
+ // TODO: Refactor the Config class so it never produces any config objects that are incompatible with MessageChannel
1341
+ /**
1342
+ * Serializes the config object so it can be passed over a Worker Thread MessageChannel.
1343
+ * @returns {Object} The serialized config object.
1344
+ */
1345
+ serialize () {
1346
+ // URL objects cannot be serialized over the MessageChannel, so we need to convert them to strings first
1347
+ if (this.url instanceof URL) {
1348
+ const config = { ...this }
1349
+ config.url = this.url.toString()
1350
+ return config
1351
+ }
1352
+
1353
+ return this
1354
+ }
1315
1355
  }
1316
1356
 
1317
1357
  function maybeInt (number) {
@@ -40,6 +40,15 @@ class Crashtracker {
40
40
  }
41
41
  }
42
42
 
43
+ withProfilerSerializing (f) {
44
+ binding.beginProfilerSerializing()
45
+ try {
46
+ return f()
47
+ } finally {
48
+ binding.endProfilerSerializing()
49
+ }
50
+ }
51
+
43
52
  // TODO: Send only configured values when defaults are fixed.
44
53
  _getConfig (config) {
45
54
  const { hostname = '127.0.0.1', port = 8126 } = config
@@ -3,6 +3,9 @@
3
3
  class NoopCrashtracker {
4
4
  configure () {}
5
5
  start () {}
6
+ withProfilerSerializing (f) {
7
+ return f()
8
+ }
6
9
  }
7
10
 
8
11
  module.exports = new NoopCrashtracker()
@@ -15,7 +15,7 @@ function fnv64 (data) {
15
15
  data = Buffer.from(data, 'utf-8')
16
16
  }
17
17
  const byteArray = new Uint8Array(data)
18
- return fnv(byteArray, FNV1_64_INIT, FNV_64_PRIME, BigInt(2) ** BigInt(64))
18
+ return fnv(byteArray, FNV1_64_INIT, FNV_64_PRIME, 2n ** 64n)
19
19
  }
20
20
 
21
21
  module.exports = {
@@ -23,10 +23,10 @@ async function addBreakpoint (probe) {
23
23
  delete probe.where
24
24
 
25
25
  // Optimize for fast calculations when probe is hit
26
- const snapshotsPerSecond = probe.sampling.snapshotsPerSecond ?? (probe.captureSnapshot
26
+ const snapshotsPerSecond = probe.sampling?.snapshotsPerSecond ?? (probe.captureSnapshot
27
27
  ? MAX_SNAPSHOTS_PER_SECOND_PER_PROBE
28
28
  : MAX_NON_SNAPSHOTS_PER_SECOND_PER_PROBE)
29
- probe.sampling.nsBetweenSampling = BigInt(1 / snapshotsPerSecond * 1e9)
29
+ probe.nsBetweenSampling = BigInt(1 / snapshotsPerSecond * 1e9)
30
30
  probe.lastCaptureNs = 0n
31
31
 
32
32
  // TODO: Inbetween `await session.post('Debugger.enable')` and here, the scripts are parsed and cached.
@@ -5,6 +5,7 @@ const { format } = require('node:url')
5
5
  const log = require('../../log')
6
6
 
7
7
  const config = module.exports = {
8
+ dynamicInstrumentation: parentConfig.dynamicInstrumentation,
8
9
  runtimeId: parentConfig.tags['runtime-id'],
9
10
  service: parentConfig.service,
10
11
  commitSHA: parentConfig.commitSHA,
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  module.exports = {
4
+ MAX_SNAPSHOTS_PER_SECOND_GLOBALLY: 25,
4
5
  MAX_SNAPSHOTS_PER_SECOND_PER_PROBE: 1,
5
6
  MAX_NON_SNAPSHOTS_PER_SECOND_PER_PROBE: 5_000
6
7
  }