dd-trace 4.45.0 → 4.47.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 (153) hide show
  1. package/LICENSE-3rdparty.csv +2 -0
  2. package/index.d.ts +20 -8
  3. package/package.json +11 -5
  4. package/packages/datadog-instrumentations/src/aerospike.js +1 -1
  5. package/packages/datadog-instrumentations/src/apollo-server.js +1 -1
  6. package/packages/datadog-instrumentations/src/aws-sdk.js +4 -4
  7. package/packages/datadog-instrumentations/src/body-parser.js +4 -4
  8. package/packages/datadog-instrumentations/src/cassandra-driver.js +2 -2
  9. package/packages/datadog-instrumentations/src/child_process.js +2 -2
  10. package/packages/datadog-instrumentations/src/connect.js +4 -4
  11. package/packages/datadog-instrumentations/src/cookie-parser.js +4 -4
  12. package/packages/datadog-instrumentations/src/couchbase.js +12 -12
  13. package/packages/datadog-instrumentations/src/cucumber.js +294 -56
  14. package/packages/datadog-instrumentations/src/dns.js +10 -10
  15. package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
  16. package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +3 -3
  17. package/packages/datadog-instrumentations/src/express.js +4 -4
  18. package/packages/datadog-instrumentations/src/fastify.js +6 -6
  19. package/packages/datadog-instrumentations/src/fetch.js +1 -1
  20. package/packages/datadog-instrumentations/src/find-my-way.js +2 -2
  21. package/packages/datadog-instrumentations/src/fs.js +2 -2
  22. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +2 -2
  23. package/packages/datadog-instrumentations/src/grpc/client.js +4 -6
  24. package/packages/datadog-instrumentations/src/grpc/server.js +2 -2
  25. package/packages/datadog-instrumentations/src/hapi.js +10 -13
  26. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  27. package/packages/datadog-instrumentations/src/http/client.js +3 -3
  28. package/packages/datadog-instrumentations/src/jest.js +8 -5
  29. package/packages/datadog-instrumentations/src/kafkajs.js +67 -31
  30. package/packages/datadog-instrumentations/src/knex.js +2 -2
  31. package/packages/datadog-instrumentations/src/koa.js +5 -5
  32. package/packages/datadog-instrumentations/src/ldapjs.js +1 -1
  33. package/packages/datadog-instrumentations/src/mariadb.js +8 -8
  34. package/packages/datadog-instrumentations/src/memcached.js +2 -2
  35. package/packages/datadog-instrumentations/src/microgateway-core.js +7 -5
  36. package/packages/datadog-instrumentations/src/mocha/common.js +1 -1
  37. package/packages/datadog-instrumentations/src/mocha/main.js +139 -53
  38. package/packages/datadog-instrumentations/src/mocha/utils.js +37 -18
  39. package/packages/datadog-instrumentations/src/mocha/worker.js +29 -1
  40. package/packages/datadog-instrumentations/src/mocha.js +4 -0
  41. package/packages/datadog-instrumentations/src/moleculer/server.js +2 -2
  42. package/packages/datadog-instrumentations/src/mongodb-core.js +7 -7
  43. package/packages/datadog-instrumentations/src/mongoose.js +5 -6
  44. package/packages/datadog-instrumentations/src/mysql.js +3 -3
  45. package/packages/datadog-instrumentations/src/mysql2.js +6 -6
  46. package/packages/datadog-instrumentations/src/net.js +2 -2
  47. package/packages/datadog-instrumentations/src/next.js +5 -5
  48. package/packages/datadog-instrumentations/src/openai.js +62 -71
  49. package/packages/datadog-instrumentations/src/oracledb.js +8 -8
  50. package/packages/datadog-instrumentations/src/passport-http.js +1 -1
  51. package/packages/datadog-instrumentations/src/passport-local.js +1 -1
  52. package/packages/datadog-instrumentations/src/passport-utils.js +1 -1
  53. package/packages/datadog-instrumentations/src/pg.js +60 -5
  54. package/packages/datadog-instrumentations/src/pino.js +4 -4
  55. package/packages/datadog-instrumentations/src/playwright.js +6 -4
  56. package/packages/datadog-instrumentations/src/redis.js +2 -2
  57. package/packages/datadog-instrumentations/src/restify.js +4 -4
  58. package/packages/datadog-instrumentations/src/rhea.js +4 -4
  59. package/packages/datadog-instrumentations/src/router.js +5 -5
  60. package/packages/datadog-instrumentations/src/sharedb.js +2 -2
  61. package/packages/datadog-instrumentations/src/vitest.js +188 -12
  62. package/packages/datadog-instrumentations/src/winston.js +2 -3
  63. package/packages/datadog-plugin-amqplib/src/consumer.js +1 -3
  64. package/packages/datadog-plugin-aws-sdk/src/base.js +33 -0
  65. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
  66. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -0
  67. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
  68. package/packages/datadog-plugin-cucumber/src/index.js +24 -1
  69. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +39 -10
  70. package/packages/datadog-plugin-cypress/src/support.js +4 -1
  71. package/packages/datadog-plugin-hapi/src/index.js +2 -2
  72. package/packages/datadog-plugin-http/src/client.js +1 -42
  73. package/packages/datadog-plugin-http2/src/client.js +1 -26
  74. package/packages/datadog-plugin-jest/src/index.js +18 -1
  75. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +20 -0
  76. package/packages/datadog-plugin-kafkajs/src/consumer.js +1 -2
  77. package/packages/datadog-plugin-kafkajs/src/index.js +3 -1
  78. package/packages/datadog-plugin-mocha/src/index.js +18 -0
  79. package/packages/datadog-plugin-openai/src/index.js +85 -65
  80. package/packages/datadog-plugin-playwright/src/index.js +9 -0
  81. package/packages/datadog-plugin-rhea/src/consumer.js +1 -3
  82. package/packages/datadog-plugin-vitest/src/index.js +68 -3
  83. package/packages/datadog-shimmer/src/shimmer.js +144 -10
  84. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  85. package/packages/dd-trace/src/appsec/blocking.js +23 -17
  86. package/packages/dd-trace/src/appsec/channels.js +4 -2
  87. package/packages/dd-trace/src/appsec/graphql.js +3 -1
  88. package/packages/dd-trace/src/appsec/iast/iast-log.js +2 -1
  89. package/packages/dd-trace/src/appsec/rasp/index.js +103 -0
  90. package/packages/dd-trace/src/appsec/rasp/sql_injection.js +86 -0
  91. package/packages/dd-trace/src/appsec/rasp/ssrf.js +37 -0
  92. package/packages/dd-trace/src/appsec/rasp/utils.js +63 -0
  93. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -0
  94. package/packages/dd-trace/src/appsec/remote_config/index.js +16 -7
  95. package/packages/dd-trace/src/appsec/remote_config/manager.js +93 -52
  96. package/packages/dd-trace/src/appsec/rule_manager.js +8 -0
  97. package/packages/dd-trace/src/appsec/telemetry.js +3 -3
  98. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +33 -14
  99. package/packages/dd-trace/src/appsec/waf/waf_manager.js +2 -1
  100. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +4 -0
  101. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +15 -1
  102. package/packages/dd-trace/src/config.js +100 -40
  103. package/packages/dd-trace/src/constants.js +11 -1
  104. package/packages/dd-trace/src/data_streams_context.js +3 -0
  105. package/packages/dd-trace/src/datastreams/fnv.js +23 -0
  106. package/packages/dd-trace/src/datastreams/pathway.js +12 -5
  107. package/packages/dd-trace/src/datastreams/processor.js +35 -0
  108. package/packages/dd-trace/src/datastreams/schemas/schema.js +8 -0
  109. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +125 -0
  110. package/packages/dd-trace/src/datastreams/schemas/schema_sampler.js +29 -0
  111. package/packages/dd-trace/src/debugger/devtools_client/config.js +24 -0
  112. package/packages/dd-trace/src/debugger/devtools_client/index.js +57 -0
  113. package/packages/dd-trace/src/debugger/devtools_client/inspector_promises_polyfill.js +23 -0
  114. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +164 -0
  115. package/packages/dd-trace/src/debugger/devtools_client/send.js +28 -0
  116. package/packages/dd-trace/src/debugger/devtools_client/session.js +7 -0
  117. package/packages/dd-trace/src/debugger/devtools_client/state.js +47 -0
  118. package/packages/dd-trace/src/debugger/devtools_client/status.js +109 -0
  119. package/packages/dd-trace/src/debugger/index.js +92 -0
  120. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +29 -2
  121. package/packages/dd-trace/src/exporters/common/request.js +1 -1
  122. package/packages/dd-trace/src/lambda/handler.js +1 -0
  123. package/packages/dd-trace/src/lambda/index.js +12 -1
  124. package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -6
  125. package/packages/dd-trace/src/payload-tagging/config/aws.json +30 -0
  126. package/packages/dd-trace/src/payload-tagging/config/index.js +30 -0
  127. package/packages/dd-trace/src/payload-tagging/index.js +93 -0
  128. package/packages/dd-trace/src/payload-tagging/tagging.js +83 -0
  129. package/packages/dd-trace/src/plugin_manager.js +11 -10
  130. package/packages/dd-trace/src/plugins/ci_plugin.js +33 -8
  131. package/packages/dd-trace/src/plugins/util/env.js +5 -2
  132. package/packages/dd-trace/src/plugins/util/test.js +24 -4
  133. package/packages/dd-trace/src/profiler.js +15 -5
  134. package/packages/dd-trace/src/profiling/config.js +7 -4
  135. package/packages/dd-trace/src/profiling/exporter_cli.js +13 -1
  136. package/packages/dd-trace/src/profiling/exporters/agent.js +8 -2
  137. package/packages/dd-trace/src/profiling/profiler.js +0 -9
  138. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns.js +13 -0
  139. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookup.js +16 -0
  140. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookupservice.js +16 -0
  141. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_resolve.js +24 -0
  142. package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_reverse.js +16 -0
  143. package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +48 -0
  144. package/packages/dd-trace/src/profiling/profilers/event_plugins/net.js +24 -0
  145. package/packages/dd-trace/src/profiling/profilers/events.js +108 -32
  146. package/packages/dd-trace/src/profiling/profilers/shared.js +5 -0
  147. package/packages/dd-trace/src/profiling/profilers/wall.js +9 -3
  148. package/packages/dd-trace/src/profiling/ssi-heuristics.js +59 -60
  149. package/packages/dd-trace/src/proxy.js +31 -24
  150. package/packages/dd-trace/src/span_stats.js +4 -2
  151. package/packages/dd-trace/src/telemetry/index.js +23 -6
  152. package/packages/dd-trace/src/telemetry/logs/index.js +20 -0
  153. package/packages/dd-trace/src/appsec/rasp.js +0 -176
