dd-trace 5.46.0 → 5.48.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 (54) hide show
  1. package/LICENSE-3rdparty.csv +1 -2
  2. package/index.d.ts +39 -0
  3. package/package.json +8 -9
  4. package/packages/datadog-instrumentations/orchestrion.yml +52 -0
  5. package/packages/datadog-instrumentations/src/cucumber.js +2 -1
  6. package/packages/datadog-instrumentations/src/jest.js +11 -2
  7. package/packages/datadog-instrumentations/src/langchain.js +49 -53
  8. package/packages/datadog-instrumentations/src/mocha/main.js +1 -1
  9. package/packages/datadog-instrumentations/src/mocha/utils.js +11 -3
  10. package/packages/datadog-instrumentations/src/orchestrion-config/index.js +5 -0
  11. package/packages/datadog-instrumentations/src/playwright.js +14 -2
  12. package/packages/datadog-instrumentations/src/vitest.js +11 -3
  13. package/packages/datadog-plugin-cucumber/src/index.js +11 -4
  14. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +17 -5
  15. package/packages/datadog-plugin-jest/src/index.js +11 -4
  16. package/packages/datadog-plugin-langchain/src/index.js +18 -12
  17. package/packages/datadog-plugin-langchain/src/tracing.js +66 -6
  18. package/packages/datadog-plugin-mocha/src/index.js +17 -5
  19. package/packages/datadog-plugin-mongodb-core/src/index.js +5 -1
  20. package/packages/datadog-plugin-playwright/src/index.js +10 -3
  21. package/packages/datadog-plugin-vitest/src/index.js +13 -8
  22. package/packages/datadog-shimmer/src/shimmer.js +3 -42
  23. package/packages/dd-trace/src/appsec/iast/taint-tracking/filter.js +3 -3
  24. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +0 -3
  25. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +24 -11
  26. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-telemetry.js +3 -32
  27. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +98 -56
  28. package/packages/dd-trace/src/appsec/sdk/index.js +23 -1
  29. package/packages/dd-trace/src/appsec/sdk/noop.js +10 -0
  30. package/packages/dd-trace/src/appsec/sdk/set_user.js +2 -1
  31. package/packages/dd-trace/src/appsec/sdk/track_event.js +129 -8
  32. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +0 -1
  33. package/packages/dd-trace/src/appsec/telemetry/index.js +2 -2
  34. package/packages/dd-trace/src/appsec/telemetry/user.js +2 -2
  35. package/packages/dd-trace/src/config.js +9 -0
  36. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +14 -1
  37. package/packages/dd-trace/src/debugger/devtools_client/condition.js +36 -2
  38. package/packages/dd-trace/src/debugger/devtools_client/index.js +75 -33
  39. package/packages/dd-trace/src/debugger/devtools_client/send.js +4 -1
  40. package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
  41. package/packages/dd-trace/src/exporters/common/docker.js +37 -7
  42. package/packages/dd-trace/src/exporters/common/request.js +1 -4
  43. package/packages/dd-trace/src/llmobs/plugins/base.js +2 -2
  44. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +62 -3
  45. package/packages/dd-trace/src/llmobs/plugins/openai.js +1 -0
  46. package/packages/dd-trace/src/llmobs/plugins/vertexai.js +2 -1
  47. package/packages/dd-trace/src/log/index.js +2 -0
  48. package/packages/dd-trace/src/log/writer.js +19 -2
  49. package/packages/dd-trace/src/opentracing/propagation/text_map.js +17 -3
  50. package/packages/dd-trace/src/opentracing/span.js +10 -0
  51. package/packages/dd-trace/src/plugins/util/test.js +7 -0
  52. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -5
  53. package/packages/dd-trace/src/profiling/profilers/wall.js +3 -1
  54. package/packages/dd-trace/src/proxy.js +5 -1
@@ -1,6 +1,10 @@
1
1
  'use strict'
2
2
 
3
- module.exports = compile
3
+ module.exports = {
4
+ compile,
5
+ compileSegments,
6
+ templateRequiresEvaluation
7
+ }
4
8
 
5
9
  const identifierRegex = /^[@a-zA-Z_$][\w$]*$/
6
10
 
