dd-trace 5.67.0 → 5.68.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 (104) hide show
  1. package/LICENSE-3rdparty.csv +0 -3
  2. package/README.md +0 -2
  3. package/ci/init.js +52 -54
  4. package/ext/exporters.d.ts +2 -1
  5. package/ext/exporters.js +2 -1
  6. package/index.d.ts +47 -2
  7. package/initialize.mjs +1 -1
  8. package/package.json +8 -11
  9. package/packages/datadog-esbuild/index.js +56 -0
  10. package/packages/datadog-instrumentations/src/aws-sdk.js +42 -4
  11. package/packages/datadog-instrumentations/src/azure-functions.js +1 -1
  12. package/packages/datadog-instrumentations/src/azure-service-bus.js +1 -1
  13. package/packages/datadog-instrumentations/src/cassandra-driver.js +2 -2
  14. package/packages/datadog-instrumentations/src/connect.js +6 -2
  15. package/packages/datadog-instrumentations/src/cucumber.js +31 -6
  16. package/packages/datadog-instrumentations/src/express.js +5 -6
  17. package/packages/datadog-instrumentations/src/fastify.js +3 -3
  18. package/packages/datadog-instrumentations/src/helpers/hook.js +28 -15
  19. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  20. package/packages/datadog-instrumentations/src/helpers/instrument.js +11 -2
  21. package/packages/datadog-instrumentations/src/helpers/register.js +10 -3
  22. package/packages/datadog-instrumentations/src/http2/client.js +1 -0
  23. package/packages/datadog-instrumentations/src/http2/server.js +0 -1
  24. package/packages/datadog-instrumentations/src/ioredis.js +12 -1
  25. package/packages/datadog-instrumentations/src/jest.js +48 -36
  26. package/packages/datadog-instrumentations/src/limitd-client.js +2 -1
  27. package/packages/datadog-instrumentations/src/mocha/main.js +15 -7
  28. package/packages/datadog-instrumentations/src/mocha/utils.js +3 -0
  29. package/packages/datadog-instrumentations/src/mongoose.js +2 -1
  30. package/packages/datadog-instrumentations/src/oracledb.js +19 -13
  31. package/packages/datadog-instrumentations/src/pg.js +9 -5
  32. package/packages/datadog-instrumentations/src/pino.js +18 -6
  33. package/packages/datadog-instrumentations/src/playwright.js +15 -1
  34. package/packages/datadog-instrumentations/src/sequelize.js +1 -1
  35. package/packages/datadog-instrumentations/src/vitest.js +155 -62
  36. package/packages/datadog-plugin-ai/src/tracing.js +3 -3
  37. package/packages/datadog-plugin-aws-sdk/src/base.js +23 -8
  38. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +2 -2
  39. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +101 -2
  40. package/packages/datadog-plugin-aws-sdk/src/util.js +1 -1
  41. package/packages/datadog-plugin-cucumber/src/index.js +4 -56
  42. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +6 -2
  43. package/packages/datadog-plugin-cypress/src/support.js +4 -0
  44. package/packages/datadog-plugin-express/src/code_origin.js +2 -2
  45. package/packages/datadog-plugin-fastify/src/code_origin.js +1 -2
  46. package/packages/datadog-plugin-jest/src/index.js +0 -21
  47. package/packages/datadog-plugin-mocha/src/index.js +3 -57
  48. package/packages/datadog-plugin-mongodb-core/src/index.js +20 -7
  49. package/packages/datadog-plugin-playwright/src/index.js +11 -5
  50. package/packages/datadog-plugin-vitest/src/index.js +5 -1
  51. package/packages/datadog-plugin-ws/src/close.js +1 -1
  52. package/packages/datadog-plugin-ws/src/producer.js +6 -1
  53. package/packages/datadog-plugin-ws/src/receiver.js +6 -1
  54. package/packages/dd-trace/src/appsec/iast/security-controls/parser.js +1 -1
  55. package/packages/dd-trace/src/appsec/telemetry/waf.js +2 -2
  56. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -4
  57. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +11 -3
  58. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +10 -1
  59. package/packages/dd-trace/src/config.js +69 -304
  60. package/packages/dd-trace/src/config_defaults.js +186 -0
  61. package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -1
  62. package/packages/dd-trace/src/datastreams/fnv.js +2 -2
  63. package/packages/dd-trace/src/datastreams/writer.js +3 -2
  64. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -1
  65. package/packages/dd-trace/src/dogstatsd.js +4 -3
  66. package/packages/dd-trace/src/encode/0.4.js +1 -5
  67. package/packages/dd-trace/src/exporter.js +1 -0
  68. package/packages/dd-trace/src/exporters/agent/index.js +3 -2
  69. package/packages/dd-trace/src/exporters/agent/writer.js +1 -1
  70. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +3 -2
  71. package/packages/dd-trace/src/exporters/common/request.js +2 -1
  72. package/packages/dd-trace/src/exporters/span-stats/index.js +3 -2
  73. package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
  74. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +4 -3
  75. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -1
  76. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +40 -13
  77. package/packages/dd-trace/src/llmobs/plugins/openai.js +7 -1
  78. package/packages/dd-trace/src/llmobs/tagger.js +8 -0
  79. package/packages/dd-trace/src/llmobs/telemetry.js +2 -1
  80. package/packages/dd-trace/src/log/index.js +28 -17
  81. package/packages/dd-trace/src/log/log.js +29 -5
  82. package/packages/dd-trace/src/log/writer.js +5 -5
  83. package/packages/dd-trace/src/noop/span.js +1 -0
  84. package/packages/dd-trace/src/opentelemetry/span.js +14 -3
  85. package/packages/dd-trace/src/opentracing/span.js +18 -4
  86. package/packages/dd-trace/src/plugin_manager.js +20 -2
  87. package/packages/dd-trace/src/plugins/ci_plugin.js +97 -3
  88. package/packages/dd-trace/src/plugins/index.js +2 -0
  89. package/packages/dd-trace/src/plugins/util/git-cache.js +129 -0
  90. package/packages/dd-trace/src/plugins/util/git.js +40 -26
  91. package/packages/dd-trace/src/plugins/util/test.js +37 -27
  92. package/packages/dd-trace/src/plugins/util/web.js +1 -1
  93. package/packages/dd-trace/src/profiler.js +4 -1
  94. package/packages/dd-trace/src/profiling/config.js +73 -42
  95. package/packages/dd-trace/src/profiling/profiler.js +3 -1
  96. package/packages/dd-trace/src/profiling/profilers/events.js +3 -8
  97. package/packages/dd-trace/src/profiling/profilers/space.js +1 -0
  98. package/packages/dd-trace/src/profiling/profilers/wall.js +196 -117
  99. package/packages/dd-trace/src/remote_config/capabilities.js +5 -0
  100. package/packages/dd-trace/src/remote_config/manager.js +3 -2
  101. package/packages/dd-trace/src/startup-log.js +2 -1
  102. package/packages/dd-trace/src/supported-configurations.json +3 -0
  103. package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
  104. package/register.js +1 -1
