dd-trace 5.5.0 → 5.6.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 (52) hide show
  1. package/README.md +7 -0
  2. package/ci/cypress/after-spec.js +1 -0
  3. package/index.d.ts +1498 -1488
  4. package/package.json +3 -3
  5. package/packages/datadog-core/src/utils/src/get.js +11 -0
  6. package/packages/datadog-core/src/utils/src/has.js +14 -0
  7. package/packages/datadog-core/src/utils/src/set.js +16 -0
  8. package/packages/datadog-instrumentations/src/amqplib.js +1 -1
  9. package/packages/datadog-instrumentations/src/cucumber.js +2 -1
  10. package/packages/datadog-instrumentations/src/grpc/server.js +3 -1
  11. package/packages/datadog-instrumentations/src/jest.js +11 -5
  12. package/packages/datadog-instrumentations/src/mocha.js +4 -1
  13. package/packages/datadog-instrumentations/src/mongodb-core.js +34 -3
  14. package/packages/datadog-instrumentations/src/playwright.js +78 -16
  15. package/packages/datadog-plugin-amqplib/src/consumer.js +5 -4
  16. package/packages/datadog-plugin-amqplib/src/producer.js +3 -4
  17. package/packages/datadog-plugin-aws-sdk/src/base.js +3 -2
  18. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +4 -11
  19. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +3 -6
  20. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +5 -7
  21. package/packages/datadog-plugin-cypress/src/after-spec.js +3 -0
  22. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +123 -58
  23. package/packages/datadog-plugin-cypress/src/support.js +50 -3
  24. package/packages/datadog-plugin-graphql/src/index.js +1 -1
  25. package/packages/datadog-plugin-graphql/src/resolve.js +10 -8
  26. package/packages/datadog-plugin-grpc/src/util.js +1 -1
  27. package/packages/datadog-plugin-jest/src/index.js +11 -2
  28. package/packages/datadog-plugin-kafkajs/src/consumer.js +4 -3
  29. package/packages/datadog-plugin-kafkajs/src/producer.js +3 -5
  30. package/packages/datadog-plugin-playwright/src/index.js +34 -3
  31. package/packages/datadog-plugin-rhea/src/consumer.js +5 -3
  32. package/packages/datadog-plugin-rhea/src/producer.js +3 -4
  33. package/packages/dd-trace/src/appsec/iast/index.js +10 -0
  34. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +18 -5
  35. package/packages/dd-trace/src/appsec/recommended.json +67 -27
  36. package/packages/dd-trace/src/appsec/remote_config/index.js +1 -1
  37. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -3
  38. package/packages/dd-trace/src/config.js +451 -460
  39. package/packages/dd-trace/src/data_streams_context.js +1 -1
  40. package/packages/dd-trace/src/datastreams/pathway.js +58 -1
  41. package/packages/dd-trace/src/datastreams/processor.js +3 -5
  42. package/packages/dd-trace/src/format.js +0 -1
  43. package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -1
  44. package/packages/dd-trace/src/plugins/util/test.js +2 -0
  45. package/packages/dd-trace/src/plugins/util/web.js +1 -1
  46. package/packages/dd-trace/src/profiling/exporters/agent.js +38 -2
  47. package/packages/dd-trace/src/telemetry/index.js +22 -34
  48. package/packages/dd-trace/src/tracer.js +3 -3
  49. package/register.js +4 -0
  50. /package/packages/{utils → datadog-core/src/utils}/src/kebabcase.js +0 -0
  51. /package/packages/{utils → datadog-core/src/utils}/src/pick.js +0 -0
  52. /package/packages/{utils → datadog-core/src/utils}/src/uniq.js +0 -0
@@ -25,7 +25,10 @@ const {
25
25
  TEST_ITR_UNSKIPPABLE,
26
26
  TEST_ITR_FORCED_RUN,
27
27
  ITR_CORRELATION_ID,
28
- TEST_SOURCE_FILE
28
+ TEST_SOURCE_FILE,
29
+ TEST_IS_NEW,
30
+ TEST_EARLY_FLAKE_IS_RETRY,
31
+ TEST_EARLY_FLAKE_IS_ENABLED
29
32
  } = require('../../dd-trace/src/plugins/util/test')
30
33
  const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
31
34
  const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
@@ -113,12 +116,9 @@ function getLibraryConfiguration (tracer, testConfiguration) {
113
116
  })
114
117
  }
115
118
 