@@ -35,7 +39,37 @@ const reservedWords = new Set([
35
39
 
36
40
  const PRIMITIVE_TYPES = new Set(['string', 'number', 'bigint', 'boolean', 'undefined', 'symbol', 'null'])
37
41
 
38
- // TODO: Consider storing some of these functions on `process` so they can be reused across probes
42
+ function templateRequiresEvaluation (segments) {
43
+ if (segments === undefined) return false // There should always be segments, but just in case
44
+ for (const { str } of segments) {
45
+ if (str === undefined) return true
46
+ }
47
+ return false
48
+ }
49
+
50
+ function compileSegments (segments) {
51
+ let result = '['
52
+ for (let i = 0; i < segments.length; i++) {
53
+ const { str, dsl, json } = segments[i]
54
+ result += str !== undefined
55
+ ? JSON.stringify(str)
56
+ : `(() => {
57
+ try {
58
+ const result = ${compile(json)}
59
+ return typeof result === 'string' ? result : $dd_inspect(result, $dd_segmentInspectOptions)
60
+ } catch (e) {
61
+ return { expr: ${JSON.stringify(dsl)}, message: \`\${e.name}: \${e.message}\` }
62
+ }
63
+ })()`
64
+ if (i !== segments.length - 1) {
65
+ result += ','
66
+ }
67
+ }
68
+ return `${result}]`
69
+ }
70
+
71
+ // TODO: Consider storing some of these functions that doesn't require closure access to the current scope on `process`
72
+ // so they can be reused across probes
39
73
  function compile (node) {
40
74
  if (node === null || typeof node === 'number' || typeof node === 'boolean') {
41
75
  return node
@@ -16,10 +16,20 @@ const { NODE_MAJOR } = require('../../../../../version')
16
16
  require('./remote_config')
17
17
 
18
18
  // Expression to run on a call frame of the paused thread to get its active trace and span id.
19
- const expression = `
20
- const context = global.require('dd-trace').scope().active()?.context();
21
- ({ trace_id: context?.toTraceId(), span_id: context?.toSpanId() })
19
+ const templateExpressionSetupCode = `
20
+ const $dd_inspect = global.require('node:util').inspect;
21
+ const $dd_segmentInspectOptions = {
22
+ depth: 0,
23
+ customInspect: false,
24
+ maxArrayLength: 3,
25
+ maxStringLength: 8 * 1024,
26
+ breakLength: Infinity
27
+ };
22
28
  `
29
+ const getDDTagsExpression = `(() => {
30
+ const context = global.require('dd-trace').scope().active()?.context();
31
+ return { trace_id: context?.toTraceId(), span_id: context?.toSpanId() }
32
+ })()`
23
33
 
24
34
  // There doesn't seem to be an official standard for the content of these fields, so we're just populating them with
25
35
  // something that should be useful to a Node.js developer.
@@ -45,6 +55,7 @@ session.on('Debugger.paused', async ({ params }) => {
45
55
  let sampled = false
46
56
  let numberOfProbesWithSnapshots = 0
47
57
  const probes = []
58
+ let templateExpressions = ''
48
59
 
49
60
  // V8 doesn't allow setting more than one breakpoint at a specific location, however, it's possible to set two
50
61
  // breakpoints just next to each other that will "snap" to the same logical location, which in turn will be hit at the
@@ -79,15 +90,6 @@ session.on('Debugger.paused', async ({ params }) => {
79
90
  continue
80
91
  }
81
92
 
82
- if (shouldVerifyConditions && probe.condition !== undefined) {
83
- const { result } = await session.post('Debugger.evaluateOnCallFrame', {
84
- callFrameId: params.callFrames[0].callFrameId,
85
- expression: probe.condition,
86
- returnByValue: true
87
- })
88
- if (result.value !== true) continue
89
- }
90
-
91
93
  if (probe.captureSnapshot === true) {
92
94
  // This algorithm to calculate number of sampled snapshots within the last second is not perfect, as it's not a
93
95
  // sliding window. But it's quick and easy :)
@@ -107,9 +109,24 @@ session.on('Debugger.paused', async ({ params }) => {
107
109
  maxLength = highestOrUndefined(probe.capture.maxLength, maxLength)
108
110
  }
109
111
 
112
+ if (shouldVerifyConditions && probe.condition !== undefined) {
113
+ // TODO: Bundle all conditions and evaluate them in a single call
114
+ // TODO: Handle errors
115
+ const { result } = await session.post('Debugger.evaluateOnCallFrame', {
116
+ callFrameId: params.callFrames[0].callFrameId,
117
+ expression: probe.condition,
118
+ returnByValue: true
119
+ })
120
+ if (result.value !== true) continue
121
+ }
122
+
110
123
  sampled = true
111
124
  probe.lastCaptureNs = start
112
125
 
126
+ if (probe.templateRequiresEvaluation) {
127
+ templateExpressions += `,${probe.template}`
128
+ }
129
+
113
130
  probes.push(probe)
114
131
  }
115
132
  }
@@ -119,7 +136,22 @@ session.on('Debugger.paused', async ({ params }) => {
119
136
  }
120
137
 
121
138
  const timestamp = Date.now()
122
- const dd = await getDD(params.callFrames[0].callFrameId)
139
+
140
+ let evalResults = null
141
+ const { result } = await session.post('Debugger.evaluateOnCallFrame', {
142
+ callFrameId: params.callFrames[0].callFrameId,
143
+ expression: templateExpressions.length === 0
144
+ ? `[${getDDTagsExpression}]`
145
+ : `${templateExpressionSetupCode}[${getDDTagsExpression}${templateExpressions}]`,
146
+ returnByValue: true,
147
+ includeCommandLineAPI: true
148
+ })
149
+ if (result?.subtype === 'error') {
150
+ log.error('[debugger:devtools_client] Error evaluating code on call frame: %s', result?.description)
151
+ evalResults = []
152
+ } else {
153
+ evalResults = result?.value ?? []
154
+ }
123
155
 
124
156
  let processLocalState
125
157
  if (numberOfProbesWithSnapshots !== 0) {
@@ -155,6 +187,8 @@ session.on('Debugger.paused', async ({ params }) => {
155
187
  }
156
188
 
157
189
  const stack = getStackFromCallFrames(params.callFrames)
190
+ const dd = processDD(evalResults[0]) // the first result is the dd tags, the rest are the probe template results
191
+ let messageIndex = 1
158
192
 
159
193
  // TODO: Send multiple probes in one HTTP request as an array (DEBUG-2848)
160
194
  for (const probe of probes) {
@@ -179,9 +213,33 @@ session.on('Debugger.paused', async ({ params }) => {
179
213
  }
180
214
  }
181
215
 
216
+ let message = ''
217
+ if (probe.templateRequiresEvaluation) {
218
+ const results = evalResults[messageIndex++]
219
+ if (results === undefined) {
220
+ log.error('[debugger:devtools_client] No evaluation results for probe %s', probe.id)
221
+ } else {
222
+ for (const result of results) {
223
+ if (typeof result === 'string') {
224
+ message += result
225
+ } else {
226
+ // If `result` isn't a string, it's an evaluation error object
227
+ if (snapshot.evaluationErrors === undefined) {
228
+ snapshot.evaluationErrors = [result]
229
+ } else {
230
+ snapshot.evaluationErrors.push(result)
231
+ }
232
+ message += `{${result.message}}`
233
+ }
234
+ }
235
+ }
236
+ } else {
237
+ message = probe.template
238
+ }
239
+
182
240
  ackEmitting(probe)
183
- // TODO: Process template (DEBUG-2628)
184
- send(probe.template, logger, dd, snapshot)
241
+
242
+ send(message, logger, dd, snapshot)
185
243
  }
186
244
  })
187
245
 
@@ -189,22 +247,6 @@ function highestOrUndefined (num, max) {
189
247
  return num === undefined ? max : Math.max(num, max ?? 0)
190
248
  }
191
249
 
192
- async function getDD (callFrameId) {
193
- // TODO: Consider if an `objectGroup` should be used, so it can be explicitly released using
194
- // `Runtime.releaseObjectGroup`
195
- const { result } = await session.post('Debugger.evaluateOnCallFrame', {
196
- callFrameId,
197
- expression,
198
- returnByValue: true,
199
- includeCommandLineAPI: true
200
- })
201
-
202
- if (result?.value?.trace_id === undefined) {
203
- if (result?.subtype === 'error') {
204
- log.error('[debugger:devtools_client] Error getting trace/span id:', result.description)
205
- }
206
- return
207
- }
208
-
209
- return result.value
250
+ function processDD (result) {
251
+ return result?.trace_id === undefined ? undefined : result
210
252
  }
@@ -12,6 +12,7 @@ const { version } = require('../../../../../package.json')
12
12
 
13
13
  module.exports = send
14
14
 
15
+ const MAX_MESSAGE_LENGTH = 8 * 1024 // 8KB
15
16
  const MAX_LOG_PAYLOAD_SIZE = 1024 * 1024 // 1MB
16
17
 
17
18
  const ddsource = 'dd_debugger'
@@ -36,7 +37,9 @@ function send (message, logger, dd, snapshot) {
36
37
  ddsource,
37
38
  hostname,
38
39
  service,
39
- message,
40
+ message: message?.length > MAX_MESSAGE_LENGTH
41
+ ? message.slice(0, MAX_MESSAGE_LENGTH) + '…'
42
+ : message,
40
43
  logger,
41
44
  dd,
42
45
  debugger: { snapshot }
@@ -27,7 +27,7 @@ const self = module.exports = {
27
27
 
28
28
  async getGeneratedPosition (url, source, line, sourceMapURL) {
29
29
  const dir = dirname(new URL(url).pathname)
30
- return await SourceMapConsumer.with(
30
+ return SourceMapConsumer.with(
31
31
  await self.loadSourceMap(dir, sourceMapURL),
32
32
  null,
33
33
  (consumer) => consumer.generatedPositionFor({ source, line, column: 0 })
@@ -2,34 +2,64 @@
2
2
 
3
3
  const fs = require('fs')
4
4
 
5
+ const { DD_EXTERNAL_ENV } = process.env
6
+
5
7
  // The second part is the PCF / Garden regexp. We currently assume no suffix($) to avoid matching pod UIDs
6
8
  // See https://github.com/DataDog/datadog-agent/blob/7.40.x/pkg/util/cgroups/reader.go#L50
7
9
  const uuidSource =
8
10
  '[0-9a-f]{8}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{12}|[0-9a-f]{8}(?:-[0-9a-f]{4}){4}$'
9
11
  const containerSource = '[0-9a-f]{64}'
10
12
  const taskSource = '[0-9a-f]{32}-\\d+'
13
+ const lineReg = /^(\d+):([^:]*):(.+)$/
11
14
  const entityReg = new RegExp(`.*(${uuidSource}|${containerSource}|${taskSource})(?:\\.scope)?$`, 'm')
12
15
 
16
+ const cgroup = readControlGroup()
13
17
  const entityId = getEntityId()
18
+ const inode = getInode()
14
19
 
15
20
  function getEntityId () {
16
- const cgroup = readControlGroup() || ''
17
- const match = cgroup.trim().match(entityReg) || []
21
+ const match = cgroup.match(entityReg) || []
18
22
 
19
23
  return match[1]
20
24
  }
21
25
 
26
+ function getInode () {
27
+ const match = cgroup.match(lineReg) || []
28
+
29
+ return readInode(match[3])
30
+ }
31
+
22
32
  function readControlGroup () {
23
33
  try {
24
- return fs.readFileSync('/proc/self/cgroup').toString()
34
+ return fs.readFileSync('/proc/self/cgroup').toString().trim()
35
+ } catch (err) {
36
+ return ''
37
+ }
38
+ }
39
+
40
+ function readInode (path) {
41
+ if (!path) return 0
42
+
43
+ const strippedPath = path.replace(/^\//, '').replace(/\/$/, '')
44
+
45
+ try {
46
+ return fs.statSync(`/sys/fs/cgroup/${strippedPath}`).ino
25
47
  } catch (err) {
26
- // ignore
48
+ return 0
27
49
  }
28
50
  }
29
51
 
30
52
  module.exports = {
31
- // can be the container ID but not always depending on the orchestrator
32
- id () {
33
- return entityId
53
+ inject (carrier) {
54
+ if (entityId) {
55
+ carrier['Datadog-Container-Id'] = entityId
56
+ carrier['Datadog-Entity-ID'] = `ci-${entityId}`
57
+ } else if (inode) {
58
+ carrier['Datadog-Entity-ID'] = `in-${inode}`
59
+ }
60
+
61
+ if (DD_EXTERNAL_ENV) {
62
+ carrier['Datadog-External-Env'] = DD_EXTERNAL_ENV
63
+ }
34
64
  }
35
65
  }
@@ -15,7 +15,6 @@ const { storage } = require('../../../../datadog-core')
15
15
  const log = require('../../log')
16
16
 
17
17
  const maxActiveRequests = 8
18
- const containerId = docker.id()
19
18
 
20
19
  let activeRequests = 0
21
20
 
@@ -63,9 +62,7 @@ function request (data, options, callback) {
63
62
  options.headers['Content-Length'] = byteLength(dataArray)
64
63
  }
65
64
 
66
- if (containerId) {
67
- options.headers['Datadog-Container-ID'] = containerId
68
- }
65
+ docker.inject(options.headers)
69
66
 
70
67
  options.agent = isSecure ? httpsAgent : httpAgent
71
68
 
@@ -37,13 +37,13 @@ class LLMObsPlugin extends TracingPlugin {
37
37
  // register options may not be set for operations we do not trace with llmobs
38
38
  // ie OpenAI fine tuning jobs, file jobs, etc.
39
39
  if (registerOptions) {
40
- telemetry.incrementLLMObsSpanStartCount({ autoinstrumented: true, integration: this.constructor.id })
40
+ telemetry.incrementLLMObsSpanStartCount({ autoinstrumented: true, integration: this.constructor.integration })
41
41
 
42
42
  ctx.llmobs = {} // initialize context-based namespace
43
43
  llmobsStorage.enterWith({ span })
44
44
  ctx.llmobs.parent = parent
45
45
 
46
- this._tagger.registerLLMObsSpan(span, { parent, integration: this.constructor.id, ...registerOptions })
46
+ this._tagger.registerLLMObsSpan(span, { parent, integration: this.constructor.integration, ...registerOptions })
47
47
  }
48
48
  }
49
49
 
@@ -20,7 +20,8 @@ const ChatModelHandler = require('./handlers/chat_model')
20
20
  const LlmHandler = require('./handlers/llm')
21
21
  const EmbeddingHandler = require('./handlers/embedding')
22
22
 
23
- class LangChainLLMObsPlugin extends LLMObsPlugin {
23
+ class BaseLangChainLLMObsPlugin extends LLMObsPlugin {
24
+ static get integration () { return 'langchain' }
24
25
  static get id () { return 'langchain' }
25
26
  static get prefix () {
26
27
  return 'tracing:apm:langchain:invoke'
@@ -55,8 +56,11 @@ class LangChainLLMObsPlugin extends LLMObsPlugin {
55
56
  }
56
57
 
57
58
  setLLMObsTags (ctx) {
59
+ ctx.args = ctx.arguments
60
+ ctx.instance = ctx.self
61
+
58
62
  const span = ctx.currentStore?.span
59
- const type = ctx.type // langchain operation type (oneof chain,chat_model,llm,embedding)
63
+ const type = ctx.type = this.constructor.lcType // langchain operation type (oneof chain,chat_model,llm,embedding)
60
64
 
61
65
  if (!Object.keys(this._handlers).includes(type)) {
62
66
  log.warn(`Unsupported LangChain operation type: ${type}`)
@@ -129,4 +133,59 @@ class LangChainLLMObsPlugin extends LLMObsPlugin {
129
133
  }
130
134
  }
131
135
 
132
- module.exports = LangChainLLMObsPlugin
136
+ class RunnableSequenceInvokePlugin extends BaseLangChainLLMObsPlugin {
137
+ static get id () { return 'llmobs_langchain_rs_invoke' }
138
+ static get lcType () { return 'chain' }
139
+ static get prefix () {
140
+ return 'tracing:orchestrion:@langchain/core:RunnableSequence_invoke'
141
+ }
142
+ }
143
+
144
+ class RunnableSequenceBatchPlugin extends BaseLangChainLLMObsPlugin {
145
+ static get id () { return 'llmobs_langchain_rs_batch' }
146
+ static get lcType () { return 'chain' }
147
+ static get prefix () {
148
+ return 'tracing:orchestrion:@langchain/core:RunnableSequence_batch'
149
+ }
150
+ }
151
+
152
+ class BaseChatModelGeneratePlugin extends BaseLangChainLLMObsPlugin {
153
+ static get id () { return 'llmobs_langchain_chat_model_generate' }
154
+ static get lcType () { return 'chat_model' }
155
+ static get prefix () {
156
+ return 'tracing:orchestrion:@langchain/core:BaseChatModel_generate'
157
+ }
158
+ }
159
+
160
+ class BaseLLMGeneratePlugin extends BaseLangChainLLMObsPlugin {
161
+ static get id () { return 'llmobs_langchain_llm_generate' }
162
+ static get lcType () { return 'llm' }
163
+ static get prefix () {
164
+ return 'tracing:orchestrion:@langchain/core:BaseLLM_generate'
165
+ }
166
+ }
167
+
168
+ class EmbeddingsEmbedQueryPlugin extends BaseLangChainLLMObsPlugin {
169
+ static get id () { return 'llmobs_langchain_embeddings_embed_query' }
170
+ static get lcType () { return 'embedding' }
171
+ static get prefix () {
172
+ return 'tracing:apm:@langchain/core:Embeddings_embedQuery'
173
+ }
174
+ }
175
+
176
+ class EmbeddingsEmbedDocumentsPlugin extends BaseLangChainLLMObsPlugin {
177
+ static get id () { return 'llmobs_langchain_embeddings_embed_documents' }
178
+ static get lcType () { return 'embedding' }
179
+ static get prefix () {
180
+ return 'tracing:apm:@langchain/core:Embeddings_embedDocuments'
181
+ }
182
+ }
183
+
184
+ module.exports = [
185
+ RunnableSequenceInvokePlugin,
186
+ RunnableSequenceBatchPlugin,
187
+ BaseChatModelGeneratePlugin,
188
+ BaseLLMGeneratePlugin,
189
+ EmbeddingsEmbedQueryPlugin,
190
+ EmbeddingsEmbedDocumentsPlugin
191
+ ]
@@ -11,6 +11,7 @@ function isIterable (obj) {
11
11
 
12
12
  class OpenAiLLMObsPlugin extends LLMObsPlugin {
13
13
  static get id () { return 'openai' }
14
+ static get integration () { return 'openai' }
14
15
  static get prefix () {
15
16
  return 'tracing:apm:openai:request'
16
17
  }
@@ -7,7 +7,8 @@ const {
7
7
  } = require('../../../../datadog-plugin-google-cloud-vertexai/src/utils')
8
8
 
9
9
  class VertexAILLMObsPlugin extends LLMObsPlugin {
10
- static get id () { return 'vertexai' } // used for llmobs telemetry
10
+ static get integration () { return 'vertexai' } // used for llmobs telemetry
11
+ static get id () { return 'vertexai' }
11
12
  static get prefix () {
12
13
  return 'tracing:apm:vertexai:request'
13
14
  }
@@ -121,6 +121,8 @@ const log = {
121
121
  }
122
122
  }
123
123
 
124
+ logWriter.setStackTraceLimitFunction(log.error)
125
+
124
126
  log.reset()
125
127
 
126
128
  log.toggle(log.isEnabled(), log.getLogLevel())
@@ -13,6 +13,7 @@ const defaultLogger = {
13
13
  let enabled = false
14
14
  let logger = defaultLogger
15
15
  let logChannel = new LogChannel()
16
+ let stackTraceLimitFunction = onError
16
17
 
17
18
  function withNoop (fn) {
18
19
  const store = storage('legacy').getStore()
@@ -61,12 +62,28 @@ function getErrorLog (err) {
61
62
  }
62
63
  }
63
64
 
65
+ function setStackTraceLimitFunction (fn) {
66
+ if (typeof fn !== 'function') {
67
+ throw new TypeError('stackTraceLimitFunction must be a function')
68
+ }
69
+ stackTraceLimitFunction = fn
70
+ }
71
+
64
72
  function onError (err) {
65
73
  const { formatted, cause } = getErrorLog(err)
66
74
 
67
75
  // calling twice logger.error() because Error cause is only available in nodejs v16.9.0
68
76
  // TODO: replace it with Error(message, { cause }) when cause has broad support
69
- if (formatted) withNoop(() => logger.error(new Error(formatted)))
77
+ if (formatted) {
78
+ withNoop(() => {
79
+ const l = Error.stackTraceLimit
80
+ Error.stackTraceLimit = 0
81
+ const e = new Error(formatted)
82
+ Error.stackTraceLimit = l
83
+ Error.captureStackTrace(e, stackTraceLimitFunction)
84
+ logger.error(e)
85
+ })
86
+ }
70
87
  if (cause) withNoop(() => logger.error(cause))
71
88
  }
72
89
 
@@ -122,4 +139,4 @@ function trace (...args) {
122
139
  onTrace(Log.parse(...args))
123
140
  }
124
141
 
125
- module.exports = { use, toggle, reset, error, warn, info, debug, trace }
142
+ module.exports = { use, toggle, reset, error, warn, info, debug, trace, setStackTraceLimitFunction }
@@ -298,6 +298,7 @@ class TextMapPropagator {
298
298
 
299
299
  _extractSpanContext (carrier) {
300
300
  let context = null
301
+ let style = ''
301
302
  for (const extractor of this._config.tracePropagationStyle.extract) {
302
303
  let extractedContext = null
303
304
  switch (extractor) {
@@ -331,9 +332,9 @@ class TextMapPropagator {
331
332
 
332
333
  if (context === null) {
333
334
  context = extractedContext
335
+ style = extractor
334
336
  if (this._config.tracePropagationExtractFirst) {
335
- this._extractBaggageItems(carrier, context)
336
- return context
337
+ break
337
338
  }
338
339
  } else {
339
340
  // If extractor is tracecontext, add tracecontext specific information to the context
@@ -342,7 +343,7 @@ class TextMapPropagator {
342
343
  this._extractTraceparentContext(carrier), context, carrier)
343
344
  }
344
345
  if (extractedContext._traceId && extractedContext._spanId &&
345
- extractedContext.toTraceId(true) !== context.toTraceId(true)) {
346
+ extractedContext.toTraceId(true) !== context.toTraceId(true)) {
346
347
  const link = {
347
348
  context: extractedContext,
348
349
  attributes: { reason: 'terminated_context', context_headers: extractor }
@@ -354,6 +355,19 @@ class TextMapPropagator {
354
355
 
355
356
  this._extractBaggageItems(carrier, context)
356
357
 
358
+ if (this._config.tracePropagationBehaviorExtract === 'ignore') {
359
+ context._links = []
360
+ } else if (this._config.tracePropagationBehaviorExtract === 'restart') {
361
+ context._links = []
362
+ context._links.push({
363
+ context,
364
+ attributes:
365
+ {
366
+ reason: 'propagation_behavior_extract', context_headers: style
367
+ }
368
+ })
369
+ }
370
+
357
371
  return context || this._extractSqsdContext(carrier)
358
372
  }
359
373
 
@@ -319,6 +319,12 @@ class DatadogSpan {
319
319
  let spanContext
320
320
  let startTime
321
321
 
322
+ let baggage = {}
323
+ if (parent && parent._isRemote && this._parentTracer?._config?.tracePropagationBehaviorExtract !== 'continue') {
324
+ baggage = parent._baggageItems
325
+ parent = null
326
+ }
327
+
322
328
  if (fields.context) {
323
329
  spanContext = fields.context
324
330
  if (!spanContext._trace.startTime) {
@@ -352,6 +358,10 @@ class DatadogSpan {
352
358
  .padStart(8, '0')
353
359
  .padEnd(16, '0')
354
360
  }
361
+
362
+ if (this._parentTracer?._config?.tracePropagationBehaviorExtract === 'restart') {
363
+ spanContext._baggageItems = baggage
364
+ }
355
365
  }
356
366
 
357
367
  spanContext._trace.ticks = spanContext._trace.ticks || now()
@@ -120,6 +120,12 @@ const TEST_LEVEL_EVENT_TYPES = [
120
120
  'test_module_end',
121
121
  'test_session_end'
122
122
  ]
123
+ const TEST_RETRY_REASON_TYPES = {
124
+ efd: 'early_flake_detection',
125
+ atr: 'auto_test_retry',
126
+ atf: 'attempt_to_fix',
127
+ ext: 'external'
128
+ }
123
129
 
124
130
  const DD_TEST_IS_USER_PROVIDED_SERVICE = '_dd.test.is_user_provided_service'
125
131
 
@@ -227,6 +233,7 @@ module.exports = {
227
233
  DD_CAPABILITIES_TEST_MANAGEMENT_DISABLE,
228
234
  DD_CAPABILITIES_TEST_MANAGEMENT_ATTEMPT_TO_FIX,
229
235
  TEST_LEVEL_EVENT_TYPES,
236
+ TEST_RETRY_REASON_TYPES,
230
237
  getNumFromKnownTests,
231
238
  getFileAndLineNumberFromError,
232
239
  DI_ERROR_DEBUG_INFO_CAPTURED,
@@ -16,8 +16,6 @@ const perf = require('perf_hooks').performance
16
16
  const telemetryMetrics = require('../../telemetry/metrics')
17
17
  const profilersNamespace = telemetryMetrics.manager.namespace('profilers')
18
18
 
19
- const containerId = docker.id()
20
-
21
19
  const statusCodeCounters = []
22
20
  const requestCounter = profilersNamespace.count('profile_api.requests', [])
23
21
  const sizeDistribution = profilersNamespace.distribution('profile_api.bytes', [])
@@ -155,9 +153,7 @@ class AgentExporter extends EventSerializer {
155
153
  timeout: this._backoffTime * Math.pow(2, attempt)
156
154
  }
157
155
 
158
- if (containerId) {
159
- options.headers['Datadog-Container-ID'] = containerId
160
- }
156
+ docker.inject(options.headers)
161
157
 
162
158
  if (this._url.protocol === 'unix:') {
163
159
  options.socketPath = this._url.pathname
@@ -70,7 +70,9 @@ function ensureChannelsActivated () {
70
70
  class NativeWallProfiler {
71
71
  constructor (options = {}) {
72
72
  this.type = 'wall'
73
- this._asyncIdEnabled = !!options.asyncIdEnabled
73
+ // Currently there's a crash sometimes on worker threads trying to collect async IDs so for the
74
+ // time being we'll constrain it to only the main thread.
75
+ this._asyncIdEnabled = !!options.asyncIdEnabled && require('worker_threads').isMainThread
74
76
  this._codeHotspotsEnabled = !!options.codeHotspotsEnabled
75
77
  this._cpuProfilingEnabled = !!options.cpuProfilingEnabled
76
78
  this._endpointCollectionEnabled = !!options.endpointCollectionEnabled
@@ -70,7 +70,8 @@ class Tracer extends NoopProxy {
70
70
  this._modules = {
71
71
  appsec: new LazyModule(() => require('./appsec')),
72
72
  iast: new LazyModule(() => require('./appsec/iast')),
73
- llmobs: new LazyModule(() => require('./llmobs'))
73
+ llmobs: new LazyModule(() => require('./llmobs')),
74
+ rewriter: new LazyModule(() => require('./appsec/iast/taint-tracking/rewriter'))
74
75
  }
75
76
  }
76
77
 
@@ -178,6 +179,8 @@ class Tracer extends NoopProxy {
178
179
 
179
180
  this._enableOrDisableTracing(config)
180
181
 
182
+ this._modules.rewriter.enable(config)
183
+
181
184
  if (config.tracing) {
182
185
  if (config.isManualApiEnabled) {
183
186
  const TestApiManualPlugin = require('./ci-visibility/test-api-manual/test-api-manual-plugin')
@@ -247,6 +250,7 @@ class Tracer extends NoopProxy {
247
250
  if (config.iast.enabled) {
248
251
  this._modules.iast.enable(config, this._tracer)
249
252
  }
253
+ // This needs to be after the IAST module is enabled
250
254
  } else if (this._tracingInitialized) {
251
255
  this._modules.appsec.disable()
252
256
  this._modules.iast.disable()