dd-trace 5.0.0 → 5.2.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 (46) hide show
  1. package/LICENSE-3rdparty.csv +0 -3
  2. package/MIGRATING.md +15 -0
  3. package/README.md +11 -9
  4. package/package.json +4 -6
  5. package/packages/datadog-instrumentations/src/amqplib.js +1 -1
  6. package/packages/datadog-instrumentations/src/cucumber.js +3 -1
  7. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  8. package/packages/datadog-instrumentations/src/jest.js +1 -0
  9. package/packages/datadog-instrumentations/src/mocha.js +9 -2
  10. package/packages/datadog-instrumentations/src/mquery.js +65 -0
  11. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +28 -1
  12. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +19 -1
  13. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -10
  14. package/packages/datadog-plugin-cucumber/src/index.js +11 -7
  15. package/packages/datadog-plugin-cypress/src/plugin.js +60 -46
  16. package/packages/datadog-plugin-graphql/src/index.js +1 -6
  17. package/packages/datadog-plugin-grpc/src/util.js +1 -1
  18. package/packages/datadog-plugin-jest/src/index.js +7 -1
  19. package/packages/datadog-plugin-mocha/src/index.js +11 -2
  20. package/packages/datadog-plugin-playwright/src/index.js +2 -0
  21. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  22. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +3 -3
  23. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +22 -17
  24. package/packages/dd-trace/src/appsec/iast/analyzers/weak-randomness-analyzer.js +19 -0
  25. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +1 -0
  26. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +12 -1
  27. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  28. package/packages/dd-trace/src/appsec/remote_config/manager.js +9 -8
  29. package/packages/dd-trace/src/appsec/reporter.js +2 -1
  30. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +4 -2
  31. package/packages/dd-trace/src/config.js +10 -6
  32. package/packages/dd-trace/src/datastreams/processor.js +30 -5
  33. package/packages/dd-trace/src/datastreams/writer.js +9 -0
  34. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +25 -2
  35. package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -1
  36. package/packages/dd-trace/src/plugins/ci_plugin.js +9 -3
  37. package/packages/dd-trace/src/plugins/util/test.js +2 -0
  38. package/packages/dd-trace/src/plugins/util/web.js +1 -1
  39. package/packages/dd-trace/src/profiling/config.js +19 -5
  40. package/packages/dd-trace/src/telemetry/index.js +8 -3
  41. package/packages/dd-trace/src/telemetry/send-data.js +2 -2
  42. package/packages/dd-trace/src/tracer.js +1 -0
  43. package/packages/utils/src/kebabcase.js +16 -0
  44. package/packages/utils/src/pick.js +11 -0
  45. package/packages/utils/src/uniq.js +5 -0
  46. package/scripts/st.js +105 -0
@@ -17,10 +17,7 @@ require,istanbul-lib-coverage,BSD-3-Clause,Copyright 2012-2015 Yahoo! Inc.
17
17
  require,jest-docblock,MIT,Copyright Meta Platforms, Inc. and affiliates.
18
18
  require,koalas,MIT,Copyright 2013-2017 Brian Woodward
19
19
  require,limiter,MIT,Copyright 2011 John Hurliman
20
- require,lodash.kebabcase,MIT,Copyright JS Foundation and other contributors
21
- require,lodash.pick,MIT,Copyright JS Foundation and other contributors
22
20
  require,lodash.sortby,MIT,Copyright JS Foundation and other contributors
23
- require,lodash.uniq,MIT,Copyright JS Foundation and other contributors
24
21
  require,lru-cache,ISC,Copyright (c) 2010-2022 Isaac Z. Schlueter and Contributors
25
22
  require,methods,MIT,Copyright 2013-2014 TJ Holowaychuk
26
23
  require,module-details-from-path,MIT,Copyright 2016 Thomas Watson Steen
package/MIGRATING.md CHANGED
@@ -4,6 +4,21 @@ This guide describes the steps to upgrade dd-trace from a major version to the
4
4
  next. If you are having any issues related to migrating, please feel free to
