dd-trace 5.86.0 → 5.88.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 (105) hide show
  1. package/LICENSE-3rdparty.csv +60 -32
  2. package/ext/exporters.d.ts +1 -0
  3. package/ext/exporters.js +1 -0
  4. package/index.d.ts +243 -7
  5. package/package.json +9 -6
  6. package/packages/datadog-instrumentations/src/ai.js +54 -90
  7. package/packages/datadog-instrumentations/src/cucumber.js +14 -0
  8. package/packages/datadog-instrumentations/src/helpers/hook.js +17 -11
  9. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  10. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +55 -14
  11. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +15 -13
  12. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +103 -0
  13. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/bullmq.js +108 -0
  14. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -1
  15. package/packages/datadog-instrumentations/src/helpers/rewriter/transformer.js +21 -0
  16. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +138 -12
  17. package/packages/datadog-instrumentations/src/http/client.js +119 -1
  18. package/packages/datadog-instrumentations/src/jest.js +179 -15
  19. package/packages/datadog-instrumentations/src/kafkajs.js +20 -17
  20. package/packages/datadog-instrumentations/src/mocha/utils.js +6 -0
  21. package/packages/datadog-instrumentations/src/mysql2.js +131 -64
  22. package/packages/datadog-instrumentations/src/playwright.js +9 -1
  23. package/packages/datadog-instrumentations/src/stripe.js +92 -0
  24. package/packages/datadog-instrumentations/src/vitest.js +11 -0
  25. package/packages/datadog-plugin-amqplib/src/consumer.js +14 -10
  26. package/packages/datadog-plugin-amqplib/src/producer.js +23 -19
  27. package/packages/datadog-plugin-azure-functions/src/index.js +53 -37
  28. package/packages/datadog-plugin-bullmq/src/consumer.js +33 -11
  29. package/packages/datadog-plugin-bullmq/src/producer.js +60 -31
  30. package/packages/datadog-plugin-cucumber/src/index.js +9 -6
  31. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +33 -0
  32. package/packages/datadog-plugin-cypress/src/support.js +48 -8
  33. package/packages/datadog-plugin-jest/src/index.js +12 -2
  34. package/packages/datadog-plugin-jest/src/util.js +2 -1
  35. package/packages/datadog-plugin-kafkajs/src/consumer.js +22 -12
  36. package/packages/datadog-plugin-kafkajs/src/producer.js +33 -22
  37. package/packages/datadog-plugin-mocha/src/index.js +9 -6
  38. package/packages/datadog-plugin-playwright/src/index.js +10 -6
  39. package/packages/datadog-plugin-vitest/src/index.js +13 -8
  40. package/packages/dd-trace/src/appsec/addresses.js +11 -0
  41. package/packages/dd-trace/src/appsec/channels.js +5 -1
  42. package/packages/dd-trace/src/appsec/downstream_requests.js +302 -0
  43. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +1 -1
  44. package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +1 -1
  45. package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +1 -1
  46. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +4 -5
  47. package/packages/dd-trace/src/appsec/iast/path-line.js +36 -25
  48. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +1 -1
  49. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +3 -4
  50. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +3 -2
  51. package/packages/dd-trace/src/appsec/index.js +103 -0
  52. package/packages/dd-trace/src/appsec/rasp/ssrf.js +66 -4
  53. package/packages/dd-trace/src/azure_metadata.js +0 -2
  54. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +14 -1
  55. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
  56. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +2 -0
  57. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +1 -1
  58. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -1
  59. package/packages/dd-trace/src/ci-visibility/requests/request.js +236 -0
  60. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +1 -1
  61. package/packages/dd-trace/src/config/defaults.js +148 -195
  62. package/packages/dd-trace/src/config/helper.js +43 -1
  63. package/packages/dd-trace/src/config/index.js +42 -14
  64. package/packages/dd-trace/src/config/supported-configurations.json +4115 -510
  65. package/packages/dd-trace/src/constants.js +0 -2
  66. package/packages/dd-trace/src/crashtracking/crashtracker.js +10 -3
  67. package/packages/dd-trace/src/datastreams/pathway.js +22 -3
  68. package/packages/dd-trace/src/datastreams/processor.js +14 -1
  69. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -2
  70. package/packages/dd-trace/src/debugger/devtools_client/index.js +75 -23
  71. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +23 -1
  72. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +3 -3
  73. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +168 -36
  74. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +18 -0
  75. package/packages/dd-trace/src/encode/agentless-json.js +141 -0
  76. package/packages/dd-trace/src/exporter.js +2 -0
  77. package/packages/dd-trace/src/exporters/agent/writer.js +22 -8
  78. package/packages/dd-trace/src/exporters/agentless/index.js +89 -0
  79. package/packages/dd-trace/src/exporters/agentless/writer.js +184 -0
  80. package/packages/dd-trace/src/exporters/common/agents.js +1 -1
  81. package/packages/dd-trace/src/exporters/common/request.js +4 -4
  82. package/packages/dd-trace/src/llmobs/constants/writers.js +1 -1
  83. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +5 -3
  84. package/packages/dd-trace/src/llmobs/sdk.js +34 -5
  85. package/packages/dd-trace/src/opentelemetry/context_manager.js +19 -46
  86. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +3 -4
  87. package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -5
  88. package/packages/dd-trace/src/opentracing/span.js +6 -4
  89. package/packages/dd-trace/src/plugins/ci_plugin.js +57 -5
  90. package/packages/dd-trace/src/plugins/database.js +57 -45
  91. package/packages/dd-trace/src/plugins/outbound.js +27 -2
  92. package/packages/dd-trace/src/plugins/tracing.js +39 -4
  93. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +7 -0
  94. package/packages/dd-trace/src/plugins/util/test.js +48 -0
  95. package/packages/dd-trace/src/plugins/util/web.js +8 -7
  96. package/packages/dd-trace/src/profiling/exporter_cli.js +1 -0
  97. package/packages/dd-trace/src/propagation-hash/index.js +145 -0
  98. package/packages/dd-trace/src/proxy.js +4 -0
  99. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +1 -1
  100. package/packages/dd-trace/src/startup-log.js +3 -3
  101. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/bullmq.json +0 -106
  102. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +0 -741
  103. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +0 -11
  104. package/packages/dd-trace/src/plugins/util/serverless.js +0 -8
  105. package/packages/dd-trace/src/scope/noop/scope.js +0 -21