@@ -7,7 +7,7 @@ const {
7
7
 
8
8
  const shimmer = require('../../datadog-shimmer')
9
9
 
10
- addHook({ name: 'sequelize', versions: ['>=4'] }, Sequelize => {
10
+ addHook({ name: 'sequelize', versions: ['>=4'], file: ['lib/sequelize.js'] }, Sequelize => {
11
11
  const startCh = channel('datadog:sequelize:query:start')
12
12
  const finishCh = channel('datadog:sequelize:query:finish')
13
13
 
@@ -3,6 +3,10 @@
3
3
  const { addHook, channel } = require('./helpers/instrument')
4
4
  const shimmer = require('../../datadog-shimmer')
5
5
  const log = require('../../dd-trace/src/log')
6
+ const {
7
+ VITEST_WORKER_TRACE_PAYLOAD_CODE,
8
+ VITEST_WORKER_LOGS_PAYLOAD_CODE
9
+ } = require('../../dd-trace/src/plugins/util/test')
6
10
 
7
11
  // test hooks
8
12
  const testStartCh = channel('ci:vitest:test:start')
@@ -30,6 +34,9 @@ const isEarlyFlakeDetectionFaultyCh = channel('ci:vitest:is-early-flake-detectio
30
34
  const testManagementTestsCh = channel('ci:vitest:test-management-tests')
31
35
  const impactedTestsCh = channel('ci:vitest:modified-tests')
32
36
 
37
+ const workerReportTraceCh = channel('ci:vitest:worker-report:trace')
38
+ const workerReportLogsCh = channel('ci:vitest:worker-report:logs')
39
+
33
40
  const taskToCtx = new WeakMap()
34
41
  const taskToStatuses = new WeakMap()
35
42
  const newTasks = new WeakSet()
@@ -40,9 +47,26 @@ const modifiedTasks = new WeakSet()
40
47
  let isRetryReasonEfd = false
41
48
  let isRetryReasonAttemptToFix = false
42
49
  const switchedStatuses = new WeakSet()
50
+ const workerProcesses = new WeakSet()
51
+ let isFlakyTestRetriesEnabled = false
52
+ let flakyTestRetriesCount = 0
53
+ let isEarlyFlakeDetectionEnabled = false
54
+ let earlyFlakeDetectionNumRetries = 0
55
+ let isEarlyFlakeDetectionFaulty = false
56
+ let isKnownTestsEnabled = false
57
+ let isTestManagementTestsEnabled = false
58
+ let isImpactedTestsEnabled = false
59
+ let testManagementAttemptToFixRetries = 0
60
+ let isDiEnabled = false
61
+ let testCodeCoverageLinesTotal
62
+ let isSessionStarted = false
43
63
 
44
64
  const BREAKPOINT_HIT_GRACE_PERIOD_MS = 400
45
65
 
66
+ function getTestCommand () {
67
+ return `vitest ${process.argv.slice(2).join(' ')}`
68
+ }
69
+
46
70
  function waitForHitProbe () {
47
71
  return new Promise(resolve => {
48
72
  setTimeout(() => {
@@ -51,6 +75,10 @@ function waitForHitProbe () {
51
75
  })
52
76
  }
53
77
 
78
+ function isValidKnownTests (receivedKnownTests) {
79
+ return !!receivedKnownTests.vitest
80
+ }
81
+
54
82
  function getProvidedContext () {
55
83
  try {
56
84
  const {
@@ -121,6 +149,10 @@ function getChannelPromise (channelToPublishTo, frameworkVersion) {
121
149
  })
122
150
  }
123
151
 
152
+ function isCliApiPackage (vitestPackage) {
153
+ return vitestPackage.s?.name === 'startVitest'
154
+ }
155
+
124
156
  function getSessionStatus (state) {
125
157
  if (state.getCountOfFailedTests() > 0) {
126
158
  return 'fail'
@@ -183,16 +215,6 @@ function getSortWrapper (sort, frameworkVersion) {
183
215
  // There isn't any other async function that we seem to be able to hook into
184
216
  // So we will use the sort from BaseSequencer. This means that a custom sequencer
185
217
  // will not work. This will be a known limitation.
186
- let isFlakyTestRetriesEnabled = false
187
- let flakyTestRetriesCount = 0
188
- let isEarlyFlakeDetectionEnabled = false
189
- let earlyFlakeDetectionNumRetries = 0
190
- let isEarlyFlakeDetectionFaulty = false
191
- let isKnownTestsEnabled = false
192
- let isTestManagementTestsEnabled = false
193
- let isImpactedTestsEnabled = false
194
- let testManagementAttemptToFixRetries = 0
195
- let isDiEnabled = false
196
218
 
197
219
  try {
198
220
  const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh, frameworkVersion)
@@ -235,28 +257,33 @@ function getSortWrapper (sort, frameworkVersion) {
235
257
 
236
258
  const testFilepaths = await getFilePaths.call(this.ctx)
237
259
 
238
- isEarlyFlakeDetectionFaultyCh.publish({
239
- knownTests: knownTests.vitest || {},
240
- testFilepaths,
241
- onDone: (isFaulty) => {
242
- isEarlyFlakeDetectionFaulty = isFaulty
260
+ if (isValidKnownTests(knownTests)) {
261
+ isEarlyFlakeDetectionFaultyCh.publish({
262
+ knownTests: knownTests.vitest,
263
+ testFilepaths,
264
+ onDone: (isFaulty) => {
265
+ isEarlyFlakeDetectionFaulty = isFaulty
266
+ }
267
+ })
268
+ if (isEarlyFlakeDetectionFaulty) {
269
+ isEarlyFlakeDetectionEnabled = false
270
+ log.warn('New test detection is disabled because the number of new tests is too high.')
271
+ } else {
272
+ // TODO: use this to pass session and module IDs to the worker, instead of polluting process.env
273
+ // Note: setting this.ctx.config.provide directly does not work because it's cached
274
+ try {
275
+ const workspaceProject = this.ctx.getCoreWorkspaceProject()
276
+ workspaceProject._provided._ddIsKnownTestsEnabled = isKnownTestsEnabled
277
+ workspaceProject._provided._ddKnownTests = knownTests
278
+ workspaceProject._provided._ddIsEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
279
+ workspaceProject._provided._ddEarlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
280
+ } catch {
281
+ log.warn('Could not send known tests to workers so Early Flake Detection will not work.')
282
+ }
243
283
  }
244
- })
245
- if (isEarlyFlakeDetectionFaulty) {
246
- isEarlyFlakeDetectionEnabled = false
247
- log.warn('New test detection is disabled because the number of new tests is too high.')
248
284
  } else {
249
- // TODO: use this to pass session and module IDs to the worker, instead of polluting process.env
250
- // Note: setting this.ctx.config.provide directly does not work because it's cached
251
- try {
252
- const workspaceProject = this.ctx.getCoreWorkspaceProject()
253
- workspaceProject._provided._ddIsKnownTestsEnabled = isKnownTestsEnabled
254
- workspaceProject._provided._ddKnownTests = knownTests.vitest || {}
255
- workspaceProject._provided._ddIsEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
256
- workspaceProject._provided._ddEarlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
257
- } catch {
258
- log.warn('Could not send known tests to workers so Early Flake Detection will not work.')
259
- }
285
+ isEarlyFlakeDetectionFaulty = true
286
+ isEarlyFlakeDetectionEnabled = false
260
287
  }
261
288
  }
262
289
  }
@@ -303,8 +330,6 @@ function getSortWrapper (sort, frameworkVersion) {
303
330
  }
304
331
  }
305
332
 
306
- let testCodeCoverageLinesTotal
307
-
308
333
  if (this.ctx.coverageProvider?.generateCoverage) {
309
334
  shimmer.wrap(this.ctx.coverageProvider, 'generateCoverage', generateCoverage => async function () {
310
335
  const totalCodeCoverage = await generateCoverage.apply(this, arguments)
@@ -318,48 +343,104 @@ function getSortWrapper (sort, frameworkVersion) {
318
343
  })
319
344
  }
320
345
 
321
- shimmer.wrap(this.ctx, 'exit', exit => async function () {
322
- let onFinish
346
+ shimmer.wrap(this.ctx, 'exit', getFinishWrapper)
347
+ shimmer.wrap(this.ctx, 'close', getFinishWrapper)
323
348
 
324
- const flushPromise = new Promise(resolve => {
325
- onFinish = resolve
326
- })
327
- const failedSuites = this.state.getFailedFilepaths()
328
- let error
329
- if (failedSuites.length) {
330
- error = new Error(`Test suites failed: ${failedSuites.length}.`)
331
- }
349
+ return sort.apply(this, arguments)
350
+ }
351
+ }
332
352
 
333
- testSessionFinishCh.publish({
334
- status: getSessionStatus(this.state),
335
- testCodeCoverageLinesTotal,
336
- error,
337
- isEarlyFlakeDetectionEnabled,
338
- isEarlyFlakeDetectionFaulty,
339
- isTestManagementTestsEnabled,
340
- onFinish
341
- })
353
+ function getFinishWrapper (exitOrClose) {
354
+ let isClosed = false
355
+ return async function () {
356
+ if (isClosed) { // needed because exit calls close
357
+ return exitOrClose.apply(this, arguments)
358
+ }
359
+ isClosed = true
360
+ let onFinish
342
361
 
343
- await flushPromise
362
+ const flushPromise = new Promise(resolve => {
363
+ onFinish = resolve
364
+ })
365
+ const failedSuites = this.state.getFailedFilepaths()
366
+ let error
367
+ if (failedSuites.length) {
368
+ error = new Error(`Test suites failed: ${failedSuites.length}.`)
369
+ }
344
370
 
345
- return exit.apply(this, arguments)
371
+ testSessionFinishCh.publish({
372
+ status: getSessionStatus(this.state),
373
+ testCodeCoverageLinesTotal,
374
+ error,
375
+ isEarlyFlakeDetectionEnabled,
376
+ isEarlyFlakeDetectionFaulty,
377
+ isTestManagementTestsEnabled,
378
+ onFinish
346
379
  })
347
380
 
348
- return sort.apply(this, arguments)
381
+ await flushPromise
382
+
383
+ return exitOrClose.apply(this, arguments)
384
+ }
385
+ }
386
+
387
+ function getCliOrStartVitestWrapper (frameworkVersion) {
388
+ return function (oldCliOrStartVitest) {
389
+ return function () {
390
+ if (!testSessionStartCh.hasSubscribers || isSessionStarted) {
391
+ return oldCliOrStartVitest.apply(this, arguments)
392
+ }
393
+ isSessionStarted = true
394
+ testSessionStartCh.publish({ command: getTestCommand(), frameworkVersion })
395
+ return oldCliOrStartVitest.apply(this, arguments)
396
+ }
349
397
  }
350
398
  }
351
399
 
352
400
  function getCreateCliWrapper (vitestPackage, frameworkVersion) {
353
- shimmer.wrap(vitestPackage, 'c', oldCreateCli => function () {
354
- if (!testSessionStartCh.hasSubscribers) {
355
- return oldCreateCli.apply(this, arguments)
401
+ shimmer.wrap(vitestPackage, 'c', getCliOrStartVitestWrapper(frameworkVersion))
402
+
403
+ return vitestPackage
404
+ }
405
+
406
+ function threadHandler (thread) {
407
+ if (workerProcesses.has(thread.process)) {
408
+ return
409
+ }
410
+ workerProcesses.add(thread.process)
411
+ thread.process.on('message', (message) => {
412
+ if (message.__tinypool_worker_message__ && message.data) {
413
+ if (message.interprocessCode === VITEST_WORKER_TRACE_PAYLOAD_CODE) {
414
+ workerReportTraceCh.publish(message.data)
415
+ } else if (message.interprocessCode === VITEST_WORKER_LOGS_PAYLOAD_CODE) {
416
+ workerReportLogsCh.publish(message.data)
417
+ }
356
418
  }
357
- const processArgv = process.argv.slice(2).join(' ')
358
- testSessionStartCh.publish({ command: `vitest ${processArgv}`, frameworkVersion })
359
- return oldCreateCli.apply(this, arguments)
360
419
  })
420
+ }
361
421
 
362
- return vitestPackage
422
+ addHook({
423
+ name: 'tinypool',
424
+ versions: ['>=1.0.0'],
425
+ file: 'dist/index.js'
426
+ }, (TinyPool) => {
427
+ shimmer.wrap(TinyPool.prototype, 'run', run => async function () {
428
+ // We have to do this before and after because the threads list gets recycled, that is, the processes are re-created
429
+ this.threads.forEach(threadHandler)
430
+ const runResult = await run.apply(this, arguments)
431
+ this.threads.forEach(threadHandler)
432
+ return runResult
433
+ })
434
+
435
+ return TinyPool
436
+ })
437
+
438
+ function getStartVitestWrapper (cliApiPackage, frameworkVersion) {
439
+ if (!isCliApiPackage(cliApiPackage)) {
440
+ return cliApiPackage
441
+ }
442
+ shimmer.wrap(cliApiPackage, 's', getCliOrStartVitestWrapper(frameworkVersion))
443
+ return cliApiPackage
363
444
  }
364
445
 
365
446
  addHook({
@@ -735,6 +816,7 @@ addHook({
735
816
  })
736
817
 
737
818
  // Can't specify file because compiled vitest includes hashes in their files
819
+ // Following 3 wrappers are for test session start
738
820
  addHook({
739
821
  name: 'vitest',
740
822
  versions: ['>=1.6.0 <2.0.5'],
@@ -747,6 +829,18 @@ addHook({
747
829
  filePattern: 'dist/chunks/cac.*'
748
830
  }, getCreateCliWrapper)
749
831
 
832
+ addHook({
833
+ name: 'vitest',
834
+ versions: ['>=1.6.0 <2.0.5'],
835
+ filePattern: 'dist/vendor/cli-api.*'
836
+ }, getStartVitestWrapper)
837
+
838
+ addHook({
839
+ name: 'vitest',
840
+ versions: ['>=2.0.5'],
841
+ filePattern: 'dist/chunks/cli-api.*'
842
+ }, getStartVitestWrapper)
843
+
750
844
  // test suite start and finish
751
845
  // only relevant for workers
752
846
  addHook({
@@ -857,7 +951,6 @@ addHook({
857
951
 
858
952
  testSuiteFinishCh.publish({ status: testSuiteResult.state, onFinish, ...testSuiteCtx.currentStore })
859
953
 
860
- // TODO: fix too frequent flushes
861
954
  await onFinishPromise
862
955
 
863
956
  return startTestsResponse
@@ -8,14 +8,14 @@ class VercelAITracingPlugin extends TracingPlugin {
8
8
  static prefix = 'tracing:dd-trace:vercel-ai'
9
9
 
10
10
  bindStart (ctx) {
11
- const attributes = ctx.attributes
11
+ const { attributes, name } = ctx
12
12
 
13
13
  const model = attributes['ai.model.id']
14
14
  const modelProvider = getModelProvider(attributes)
15
15
 
16
- this.startSpan(ctx.name, {
16
+ this.startSpan(name, {
17
17
  meta: {
18
- 'resource.name': ctx.name,
18
+ 'resource.name': attributes['resource.name'] ?? name,
19
19
  'ai.request.model': model,
20
20
  'ai.request.model_provider': modelProvider
21
21
  }
@@ -4,7 +4,6 @@ const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
4
4
  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
- const coalesce = require('koalas')
8
7
  const { tagsFromRequest, tagsFromResponse } = require('../../dd-trace/src/payload-tagging')
9
8
  const { getEnvironmentVariable } = require('../../dd-trace/src/config-helper')
10
9
 
@@ -59,6 +58,7 @@ class BaseAwsSdkPlugin extends ClientPlugin {
59
58
  'aws.operation': operation,
60
59
  'aws.region': awsRegion,
61
60
  region: awsRegion,
61
+ 'aws.partition': getPartition(awsRegion),
62
62
  aws_service: awsService,
63
63
  'aws.service': awsService,
64
64
  component: 'aws-sdk'
@@ -115,6 +115,11 @@ class BaseAwsSdkPlugin extends ClientPlugin {
115
115
  span.setTag('aws.region', region)
116
116
  span.setTag('region', region)
117
117
 
118
+ const partition = getPartition(region)
119
+ if (partition) {
120
+ span.setTag('aws.partition', partition)
121
+ }
122
+
118
123
  if (!this._tracerConfig?._isInServerlessEnvironment()) return
119
124
 
120
125
  const hostname = getHostname(store, region)
@@ -267,13 +272,10 @@ function normalizeConfig (config, serviceIdentifier) {
267
272
  // check if AWS batch propagation or AWS_[SERVICE] batch propagation is enabled via env variable
268
273
  const serviceId = serviceIdentifier.toUpperCase()
269
274
  const batchPropagationEnabled = isTrue(
270
- coalesce(
271
- specificConfig.batchPropagationEnabled,
272
- getEnvironmentVariable(`DD_TRACE_AWS_SDK_${serviceId}_BATCH_PROPAGATION_ENABLED`),
273
- config.batchPropagationEnabled,
274
- getEnvironmentVariable('DD_TRACE_AWS_SDK_BATCH_PROPAGATION_ENABLED'),
275
- false
276
- )
275
+ specificConfig.batchPropagationEnabled ??
276
+ getEnvironmentVariable(`DD_TRACE_AWS_SDK_${serviceId}_BATCH_PROPAGATION_ENABLED`) ??
277
+ config.batchPropagationEnabled ??
278
+ getEnvironmentVariable('DD_TRACE_AWS_SDK_BATCH_PROPAGATION_ENABLED')
277
279
  )
278
280
 
279
281
  // Merge the specific config back into the main config
@@ -317,4 +319,17 @@ function getHostname (store, region) {
317
319
  }
318
320
  }
319
321
 
322
+ function getPartition (region) {
323
+ if (!region) return
324
+
325
+ let partition = 'aws'
326
+ if (region.startsWith('cn-')) {
327
+ partition = 'aws-cn'
328
+ } else if (region.startsWith('us-gov-')) {
329
+ partition = 'aws-us-gov'
330
+ }
331
+
332
+ return partition
333
+ }
334
+
320
335
  module.exports = BaseAwsSdkPlugin
@@ -3,7 +3,7 @@
3
3
  const BaseAwsSdkPlugin = require('../../base')
4
4
  const { parseModelId } = require('./utils')
5
5
 
6
- const enabledOperations = new Set(['invokeModel'])
6
+ const enabledOperations = new Set(['invokeModel', 'invokeModelWithResponseStream'])
7
7
 
8
8
  class BedrockRuntime extends BaseAwsSdkPlugin {
9
9
  static id = 'bedrockruntime'
@@ -17,7 +17,7 @@ class BedrockRuntime extends BaseAwsSdkPlugin {
17
17
  return super.isEnabled(request)
18
18
  }
19
19
 
20
- generateTags (params, operation, response) {
20
+ generateTags (params, operation) {
21
21
  const { modelProvider, modelName } = parseModelId(params.modelId)
22
22
 
23
23
  return {
@@ -23,6 +23,100 @@ const PROVIDER = {
23
23
  MISTRAL: 'MISTRAL'
24
24
  }
25
25
 
26
+ /**
27
+ * Coerce the chunks into a single response body.
28
+ *
29
+ * @param {Array<{ chunk: { bytes: Buffer } }>} chunks
30
+ * @param {string} provider
31
+ * @returns {Object}
32
+ */
33
+ function extractTextAndResponseReasonFromStream (chunks, modelProvider, modelName) {
34
+ const modelProviderUpper = modelProvider.toUpperCase()
35
+
36
+ // streaming unsupported for AMAZON embedding models, COHERE embedding models, STABILITY
37
+ if (
38
+ (modelProviderUpper === PROVIDER.AMAZON && modelName.includes('embed')) ||
39
+ (modelProviderUpper === PROVIDER.COHERE && modelName.includes('embed')) ||
40
+ modelProviderUpper === PROVIDER.STABILITY
41
+ ) {
42
+ return {}
43
+ }
44
+
45
+ let message = ''
46
+ let inputTokens = 0
47
+ let outputTokens = 0
48
+ let cacheReadTokens = 0
49
+ let cacheWriteTokens = 0
50
+
51
+ for (const { chunk: { bytes } } of chunks) {
52
+ const body = JSON.parse(Buffer.from(bytes).toString('utf8'))
53
+
54
+ switch (modelProviderUpper) {
55
+ case PROVIDER.AMAZON: {
56
+ message += body?.outputText
57
+
58
+ inputTokens = body?.inputTextTokenCount
59
+ outputTokens = body?.totalOutputTextTokenCount
60
+
61
+ break
62
+ }
63
+ case PROVIDER.AI21: {
64
+ const content = body?.choices?.[0]?.delta?.content
65
+ if (content) {
66
+ message += content
67
+ }
68
+
69
+ break
70
+ }
71
+ case PROVIDER.ANTHROPIC: {
72
+ if (body.completion) {
73
+ message += body.completion
74
+ } else if (body.delta?.text) {
75
+ message += body.delta.text
76
+ }
77
+
78
+ if (body.message?.usage?.input_tokens) inputTokens = body.message.usage.input_tokens
79
+ if (body.message?.usage?.output_tokens) outputTokens = body.message.usage.output_tokens
80
+
81
+ break
82
+ }
83
+ case PROVIDER.COHERE: {
84
+ if (body?.event_type === 'stream-end') {
85
+ message = body.response?.text
86
+ }
87
+
88
+ break
89
+ }
90
+ case PROVIDER.META: {
91
+ message += body?.generation
92
+ break
93
+ }
94
+ case PROVIDER.MISTRAL: {
95
+ message += body?.outputs?.[0]?.text
96
+ break
97
+ }
98
+ }
99
+
100
+ // by default, it seems newer versions of the AWS SDK include the input/output token counts in the response body
101
+ const invocationMetrics = body['amazon-bedrock-invocationMetrics']
102
+ if (invocationMetrics) {
103
+ inputTokens = invocationMetrics.inputTokenCount
104
+ outputTokens = invocationMetrics.outputTokenCount
105
+ cacheReadTokens = invocationMetrics.cacheReadInputTokenCount
106
+ cacheWriteTokens = invocationMetrics.cacheWriteInputTokenCount
107
+ }
108
+ }
109
+
110
+ return new Generation({
111
+ message,
112
+ role: 'assistant',
113
+ inputTokens,
114
+ outputTokens,
115
+ cacheReadTokens,
116
+ cacheWriteTokens
117
+ })
118
+ }
119
+
26
120
  class Generation {
27
121
  constructor ({
28
122
  message = '',
@@ -30,7 +124,9 @@ class Generation {
30
124
  choiceId = '',
31
125
  role,
32
126
  inputTokens,
33
- outputTokens
127
+ outputTokens,
128
+ cacheReadTokens,
129
+ cacheWriteTokens
34
130
  } = {}) {
35
131
  // stringify message as it could be a single generated message as well as a list of embeddings
36
132
  this.message = typeof message === 'string' ? message : JSON.stringify(message) || ''
@@ -39,7 +135,9 @@ class Generation {
39
135
  this.role = role
40
136
  this.usage = {
41
137
  inputTokens,
42
- outputTokens
138
+ outputTokens,
139
+ cacheReadTokens,
140
+ cacheWriteTokens
43
141
  }
44
142
  }
45
143
  }
@@ -334,6 +432,7 @@ function extractTextAndResponseReason (response, provider, modelName) {
334
432
  module.exports = {
335
433
  Generation,
336
434
  RequestParams,
435
+ extractTextAndResponseReasonFromStream,
337
436
  parseModelId,
338
437
  extractRequestParams,
339
438
  extractTextAndResponseReason,
@@ -135,7 +135,7 @@ const extractQueueMetadata = queueURL => {
135
135
  let partition = 'aws'
136
136
  if (region.startsWith('cn-')) {
137
137
  partition = 'aws-cn'
138
- } else if (region.startsWith('us-gov')) {
138
+ } else if (region.startsWith('us-gov-')) {
139
139
  partition = 'aws-us-gov'
140
140
  }
141
141
 
@@ -21,12 +21,6 @@ const {
21
21
  TEST_EARLY_FLAKE_ABORT_REASON,
22
22
  TEST_IS_NEW,
23
23
  TEST_IS_RETRY,
24
- TEST_SUITE_ID,
25
- TEST_SESSION_ID,
26
- TEST_COMMAND,
27
- TEST_MODULE,
28
- TEST_MODULE_ID,
29
- TEST_SUITE,
30
24
  CUCUMBER_IS_PARALLEL,
31
25
  TEST_RETRY_REASON,
32
26
  TEST_MANAGEMENT_ENABLED,
@@ -55,25 +49,11 @@ const {
55
49
  TEST_BROWSER_DRIVER,
56
50
  TELEMETRY_TEST_SESSION
57
51
  } = require('../../dd-trace/src/ci-visibility/telemetry')
58
- const id = require('../../dd-trace/src/id')
59
52
 
60
53
  const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
61
54
  const BREAKPOINT_SET_GRACE_PERIOD_MS = 200
62
55
  const isCucumberWorker = !!getEnvironmentVariable('CUCUMBER_WORKER_ID')
63
56
 
64
- function getTestSuiteTags (testSuiteSpan) {
65
- const suiteTags = {
66
- [TEST_SUITE_ID]: testSuiteSpan.context().toSpanId(),
67
- [TEST_SESSION_ID]: testSuiteSpan.context().toTraceId(),
68
- [TEST_COMMAND]: testSuiteSpan.context()._tags[TEST_COMMAND],
69
- [TEST_MODULE]: 'cucumber'
70
- }
71
- if (testSuiteSpan.context()._parentId) {
72
- suiteTags[TEST_MODULE_ID] = testSuiteSpan.context()._parentId.toString(10)
73
- }
74
- return suiteTags
75
- }
76
-
77
57
  class CucumberPlugin extends CiPlugin {
78
58
  static id = 'cucumber'
79
59
 
@@ -82,8 +62,6 @@ class CucumberPlugin extends CiPlugin {
82
62
 
83
63
  this.sourceRoot = process.cwd()
84
64
 
85
- this.testSuiteSpanByPath = {}
86
-
87
65
  this.addSub('ci:cucumber:session:finish', ({
88
66
  status,
89
67
  isSuitesSkipped,
@@ -185,7 +163,7 @@ class CucumberPlugin extends CiPlugin {
185
163
  },
186
164
  integrationName: this.constructor.id
187
165
  })
188
- this.testSuiteSpanByPath[testSuitePath] = testSuiteSpan
166
+ this._testSuiteSpansByTestSuite.set(testSuitePath, testSuiteSpan)
189
167
 
190
168
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
191
169
  if (this.libraryConfig?.isCodeCoverageEnabled) {
@@ -194,7 +172,7 @@ class CucumberPlugin extends CiPlugin {
194
172
  })
195
173
 
196
174
  this.addSub('ci:cucumber:test-suite:finish', ({ status, testSuitePath }) => {
197
- const testSuiteSpan = this.testSuiteSpanByPath[testSuitePath]
175
+ const testSuiteSpan = this._testSuiteSpansByTestSuite.get(testSuitePath)
198
176
  testSuiteSpan.setTag(TEST_STATUS, status)
199
177
  testSuiteSpan.finish()
200
178
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
@@ -207,7 +185,7 @@ class CucumberPlugin extends CiPlugin {
207
185
  if (!coverageFiles.length) {
208
186
  this.telemetry.count(TELEMETRY_CODE_COVERAGE_EMPTY)
209
187
  }
210
- const testSuiteSpan = this.testSuiteSpanByPath[testSuitePath]
188
+ const testSuiteSpan = this._testSuiteSpansByTestSuite.get(testSuitePath)
211
189
 
212
190
  const relativeCoverageFiles = [...coverageFiles, suiteFile]
213
191
  .map(filename => getTestSuitePath(filename, this.repositoryRoot))
@@ -303,36 +281,6 @@ class CucumberPlugin extends CiPlugin {
303
281
  return ctx.currentStore
304
282
  })
305
283
 
306
- this.addSub('ci:cucumber:worker-report:trace', (traces) => {
307
- const formattedTraces = JSON.parse(traces).map(trace =>
308
- trace.map(span => ({
309
- ...span,
310
- span_id: id(span.span_id),
311
- trace_id: id(span.trace_id),
312
- parent_id: id(span.parent_id)
313
- }))
314
- )
315
-
316
- // We have to update the test session, test module and test suite ids
317
- // before we export them in the main process
318
- formattedTraces.forEach(trace => {
319
- trace.forEach(span => {
320
- if (span.name === 'cucumber.test') {
321
- const testSuite = span.meta[TEST_SUITE]
322
- const testSuiteSpan = this.testSuiteSpanByPath[testSuite]
323
-
324
- const testSuiteTags = getTestSuiteTags(testSuiteSpan)
325
- span.meta = {
326
- ...span.meta,
327
- ...testSuiteTags
328
- }
329
- }
330
- })
331
-
332
- this.tracer._exporter.export(trace)
333
- })
334
- })
335
-
336
284
  this.addSub('ci:cucumber:test:finish', ({
337
285
  span,
338
286
  isStep,
@@ -501,7 +449,7 @@ class CucumberPlugin extends CiPlugin {
501
449
  }
502
450
 
503
451
  startTestSpan (testName, testSuite, extraTags) {
504
- const testSuiteSpan = this.testSuiteSpanByPath[testSuite]
452
+ const testSuiteSpan = this._testSuiteSpansByTestSuite.get(testSuite)
505
453
  return super.startTestSpan(
506
454
  testName,
507
455
  testSuite,