5
5
  open an issue or contact our [support](https://www.datadoghq.com/support/) team.
6
6
 
7
+ ## 4.0 to 5.0
8
+
9
+ ### Node 16 is no longer supported
10
+
11
+ Node.js 16 has reached EOL in September 2023 and is no longer supported. Generally
12
+ speaking, we highly recommend always keeping Node.js up to date regardless of
13
+ our support policy.
14
+
15
+ ### Update `trace<T>` TypeScript declaration
16
+
17
+ The TypeScript declaration for `trace<T>` has been updated to enforce
18
+ that calls to `tracer.trace(name, fn)` must receive a function which takes at least
19
+ the span object. Previously the span was technically optional when it should not have
20
+ been as the span must be handled.
21
+
7
22
  ## 3.0 to 4.0
8
23
 
9
24
  ### Node 14 is no longer supported
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # `dd-trace`: Node.js APM Tracer Library
2
2
 
3
- [![npm v4](https://img.shields.io/npm/v/dd-trace/latest?color=blue&label=dd-trace%40v4&logo=npm)](https://www.npmjs.com/package/dd-trace)
4
- [![npm v3](https://img.shields.io/npm/v/dd-trace/latest-node14?color=blue&label=dd-trace%40v3&logo=npm)](https://www.npmjs.com/package/dd-trace/v/latest-node12)
3
+ [![npm v5](https://img.shields.io/npm/v/dd-trace/latest?color=blue&label=dd-trace%40v5&logo=npm)](https://www.npmjs.com/package/dd-trace)
4
+ [![npm v4](https://img.shields.io/npm/v/dd-trace/latest-node16?color=blue&label=dd-trace%40v4&logo=npm)](https://www.npmjs.com/package/dd-trace/v/latest-node16)
5
+ [![npm v3](https://img.shields.io/npm/v/dd-trace/latest-node14?color=blue&label=dd-trace%40v3&logo=npm)](https://www.npmjs.com/package/dd-trace/v/latest-node14)
5
6
  [![codecov](https://codecov.io/gh/DataDog/dd-trace-js/branch/master/graph/badge.svg)](https://codecov.io/gh/DataDog/dd-trace-js)
6
7
 
7
8
  <img align="right" src="https://user-images.githubusercontent.com/551402/208212084-1d0c07e2-4135-4c61-b2da-8f2fddbc66ed.png" alt="Bits the dog JavaScript" width="200px"/>
@@ -28,24 +29,25 @@ Most of the documentation for `dd-trace` is available on these webpages:
28
29
  | [`v1`](https://github.com/DataDog/dd-trace-js/tree/v1.x) | ![npm v1](https://img.shields.io/npm/v/dd-trace/legacy-v1?color=white&label=%20&style=flat-square) | `>= v12` | **End of Life** | 2021-07-13 | 2022-02-25 |
29
30
  | [`v2`](https://github.com/DataDog/dd-trace-js/tree/v2.x) | ![npm v2](https://img.shields.io/npm/v/dd-trace/latest-node12?color=white&label=%20&style=flat-square) | `>= v12` | **End of Life** | 2022-01-28 | 2023-08-15 |
30
31
  | [`v3`](https://github.com/DataDog/dd-trace-js/tree/v3.x) | ![npm v3](https://img.shields.io/npm/v/dd-trace/latest-node14?color=white&label=%20&style=flat-square) | `>= v14` | **Maintenance** | 2022-08-15 | 2024-05-15 |
31
- | [`v4`](https://github.com/DataDog/dd-trace-js/tree/v4.x) | ![npm v4](https://img.shields.io/npm/v/dd-trace/latest?color=white&label=%20&style=flat-square) | `>= v16` | **Current** | 2023-05-12 | Unknown |
32
+ | [`v4`](https://github.com/DataDog/dd-trace-js/tree/v4.x) | ![npm v4](https://img.shields.io/npm/v/dd-trace/latest-node16?color=white&label=%20&style=flat-square) | `>= v16` | **Maintenance** | 2023-05-12 | 2025-01-11 |
33
+ | [`v5`](https://github.com/DataDog/dd-trace-js/tree/v5.x) | ![npm v5](https://img.shields.io/npm/v/dd-trace/latest?color=white&label=%20&style=flat-square) | `>= v18` | **Current** | 2024-01-11 | Unknown |
32
34
 
33
- We currently maintain two release lines, namely `v3` and `v4`.
34
- Features and bug fixes that are merged are released to the `v4` line and, if appropriate, also the `v3` line.
35
+ We currently maintain three release lines, namely `v5`, `v4` and `v3`.
36
+ Features and bug fixes that are merged are released to the `v5` line and, if appropriate, also the `v4` & `v3` line.
35
37
 
36
- For any new projects it is recommended to use the `v4` release line:
38
+ For any new projects it is recommended to use the `v5` release line:
37
39
 
38
40
  ```sh
39
41
  $ npm install dd-trace
40
42
  $ yarn add dd-trace
41
43
  ```
42
44
 
43
- However, existing projects that already use the `v3` release line, or projects that need to support EOL versions of Node.js, may continue to use these release lines.
45
+ However, existing projects that already use the `v4` & `v3` release line, or projects that need to support EOL versions of Node.js, may continue to use these release lines.
44
46
  This is done by specifying the version when installing the package.
45
47
 
46
48
  ```sh
47
- $ npm install dd-trace@3
48
- $ yarn add dd-trace@3
49
+ $ npm install dd-trace@4
50
+ $ yarn add dd-trace@4
49
51
  ```
50
52
 
51
53
  Any backwards-breaking functionality that is introduced into the library will result in an increase of the major version of the library and therefore a new release line.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.0.0",
3
+ "version": "5.2.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -36,6 +36,7 @@
36
36
  "test:integration:cucumber": "mocha --colors --timeout 30000 \"integration-tests/cucumber/*.spec.js\"",
37
37
  "test:integration:cypress": "mocha --colors --timeout 30000 \"integration-tests/cypress/*.spec.js\"",
38
38
  "test:integration:playwright": "mocha --colors --timeout 30000 \"integration-tests/playwright/*.spec.js\"",
39
+ "test:integration:profiler": "mocha --colors --timeout 90000 \"integration-tests/profiler/*.spec.js\"",
39
40
  "test:integration:serverless": "mocha --colors --timeout 30000 \"integration-tests/serverless/*.spec.js\"",
40
41
  "test:integration:plugins": "mocha --colors --exit -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
41
42
  "test:unit:plugins": "mocha --colors --exit -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-instrumentations/test/@($(echo $PLUGINS)).spec.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/**/*.spec.js\" --exclude \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
@@ -68,7 +69,7 @@
68
69
  "node": ">=18"
69
70
  },
70
71
  "dependencies": {
71
- "@datadog/native-appsec": "6.0.0",
72
+ "@datadog/native-appsec": "7.0.0",
72
73
  "@datadog/native-iast-rewriter": "2.2.2",
73
74
  "@datadog/native-iast-taint-tracking": "1.6.4",
74
75
  "@datadog/native-metrics": "^2.0.0",
@@ -79,17 +80,14 @@
79
80
  "crypto-randomuuid": "^1.0.0",
80
81
  "dc-polyfill": "^0.1.2",
81
82
  "ignore": "^5.2.4",
82
- "import-in-the-middle": "^1.7.1",
83
+ "import-in-the-middle": "^1.7.3",
83
84
  "int64-buffer": "^0.1.9",
84
85
  "ipaddr.js": "^2.1.0",
85
86
  "istanbul-lib-coverage": "3.2.0",
86
87
  "jest-docblock": "^29.7.0",
87
88
  "koalas": "^1.0.2",
88
89
  "limiter": "1.1.5",
89
- "lodash.kebabcase": "^4.1.1",
90
- "lodash.pick": "^4.4.0",
91
90
  "lodash.sortby": "^4.7.0",
92
- "lodash.uniq": "^4.5.0",
93
91
  "lru-cache": "^7.14.0",
94
92
  "methods": "^1.1.2",
95
93
  "module-details-from-path": "^1.0.3",
@@ -5,7 +5,7 @@ const {
5
5
  addHook,
6
6
  AsyncResource
7
7
  } = require('./helpers/instrument')
8
- const kebabCase = require('lodash.kebabcase')
8
+ const kebabCase = require('../../utils/src/kebabcase')
9
9
  const shimmer = require('../../datadog-shimmer')
10
10
 
11
11
  const startCh = channel('apm:amqplib:command:start')
@@ -44,6 +44,7 @@ const patched = new WeakSet()
44
44
  let pickleByFile = {}
45
45
  const pickleResultByFile = {}
46
46
  let skippableSuites = []
47
+ let itrCorrelationId = ''
47
48
  let isForcedToRun = false
48
49
  let isUnskippable = false
49
50
 
@@ -102,7 +103,7 @@ function wrapRun (pl, isLatestVersion) {
102
103
  const testSuitePath = getTestSuitePath(testSuiteFullPath, process.cwd())
103
104
  isForcedToRun = isUnskippable && skippableSuites.includes(testSuitePath)
104
105
 
105
- testSuiteStartCh.publish({ testSuitePath, isUnskippable, isForcedToRun })
106
+ testSuiteStartCh.publish({ testSuitePath, isUnskippable, isForcedToRun, itrCorrelationId })
106
107
  }
107
108
 
108
109
  const testSourceLine = this.gherkinDocument &&
@@ -304,6 +305,7 @@ addHook({
304
305
  this.pickleIds = picklesToRun
305
306
 
306
307
  skippedSuites = Array.from(filteredPickles.skippedSuites)
308
+ itrCorrelationId = skippableResponse.itrCorrelationId
307
309
  }
308
310
 
309
311
  pickleByFile = getPickleByFile(this)
@@ -73,6 +73,7 @@ module.exports = {
73
73
  'mongodb': () => require('../mongodb'),
74
74
  'mongodb-core': () => require('../mongodb-core'),
75
75
  'mongoose': () => require('../mongoose'),
76
+ 'mquery': () => require('../mquery'),
76
77
  'mysql': () => require('../mysql'),
77
78
  'mysql2': () => require('../mysql2'),
78
79
  'net': () => require('../net'),
@@ -497,6 +497,7 @@ addHook({
497
497
  _ddTestCommand,
498
498
  _ddForcedToRun,
499
499
  _ddUnskippable,
500
+ _ddItrCorrelationId,
500
501
  ...restOfTestEnvironmentOptions
501
502
  } = testEnvironmentOptions
502
503
 
@@ -53,6 +53,7 @@ let isSuitesSkipped = false
53
53
  let skippedSuites = []
54
54
  const unskippableSuites = []
55
55
  let isForcedToRun = false
56
+ let itrCorrelationId = ''
56
57
 
57
58
  function getSuitesByTestFile (root) {
58
59
  const suitesByTestFile = {}
@@ -191,7 +192,12 @@ function mochaHook (Runner) {
191
192
  const isUnskippable = unskippableSuites.includes(suite.file)
192
193
  isForcedToRun = isUnskippable && suitesToSkip.includes(getTestSuitePath(suite.file, process.cwd()))
193
194
  asyncResource.runInAsyncScope(() => {
194
- testSuiteStartCh.publish({ testSuite: suite.file, isUnskippable, isForcedToRun })
195
+ testSuiteStartCh.publish({
196
+ testSuite: suite.file,
197
+ isUnskippable,
198
+ isForcedToRun,
199
+ itrCorrelationId
200
+ })
195
201
  })
196
202
  }
197
203
  })
@@ -395,11 +401,12 @@ addHook({
395
401
  }
396
402
  })
397
403
 
398
- const onReceivedSkippableSuites = ({ err, skippableSuites }) => {
404
+ const onReceivedSkippableSuites = ({ err, skippableSuites, itrCorrelationId: responseItrCorrelationId }) => {
399
405
  if (err) {
400
406
  suitesToSkip = []
401
407
  } else {
402
408
  suitesToSkip = skippableSuites
409
+ itrCorrelationId = responseItrCorrelationId
403
410
  }
404
411
  // We remove the suites that we skip through ITR
405
412
  const filteredSuites = getFilteredSuites(runner.suite.suites)
@@ -0,0 +1,65 @@
1
+ 'use strict'
2
+
3
+ const dc = require('dc-polyfill')
4
+ const {
5
+ channel,
6
+ addHook
7
+ } = require('./helpers/instrument')
8
+ const shimmer = require('../../datadog-shimmer')
9
+
10
+ const prepareCh = channel('datadog:mquery:filter:prepare')
11
+ const tracingCh = dc.tracingChannel('datadog:mquery:filter')
12
+
13
+ const methods = [
14
+ 'find',
15
+ 'findOne',
16
+ 'findOneAndRemove',
17
+ 'findOneAndDelete',
18
+ 'count',
19
+ 'distinct',
20
+ 'where'
21
+ ]
22
+
23
+ const methodsOptionalArgs = ['findOneAndUpdate']
24
+
25
+ function getFilters (args, methodName) {
26
+ const [arg0, arg1] = args
27
+
28
+ const filters = arg0 && typeof arg0 === 'object' ? [arg0] : []
29
+
30
+ if (arg1 && typeof arg1 === 'object' && methodsOptionalArgs.includes(methodName)) {
31
+ filters.push(arg1)
32
+ }
33
+
34
+ return filters
35
+ }
36
+
37
+ addHook({
38
+ name: 'mquery',
39
+ versions: ['>=5.0.0']
40
+ }, Query => {
41
+ [...methods, ...methodsOptionalArgs].forEach(methodName => {
42
+ if (!(methodName in Query.prototype)) return
43
+
44
+ shimmer.wrap(Query.prototype, methodName, method => {
45
+ return function () {
46
+ if (prepareCh.hasSubscribers) {
47
+ const filters = getFilters(arguments, methodName)
48
+ if (filters?.length) {
49
+ prepareCh.publish({ filters })
50
+ }
51
+ }
52
+
53
+ return method.apply(this, arguments)
54
+ }
55
+ })
56
+ })
57
+
58
+ shimmer.wrap(Query.prototype, 'exec', originalExec => {
59
+ return function wrappedExec () {
60
+ return tracingCh.tracePromise(originalExec, {}, this, arguments)
61
+ }
62
+ })
63
+
64
+ return Query
65
+ })
@@ -1,6 +1,11 @@
1
1
  'use strict'
2
+ const {
3
+ CONTEXT_PROPAGATION_KEY
4
+ } = require('../../../dd-trace/src/datastreams/processor')
5
+ const { encodePathwayContext } = require('../../../dd-trace/src/datastreams/pathway')
2
6
  const log = require('../../../dd-trace/src/log')
3
7
  const BaseAwsSdkPlugin = require('../base')
8
+
4
9
  class Kinesis extends BaseAwsSdkPlugin {
5
10
  static get id () { return 'kinesis' }
6
11
  static get peerServicePrecursors () { return ['streamname'] }
@@ -37,8 +42,9 @@ class Kinesis extends BaseAwsSdkPlugin {
37
42
  if (!request.params) {
38
43
  return
39
44
  }
40
-
41
45
  const traceData = {}
46
+
47
+ // inject data with DD context
42
48
  this.tracer.inject(span, 'text_map', traceData)
43
49
  let injectPath
44
50
  if (request.params.Records && request.params.Records.length > 0) {
@@ -49,9 +55,30 @@ class Kinesis extends BaseAwsSdkPlugin {
49
55
  log.error('No valid payload passed, unable to pass trace context')
50
56
  return
51
57
  }
58
+
52
59
  const parsedData = this._tryParse(injectPath.Data)
53
60
  if (parsedData) {
54
61
  parsedData._datadog = traceData
62
+
63
+ // set DSM hash if enabled
64
+ if (this.config.dsmEnabled) {
65
+ // get payload size of request data
66
+ const payloadSize = Buffer.from(JSON.stringify(parsedData)).byteLength
67
+ let stream
68
+ // users can optionally use either stream name or stream arn
69
+ if (request.params && request.params.StreamArn) {
70
+ stream = request.params.StreamArn
71
+ } else if (request.params && request.params.StreamName) {
72
+ stream = request.params.StreamName
73
+ }
74
+ const dataStreamsContext = this.tracer
75
+ .setCheckpoint(['direction:out', `topic:${stream}`, 'type:kinesis'], span, payloadSize)
76
+ if (dataStreamsContext) {
77
+ const pathwayCtx = encodePathwayContext(dataStreamsContext)
78
+ parsedData._datadog[CONTEXT_PROPAGATION_KEY] = pathwayCtx.toJSON()
79
+ }
80
+ }
81
+
55
82
  const finalData = Buffer.from(JSON.stringify(parsedData))
56
83
  const byteSize = finalData.length
57
84
  // Kinesis max payload size is 1MB
@@ -1,4 +1,6 @@
1
1
  'use strict'
2
+ const { CONTEXT_PROPAGATION_KEY, getHeadersSize } = require('../../../dd-trace/src/datastreams/processor')
3
+ const { encodePathwayContext } = require('../../../dd-trace/src/datastreams/pathway')
2
4
  const log = require('../../../dd-trace/src/log')
3
5
  const BaseAwsSdkPlugin = require('../base')
4
6
 
@@ -11,6 +13,7 @@ class Sns extends BaseAwsSdkPlugin {
11
13
 
12
14
  if (!params.TopicArn && !(response.data && response.data.TopicArn)) return {}
13
15
  const TopicArn = params.TopicArn || response.data.TopicArn
16
+
14
17
  // Split the ARN into its parts
15
18
  // ex.'arn:aws:sns:us-east-1:123456789012:my-topic'
16
19
  const arnParts = TopicArn.split(':')
@@ -72,10 +75,25 @@ class Sns extends BaseAwsSdkPlugin {
72
75
  }
73
76
  const ddInfo = {}
74
77
  this.tracer.inject(span, 'text_map', ddInfo)
78
+ // add ddInfo before checking DSM so we can include DD attributes in payload size
75
79
  params.MessageAttributes._datadog = {
76
80
  DataType: 'Binary',
77
- BinaryValue: Buffer.from(JSON.stringify(ddInfo)) // BINARY types are automatically base64 encoded
81
+ BinaryValue: ddInfo
82
+ }
83
+ if (this.config.dsmEnabled) {
84
+ const payloadSize = getHeadersSize({
85
+ Message: params.Message,
86
+ MessageAttributes: params.MessageAttributes
87
+ })
88
+ const dataStreamsContext = this.tracer
89
+ .setCheckpoint(['direction:out', `topic:${params.TopicArn}`, 'type:sns'], span, payloadSize)
90
+ if (dataStreamsContext) {
91
+ const pathwayCtx = encodePathwayContext(dataStreamsContext)
92
+ ddInfo[CONTEXT_PROPAGATION_KEY] = pathwayCtx.toJSON()
93
+ }
78
94
  }
95
+ // BINARY types are automatically base64 encoded
96
+ params.MessageAttributes._datadog.BinaryValue = Buffer.from(JSON.stringify(ddInfo))
79
97
  }
80
98
  }
81
99
 
@@ -3,6 +3,8 @@
3
3
  const log = require('../../../dd-trace/src/log')
4
4
  const BaseAwsSdkPlugin = require('../base')
5
5
  const { storage } = require('../../../datadog-core')
6
+ const { CONTEXT_PROPAGATION_KEY, getHeadersSize } = require('../../../dd-trace/src/datastreams/processor')
7
+ const { encodePathwayContext } = require('../../../dd-trace/src/datastreams/pathway')
6
8
 
7
9
  class Sqs extends BaseAwsSdkPlugin {
8
10
  static get id () { return 'sqs' }
@@ -19,20 +21,27 @@ class Sqs extends BaseAwsSdkPlugin {
19
21
  const { request, response } = obj
20
22
  const store = storage.getStore()
21
23
  const plugin = this
22
- const maybeChildOf = this.responseExtract(request.params, request.operation, response)
23
- if (maybeChildOf) {
24
+ const contextExtraction = this.responseExtract(request.params, request.operation, response)
25
+ let span
26
+ let parsedMessageAttributes
27
+ if (contextExtraction && contextExtraction.datadogContext) {
24
28
  obj.needsFinish = true
25
29
  const options = {
26
- childOf: maybeChildOf,
30
+ childOf: contextExtraction.datadogContext,
27
31
  tags: Object.assign(
28
32
  {},
29
33
  this.requestTags.get(request) || {},
30
34
  { 'span.kind': 'server' }
31
35
  )
32
36
  }
33
- const span = plugin.tracer.startSpan('aws.response', options)
37
+ parsedMessageAttributes = contextExtraction.parsedAttributes
38
+ span = plugin.tracer.startSpan('aws.response', options)
34
39
  this.enter(span, store)
35
40
  }
41
+ // extract DSM context after as we might not have a parent-child but may have a DSM context
42
+ this.responseExtractDSMContext(
43
+ request.operation, request.params, response, span ?? null, parsedMessageAttributes ?? null
44
+ )
36
45
  })
37
46
 
38
47
  this.addSub('apm:aws:response:finish:sqs', err => {
@@ -133,19 +142,69 @@ class Sqs extends BaseAwsSdkPlugin {
133
142
 
134
143
  const datadogAttribute = message.MessageAttributes._datadog
135
144
 
145
+ const parsedAttributes = this.parseDatadogAttributes(datadogAttribute)
146
+ if (parsedAttributes) {
147
+ return {
148
+ datadogContext: this.tracer.extract('text_map', parsedAttributes),
149
+ parsedAttributes: parsedAttributes
150
+ }
151
+ }
152
+ }
153
+
154
+ parseDatadogAttributes (attributes) {
136
155
  try {
137
- if (datadogAttribute.StringValue) {
138
- const textMap = datadogAttribute.StringValue
139
- return this.tracer.extract('text_map', JSON.parse(textMap))
140
- } else if (datadogAttribute.Type === 'Binary') {
141
- const buffer = Buffer.from(datadogAttribute.Value, 'base64')
142
- return this.tracer.extract('text_map', JSON.parse(buffer))
156
+ if (attributes.StringValue) {
157
+ const textMap = attributes.StringValue
158
+ return JSON.parse(textMap)
159
+ } else if (attributes.Type === 'Binary') {
160
+ const buffer = Buffer.from(attributes.Value, 'base64')
161
+ return JSON.parse(buffer)
143
162
  }
144
163
  } catch (e) {
145
164
  log.error(e)
146
165
  }
147
166
  }
148
167
 
168
+ responseExtractDSMContext (operation, params, response, span, parsedAttributes) {
169
+ if (!this.config.dsmEnabled) return
170
+ if (operation !== 'receiveMessage') return
171
+ if (!response || !response.Messages || !response.Messages[0]) return
172
+
173
+ // we only want to set the payloadSize on the span if we have one message
174
+ span = response.Messages.length > 1 ? null : span
175
+
176
+ response.Messages.forEach(message => {
177
+ // we may have already parsed the message attributes when extracting trace context
178
+ if (!parsedAttributes) {
179
+ if (message.Body) {
180
+ try {
181
+ const body = JSON.parse(message.Body)
182
+
183
+ // SNS to SQS
184
+ if (body.Type === 'Notification') {
185
+ message = body
186
+ }
187
+ } catch (e) {
188
+ // SQS to SQS
189
+ }
190
+ }
191
+ if (message.MessageAttributes && message.MessageAttributes._datadog) {
192
+ parsedAttributes = this.parseDatadogAttributes(message.MessageAttributes._datadog)
193
+ }
194
+ }
195
+ if (parsedAttributes && parsedAttributes[CONTEXT_PROPAGATION_KEY]) {
196
+ const payloadSize = getHeadersSize({
197
+ Body: message.Body,
198
+ MessageAttributes: message.MessageAttributes
199
+ })
200
+ const queue = params.QueueUrl.split('/').pop()
201
+ this.tracer.decodeDataStreamsContext(Buffer.from(parsedAttributes[CONTEXT_PROPAGATION_KEY]))
202
+ this.tracer
203
+ .setCheckpoint(['direction:in', `topic:${queue}`, 'type:sqs'], span, payloadSize)
204
+ }
205
+ })
206
+ }
207
+
149
208
  requestInject (span, request) {
150
209
  const operation = request.operation
151
210
  if (operation === 'sendMessage') {
@@ -164,6 +223,20 @@ class Sqs extends BaseAwsSdkPlugin {
164
223
  DataType: 'String',
165
224
  StringValue: JSON.stringify(ddInfo)
166
225
  }
226
+ if (this.config.dsmEnabled) {
227
+ const payloadSize = getHeadersSize({
228
+ Body: request.params.MessageBody,
229
+ MessageAttributes: request.params.MessageAttributes
230
+ })
231
+ const queue = request.params.QueueUrl.split('/').pop()
232
+ const dataStreamsContext = this.tracer
233
+ .setCheckpoint(['direction:out', `topic:${queue}`, 'type:sqs'], span, payloadSize)
234
+ if (dataStreamsContext) {
235
+ const pathwayCtx = encodePathwayContext(dataStreamsContext)
236
+ ddInfo[CONTEXT_PROPAGATION_KEY] = pathwayCtx.toJSON()
237
+ }
238
+ }
239
+ request.params.MessageAttributes._datadog.StringValue = JSON.stringify(ddInfo)
167
240
  }
168
241
  }
169
242
  }
@@ -13,7 +13,8 @@ const {
13
13
  addIntelligentTestRunnerSpanTags,
14
14
  TEST_ITR_UNSKIPPABLE,
15
15
  TEST_ITR_FORCED_RUN,
16
- TEST_CODE_OWNERS
16
+ TEST_CODE_OWNERS,
17
+ ITR_CORRELATION_ID
17
18
  } = require('../../dd-trace/src/plugins/util/test')
18
19
  const { RESOURCE_NAME } = require('../../../ext/tags')
19
20
  const { COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants')
@@ -74,7 +75,7 @@ class CucumberPlugin extends CiPlugin {
74
75
  this.tracer._exporter.flush()
75
76
  })
76
77
 
77
- this.addSub('ci:cucumber:test-suite:start', ({ testSuitePath, isUnskippable, isForcedToRun }) => {
78
+ this.addSub('ci:cucumber:test-suite:start', ({ testSuitePath, isUnskippable, isForcedToRun, itrCorrelationId }) => {
78
79
  const testSuiteMetadata = getTestSuiteCommonTags(
79
80
  this.command,
80
81
  this.frameworkVersion,
@@ -89,6 +90,9 @@ class CucumberPlugin extends CiPlugin {
89
90
  this.telemetry.count(TELEMETRY_ITR_FORCED_TO_RUN, { testLevel: 'suite' })
90
91
  testSuiteMetadata[TEST_ITR_FORCED_RUN] = 'true'
91
92
  }
93
+ if (itrCorrelationId) {
94
+ testSuiteMetadata[ITR_CORRELATION_ID] = itrCorrelationId
95
+ }
92
96
  this.testSuiteSpan = this.tracer.startSpan('cucumber.test_suite', {
93
97
  childOf: this.testModuleSpan,
94
98
  tags: {
@@ -169,12 +173,12 @@ class CucumberPlugin extends CiPlugin {
169
173
  }
170
174
 
171
175
  span.finish()
172
- this.telemetry.ciVisEvent(
173
- TELEMETRY_EVENT_FINISHED,
174
- 'test',
175
- { hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] }
176
- )
177
176
  if (!isStep) {
177
+ this.telemetry.ciVisEvent(
178
+ TELEMETRY_EVENT_FINISHED,
179
+ 'test',
180
+ { hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] }
181
+ )
178
182
  finishAllTraceSpans(span)
179
183
  }
180
184
  })