@@ -48,14 +48,14 @@ addHook({ name: 'sharedb', versions: ['>=1'], file: 'lib/agent.js' }, Agent => {
48
48
 
49
49
  callback = callbackResource.bind(callback)
50
50
 
51
- arguments[1] = asyncResource.bind(function (error, res) {
51
+ arguments[1] = shimmer.wrapFunction(callback, callback => asyncResource.bind(function (error, res) {
52
52
  if (error) {
53
53
  errorCh.publish(error)
54
54
  }
55
55
  finishCh.publish({ request, res })
56
56
 
57
57
  return callback.apply(this, arguments)
58
- })
58
+ }))
59
59
 
60
60
  try {
61
61
  return origHandleMessageFn.apply(this, arguments)
@@ -1,6 +1,6 @@
1
1
  const { addHook, channel, AsyncResource } = require('./helpers/instrument')
2
2
  const shimmer = require('../../datadog-shimmer')
3
- const { NUM_FAILED_TEST_RETRIES } = require('../../dd-trace/src/plugins/util/test')
3
+ const log = require('../../dd-trace/src/log')
4
4
 
5
5
  // test hooks
6
6
  const testStartCh = channel('ci:vitest:test:start')
@@ -8,6 +8,7 @@ const testFinishTimeCh = channel('ci:vitest:test:finish-time')
8
8
  const testPassCh = channel('ci:vitest:test:pass')
9
9
  const testErrorCh = channel('ci:vitest:test:error')
10
10
  const testSkipCh = channel('ci:vitest:test:skip')
11
+ const isNewTestCh = channel('ci:vitest:test:is-new')
11
12
 
12
13
  // test suite hooks
13
14
  const testSuiteStartCh = channel('ci:vitest:test-suite:start')
@@ -18,9 +19,13 @@ const testSuiteErrorCh = channel('ci:vitest:test-suite:error')
18
19
  const testSessionStartCh = channel('ci:vitest:session:start')
19
20
  const testSessionFinishCh = channel('ci:vitest:session:finish')
20
21
  const libraryConfigurationCh = channel('ci:vitest:library-configuration')
22
+ const knownTestsCh = channel('ci:vitest:known-tests')
23
+ const isEarlyFlakeDetectionFaultyCh = channel('ci:vitest:is-early-flake-detection-faulty')
21
24
 
22
25
  const taskToAsync = new WeakMap()
23
-
26
+ const taskToStatuses = new WeakMap()
27
+ const newTasks = new WeakSet()
28
+ const switchedStatuses = new WeakSet()
24
29
  const sessionAsyncResource = new AsyncResource('bound-anonymous-fn')
25
30
 
26
31
  function isReporterPackage (vitestPackage) {
@@ -108,17 +113,60 @@ function getSortWrapper (sort) {
108
113
  // So we will use the sort from BaseSequencer. This means that a custom sequencer
109
114
  // will not work. This will be a known limitation.
110
115
  let isFlakyTestRetriesEnabled = false
116
+ let flakyTestRetriesCount = 0
117
+ let isEarlyFlakeDetectionEnabled = false
118
+ let earlyFlakeDetectionNumRetries = 0
119
+ let isEarlyFlakeDetectionFaulty = false
120
+ let knownTests = {}
111
121
 
112
122
  try {
113
123
  const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh)
114
124
  if (!err) {
115
125
  isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
126
+ flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
127
+ isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
128
+ earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
116
129
  }
117
130
  } catch (e) {
118
131
  isFlakyTestRetriesEnabled = false
132
+ isEarlyFlakeDetectionEnabled = false
133
+ }
134
+
135
+ if (isFlakyTestRetriesEnabled && !this.ctx.config.retry && flakyTestRetriesCount > 0) {
136
+ this.ctx.config.retry = flakyTestRetriesCount
119
137
  }
120
- if (isFlakyTestRetriesEnabled && !this.ctx.config.retry) {
121
- this.ctx.config.retry = NUM_FAILED_TEST_RETRIES
138
+
139
+ if (isEarlyFlakeDetectionEnabled) {
140
+ const knownTestsResponse = await getChannelPromise(knownTestsCh)
141
+ if (!knownTestsResponse.err) {
142
+ knownTests = knownTestsResponse.knownTests
143
+ const testFilepaths = await this.ctx.getTestFilepaths()
144
+
145
+ isEarlyFlakeDetectionFaultyCh.publish({
146
+ knownTests: knownTests.vitest || {},
147
+ testFilepaths,
148
+ onDone: (isFaulty) => {
149
+ isEarlyFlakeDetectionFaulty = isFaulty
150
+ }
151
+ })
152
+ if (isEarlyFlakeDetectionFaulty) {
153
+ isEarlyFlakeDetectionEnabled = false
154
+ log.warn('Early flake detection is disabled because the number of new tests is too high.')
155
+ } else {
156
+ // TODO: use this to pass session and module IDs to the worker, instead of polluting process.env
157
+ // Note: setting this.ctx.config.provide directly does not work because it's cached
158
+ try {
159
+ const workspaceProject = this.ctx.getCoreWorkspaceProject()
160
+ workspaceProject._provided._ddKnownTests = knownTests.vitest
161
+ workspaceProject._provided._ddIsEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
162
+ workspaceProject._provided._ddEarlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
163
+ } catch (e) {
164
+ log.warn('Could not send known tests to workers so Early Flake Detection will not work.')
165
+ }
166
+ }
167
+ } else {
168
+ isEarlyFlakeDetectionEnabled = false
169
+ }
122
170
  }
123
171
 
124
172
  let testCodeCoverageLinesTotal
@@ -153,6 +201,8 @@ function getSortWrapper (sort) {
153
201
  status: getSessionStatus(this.state),
154
202
  testCodeCoverageLinesTotal,
155
203
  error,
204
+ isEarlyFlakeDetectionEnabled,
205
+ isEarlyFlakeDetectionFaulty,
156
206
  onFinish
157
207
  })
158
208
  })
@@ -187,12 +237,83 @@ addHook({
187
237
  file: 'dist/runners.js'
188
238
  }, (vitestPackage) => {
189
239
  const { VitestTestRunner } = vitestPackage
240
+
241
+ // `onBeforeRunTask` is run before any repetition or attempt is run
242
+ shimmer.wrap(VitestTestRunner.prototype, 'onBeforeRunTask', onBeforeRunTask => async function (task) {
243
+ const testName = getTestName(task)
244
+ try {
245
+ const {
246
+ _ddKnownTests: knownTests,
247
+ _ddIsEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled,
248
+ _ddEarlyFlakeDetectionNumRetries: numRepeats
249
+ } = globalThis.__vitest_worker__.providedContext
250
+
251
+ if (isEarlyFlakeDetectionEnabled) {
252
+ isNewTestCh.publish({
253
+ knownTests,
254
+ testSuiteAbsolutePath: task.file.filepath,
255
+ testName,
256
+ onDone: (isNew) => {
257
+ if (isNew) {
258
+ task.repeats = numRepeats
259
+ newTasks.add(task)
260
+ taskToStatuses.set(task, [])
261
+ }
262
+ }
263
+ })
264
+ }
265
+ } catch (e) {
266
+ log.error('Vitest workers could not parse known tests, so Early Flake Detection will not work.')
267
+ }
268
+
269
+ return onBeforeRunTask.apply(this, arguments)
270
+ })
271
+
272
+ // `onAfterRunTask` is run after all repetitions or attempts are run
273
+ shimmer.wrap(VitestTestRunner.prototype, 'onAfterRunTask', onAfterRunTask => async function (task) {
274
+ const {
275
+ _ddIsEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled
276
+ } = globalThis.__vitest_worker__.providedContext
277
+
278
+ if (isEarlyFlakeDetectionEnabled && taskToStatuses.has(task)) {
279
+ const statuses = taskToStatuses.get(task)
280
+ // If the test has passed at least once, we consider it passed
281
+ if (statuses.includes('pass')) {
282
+ if (task.result.state === 'fail') {
283
+ switchedStatuses.add(task)
284
+ }
285
+ task.result.state = 'pass'
286
+ }
287
+ }
288
+
289
+ return onAfterRunTask.apply(this, arguments)
290
+ })
291
+
190
292
  // test start (only tests that are not marked as skip or todo)
293
+ // `onBeforeTryTask` is run for every repetition and attempt of the test
191
294
  shimmer.wrap(VitestTestRunner.prototype, 'onBeforeTryTask', onBeforeTryTask => async function (task, retryInfo) {
192
295
  if (!testStartCh.hasSubscribers) {
193
296
  return onBeforeTryTask.apply(this, arguments)
194
297
  }
195
- const { retry: numAttempt } = retryInfo
298
+ const testName = getTestName(task)
299
+ let isNew = false
300
+ let isEarlyFlakeDetectionEnabled = false
301
+
302
+ try {
303
+ const {
304
+ _ddIsEarlyFlakeDetectionEnabled
305
+ } = globalThis.__vitest_worker__.providedContext
306
+
307
+ isEarlyFlakeDetectionEnabled = _ddIsEarlyFlakeDetectionEnabled
308
+
309
+ if (isEarlyFlakeDetectionEnabled) {
310
+ isNew = newTasks.has(task)
311
+ }
312
+ } catch (e) {
313
+ log.error('Vitest workers could not parse known tests, so Early Flake Detection will not work.')
314
+ }
315
+ const { retry: numAttempt, repeats: numRepetition } = retryInfo
316
+
196
317
  // We finish the previous test here because we know it has failed already
197
318
  if (numAttempt > 0) {
198
319
  const asyncResource = taskToAsync.get(task)
@@ -204,14 +325,58 @@ addHook({
204
325
  }
205
326
  }
206
327
 
328
+ const lastExecutionStatus = task.result.state
329
+
330
+ // These clauses handle task.repeats, whether EFD is enabled or not
331
+ // The only thing that EFD does is to forcefully pass the test if it has passed at least once
332
+ if (numRepetition > 0 && numRepetition < task.repeats) { // it may or may have not failed
333
+ // Here we finish the earlier iteration,
334
+ // as long as it's not the _last_ iteration (which will be finished normally)
335
+
336
+ // TODO: check test duration (not to repeat if it's too slow)
337
+ const asyncResource = taskToAsync.get(task)
338
+ if (asyncResource) {
339
+ if (lastExecutionStatus === 'fail') {
340
+ const testError = task.result?.errors?.[0]
341
+ asyncResource.runInAsyncScope(() => {
342
+ testErrorCh.publish({ error: testError })
343
+ })
344
+ } else {
345
+ asyncResource.runInAsyncScope(() => {
346
+ testPassCh.publish({ task })
347
+ })
348
+ }
349
+ if (isEarlyFlakeDetectionEnabled) {
350
+ const statuses = taskToStatuses.get(task)
351
+ statuses.push(lastExecutionStatus)
352
+ // If we don't "reset" the result.state to "pass", once a repetition fails,
353
+ // vitest will always consider the test as failed, so we can't read the actual status
354
+ task.result.state = 'pass'
355
+ }
356
+ }
357
+ } else if (numRepetition === task.repeats) {
358
+ const asyncResource = taskToAsync.get(task)
359
+ if (lastExecutionStatus === 'fail') {
360
+ const testError = task.result?.errors?.[0]
361
+ asyncResource.runInAsyncScope(() => {
362
+ testErrorCh.publish({ error: testError })
363
+ })
364
+ } else {
365
+ asyncResource.runInAsyncScope(() => {
366
+ testPassCh.publish({ task })
367
+ })
368
+ }
369
+ }
370
+
207
371
  const asyncResource = new AsyncResource('bound-anonymous-fn')
208
372
  taskToAsync.set(task, asyncResource)
209
373
 
210
374
  asyncResource.runInAsyncScope(() => {
211
375
  testStartCh.publish({
212
- testName: getTestName(task),
376
+ testName,
213
377
  testSuiteAbsolutePath: task.file.filepath,
214
- isRetry: numAttempt > 0
378
+ isRetry: numAttempt > 0 || numRepetition > 0,
379
+ isNew
215
380
  })
216
381
  })
217
382
  return onBeforeTryTask.apply(this, arguments)
@@ -229,7 +394,7 @@ addHook({
229
394
  const asyncResource = taskToAsync.get(task)
230
395
 
231
396
  if (asyncResource) {
232
- // We don't finish here because the test might fail in a later hook
397
+ // We don't finish here because the test might fail in a later hook (afterEach)
233
398
  asyncResource.runInAsyncScope(() => {
234
399
  testFinishTimeCh.publish({ status, task })
235
400
  })
@@ -269,7 +434,16 @@ addHook({
269
434
 
270
435
  addHook({
271
436
  name: 'vitest',
272
- versions: ['>=2.0.5'],
437
+ versions: ['>=2.1.0'],
438
+ filePattern: 'dist/chunks/RandomSequencer.*'
439
+ }, (randomSequencerPackage) => {
440
+ shimmer.wrap(randomSequencerPackage.B.prototype, 'sort', getSortWrapper)
441
+ return randomSequencerPackage
442
+ })
443
+
444
+ addHook({
445
+ name: 'vitest',
446
+ versions: ['>=2.0.5 <2.1.0'],
273
447
  filePattern: 'dist/chunks/index.*'
274
448
  }, (vitestPackage) => {
275
449
  if (isReporterPackageNewest(vitestPackage)) {
@@ -322,19 +496,21 @@ addHook({
322
496
  testTasks.forEach(task => {
323
497
  const testAsyncResource = taskToAsync.get(task)
324
498
  const { result } = task
499
+ // We have to trick vitest into thinking that the test has passed
500
+ // but we want to report it as failed if it did fail
501
+ const isSwitchedStatus = switchedStatuses.has(task)
325
502
 
326
503
  if (result) {
327
504
  const { state, duration, errors } = result
328
505
  if (state === 'skip') { // programmatic skip
329
506
  testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.file.filepath })
330
- } else if (state === 'pass') {
507
+ } else if (state === 'pass' && !isSwitchedStatus) {
331
508
  if (testAsyncResource) {
332
509
  testAsyncResource.runInAsyncScope(() => {
333
510
  testPassCh.publish({ task })
334
511
  })
335
512
  }
336
- } else if (state === 'fail') {
337
- // If it's failing, we have no accurate finish time, so we have to use `duration`
513
+ } else if (state === 'fail' || isSwitchedStatus) {
338
514
  let testError
339
515
 
340
516
  if (errors?.length) {
@@ -42,13 +42,12 @@ function wrapMethod (method, logCh) {
42
42
 
43
43
  if (patched.has(transport) || typeof transport.log !== 'function') continue
44
44
 
45
- const log = transport.log
46
- transport.log = function wrappedLog (level, msg, meta, callback) {
45
+ shimmer.wrap(transport, 'log', log => function wrappedLog (level, msg, meta, callback) {
47
46
  const payload = { message: meta || {} }
48
47
  logCh.publish(payload)
49
48
  arguments[2] = payload.message
50
49
  log.apply(this, arguments)
51
- }
50
+ })
52
51
  patched.add(transport)
53
52
  }
54
53
  }
@@ -3,7 +3,6 @@
3
3
  const { TEXT_MAP } = require('../../../ext/formats')
4
4
  const ConsumerPlugin = require('../../dd-trace/src/plugins/consumer')
5
5
  const { getAmqpMessageSize } = require('../../dd-trace/src/datastreams/processor')
6
- const { DsmPathwayCodec } = require('../../dd-trace/src/datastreams/pathway')
7
6
  const { getResourceName } = require('./util')
8
7
 
9
8
  class AmqplibConsumerPlugin extends ConsumerPlugin {
@@ -30,8 +29,7 @@ class AmqplibConsumerPlugin extends ConsumerPlugin {
30
29
  })
31
30
 
32
31
  if (
33
- this.config.dsmEnabled && message?.properties?.headers &&
34
- DsmPathwayCodec.contextExists(message.properties.headers)
32
+ this.config.dsmEnabled && message?.properties?.headers
35
33
  ) {
36
34
  const payloadSize = getAmqpMessageSize({ headers: message.properties.headers, content: message.content })
37
35
  const queue = fields.queue ? fields.queue : fields.routingKey
@@ -5,9 +5,11 @@ const ClientPlugin = require('../../dd-trace/src/plugins/client')
5
5
  const { storage } = require('../../datadog-core')
6
6
  const { isTrue } = require('../../dd-trace/src/util')
7
7
  const coalesce = require('koalas')
8
+ const { tagsFromRequest, tagsFromResponse } = require('../../dd-trace/src/payload-tagging')
8
9
 
9
10
  class BaseAwsSdkPlugin extends ClientPlugin {
10
11
  static get id () { return 'aws' }
12
+ static get isPayloadReporter () { return false }
11
13
 
12
14
  get serviceIdentifier () {
13
15
  const id = this.constructor.id.toLowerCase()
@@ -20,6 +22,14 @@ class BaseAwsSdkPlugin extends ClientPlugin {
20
22
  return id
21
23
  }
22
24
 
25
+ get cloudTaggingConfig () {
26
+ return this._tracerConfig.cloudPayloadTagging
27
+ }
28
+
29
+ get payloadTaggingRules () {
30
+ return this.cloudTaggingConfig.rules.aws?.[this.constructor.id]
31
+ }
32
+
23
33
  constructor (...args) {
24
34
  super(...args)
25
35
 
@@ -51,6 +61,12 @@ class BaseAwsSdkPlugin extends ClientPlugin {
51
61
 
52
62
  this.requestInject(span, request)
53
63
 
64
+ if (this.constructor.isPayloadReporter && this.cloudTaggingConfig.requestsEnabled) {
65
+ const maxDepth = this.cloudTaggingConfig.maxDepth
66
+ const requestTags = tagsFromRequest(this.payloadTaggingRules, request.params, { maxDepth })
67
+ span.addTags(requestTags)
68
+ }
69
+
54
70
  const store = storage.getStore()
55
71
 
56
72
  this.enter(span, store)
@@ -116,6 +132,7 @@ class BaseAwsSdkPlugin extends ClientPlugin {
116
132
  const params = response.request.params
117
133
  const operation = response.request.operation
118
134
  const extraTags = this.generateTags(params, operation, response) || {}
135
+
119
136
  const tags = Object.assign({
120
137
  'aws.response.request_id': response.requestId,
121
138
  'resource.name': operation,
@@ -123,6 +140,22 @@ class BaseAwsSdkPlugin extends ClientPlugin {
123
140
  }, extraTags)
124
141
 
125
142
  span.addTags(tags)
143
+
144
+ if (this.constructor.isPayloadReporter && this.cloudTaggingConfig.responsesEnabled) {
145
+ const maxDepth = this.cloudTaggingConfig.maxDepth
146
+ const responseBody = this.extractResponseBody(response)
147
+ const responseTags = tagsFromResponse(this.payloadTaggingRules, responseBody, { maxDepth })
148
+ span.addTags(responseTags)
149
+ }
150
+ }
151
+
152
+ extractResponseBody (response) {
153
+ if (response.hasOwnProperty('data')) {
154
+ return response.data
155
+ }
156
+ return Object.fromEntries(
157
+ Object.entries(response).filter(([key]) => !['request', 'requestId', 'error', '$metadata'].includes(key))
158
+ )
126
159
  }
127
160
 
128
161
  generateTags () {
@@ -113,7 +113,7 @@ class Kinesis extends BaseAwsSdkPlugin {
113
113
  const parsedAttributes = JSON.parse(Buffer.from(record.Data).toString())
114
114
 
115
115
  if (
116
- parsedAttributes?._datadog && streamName && DsmPathwayCodec.contextExists(parsedAttributes._datadog)
116
+ parsedAttributes?._datadog && streamName
117
117
  ) {
118
118
  const payloadSize = getSizeOrZero(record.Data)
119
119
  this.tracer.decodeDataStreamsContext(parsedAttributes._datadog)
@@ -7,6 +7,7 @@ const BaseAwsSdkPlugin = require('../base')
7
7
  class Sns extends BaseAwsSdkPlugin {
8
8
  static get id () { return 'sns' }
9
9
  static get peerServicePrecursors () { return ['topicname'] }
10
+ static get isPayloadReporter () { return true }
10
11
 
11
12
  generateTags (params, operation, response) {
12
13
  if (!params) return {}
@@ -20,6 +21,7 @@ class Sns extends BaseAwsSdkPlugin {
20
21
 
21
22
  // Get the topic name from the last part of the ARN
22
23
  const topicName = arnParts[arnParts.length - 1]
24
+
23
25
  return {
24
26
  'resource.name': `${operation} ${params.TopicArn || response.data.TopicArn}`,
25
27
  'aws.sns.topic_arn': TopicArn,
@@ -194,7 +194,7 @@ class Sqs extends BaseAwsSdkPlugin {
194
194
  parsedAttributes = this.parseDatadogAttributes(message.MessageAttributes._datadog)
195
195
  }
196
196
  }
197
- if (parsedAttributes && DsmPathwayCodec.contextExists(parsedAttributes)) {
197
+ if (parsedAttributes) {
198
198
  const payloadSize = getHeadersSize({
199
199
  Body: message.Body,
200
200
  MessageAttributes: message.MessageAttributes
@@ -17,6 +17,7 @@ const {
17
17
  ITR_CORRELATION_ID,
18
18
  TEST_SOURCE_FILE,
19
19
  TEST_EARLY_FLAKE_ENABLED,
20
+ TEST_EARLY_FLAKE_ABORT_REASON,
20
21
  TEST_IS_NEW,
21
22
  TEST_IS_RETRY,
22
23
  TEST_SUITE_ID,
@@ -79,6 +80,7 @@ class CucumberPlugin extends CiPlugin {
79
80
  hasUnskippableSuites,
80
81
  hasForcedToRunSuites,
81
82
  isEarlyFlakeDetectionEnabled,
83
+ isEarlyFlakeDetectionFaulty,
82
84
  isParallel
83
85
  }) => {
84
86
  const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.libraryConfig || {}
@@ -99,6 +101,9 @@ class CucumberPlugin extends CiPlugin {
99
101
  if (isEarlyFlakeDetectionEnabled) {
100
102
  this.testSessionSpan.setTag(TEST_EARLY_FLAKE_ENABLED, 'true')
101
103
  }
104
+ if (isEarlyFlakeDetectionFaulty) {
105
+ this.testSessionSpan.setTag(TEST_EARLY_FLAKE_ABORT_REASON, 'faulty')
106
+ }
102
107
  if (isParallel) {
103
108
  this.testSessionSpan.setTag(CUCUMBER_IS_PARALLEL, 'true')
104
109
  }
@@ -116,7 +121,15 @@ class CucumberPlugin extends CiPlugin {
116
121
  this.tracer._exporter.flush()
117
122
  })
118
123
 
119
- this.addSub('ci:cucumber:test-suite:start', ({ testSuitePath, isUnskippable, isForcedToRun, itrCorrelationId }) => {
124
+ this.addSub('ci:cucumber:test-suite:start', ({
125
+ testFileAbsolutePath,
126
+ isUnskippable,
127
+ isForcedToRun,
128
+ itrCorrelationId
129
+ }) => {
130
+ const testSuitePath = getTestSuitePath(testFileAbsolutePath, process.cwd())
131
+ const testSourceFile = getTestSuitePath(testFileAbsolutePath, this.repositoryRoot)
132
+
120
133
  const testSuiteMetadata = getTestSuiteCommonTags(
121
134
  this.command,
122
135
  this.frameworkVersion,
@@ -134,6 +147,16 @@ class CucumberPlugin extends CiPlugin {
134
147
  if (itrCorrelationId) {
135
148
  testSuiteMetadata[ITR_CORRELATION_ID] = itrCorrelationId
136
149
  }
150
+ if (testSourceFile) {
151
+ testSuiteMetadata[TEST_SOURCE_FILE] = testSourceFile
152
+ testSuiteMetadata[TEST_SOURCE_START] = 1
153
+ }
154
+
155
+ const codeOwners = this.getCodeOwners(testSuiteMetadata)
156
+ if (codeOwners) {
157
+ testSuiteMetadata[TEST_CODE_OWNERS] = codeOwners
158
+ }
159
+
137
160
  const testSuiteSpan = this.tracer.startSpan('cucumber.test_suite', {
138
161
  childOf: this.testModuleSpan,
139
162
  tags: {
@@ -29,7 +29,9 @@ const {
29
29
  TEST_IS_NEW,
30
30
  TEST_IS_RETRY,
31
31
  TEST_EARLY_FLAKE_ENABLED,
32
- NUM_FAILED_TEST_RETRIES
32
+ getTestSessionName,
33
+ TEST_SESSION_NAME,
34
+ TEST_LEVEL_EVENT_TYPES
33
35
  } = require('../../dd-trace/src/plugins/util/test')
34
36
  const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
35
37
  const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
@@ -229,16 +231,16 @@ class CypressPlugin {
229
231
  isCodeCoverageEnabled,
230
232
  isEarlyFlakeDetectionEnabled,
231
233
  earlyFlakeDetectionNumRetries,
232
- isFlakyTestRetriesEnabled
234
+ isFlakyTestRetriesEnabled,
235
+ flakyTestRetriesCount
233
236
  }
234
237
  } = libraryConfigurationResponse
235
238
  this.isSuitesSkippingEnabled = isSuitesSkippingEnabled
236
239
  this.isCodeCoverageEnabled = isCodeCoverageEnabled
237
240
  this.isEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
238
241
  this.earlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
239
- this.isFlakyTestRetriesEnabled = isFlakyTestRetriesEnabled
240
- if (this.isFlakyTestRetriesEnabled) {
241
- this.cypressConfig.retries.runMode = NUM_FAILED_TEST_RETRIES
242
+ if (isFlakyTestRetriesEnabled) {
243
+ this.cypressConfig.retries.runMode = flakyTestRetriesCount
242
244
  }
243
245
  }
244
246
  return this.cypressConfig
@@ -246,10 +248,22 @@ class CypressPlugin {
246
248
  return this.libraryConfigurationPromise
247
249
  }
248
250
 
249
- getTestSuiteSpan (suite) {
251
+ getTestSuiteSpan ({ testSuite, testSuiteAbsolutePath }) {
250
252
  const testSuiteSpanMetadata =
251
- getTestSuiteCommonTags(this.command, this.frameworkVersion, suite, TEST_FRAMEWORK_NAME)
253
+ getTestSuiteCommonTags(this.command, this.frameworkVersion, testSuite, TEST_FRAMEWORK_NAME)
254
+
252
255
  this.ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
256
+
257
+ if (testSuiteAbsolutePath) {
258
+ const testSourceFile = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
259
+ testSuiteSpanMetadata[TEST_SOURCE_FILE] = testSourceFile
260
+ testSuiteSpanMetadata[TEST_SOURCE_START] = 1
261
+ const codeOwners = this.getTestCodeOwners({ testSuite, testSourceFile })
262
+ if (codeOwners) {
263
+ testSuiteSpanMetadata[TEST_CODE_OWNERS] = codeOwners
264
+ }
265
+ }
266
+
253
267
  return this.tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_suite`, {
254
268
  childOf: this.testModuleSpan,
255
269
  tags: {
@@ -388,6 +402,18 @@ class CypressPlugin {
388
402
  testSessionSpanMetadata[TEST_EARLY_FLAKE_ENABLED] = 'true'
389
403
  }
390
404
 
405
+ const testSessionName = getTestSessionName(this.tracer._tracer._config, this.command, this.testEnvironmentMetadata)
406
+
407
+ if (this.tracer._tracer._exporter?.setMetadataTags) {
408
+ const metadataTags = {}
409
+ for (const testLevel of TEST_LEVEL_EVENT_TYPES) {
410
+ metadataTags[testLevel] = {
411
+ [TEST_SESSION_NAME]: testSessionName
412
+ }
413
+ }
414
+ this.tracer._tracer._exporter.setMetadataTags(metadataTags)
415
+ }
416
+
391
417
  this.testSessionSpan = this.tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_session`, {
392
418
  childOf,
393
419
  tags: {
@@ -474,7 +500,10 @@ class CypressPlugin {
474
500
  // dd:testSuiteStart hasn't been triggered for whatever reason
475
501
  // We will create the test suite span on the spot if that's the case
476
502
  log.warn('There was an error creating the test suite event.')
477
- this.testSuiteSpan = this.getTestSuiteSpan(spec.relative)
503
+ this.testSuiteSpan = this.getTestSuiteSpan({
504
+ testSuite: spec.relative,
505
+ testSuiteAbsolutePath: spec.absolute
506
+ })
478
507
  }
479
508
 
480
509
  // Get tests that didn't go through `dd:afterEach`
@@ -585,7 +614,7 @@ class CypressPlugin {
585
614
 
586
615
  getTasks () {
587
616
  return {
588
- 'dd:testSuiteStart': (testSuite) => {
617
+ 'dd:testSuiteStart': ({ testSuite, testSuiteAbsolutePath }) => {
589
618
  const suitePayload = {
590
619
  isEarlyFlakeDetectionEnabled: this.isEarlyFlakeDetectionEnabled,
591
620
  knownTestsForSuite: this.knownTestsByTestSuite?.[testSuite] || [],
@@ -595,7 +624,7 @@ class CypressPlugin {
595
624
  if (this.testSuiteSpan) {
596
625
  return suitePayload
597
626
  }
598
- this.testSuiteSpan = this.getTestSuiteSpan(testSuite)
627
+ this.testSuiteSpan = this.getTestSuiteSpan({ testSuite, testSuiteAbsolutePath })
599
628
  return suitePayload
600
629
  },
601
630
  'dd:beforeEach': (test) => {
@@ -61,7 +61,10 @@ beforeEach(function () {
61
61
  })
62
62
 
63
63
  before(function () {
64
- cy.task('dd:testSuiteStart', Cypress.mocha.getRootSuite().file).then((suiteConfig) => {
64
+ cy.task('dd:testSuiteStart', {
65
+ testSuite: Cypress.mocha.getRootSuite().file,
66
+ testSuiteAbsolutePath: Cypress.spec && Cypress.spec.absolute
67
+ }).then((suiteConfig) => {
65
68
  if (suiteConfig) {
66
69
  isEarlyFlakeDetectionEnabled = suiteConfig.isEarlyFlakeDetectionEnabled
67
70
  knownTestsForSuite = suiteConfig.knownTestsForSuite
@@ -32,8 +32,8 @@ class HapiPlugin extends RouterPlugin {
32
32
  }
33
33
  })
34
34
 
35
- this.addSub('apm:hapi:extension:enter', ({ req }) => {
36
- this.enter(this._requestSpans.get(req))
35
+ this.addBind('apm:hapi:extension:start', ({ req }) => {
36
+ return this._requestSpans.get(req)
37
37
  })
38
38
  }
39
39
  }