116
- function getSkippableTests (isSuitesSkippingEnabled, tracer, testConfiguration) {
117
- if (!isSuitesSkippingEnabled) {
118
- return Promise.resolve({ skippableTests: [] })
119
- }
119
+ function getSkippableTests (tracer, testConfiguration) {
120
120
  return new Promise(resolve => {
121
- if (!tracer._tracer._exporter?.getLibraryConfiguration) {
121
+ if (!tracer._tracer._exporter?.getSkippableSuites) {
122
122
  return resolve({ err: new Error('CI Visibility was not initialized correctly') })
123
123
  }
124
124
  tracer._tracer._exporter.getSkippableSuites(testConfiguration, (err, skippableTests, correlationId) => {
@@ -131,6 +131,20 @@ function getSkippableTests (isSuitesSkippingEnabled, tracer, testConfiguration)
131
131
  })
132
132
  }
133
133
 
134
+ function getKnownTests (tracer, testConfiguration) {
135
+ return new Promise(resolve => {
136
+ if (!tracer._tracer._exporter?.getKnownTests) {
137
+ return resolve({ err: new Error('CI Visibility was not initialized correctly') })
138
+ }
139
+ tracer._tracer._exporter.getKnownTests(testConfiguration, (err, knownTests) => {
140
+ resolve({
141
+ err,
142
+ knownTests
143
+ })
144
+ })
145
+ })
146
+ }
147
+
134
148
  function getSuiteStatus (suiteStats) {
135
149
  if (!suiteStats) {
136
150
  return 'skip'
@@ -183,10 +197,14 @@ class CypressPlugin {
183
197
  this.isTestsSkipped = false
184
198
  this.isSuitesSkippingEnabled = false
185
199
  this.isCodeCoverageEnabled = false
200
+ this.isEarlyFlakeDetectionEnabled = false
201
+ this.earlyFlakeDetectionNumRetries = 0
202
+ this.testsToSkip = []
186
203
  this.skippedTests = []
187
204
  this.hasForcedToRunSuites = false
188
205
  this.hasUnskippableSuites = false
189
206
  this.unskippableSuites = []
207
+ this.knownTests = []
190
208
  }
191
209
 
192
210
  init (tracer, cypressConfig) {
@@ -274,66 +292,101 @@ class CypressPlugin {
274
292
  })
275
293
  }
276
294
 
277
- beforeRun (details) {
295
+ isNewTest (testName, testSuite) {
296
+ return !this.knownTestsByTestSuite?.[testSuite]?.includes(testName)
297
+ }
298
+
299
+ async beforeRun (details) {
278
300
  this.command = getCypressCommand(details)
279
301
  this.frameworkVersion = getCypressVersion(details)
280
302
  this.rootDir = getRootDir(details)
281
303
 
282
- return getLibraryConfiguration(this.tracer, this.testConfiguration).then(({ err, libraryConfig }) => {
283
- if (err) {
284
- log.error(err)
304
+ const libraryConfigurationResponse = await getLibraryConfiguration(this.tracer, this.testConfiguration)
305
+
306
+ if (libraryConfigurationResponse.err) {
307
+ log.error(libraryConfigurationResponse.err)
308
+ } else {
309
+ const {
310
+ libraryConfig: {
311
+ isSuitesSkippingEnabled,
312
+ isCodeCoverageEnabled,
313
+ isEarlyFlakeDetectionEnabled,
314
+ earlyFlakeDetectionNumRetries
315
+ }
316
+ } = libraryConfigurationResponse
317
+ this.isSuitesSkippingEnabled = isSuitesSkippingEnabled
318
+ this.isCodeCoverageEnabled = isCodeCoverageEnabled
319
+ this.isEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
320
+ this.earlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
321
+ }
322
+
323
+ if (this.isEarlyFlakeDetectionEnabled) {
324
+ const knownTestsResponse = await getKnownTests(
325
+ this.tracer,
326
+ this.testConfiguration
327
+ )
328
+ if (knownTestsResponse.err) {
329
+ log.error(knownTestsResponse.err)
285
330
  } else {
286
- this.isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled
287
- this.isCodeCoverageEnabled = libraryConfig.isCodeCoverageEnabled
331
+ // We use TEST_FRAMEWORK_NAME for the name of the module
332
+ this.knownTestsByTestSuite = knownTestsResponse.knownTests[TEST_FRAMEWORK_NAME]
288
333
  }
334
+ }
289
335
 
290
- return getSkippableTests(this.isSuitesSkippingEnabled, this.tracer, this.testConfiguration)
291
- .then(({ err, skippableTests, correlationId }) => {
292
- if (err) {
293
- log.error(err)
294
- } else {
295
- this.testsToSkip = skippableTests || []
296
- this.itrCorrelationId = correlationId
297
- }
336
+ if (this.isSuitesSkippingEnabled) {
337
+ const skippableTestsResponse = await getSkippableTests(
338
+ this.tracer,
339
+ this.testConfiguration
340
+ )
341
+ if (skippableTestsResponse.err) {
342
+ log.error(skippableTestsResponse.err)
343
+ } else {
344
+ const { skippableTests, correlationId } = skippableTestsResponse
345
+ this.testsToSkip = skippableTests || []
346
+ this.itrCorrelationId = correlationId
347
+ }
348
+ }
298
349
 
299
- // `details.specs` are test files
300
- details.specs?.forEach(({ absolute, relative }) => {
301
- const isUnskippableSuite = isMarkedAsUnskippable({ path: absolute })
302
- if (isUnskippableSuite) {
303
- this.unskippableSuites.push(relative)
304
- }
305
- })
350
+ // `details.specs` are test files
351
+ details.specs?.forEach(({ absolute, relative }) => {
352
+ const isUnskippableSuite = isMarkedAsUnskippable({ path: absolute })
353
+ if (isUnskippableSuite) {
354
+ this.unskippableSuites.push(relative)
355
+ }
356
+ })
306
357
 
307
- const childOf = getTestParentSpan(this.tracer)
358
+ const childOf = getTestParentSpan(this.tracer)
308
359
 
309
- const testSessionSpanMetadata =
310
- getTestSessionCommonTags(this.command, this.frameworkVersion, TEST_FRAMEWORK_NAME)
311
- const testModuleSpanMetadata =
312
- getTestModuleCommonTags(this.command, this.frameworkVersion, TEST_FRAMEWORK_NAME)
360
+ const testSessionSpanMetadata =
361
+ getTestSessionCommonTags(this.command, this.frameworkVersion, TEST_FRAMEWORK_NAME)
362
+ const testModuleSpanMetadata =
363
+ getTestModuleCommonTags(this.command, this.frameworkVersion, TEST_FRAMEWORK_NAME)
313
364
 
314
- this.testSessionSpan = this.tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_session`, {
315
- childOf,
316
- tags: {
317
- [COMPONENT]: TEST_FRAMEWORK_NAME,
318
- ...this.testEnvironmentMetadata,
319
- ...testSessionSpanMetadata
320
- }
321
- })
322
- this.ciVisEvent(TELEMETRY_EVENT_CREATED, 'session')
323
-
324
- this.testModuleSpan = this.tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_module`, {
325
- childOf: this.testSessionSpan,
326
- tags: {
327
- [COMPONENT]: TEST_FRAMEWORK_NAME,
328
- ...this.testEnvironmentMetadata,
329
- ...testModuleSpanMetadata
330
- }
331
- })
332
- this.ciVisEvent(TELEMETRY_EVENT_CREATED, 'module')
365
+ if (this.isEarlyFlakeDetectionEnabled) {
366
+ testSessionSpanMetadata[TEST_EARLY_FLAKE_IS_ENABLED] = 'true'
367
+ }
333
368
 
334
- return details
335
- })
369
+ this.testSessionSpan = this.tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_session`, {
370
+ childOf,
371
+ tags: {
372
+ [COMPONENT]: TEST_FRAMEWORK_NAME,
373
+ ...this.testEnvironmentMetadata,
374
+ ...testSessionSpanMetadata
375
+ }
336
376
  })
377
+ this.ciVisEvent(TELEMETRY_EVENT_CREATED, 'session')
378
+
379
+ this.testModuleSpan = this.tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_module`, {
380
+ childOf: this.testSessionSpan,
381
+ tags: {
382
+ [COMPONENT]: TEST_FRAMEWORK_NAME,
383
+ ...this.testEnvironmentMetadata,
384
+ ...testModuleSpanMetadata
385
+ }
386
+ })
387
+ this.ciVisEvent(TELEMETRY_EVENT_CREATED, 'module')
388
+
389
+ return details
337
390
  }
338
391
 
339
392
  afterRun (suiteStats) {
@@ -471,12 +524,18 @@ class CypressPlugin {
471
524
 
472
525
  getTasks () {
473
526
  return {
474
- 'dd:testSuiteStart': (suite) => {
527
+ 'dd:testSuiteStart': (testSuite) => {
528
+ const suitePayload = {
529
+ isEarlyFlakeDetectionEnabled: this.isEarlyFlakeDetectionEnabled,
530
+ knownTestsForSuite: this.knownTestsByTestSuite?.[testSuite] || [],
531
+ earlyFlakeDetectionNumRetries: this.earlyFlakeDetectionNumRetries
532
+ }
533
+
475
534
  if (this.testSuiteSpan) {
476
- return null
535
+ return suitePayload
477
536
  }
478
- this.testSuiteSpan = this.getTestSuiteSpan(suite)
479
- return null
537
+ this.testSuiteSpan = this.getTestSuiteSpan(testSuite)
538
+ return suitePayload
480
539
  },
481
540
  'dd:beforeEach': (test) => {
482
541
  const { testName, testSuite } = test
@@ -500,7 +559,7 @@ class CypressPlugin {
500
559
  return this.activeTestSpan ? { traceId: this.activeTestSpan.context().toTraceId() } : {}
501
560
  },
502
561
  'dd:afterEach': ({ test, coverage }) => {
503
- const { state, error, isRUMActive, testSourceLine, testSuite, testName } = test
562
+ const { state, error, isRUMActive, testSourceLine, testSuite, testName, isNew, isEfdRetry } = test
504
563
  if (this.activeTestSpan) {
505
564
  if (coverage && this.isCodeCoverageEnabled && this.tracer._tracer._exporter?.exportCoverage) {
506
565
  const coverageFiles = getCoveredFilenamesFromCoverage(coverage)
@@ -530,6 +589,12 @@ class CypressPlugin {
530
589
  if (testSourceLine) {
531
590
  this.activeTestSpan.setTag(TEST_SOURCE_START, testSourceLine)
532
591
  }
592
+ if (isNew) {
593
+ this.activeTestSpan.setTag(TEST_IS_NEW, 'true')
594
+ if (isEfdRetry) {
595
+ this.activeTestSpan.setTag(TEST_EARLY_FLAKE_IS_RETRY, 'true')
596
+ }
597
+ }
533
598
  const finishedTest = {
534
599
  testName,
535
600
  testStatus,
@@ -1,4 +1,43 @@
1
1
  /* eslint-disable */
2
+ let isEarlyFlakeDetectionEnabled = false
3
+ let knownTestsForSuite = []
4
+ let suiteTests = []
5
+ let earlyFlakeDetectionNumRetries = 0
6
+
7
+ function isNewTest (test) {
8
+ return !knownTestsForSuite.includes(test.fullTitle())
9
+ }
10
+
11
+ function retryTest (test, suiteTests) {
12
+ for (let retryIndex = 0; retryIndex < earlyFlakeDetectionNumRetries; retryIndex++) {
13
+ const clonedTest = test.clone()
14
+ // TODO: signal in framework logs that this is a retry.
15
+ // TODO: Change it so these tests are allowed to fail.
16
+ // TODO: figure out if reported duration is skewed.
17
+ suiteTests.unshift(clonedTest)
18
+ clonedTest._ddIsNew = true
19
+ clonedTest._ddIsEfdRetry = true
20
+ }
21
+ }
22
+
23
+
24
+ const oldRunTests = Cypress.mocha.getRunner().runTests
25
+ Cypress.mocha.getRunner().runTests = function (suite, fn) {
26
+ if (!isEarlyFlakeDetectionEnabled) {
27
+ return oldRunTests.apply(this, arguments)
28
+ }
29
+ // We copy the new tests at the beginning of the suite run (runTests), so that they're run
30
+ // multiple times.
31
+ suite.tests.forEach(test => {
32
+ if (!test._ddIsNew && !test.isPending() && isNewTest(test)) {
33
+ test._ddIsNew = true
34
+ retryTest(test, suite.tests)
35
+ }
36
+ })
37
+
38
+ return oldRunTests.apply(this, [suite, fn])
39
+ }
40
+
2
41
  beforeEach(function () {
3
42
  cy.task('dd:beforeEach', {
4
43
  testName: Cypress.mocha.getRunner().suite.ctx.currentTest.fullTitle(),
@@ -11,8 +50,14 @@ beforeEach(function () {
11
50
  })
12
51
  })
13
52
 
14
- before(() => {
15
- cy.task('dd:testSuiteStart', Cypress.mocha.getRootSuite().file)
53
+ before(function () {
54
+ cy.task('dd:testSuiteStart', Cypress.mocha.getRootSuite().file).then((suiteConfig) => {
55
+ if (suiteConfig) {
56
+ isEarlyFlakeDetectionEnabled = suiteConfig.isEarlyFlakeDetectionEnabled
57
+ knownTestsForSuite = suiteConfig.knownTestsForSuite
58
+ earlyFlakeDetectionNumRetries = suiteConfig.earlyFlakeDetectionNumRetries
59
+ }
60
+ })
16
61
  })
17
62
 
18
63
  after(() => {
@@ -24,7 +69,7 @@ after(() => {
24
69
  })
25
70
 
26
71
 
27
- afterEach(() => {
72
+ afterEach(function () {
28
73
  cy.window().then(win => {
29
74
  const currentTest = Cypress.mocha.getRunner().suite.ctx.currentTest
30
75
  const testInfo = {
@@ -32,6 +77,8 @@ afterEach(() => {
32
77
  testSuite: Cypress.mocha.getRootSuite().file,
33
78
  state: currentTest.state,
34
79
  error: currentTest.err,
80
+ isNew: currentTest._ddIsNew,
81
+ isEfdRetry: currentTest._ddIsEfdRetry
35
82
  }
36
83
  try {
37
84
  testInfo.testSourceLine = Cypress.mocha.getRunner().currentRunnable.invocationDetails.line
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const pick = require('../../utils/src/pick')
3
+ const pick = require('../../datadog-core/src/utils/src/pick')
4
4
  const CompositePlugin = require('../../dd-trace/src/plugins/composite')
5
5
  const log = require('../../dd-trace/src/log')
6
6
  const GraphQLExecutePlugin = require('./execute')
@@ -122,15 +122,17 @@ function getResolverInfo (info, args) {
122
122
  Object.assign(resolverVars, args)
123
123
  }
124
124
 
125
- const directives = info.fieldNodes[0].directives
126
- for (const directive of directives) {
127
- const argList = {}
128
- for (const argument of directive['arguments']) {
129
- argList[argument.name.value] = argument.value.value
130
- }
125
+ const directives = info.fieldNodes?.[0]?.directives
126
+ if (Array.isArray(directives)) {
127
+ for (const directive of directives) {
128
+ const argList = {}
129
+ for (const argument of directive['arguments']) {
130
+ argList[argument.name.value] = argument.value.value
131
+ }
131
132
 
132
- if (Object.keys(argList).length) {
133
- resolverVars[directive.name.value] = argList
133
+ if (Object.keys(argList).length) {
134
+ resolverVars[directive.name.value] = argList
135
+ }
134
136
  }
135
137
  }
136
138
 
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const pick = require('../../utils/src/pick')
3
+ const pick = require('../../datadog-core/src/utils/src/pick')
4
4
  const log = require('../../dd-trace/src/log')
5
5
 
6
6
  module.exports = {
@@ -18,7 +18,8 @@ const {
18
18
  TEST_SOURCE_FILE,
19
19
  TEST_IS_NEW,
20
20
  TEST_EARLY_FLAKE_IS_RETRY,
21
- TEST_EARLY_FLAKE_IS_ENABLED
21
+ TEST_EARLY_FLAKE_IS_ENABLED,
22
+ JEST_DISPLAY_NAME
22
23
  } = require('../../dd-trace/src/plugins/util/test')
23
24
  const { COMPONENT } = require('../../dd-trace/src/constants')
24
25
  const id = require('../../dd-trace/src/id')
@@ -144,7 +145,7 @@ class JestPlugin extends CiPlugin {
144
145
  })
145
146
  })
146
147
 
147
- this.addSub('ci:jest:test-suite:start', ({ testSuite, testEnvironmentOptions, frameworkVersion }) => {
148
+ this.addSub('ci:jest:test-suite:start', ({ testSuite, testEnvironmentOptions, frameworkVersion, displayName }) => {
148
149
  const {
149
150
  _ddTestSessionId: testSessionId,
150
151
  _ddTestCommand: testCommand,
@@ -179,6 +180,9 @@ class JestPlugin extends CiPlugin {
179
180
  if (itrCorrelationId) {
180
181
  testSuiteMetadata[ITR_CORRELATION_ID] = itrCorrelationId
181
182
  }
183
+ if (displayName) {
184
+ testSuiteMetadata[JEST_DISPLAY_NAME] = displayName
185
+ }
182
186
 
183
187
  this.testSuiteSpan = this.tracer.startSpan('jest.test_suite', {
184
188
  childOf: testSessionSpanContext,
@@ -308,6 +312,7 @@ class JestPlugin extends CiPlugin {
308
312
  suite,
309
313
  name,
310
314
  runner,
315
+ displayName,
311
316
  testParameters,
312
317
  frameworkVersion,
313
318
  testStartLine,
@@ -327,6 +332,10 @@ class JestPlugin extends CiPlugin {
327
332
  // If for whatever we don't have the source file, we'll fall back to the suite name
328
333
  extraTags[TEST_SOURCE_FILE] = testSourceFile || suite
329
334
 
335
+ if (displayName) {
336
+ extraTags[JEST_DISPLAY_NAME] = displayName
337
+ }
338
+
330
339
  if (isNew) {
331
340
  extraTags[TEST_IS_NEW] = 'true'
332
341
  if (isEfdRetry) {
@@ -1,7 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const dc = require('dc-polyfill')
4
- const { getMessageSize, CONTEXT_PROPAGATION_KEY } = require('../../dd-trace/src/datastreams/processor')
4
+ const { getMessageSize } = require('../../dd-trace/src/datastreams/processor')
5
+ const { DsmPathwayCodec } = require('../../dd-trace/src/datastreams/pathway')
5
6
  const ConsumerPlugin = require('../../dd-trace/src/plugins/consumer')
6
7
 
7
8
  const afterStartCh = dc.channel('dd-trace:kafkajs:consumer:afterStart')
@@ -77,9 +78,9 @@ class KafkajsConsumerPlugin extends ConsumerPlugin {
77
78
  'kafka.partition': partition
78
79
  }
79
80
  })
80
- if (this.config.dsmEnabled) {
81
+ if (this.config.dsmEnabled && message?.headers && DsmPathwayCodec.contextExists(message.headers)) {
81
82
  const payloadSize = getMessageSize(message)
82
- this.tracer.decodeDataStreamsContext(message.headers[CONTEXT_PROPAGATION_KEY])
83
+ this.tracer.decodeDataStreamsContext(message.headers)
83
84
  this.tracer
84
85
  .setCheckpoint(['direction:in', `group:${groupId}`, `topic:${topic}`, 'type:kafka'], span, payloadSize)
85
86
  }
@@ -1,8 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
4
- const { encodePathwayContext } = require('../../dd-trace/src/datastreams/pathway')
5
- const { getMessageSize, CONTEXT_PROPAGATION_KEY } = require('../../dd-trace/src/datastreams/processor')
4
+ const { DsmPathwayCodec } = require('../../dd-trace/src/datastreams/pathway')
5
+ const { getMessageSize } = require('../../dd-trace/src/datastreams/processor')
6
6
 
7
7
  const BOOTSTRAP_SERVERS_KEY = 'messaging.kafka.bootstrap.servers'
8
8
 
@@ -67,7 +67,6 @@ class KafkajsProducerPlugin extends ProducerPlugin {
67
67
  }
68
68
 
69
69
  start ({ topic, messages, bootstrapServers }) {
70
- let pathwayCtx
71
70
  const span = this.startSpan({
72
71
  resource: topic,
73
72
  meta: {
@@ -88,8 +87,7 @@ class KafkajsProducerPlugin extends ProducerPlugin {
88
87
  const payloadSize = getMessageSize(message)
89
88
  const dataStreamsContext = this.tracer
90
89
  .setCheckpoint(['direction:out', `topic:${topic}`, 'type:kafka'], span, payloadSize)
91
- pathwayCtx = encodePathwayContext(dataStreamsContext)
92
- message.headers[CONTEXT_PROPAGATION_KEY] = pathwayCtx
90
+ DsmPathwayCodec.encode(dataStreamsContext, message.headers)
93
91
  }
94
92
  }
95
93
  }
@@ -30,11 +30,23 @@ class PlaywrightPlugin extends CiPlugin {
30
30
  super(...args)
31
31
 
32
32
  this._testSuites = new Map()
33
+ this.numFailedTests = 0
34
+ this.numFailedSuites = 0
33
35
 
34
36
  this.addSub('ci:playwright:session:finish', ({ status, onDone }) => {
35
37
  this.testModuleSpan.setTag(TEST_STATUS, status)
36
38
  this.testSessionSpan.setTag(TEST_STATUS, status)
37
39
 
40
+ if (this.numFailedSuites > 0) {
41
+ let errorMessage = `Test suites failed: ${this.numFailedSuites}.`
42
+ if (this.numFailedTests > 0) {
43
+ errorMessage += ` Tests failed: ${this.numFailedTests}`
44
+ }
45
+ const error = new Error(errorMessage)
46
+ this.testModuleSpan.setTag('error', error)
47
+ this.testSessionSpan.setTag('error', error)
48
+ }
49
+
38
50
  this.testModuleSpan.finish()
39
51
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
40
52
  this.testSessionSpan.finish()
@@ -42,6 +54,7 @@ class PlaywrightPlugin extends CiPlugin {
42
54
  finishAllTraceSpans(this.testSessionSpan)
43
55
  appClosingTelemetry()
44
56
  this.tracer._exporter.flush(onDone)
57
+ this.numFailedTests = 0
45
58
  })
46
59
 
47
60
  this.addSub('ci:playwright:test-suite:start', (testSuiteAbsolutePath) => {
@@ -69,11 +82,21 @@ class PlaywrightPlugin extends CiPlugin {
69
82
  this._testSuites.set(testSuite, testSuiteSpan)
70
83
  })
71
84
 
72
- this.addSub('ci:playwright:test-suite:finish', (status) => {
85
+ this.addSub('ci:playwright:test-suite:finish', ({ status, error }) => {
73
86
  const store = storage.getStore()
74
87
  const span = store && store.span
75
88
  if (!span) return
76
- span.setTag(TEST_STATUS, status)
89
+ if (error) {
90
+ span.setTag('error', error)
91
+ span.setTag(TEST_STATUS, 'fail')
92
+ } else {
93
+ span.setTag(TEST_STATUS, status)
94
+ }
95
+
96
+ if (status === 'fail' || error) {
97
+ this.numFailedSuites++
98
+ }
99
+
77
100
  span.finish()
78
101
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
79
102
  })
@@ -114,11 +137,19 @@ class PlaywrightPlugin extends CiPlugin {
114
137
  if (step.error) {
115
138
  stepSpan.setTag('error', step.error)
116
139
  }
117
- stepSpan.finish(stepStartTime + step.duration)
140
+ let stepDuration = step.duration
141
+ if (stepDuration <= 0 || isNaN(stepDuration)) {
142
+ stepDuration = 0
143
+ }
144
+ stepSpan.finish(stepStartTime + stepDuration)
118
145
  })
119
146
 
120
147
  span.finish()
121
148
 
149
+ if (testStatus === 'fail') {
150
+ this.numFailedTests++
151
+ }
152
+
122
153
  this.telemetry.ciVisEvent(
123
154
  TELEMETRY_EVENT_FINISHED,
124
155
  'test',
@@ -2,7 +2,8 @@
2
2
 
3
3
  const ConsumerPlugin = require('../../dd-trace/src/plugins/consumer')
4
4
  const { storage } = require('../../datadog-core')
5
- const { getAmqpMessageSize, CONTEXT_PROPAGATION_KEY } = require('../../dd-trace/src/datastreams/processor')
5
+ const { getAmqpMessageSize } = require('../../dd-trace/src/datastreams/processor')
6
+ const { DsmPathwayCodec } = require('../../dd-trace/src/datastreams/pathway')
6
7
 
7
8
  class RheaConsumerPlugin extends ConsumerPlugin {
8
9
  static get id () { return 'rhea' }
@@ -33,12 +34,13 @@ class RheaConsumerPlugin extends ConsumerPlugin {
33
34
 
34
35
  if (
35
36
  this.config.dsmEnabled &&
36
- msgObj?.message?.delivery_annotations?.[CONTEXT_PROPAGATION_KEY]
37
+ msgObj?.message?.delivery_annotations &&
38
+ DsmPathwayCodec.contextExists(msgObj.message.delivery_annotations)
37
39
  ) {
38
40
  const payloadSize = getAmqpMessageSize(
39
41
  { headers: msgObj.message.delivery_annotations, content: msgObj.message.body }
40
42
  )
41
- this.tracer.decodeDataStreamsContext(msgObj.message.delivery_annotations[CONTEXT_PROPAGATION_KEY])
43
+ this.tracer.decodeDataStreamsContext(msgObj.message.delivery_annotations)
42
44
  this.tracer
43
45
  .setCheckpoint(['direction:in', `topic:${name}`, 'type:rabbitmq'], span, payloadSize)
44
46
  }
@@ -2,8 +2,8 @@
2
2
 
3
3
  const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
4
4
  const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
5
- const { encodePathwayContext } = require('../../dd-trace/src/datastreams/pathway')
6
- const { getAmqpMessageSize, CONTEXT_PROPAGATION_KEY } = require('../../dd-trace/src/datastreams/processor')
5
+ const { DsmPathwayCodec } = require('../../dd-trace/src/datastreams/pathway')
6
+ const { getAmqpMessageSize } = require('../../dd-trace/src/datastreams/processor')
7
7
 
8
8
  class RheaProducerPlugin extends ProducerPlugin {
9
9
  static get id () { return 'rhea' }
@@ -44,8 +44,7 @@ function addDeliveryAnnotations (msg, tracer, span) {
44
44
  const payloadSize = getAmqpMessageSize({ content: msg.body, headers: msg.delivery_annotations })
45
45
  const dataStreamsContext = tracer
46
46
  .setCheckpoint(['direction:out', `exchange:${targetName}`, 'type:rabbitmq'], span, payloadSize)
47
- const pathwayCtx = encodePathwayContext(dataStreamsContext)
48
- msg.delivery_annotations[CONTEXT_PROPAGATION_KEY] = pathwayCtx
47
+ DsmPathwayCodec.encode(dataStreamsContext, msg.delivery_annotations)
49
48
  }
50
49
  }
51
50
  }
@@ -21,7 +21,11 @@ const requestStart = dc.channel('dd-trace:incomingHttpRequestStart')
21
21
  const requestClose = dc.channel('dd-trace:incomingHttpRequestEnd')
22
22
  const iastResponseEnd = dc.channel('datadog:iast:response-end')
23
23
 
24
+ let isEnabled = false
25
+
24
26
  function enable (config, _tracer) {
27
+ if (isEnabled) return
28
+
25
29
  iastTelemetry.configure(config, config.iast?.telemetryVerbosity)
26
30
  enableAllAnalyzers(config)
27
31
  enableTaintTracking(config.iast, iastTelemetry.verbosity)
@@ -30,9 +34,15 @@ function enable (config, _tracer) {
30
34
  overheadController.configure(config.iast)
31
35
  overheadController.startGlobalContext()
32
36
  vulnerabilityReporter.start(config, _tracer)
37
+
38
+ isEnabled = true
33
39
  }
34
40
 
35
41
  function disable () {
42
+ if (!isEnabled) return
43
+
44
+ isEnabled = false
45
+
36
46
  iastTelemetry.stop()
37
47
  disableAllAnalyzers()
38
48
  disableTaintTracking()
@@ -65,16 +65,18 @@ function getRewriter (telemetryVerbosity) {
65
65
  return rewriter
66
66
  }
67
67
 
68
- let originalPrepareStackTrace = Error.prepareStackTrace
68
+ let originalPrepareStackTrace
69
+ let actualPrepareStackTrace
69
70
  function getPrepareStackTraceAccessor () {
70
- let actual = getPrepareStackTrace(originalPrepareStackTrace)
71
+ originalPrepareStackTrace = Error.prepareStackTrace
72
+ actualPrepareStackTrace = getPrepareStackTrace(originalPrepareStackTrace)
71
73
  return {
72
74
  configurable: true,
73
75
  get () {
74
- return actual
76
+ return actualPrepareStackTrace
75
77
  },
76
78
  set (value) {
77
- actual = getPrepareStackTrace(value)
79
+ actualPrepareStackTrace = getPrepareStackTrace(value)
78
80
  originalPrepareStackTrace = value
79
81
  }
80
82
  }
@@ -121,7 +123,18 @@ function enableRewriter (telemetryVerbosity) {
121
123
 
122
124
  function disableRewriter () {
123
125
  shimmer.unwrap(Module.prototype, '_compile')
124
- Error.prepareStackTrace = originalPrepareStackTrace
126
+
127
+ if (!actualPrepareStackTrace) return
128
+
129
+ try {
130
+ delete Error.prepareStackTrace
131
+
132
+ Error.prepareStackTrace = originalPrepareStackTrace
133
+
134
+ actualPrepareStackTrace = undefined
135
+ } catch (e) {
136
+ iastLog.warn(e)
137
+ }
125
138
  }
126
139
 
127
140
  function getOriginalPathAndLineFromSourceMap ({ path, line, column }) {