@@ -6,6 +6,22 @@ const shimmer = require('../../datadog-shimmer')
6
6
  const satisfies = require('../../../vendor/dist/semifies')
7
7
  const { channel, addHook } = require('./helpers/instrument')
8
8
 
9
+ /** @type {WeakMap<object, Function>} */
10
+ const wrappedOnResult = new WeakMap()
11
+
12
+ /**
13
+ * @param {unknown} sql
14
+ * @returns {string|undefined}
15
+ */
16
+ function resolveSqlString (sql) {
17
+ return typeof sql === 'string' ? sql : /** @type {{ sql?: string }} */ (sql)?.sql
18
+ }
19
+
20
+ /**
21
+ * @param {Function} Connection
22
+ * @param {string} version
23
+ * @returns {Function}
24
+ */
9
25
  function wrapConnection (Connection, version) {
10
26
  const startCh = channel('apm:mysql2:query:start')
11
27
  const finishCh = channel('apm:mysql2:query:finish')
@@ -19,15 +35,27 @@ function wrapConnection (Connection, version) {
19
35
  shimmer.wrap(Connection.prototype, 'addCommand', addCommand => function (cmd) {
20
36
  if (!startCh.hasSubscribers) return addCommand.apply(this, arguments)
21
37
 
22
- const name = cmd && cmd.constructor && cmd.constructor.name
23
- const isCommand = typeof cmd.execute === 'function'
24
- const isQuery = isCommand && (name === 'Execute' || name === 'Query')
38
+ const command = /** @type {{ execute?: Function, constructor?: { name?: string } }} */ (cmd)
39
+ if (typeof command.execute !== 'function') return addCommand.apply(this, arguments)
40
+
41
+ const name = command.constructor?.name
42
+ const isQuery = name === 'Execute' || name === 'Query'
25
43
  const ctx = {}
26
44
 
27
- // TODO: consider supporting all commands and not just queries
28
- cmd.execute = isQuery
29
- ? wrapExecute(cmd, cmd.execute, ctx, this.config)
30
- : bindExecute(cmd.execute, ctx)
45
+ if (isQuery) {
46
+ command.execute = wrapExecute(command, command.execute, ctx, this.config)
47
+
48
+ return commandAddCh.runStores(ctx, addCommand, this, ...arguments)
49
+ }
50
+
51
+ wrapCommandOnResult(command, ctx)
52
+
53
+ command.execute = shimmer.wrapFunction(
54
+ command.execute,
55
+ execute => function executeWithTrace (_packet_, _connection_) {
56
+ return commandStartCh.runStores(ctx, execute, this, ...arguments)
57
+ }
58
+ )
31
59
 
32
60
  return commandAddCh.runStores(ctx, addCommand, this, ...arguments)
33
61
  })
@@ -35,12 +63,11 @@ function wrapConnection (Connection, version) {
35
63
  shimmer.wrap(Connection.prototype, 'query', query => function (sql, values, cb) {
36
64
  if (!startOuterQueryCh.hasSubscribers) return query.apply(this, arguments)
37
65
 
38
- if (sql !== null && typeof sql === 'object') sql = sql.sql
39
-
40
- if (!sql) return query.apply(this, arguments)
66
+ const resolvedSql = resolveSqlString(sql)
67
+ if (resolvedSql === undefined) return query.apply(this, arguments)
41
68
 
42
69
  const abortController = new AbortController()
43
- startOuterQueryCh.publish({ sql, abortController })
70
+ startOuterQueryCh.publish({ sql: resolvedSql, abortController })
44
71
 
45
72
  if (abortController.signal.aborted) {
46
73
  const addCommand = this.addCommand
@@ -56,7 +83,7 @@ function wrapConnection (Connection, version) {
56
83
  cb = queryCommand.onResult
57
84
 
58
85
  process.nextTick(() => {
59
- if (cb) {
86
+ if (typeof cb === 'function') {
60
87
  cb(abortController.signal.reason)
61
88
  } else {
62
89
  queryCommand.emit('error', abortController.signal.reason)
@@ -76,12 +103,11 @@ function wrapConnection (Connection, version) {
76
103
  shimmer.wrap(Connection.prototype, 'execute', execute => function (sql, values, cb) {
77
104
  if (!startOuterQueryCh.hasSubscribers) return execute.apply(this, arguments)
78
105
 
79
- if (sql !== null && typeof sql === 'object') sql = sql.sql
80
-
81
- if (!sql) return execute.apply(this, arguments)
106
+ const resolvedSql = resolveSqlString(sql)
107
+ if (resolvedSql === undefined) return execute.apply(this, arguments)
82
108
 
83
109
  const abortController = new AbortController()
84
- startOuterQueryCh.publish({ sql, abortController })
110
+ startOuterQueryCh.publish({ sql: resolvedSql, abortController })
85
111
 
86
112
  if (abortController.signal.aborted) {
87
113
  const addCommand = this.addCommand
@@ -94,7 +120,9 @@ function wrapConnection (Connection, version) {
94
120
  this.addCommand = addCommand
95
121
  }
96
122
 
97
- result?.onResult(abortController.signal.reason)
123
+ if (typeof result?.onResult === 'function') {
124
+ result.onResult(abortController.signal.reason)
125
+ }
98
126
 
99
127
  return result
100
128
  }
@@ -104,33 +132,48 @@ function wrapConnection (Connection, version) {
104
132
 
105
133
  return Connection
106
134
 
107
- function bindExecute (execute, ctx) {
108
- return shimmer.wrapFunction(execute, execute => function executeWithTrace (packet, connection) {
109
- const onResult = this.onResult
135
+ /**
136
+ * @param {object} cmd
137
+ * @param {object} ctx
138
+ * @returns {void}
139
+ */
140
+ function wrapCommandOnResult (cmd, ctx) {
141
+ const onResult = cmd?.onResult
142
+ if (typeof onResult !== 'function') return
110
143
 
111
- if (onResult) {
112
- this.onResult = function () {
113
- return commandFinishCh.runStores(ctx, onResult, this, ...arguments)
114
- }
115
- }
144
+ const cached = wrappedOnResult.get(cmd)
116
145
 
117
- return commandStartCh.runStores(ctx, execute, this, ...arguments)
118
- })
146
+ if (cached === onResult) return
147
+
148
+ const wrapped = function () {
149
+ return commandFinishCh.runStores(ctx, onResult, this, ...arguments)
150
+ }
151
+
152
+ wrappedOnResult.set(cmd, wrapped)
153
+ cmd.onResult = wrapped
119
154
  }
120
155
 
156
+ /**
157
+ * @param {object} cmd
158
+ * @param {Function} execute
159
+ * @param {object} ctx
160
+ * @param {object} config
161
+ * @returns {Function}
162
+ */
121
163
  function wrapExecute (cmd, execute, ctx, config) {
122
164
  return shimmer.wrapFunction(execute, execute => function executeWithTrace (packet, connection) {
123
- ctx.sql = cmd.statement ? cmd.statement.query : cmd.sql
165
+ const command = /** @type {{ statement?: { query?: unknown }, sql?: unknown }} */ (cmd)
166
+ ctx.sql = command.statement ? command.statement.query : command.sql
124
167
  ctx.conf = config
125
168
 
126
169
  return startCh.runStores(ctx, () => {
127
- if (cmd.statement) {
128
- cmd.statement.query = ctx.sql
170
+ if (command.statement) {
171
+ command.statement.query = ctx.sql
129
172
  } else {
130
- cmd.sql = ctx.sql
173
+ command.sql = ctx.sql
131
174
  }
132
175
 
133
- if (this.onResult) {
176
+ if (typeof this.onResult === 'function') {
134
177
  const onResult = this.onResult
135
178
 
136
179
  this.onResult = shimmer.wrapFunction(onResult, onResult => function (error) {
@@ -141,11 +184,14 @@ function wrapConnection (Connection, version) {
141
184
  finishCh.runStores(ctx, onResult, this, ...arguments)
142
185
  })
143
186
  } else {
144
- this.once(errorMonitor, error => {
145
- ctx.error = error
146
- errorCh.publish(ctx)
147
- })
148
- this.once('end', () => finishCh.publish(ctx))
187
+ const command = /** @type {{ once?: Function }} */ (this)
188
+ if (typeof command.once === 'function') {
189
+ command.once(errorMonitor, error => {
190
+ ctx.error = error
191
+ errorCh.publish(ctx)
192
+ })
193
+ command.once('end', () => finishCh.publish(ctx))
194
+ }
149
195
  }
150
196
 
151
197
  this.execute = execute
@@ -157,9 +203,14 @@ function wrapConnection (Connection, version) {
157
203
  errorCh.publish(ctx)
158
204
  }
159
205
  })
160
- }, cmd)
206
+ })
161
207
  }
162
208
  }
209
+ /**
210
+ * @param {Function} Pool
211
+ * @param {string} version
212
+ * @returns {Function}
213
+ */
163
214
  function wrapPool (Pool, version) {
164
215
  const startOuterQueryCh = channel('datadog:mysql2:outerquery:start')
165
216
  const shouldEmitEndAfterQueryAbort = satisfies(version, '>=1.3.3')
@@ -167,12 +218,11 @@ function wrapPool (Pool, version) {
167
218
  shimmer.wrap(Pool.prototype, 'query', query => function (sql, values, cb) {
168
219
  if (!startOuterQueryCh.hasSubscribers) return query.apply(this, arguments)
169
220
 
170
- if (sql !== null && typeof sql === 'object') sql = sql.sql
171
-
172
- if (!sql) return query.apply(this, arguments)
221
+ const resolvedSql = resolveSqlString(sql)
222
+ if (resolvedSql === undefined) return query.apply(this, arguments)
173
223
 
174
224
  const abortController = new AbortController()
175
- startOuterQueryCh.publish({ sql, abortController })
225
+ startOuterQueryCh.publish({ sql: resolvedSql, abortController })
176
226
 
177
227
  if (abortController.signal.aborted) {
178
228
  const getConnection = this.getConnection
@@ -206,21 +256,22 @@ function wrapPool (Pool, version) {
206
256
  shimmer.wrap(Pool.prototype, 'execute', execute => function (sql, values, cb) {
207
257
  if (!startOuterQueryCh.hasSubscribers) return execute.apply(this, arguments)
208
258
 
209
- if (sql !== null && typeof sql === 'object') sql = sql.sql
210
-
211
- if (!sql) return execute.apply(this, arguments)
259
+ const resolvedSql = resolveSqlString(sql)
260
+ if (resolvedSql === undefined) return execute.apply(this, arguments)
212
261
 
213
262
  const abortController = new AbortController()
214
- startOuterQueryCh.publish({ sql, abortController })
263
+ startOuterQueryCh.publish({ sql: resolvedSql, abortController })
215
264
 
216
265
  if (abortController.signal.aborted) {
217
266
  if (typeof values === 'function') {
218
267
  cb = values
219
268
  }
220
269
 
221
- process.nextTick(() => {
222
- cb(abortController.signal.reason)
223
- })
270
+ if (typeof cb === 'function') {
271
+ process.nextTick(() => {
272
+ /** @type {Function} */ (cb)(abortController.signal.reason)
273
+ })
274
+ }
224
275
  return
225
276
  }
226
277
 
@@ -230,6 +281,10 @@ function wrapPool (Pool, version) {
230
281
  return Pool
231
282
  }
232
283
 
284
+ /**
285
+ * @param {Function} PoolCluster
286
+ * @returns {Function}
287
+ */
233
288
  function wrapPoolCluster (PoolCluster) {
234
289
  const startOuterQueryCh = channel('datadog:mysql2:outerquery:start')
235
290
  const wrappedPoolNamespaces = new WeakSet()
@@ -239,12 +294,11 @@ function wrapPoolCluster (PoolCluster) {
239
294
 
240
295
  if (startOuterQueryCh.hasSubscribers && !wrappedPoolNamespaces.has(poolNamespace)) {
241
296
  shimmer.wrap(poolNamespace, 'query', query => function (sql, values, cb) {
242
- if (sql !== null && typeof sql === 'object') sql = sql.sql
243
-
244
- if (!sql) return query.apply(this, arguments)
297
+ const resolvedSql = resolveSqlString(sql)
298
+ if (resolvedSql === undefined) return query.apply(this, arguments)
245
299
 
246
300
  const abortController = new AbortController()
247
- startOuterQueryCh.publish({ sql, abortController })
301
+ startOuterQueryCh.publish({ sql: resolvedSql, abortController })
248
302
 
249
303
  if (abortController.signal.aborted) {
250
304
  const getConnection = this.getConnection
@@ -274,21 +328,22 @@ function wrapPoolCluster (PoolCluster) {
274
328
  })
275
329
 
276
330
  shimmer.wrap(poolNamespace, 'execute', execute => function (sql, values, cb) {
277
- if (sql !== null && typeof sql === 'object') sql = sql.sql
278
-
279
- if (!sql) return execute.apply(this, arguments)
331
+ const resolvedSql = resolveSqlString(sql)
332
+ if (resolvedSql === undefined) return execute.apply(this, arguments)
280
333
 
281
334
  const abortController = new AbortController()
282
- startOuterQueryCh.publish({ sql, abortController })
335
+ startOuterQueryCh.publish({ sql: resolvedSql, abortController })
283
336
 
284
337
  if (abortController.signal.aborted) {
285
338
  if (typeof values === 'function') {
286
339
  cb = values
287
340
  }
288
341
 
289
- process.nextTick(() => {
290
- cb(abortController.signal.reason)
291
- })
342
+ if (typeof cb === 'function') {
343
+ process.nextTick(() => {
344
+ /** @type {Function} */ (cb)(abortController.signal.reason)
345
+ })
346
+ }
292
347
 
293
348
  return
294
349
  }
@@ -305,9 +360,21 @@ function wrapPoolCluster (PoolCluster) {
305
360
  return PoolCluster
306
361
  }
307
362
 
308
- addHook({ name: 'mysql2', file: 'lib/base/connection.js', versions: ['>=3.11.5'] }, wrapConnection)
309
- addHook({ name: 'mysql2', file: 'lib/connection.js', versions: ['1 - 3.11.4'] }, wrapConnection)
310
- addHook({ name: 'mysql2', file: 'lib/pool.js', versions: ['1 - 3.11.4'] }, wrapPool)
363
+ addHook(
364
+ { name: 'mysql2', file: 'lib/base/connection.js', versions: ['>=3.11.5'] },
365
+ /** @type {(moduleExports: unknown, version: string) => unknown} */ (wrapConnection)
366
+ )
367
+ addHook(
368
+ { name: 'mysql2', file: 'lib/connection.js', versions: ['1 - 3.11.4'] },
369
+ /** @type {(moduleExports: unknown, version: string) => unknown} */ (wrapConnection)
370
+ )
371
+ addHook(
372
+ { name: 'mysql2', file: 'lib/pool.js', versions: ['1 - 3.11.4'] },
373
+ /** @type {(moduleExports: unknown, version: string) => unknown} */ (wrapPool)
374
+ )
311
375
 
312
376
  // PoolNamespace.prototype.query does not exist in mysql2<2.3.0
313
- addHook({ name: 'mysql2', file: 'lib/pool_cluster.js', versions: ['2.3.0 - 3.11.4'] }, wrapPoolCluster)
377
+ addHook(
378
+ { name: 'mysql2', file: 'lib/pool_cluster.js', versions: ['2.3.0 - 3.11.4'] },
379
+ /** @type {(moduleExports: unknown, version: string) => unknown} */ (wrapPoolCluster)
380
+ )
@@ -41,7 +41,7 @@ const testSuiteToTestStatuses = new Map()
41
41
  const testSuiteToErrors = new Map()
42
42
  const testsToTestStatuses = new Map()
43
43
 
44
- const RUM_FLUSH_WAIT_TIME = Number(getValueFromEnvSources('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS')) || 1000
44
+ const RUM_FLUSH_WAIT_TIME = Number(getValueFromEnvSources('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS')) || 500
45
45
 
46
46
  let applyRepeatEachIndex = null
47
47
 
@@ -396,6 +396,14 @@ function testEndHandler ({
396
396
  }
397
397
  }
398
398
 
399
+ // Check if all EFD retries failed
400
+ if (testStatuses.length === earlyFlakeDetectionNumRetries + 1 &&
401
+ (test._ddIsNew || test._ddIsModified) &&
402
+ test._ddIsEfdRetry &&
403
+ testStatuses.every(status => status === 'fail')) {
404
+ test._ddHasFailedAllRetries = true
405
+ }
406
+
399
407
  // this handles tests that do not go through the worker process (because they're skipped)
400
408
  if (shouldCreateTestSpan) {
401
409
  const testResult = results.at(-1)
@@ -0,0 +1,92 @@
1
+ 'use strict'
2
+
3
+ const shimmer = require('../../datadog-shimmer')
4
+ const { channel, addHook } = require('./helpers/instrument')
5
+
6
+ const checkoutSessionCreateFinishCh = channel('datadog:stripe:checkoutSession:create:finish')
7
+ const paymentIntentCreateFinishCh = channel('datadog:stripe:paymentIntent:create:finish')
8
+ const constructEventFinishCh = channel('datadog:stripe:constructEvent:finish')
9
+
10
+ function wrapSessionCreate (create) {
11
+ return function wrappedSessionCreate () {
12
+ const promise = create.apply(this, arguments)
13
+
14
+ if (!checkoutSessionCreateFinishCh.hasSubscribers) return promise
15
+
16
+ return promise.then((result) => {
17
+ checkoutSessionCreateFinishCh.publish(result)
18
+ return result
19
+ })
20
+ }
21
+ }
22
+
23
+ function wrapPaymentIntentCreate (create) {
24
+ return function wrappedPaymentIntentCreate () {
25
+ const promise = create.apply(this, arguments)
26
+
27
+ if (!paymentIntentCreateFinishCh.hasSubscribers) return promise
28
+
29
+ return promise.then((result) => {
30
+ paymentIntentCreateFinishCh.publish(result)
31
+ return result
32
+ })
33
+ }
34
+ }
35
+
36
+ function wrapConstructEvent (constructEvent) {
37
+ return function wrappedConstructEvent () {
38
+ const result = constructEvent.apply(this, arguments)
39
+
40
+ // no need to check for hasSubscribers,
41
+ // if it's false, the publish function will be noop
42
+ constructEventFinishCh.publish(result)
43
+
44
+ return result
45
+ }
46
+ }
47
+
48
+ function wrapConstructEventAsync (constructEventAsync) {
49
+ return function wrappedConstructEventAsync () {
50
+ const promise = constructEventAsync.apply(this, arguments)
51
+
52
+ if (!constructEventFinishCh.hasSubscribers) return promise
53
+
54
+ return promise.then((result) => {
55
+ constructEventFinishCh.publish(result)
56
+ return result
57
+ })
58
+ }
59
+ }
60
+
61
+ function wrapStripe (Stripe) {
62
+ return function wrappedStripe () {
63
+ let stripe = Stripe.apply(this, arguments)
64
+
65
+ // to support both with and without "new" operator syntax
66
+ if (this instanceof Stripe) {
67
+ stripe = this
68
+ }
69
+
70
+ if (typeof stripe.checkout?.sessions?.create === 'function') {
71
+ shimmer.wrap(stripe.checkout.sessions, 'create', wrapSessionCreate)
72
+ }
73
+ if (typeof stripe.paymentIntents?.create === 'function') {
74
+ shimmer.wrap(stripe.paymentIntents, 'create', wrapPaymentIntentCreate)
75
+ }
76
+ if (typeof stripe.webhooks?.constructEvent === 'function') {
77
+ shimmer.wrap(stripe.webhooks, 'constructEvent', wrapConstructEvent)
78
+ }
79
+ if (typeof stripe.webhooks?.constructEventAsync === 'function') {
80
+ shimmer.wrap(stripe.webhooks, 'constructEventAsync', wrapConstructEventAsync)
81
+ }
82
+
83
+ return stripe
84
+ }
85
+ }
86
+
87
+ addHook({
88
+ name: 'stripe',
89
+ versions: ['9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '>=20.0.0'],
90
+ }, Stripe => {
91
+ return shimmer.wrapFunction(Stripe, wrapStripe)
92
+ })
@@ -1036,6 +1036,17 @@ addHook({
1036
1036
  }
1037
1037
  }
1038
1038
 
1039
+ // Check if all EFD retries failed
1040
+ const providedContext = getProvidedContext()
1041
+ if (providedContext.isEarlyFlakeDetectionEnabled && (newTasks.has(task) || modifiedTasks.has(task))) {
1042
+ const statuses = taskToStatuses.get(task)
1043
+ // statuses only includes repetitions (not the initial run), so we check against numRepeats (not +1)
1044
+ if (statuses && statuses.length === providedContext.numRepeats &&
1045
+ statuses.every(status => status === 'fail')) {
1046
+ hasFailedAllRetries = true
1047
+ }
1048
+ }
1049
+
1039
1050
  if (testCtx) {
1040
1051
  const isRetry = task.result?.retryCount > 0
1041
1052
  // `duration` is the duration of all the retries, so it can't be used if there are retries
@@ -9,6 +9,19 @@ class AmqplibConsumerPlugin extends ConsumerPlugin {
9
9
  static id = 'amqplib'
10
10
  static operation = 'consume'
11
11
 
12
+ start (ctx) {
13
+ if (!this.config.dsmEnabled) return
14
+ const { fields = {}, message, queue } = ctx
15
+ if (!message?.properties?.headers) return
16
+
17
+ const { span } = ctx.currentStore
18
+ const queueName = queue || fields.queue || fields.routingKey
19
+ const payloadSize = getAmqpMessageSize({ headers: message.properties.headers, content: message.content })
20
+ this.tracer.decodeDataStreamsContext(message.properties.headers)
21
+ this.tracer
22
+ .setCheckpoint(['direction:in', `topic:${queueName}`, 'type:rabbitmq'], span, payloadSize)
23
+ }
24
+
12
25
  bindStart (ctx) {
13
26
  const { method, fields = {}, message, queue } = ctx
14
27
 
@@ -17,7 +30,7 @@ class AmqplibConsumerPlugin extends ConsumerPlugin {
17
30
  const childOf = extract(this.tracer, message)
18
31
 
19
32
  const queueName = queue || fields.queue || fields.routingKey
20
- const span = this.startSpan({
33
+ this.startSpan({
21
34
  childOf,
22
35
  resource: getResourceName(method, fields),
23
36
  type: 'worker',
@@ -31,15 +44,6 @@ class AmqplibConsumerPlugin extends ConsumerPlugin {
31
44
  },
32
45
  }, ctx)
33
46
 
34
- if (
35
- this.config.dsmEnabled && message?.properties?.headers
36
- ) {
37
- const payloadSize = getAmqpMessageSize({ headers: message.properties.headers, content: message.content })
38
- this.tracer.decodeDataStreamsContext(message.properties.headers)
39
- this.tracer
40
- .setCheckpoint(['direction:in', `topic:${queueName}`, 'type:rabbitmq'], span, payloadSize)
41
- }
42
-
43
47
  return ctx.currentStore
44
48
  }
45
49
  }
@@ -10,8 +10,30 @@ class AmqplibProducerPlugin extends ProducerPlugin {
10
10
  static id = 'amqplib'
11
11
  static operation = 'publish'
12
12
 
13
+ start (ctx) {
14
+ if (!this.config.dsmEnabled) return
15
+ const { fields, message } = ctx
16
+ const { span } = ctx.currentStore
17
+
18
+ const hasRoutingKey = fields.routingKey != null
19
+ const payloadSize = getAmqpMessageSize({ content: message, headers: fields.headers })
20
+
21
+ // there are two ways to send messages in RabbitMQ:
22
+ // 1. using an exchange and a routing key in which DSM connects via the exchange
23
+ // 2. using an unnamed exchange and a routing key in which DSM connects via the topic
24
+ const exchangeOrTopicTag = hasRoutingKey && !fields.exchange
25
+ ? `topic:${fields.routingKey}`
26
+ : `exchange:${fields.exchange}`
27
+
28
+ const dataStreamsContext = this.tracer.setCheckpoint(
29
+ ['direction:out', exchangeOrTopicTag, `has_routing_key:${hasRoutingKey}`, 'type:rabbitmq'],
30
+ span, payloadSize
31
+ )
32
+ DsmPathwayCodec.encode(dataStreamsContext, fields.headers)
33
+ }
34
+
13
35
  bindStart (ctx) {
14
- const { channel = {}, method, fields, message } = ctx
36
+ const { channel = {}, method, fields } = ctx
15
37
 
16
38
  if (method !== 'basic.publish') return
17
39
 
@@ -34,24 +56,6 @@ class AmqplibProducerPlugin extends ProducerPlugin {
34
56
 
35
57
  this.tracer.inject(span, TEXT_MAP, fields.headers)
36
58
 
37
- if (this.config.dsmEnabled) {
38
- const hasRoutingKey = fields.routingKey != null
39
- const payloadSize = getAmqpMessageSize({ content: message, headers: fields.headers })
40
-
41
- // there are two ways to send messages in RabbitMQ:
42
- // 1. using an exchange and a routing key in which DSM connects via the exchange
43
- // 2. using an unnamed exchange and a routing key in which DSM connects via the topic
44
- const exchangeOrTopicTag = hasRoutingKey && !fields.exchange
45
- ? `topic:${fields.routingKey}`
46
- : `exchange:${fields.exchange}`
47
-
48
- const dataStreamsContext = this.tracer
49
- .setCheckpoint(
50
- ['direction:out', exchangeOrTopicTag, `has_routing_key:${hasRoutingKey}`, 'type:rabbitmq']
51
- , span, payloadSize)
52
- DsmPathwayCodec.encode(dataStreamsContext, fields.headers)
53
- }
54
-
55
59
  return ctx.currentStore
56
60
  }
57
61
  }