dd-trace 4.18.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 (209) hide show
  1. package/CONTRIBUTING.md +98 -0
  2. package/LICENSE-3rdparty.csv +4 -5
  3. package/MIGRATING.md +15 -0
  4. package/README.md +20 -140
  5. package/ci/cypress/after-run.js +1 -0
  6. package/ci/cypress/after-spec.js +1 -0
  7. package/ci/init.js +1 -4
  8. package/ext/kinds.d.ts +1 -0
  9. package/ext/kinds.js +2 -1
  10. package/ext/tags.d.ts +2 -1
  11. package/ext/tags.js +6 -1
  12. package/index.d.ts +1523 -1460
  13. package/package.json +19 -19
  14. package/packages/datadog-core/src/storage/async_resource.js +1 -1
  15. package/packages/datadog-core/src/utils/src/get.js +11 -0
  16. package/packages/datadog-core/src/utils/src/has.js +14 -0
  17. package/packages/datadog-core/src/utils/src/kebabcase.js +16 -0
  18. package/packages/datadog-core/src/utils/src/pick.js +11 -0
  19. package/packages/datadog-core/src/utils/src/set.js +16 -0
  20. package/packages/datadog-core/src/utils/src/uniq.js +5 -0
  21. package/packages/datadog-esbuild/index.js +1 -20
  22. package/packages/datadog-instrumentations/src/aerospike.js +47 -0
  23. package/packages/datadog-instrumentations/src/amqplib.js +2 -2
  24. package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
  25. package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
  26. package/packages/datadog-instrumentations/src/child_process.js +150 -0
  27. package/packages/datadog-instrumentations/src/couchbase.js +5 -4
  28. package/packages/datadog-instrumentations/src/crypto.js +2 -1
  29. package/packages/datadog-instrumentations/src/cucumber.js +163 -46
  30. package/packages/datadog-instrumentations/src/dns.js +2 -1
  31. package/packages/datadog-instrumentations/src/express.js +20 -0
  32. package/packages/datadog-instrumentations/src/graphql.js +18 -4
  33. package/packages/datadog-instrumentations/src/grpc/client.js +56 -36
  34. package/packages/datadog-instrumentations/src/grpc/server.js +3 -1
  35. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +1 -2
  36. package/packages/datadog-instrumentations/src/helpers/hooks.js +12 -3
  37. package/packages/datadog-instrumentations/src/helpers/instrument.js +9 -4
  38. package/packages/datadog-instrumentations/src/helpers/register.js +19 -3
  39. package/packages/datadog-instrumentations/src/http/client.js +12 -2
  40. package/packages/datadog-instrumentations/src/http/server.js +7 -4
  41. package/packages/datadog-instrumentations/src/http2/client.js +3 -1
  42. package/packages/datadog-instrumentations/src/http2/server.js +3 -1
  43. package/packages/datadog-instrumentations/src/jest.js +239 -52
  44. package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
  45. package/packages/datadog-instrumentations/src/mocha.js +154 -18
  46. package/packages/datadog-instrumentations/src/mongodb-core.js +34 -3
  47. package/packages/datadog-instrumentations/src/mongoose.js +23 -10
  48. package/packages/datadog-instrumentations/src/mquery.js +65 -0
  49. package/packages/datadog-instrumentations/src/net.js +10 -2
  50. package/packages/datadog-instrumentations/src/next.js +35 -9
  51. package/packages/datadog-instrumentations/src/playwright.js +110 -16
  52. package/packages/datadog-instrumentations/src/restify.js +14 -1
  53. package/packages/datadog-instrumentations/src/rhea.js +15 -9
  54. package/packages/datadog-plugin-aerospike/src/index.js +113 -0
  55. package/packages/datadog-plugin-amqplib/src/consumer.js +14 -1
  56. package/packages/datadog-plugin-amqplib/src/producer.js +13 -1
  57. package/packages/datadog-plugin-aws-sdk/src/base.js +3 -2
  58. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +163 -27
  59. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +46 -8
  60. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +129 -22
  61. package/packages/datadog-plugin-child_process/src/index.js +91 -0
  62. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +125 -0
  63. package/packages/datadog-plugin-cucumber/src/index.js +70 -13
  64. package/packages/datadog-plugin-cypress/src/after-run.js +3 -0
  65. package/packages/datadog-plugin-cypress/src/after-spec.js +3 -0
  66. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +625 -0
  67. package/packages/datadog-plugin-cypress/src/plugin.js +6 -454
  68. package/packages/datadog-plugin-cypress/src/support.js +50 -3
  69. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -0
  70. package/packages/datadog-plugin-graphql/src/index.js +1 -6
  71. package/packages/datadog-plugin-graphql/src/resolve.js +28 -18
  72. package/packages/datadog-plugin-grpc/src/client.js +16 -2
  73. package/packages/datadog-plugin-grpc/src/util.js +1 -1
  74. package/packages/datadog-plugin-http/src/client.js +19 -2
  75. package/packages/datadog-plugin-jest/src/index.js +118 -12
  76. package/packages/datadog-plugin-jest/src/util.js +38 -16
  77. package/packages/datadog-plugin-kafkajs/src/consumer.js +76 -6
  78. package/packages/datadog-plugin-kafkajs/src/producer.js +64 -8
  79. package/packages/datadog-plugin-mocha/src/index.js +87 -17
  80. package/packages/datadog-plugin-next/src/index.js +40 -14
  81. package/packages/datadog-plugin-playwright/src/index.js +71 -8
  82. package/packages/datadog-plugin-rhea/src/consumer.js +16 -1
  83. package/packages/datadog-plugin-rhea/src/producer.js +10 -0
  84. package/packages/dd-trace/src/appsec/activation.js +29 -0
  85. package/packages/dd-trace/src/appsec/addresses.js +5 -1
  86. package/packages/dd-trace/src/appsec/api_security_sampler.js +61 -0
  87. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
  88. package/packages/dd-trace/src/appsec/blocking.js +95 -43
  89. package/packages/dd-trace/src/appsec/channels.js +7 -3
  90. package/packages/dd-trace/src/appsec/graphql.js +146 -0
  91. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +2 -0
  92. package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +1 -1
  93. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +105 -0
  94. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +22 -17
  95. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +7 -28
  96. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +10 -6
  97. package/packages/dd-trace/src/appsec/iast/analyzers/weak-randomness-analyzer.js +19 -0
  98. package/packages/dd-trace/src/appsec/iast/context/context-plugin.js +90 -0
  99. package/packages/dd-trace/src/appsec/iast/context/kafka-ctx-plugin.js +14 -0
  100. package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
  101. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +13 -2
  102. package/packages/dd-trace/src/appsec/iast/index.js +15 -5
  103. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +1 -1
  104. package/packages/dd-trace/src/appsec/iast/path-line.js +1 -1
  105. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +2 -0
  106. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +10 -0
  107. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +53 -0
  108. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +10 -46
  109. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +13 -9
  110. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +47 -0
  111. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +19 -6
  112. package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +3 -1
  113. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +41 -3
  114. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/constants.js +7 -0
  115. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +12 -19
  116. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +20 -0
  117. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +6 -10
  118. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +18 -25
  119. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +79 -85
  120. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +27 -36
  121. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +14 -11
  122. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  123. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +2 -0
  124. package/packages/dd-trace/src/appsec/index.js +49 -33
  125. package/packages/dd-trace/src/appsec/recommended.json +1763 -106
  126. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +7 -1
  127. package/packages/dd-trace/src/appsec/remote_config/index.js +42 -16
  128. package/packages/dd-trace/src/appsec/remote_config/manager.js +9 -8
  129. package/packages/dd-trace/src/appsec/reporter.js +51 -34
  130. package/packages/dd-trace/src/appsec/rule_manager.js +11 -8
  131. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  132. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +28 -13
  133. package/packages/dd-trace/src/appsec/waf/waf_manager.js +0 -1
  134. package/packages/dd-trace/src/ci-visibility/{intelligent-test-runner/get-itr-configuration.js → early-flake-detection/get-known-tests.js} +17 -22
  135. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +25 -6
  136. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +30 -1
  137. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +2 -0
  138. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +30 -1
  139. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +95 -37
  140. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +134 -61
  141. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +37 -4
  142. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +131 -0
  143. package/packages/dd-trace/src/ci-visibility/telemetry.js +130 -0
  144. package/packages/dd-trace/src/config.js +561 -470
  145. package/packages/dd-trace/src/data_streams_context.js +1 -1
  146. package/packages/dd-trace/src/datastreams/pathway.js +58 -1
  147. package/packages/dd-trace/src/datastreams/processor.js +196 -27
  148. package/packages/dd-trace/src/datastreams/writer.js +11 -5
  149. package/packages/dd-trace/src/dogstatsd.js +3 -5
  150. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +44 -6
  151. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +14 -0
  152. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +4 -0
  153. package/packages/dd-trace/src/exporters/common/form-data.js +4 -0
  154. package/packages/dd-trace/src/exporters/common/request.js +21 -3
  155. package/packages/dd-trace/src/format.js +30 -2
  156. package/packages/dd-trace/src/id.js +12 -0
  157. package/packages/dd-trace/src/iitm.js +1 -1
  158. package/packages/dd-trace/src/log/channels.js +1 -1
  159. package/packages/dd-trace/src/noop/proxy.js +4 -0
  160. package/packages/dd-trace/src/noop/span.js +1 -0
  161. package/packages/dd-trace/src/opentelemetry/span.js +104 -4
  162. package/packages/dd-trace/src/opentelemetry/tracer.js +9 -10
  163. package/packages/dd-trace/src/opentracing/propagation/text_map.js +16 -7
  164. package/packages/dd-trace/src/opentracing/span.js +48 -4
  165. package/packages/dd-trace/src/opentracing/span_context.js +15 -6
  166. package/packages/dd-trace/src/opentracing/tracer.js +4 -3
  167. package/packages/dd-trace/src/plugin_manager.js +1 -1
  168. package/packages/dd-trace/src/plugins/ci_plugin.js +78 -19
  169. package/packages/dd-trace/src/plugins/database.js +1 -1
  170. package/packages/dd-trace/src/plugins/index.js +7 -0
  171. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  172. package/packages/dd-trace/src/plugins/util/ci.js +6 -19
  173. package/packages/dd-trace/src/plugins/util/git.js +104 -22
  174. package/packages/dd-trace/src/plugins/util/ip_extractor.js +7 -6
  175. package/packages/dd-trace/src/plugins/util/test.js +60 -10
  176. package/packages/dd-trace/src/plugins/util/url.js +26 -0
  177. package/packages/dd-trace/src/plugins/util/user-provided-git.js +4 -16
  178. package/packages/dd-trace/src/plugins/util/web.js +1 -1
  179. package/packages/dd-trace/src/priority_sampler.js +30 -38
  180. package/packages/dd-trace/src/profiler.js +5 -3
  181. package/packages/dd-trace/src/profiling/config.js +77 -24
  182. package/packages/dd-trace/src/profiling/exporters/agent.js +77 -31
  183. package/packages/dd-trace/src/profiling/exporters/file.js +2 -1
  184. package/packages/dd-trace/src/profiling/profiler.js +33 -22
  185. package/packages/dd-trace/src/profiling/profilers/events.js +270 -0
  186. package/packages/dd-trace/src/profiling/profilers/shared.js +45 -0
  187. package/packages/dd-trace/src/profiling/profilers/space.js +18 -2
  188. package/packages/dd-trace/src/profiling/profilers/wall.js +146 -70
  189. package/packages/dd-trace/src/proxy.js +56 -24
  190. package/packages/dd-trace/src/ritm.js +1 -1
  191. package/packages/dd-trace/src/sampling_rule.js +130 -0
  192. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +5 -0
  193. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  194. package/packages/dd-trace/src/span_processor.js +9 -1
  195. package/packages/dd-trace/src/span_sampler.js +6 -64
  196. package/packages/dd-trace/src/spanleak.js +98 -0
  197. package/packages/dd-trace/src/startup-log.js +7 -1
  198. package/packages/dd-trace/src/telemetry/dependencies.js +56 -10
  199. package/packages/dd-trace/src/telemetry/index.js +182 -53
  200. package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
  201. package/packages/dd-trace/src/telemetry/send-data.js +65 -7
  202. package/packages/dd-trace/src/tracer.js +12 -5
  203. package/register.js +4 -0
  204. package/scripts/install_plugin_modules.js +11 -3
  205. package/scripts/st.js +105 -0
  206. package/packages/datadog-instrumentations/src/child-process.js +0 -30
  207. package/packages/dd-trace/src/plugins/util/exec.js +0 -13
  208. package/packages/diagnostics_channel/index.js +0 -3
  209. package/packages/diagnostics_channel/src/index.js +0 -121
@@ -1,10 +1,68 @@
1
1
  'use strict'
2
+ const {
3
+ getSizeOrZero
4
+ } = require('../../../dd-trace/src/datastreams/processor')
5
+ const { DsmPathwayCodec } = require('../../../dd-trace/src/datastreams/pathway')
2
6
  const log = require('../../../dd-trace/src/log')
3
7
  const BaseAwsSdkPlugin = require('../base')
8
+ const { storage } = require('../../../datadog-core')
9
+
4
10
  class Kinesis extends BaseAwsSdkPlugin {
5
11
  static get id () { return 'kinesis' }
6
12
  static get peerServicePrecursors () { return ['streamname'] }
7
13
 
14
+ constructor (...args) {
15
+ super(...args)
16
+
17
+ // TODO(bengl) Find a way to create the response span tags without this WeakMap being populated
18
+ // in the base class
19
+ this.requestTags = new WeakMap()
20
+
21
+ this.addSub('apm:aws:response:start:kinesis', obj => {
22
+ const { request, response } = obj
23
+ const store = storage.getStore()
24
+ const plugin = this
25
+
26
+ // if we have either of these operations, we want to store the streamName param
27
+ // since it is not typically available during get/put records requests
28
+ if (request.operation === 'getShardIterator' || request.operation === 'listShards') {
29
+ this.storeStreamName(request.params, request.operation, store)
30
+ return
31
+ }
32
+
33
+ if (request.operation === 'getRecords') {
34
+ let span
35
+ const responseExtraction = this.responseExtract(request.params, request.operation, response)
36
+ if (responseExtraction && responseExtraction.maybeChildOf) {
37
+ obj.needsFinish = true
38
+ const options = {
39
+ childOf: responseExtraction.maybeChildOf,
40
+ tags: Object.assign(
41
+ {},
42
+ this.requestTags.get(request) || {},
43
+ { 'span.kind': 'server' }
44
+ )
45
+ }
46
+ span = plugin.tracer.startSpan('aws.response', options)
47
+ this.enter(span, store)
48
+ }
49
+
50
+ // get the stream name that should have been stored previously
51
+ const { streamName } = storage.getStore()
52
+
53
+ // extract DSM context after as we might not have a parent-child but may have a DSM context
54
+ this.responseExtractDSMContext(
55
+ request.operation, response, span || null, streamName
56
+ )
57
+ }
58
+ })
59
+
60
+ this.addSub('apm:aws:response:finish:kinesis', err => {
61
+ const { span } = storage.getStore()
62
+ this.finish(span, null, err)
63
+ })
64
+ }
65
+
8
66
  generateTags (params, operation, response) {
9
67
  if (!params || !params.StreamName) return {}
10
68
 
@@ -15,6 +73,55 @@ class Kinesis extends BaseAwsSdkPlugin {
15
73
  }
16
74
  }
17
75
 
76
+ storeStreamName (params, operation, store) {
77
+ if (!operation || (operation !== 'getShardIterator' && operation !== 'listShards')) return
78
+ if (!params || !params.StreamName) return
79
+
80
+ const streamName = params.StreamName
81
+ storage.enterWith({ ...store, streamName })
82
+ }
83
+
84
+ responseExtract (params, operation, response) {
85
+ if (operation !== 'getRecords') return
86
+ if (params.Limit && params.Limit !== 1) return
87
+ if (!response || !response.Records || !response.Records[0]) return
88
+
89
+ const record = response.Records[0]
90
+
91
+ try {
92
+ const decodedData = JSON.parse(Buffer.from(record.Data).toString())
93
+
94
+ return {
95
+ maybeChildOf: this.tracer.extract('text_map', decodedData._datadog),
96
+ parsedAttributes: decodedData._datadog
97
+ }
98
+ } catch (e) {
99
+ log.error(e)
100
+ }
101
+ }
102
+
103
+ responseExtractDSMContext (operation, response, span, streamName) {
104
+ if (!this.config.dsmEnabled) return
105
+ if (operation !== 'getRecords') return
106
+ if (!response || !response.Records || !response.Records[0]) return
107
+
108
+ // we only want to set the payloadSize on the span if we have one message, not repeatedly
109
+ span = response.Records.length > 1 ? null : span
110
+
111
+ response.Records.forEach(record => {
112
+ const parsedAttributes = JSON.parse(Buffer.from(record.Data).toString())
113
+
114
+ if (
115
+ parsedAttributes?._datadog && streamName && DsmPathwayCodec.contextExists(parsedAttributes._datadog)
116
+ ) {
117
+ const payloadSize = getSizeOrZero(record.Data)
118
+ this.tracer.decodeDataStreamsContext(parsedAttributes._datadog)
119
+ this.tracer
120
+ .setCheckpoint(['direction:in', `topic:${streamName}`, 'type:kinesis'], span, payloadSize)
121
+ }
122
+ })
123
+ }
124
+
18
125
  // AWS-SDK will b64 kinesis payloads
19
126
  // or will accept an already b64 encoded payload
20
127
  // This method handles both
@@ -32,40 +139,69 @@ class Kinesis extends BaseAwsSdkPlugin {
32
139
  }
33
140
 
34
141
  requestInject (span, request) {
35
- const operation = request.operation
36
- if (operation === 'putRecord' || operation === 'putRecords') {
37
- if (!request.params) {
142
+ const { operation, params } = request
143
+ if (!params) return
144
+
145
+ let stream
146
+ switch (operation) {
147
+ case 'putRecord':
148
+ stream = params.StreamArn ? params.StreamArn : (params.StreamName ? params.StreamName : '')
149
+ this.injectToMessage(span, params, stream, true)
150
+ break
151
+ case 'putRecords':
152
+ stream = params.StreamArn ? params.StreamArn : (params.StreamName ? params.StreamName : '')
153
+ for (let i = 0; i < params.Records.length; i++) {
154
+ this.injectToMessage(span, params.Records[i], stream, i === 0)
155
+ }
156
+ }
157
+ }
158
+
159
+ injectToMessage (span, params, stream, injectTraceContext) {
160
+ if (!params) {
161
+ return
162
+ }
163
+
164
+ let parsedData
165
+ if (injectTraceContext || this.config.dsmEnabled) {
166
+ parsedData = this._tryParse(params.Data)
167
+ if (!parsedData) {
168
+ log.error('Unable to parse payload, unable to pass trace context or set DSM checkpoint (if enabled)')
38
169
  return
39
170
  }
171
+ }
172
+
173
+ const ddInfo = {}
174
+ // for now, we only want to inject to the first message, this may change for batches in the future
175
+ if (injectTraceContext) { this.tracer.inject(span, 'text_map', ddInfo) }
40
176
 
41
- const traceData = {}
42
- this.tracer.inject(span, 'text_map', traceData)
43
- let injectPath
44
- if (request.params.Records && request.params.Records.length > 0) {
45
- injectPath = request.params.Records[0]
46
- } else if (request.params.Data) {
47
- injectPath = request.params
48
- } else {
49
- log.error('No valid payload passed, unable to pass trace context')
177
+ // set DSM hash if enabled
178
+ if (this.config.dsmEnabled) {
179
+ parsedData._datadog = ddInfo
180
+ const dataStreamsContext = this.setDSMCheckpoint(span, parsedData, stream)
181
+ DsmPathwayCodec.encode(dataStreamsContext, ddInfo)
182
+ }
183
+
184
+ if (Object.keys(ddInfo).length !== 0) {
185
+ parsedData._datadog = ddInfo
186
+ const finalData = Buffer.from(JSON.stringify(parsedData))
187
+ const byteSize = finalData.length
188
+ // Kinesis max payload size is 1MB
189
+ // So we must ensure adding DD context won't go over that (512b is an estimate)
190
+ if (byteSize >= 1048576) {
191
+ log.info('Payload size too large to pass context')
50
192
  return
51
193
  }
52
- const parsedData = this._tryParse(injectPath.Data)
53
- if (parsedData) {
54
- parsedData._datadog = traceData
55
- const finalData = Buffer.from(JSON.stringify(parsedData))
56
- const byteSize = finalData.length
57
- // Kinesis max payload size is 1MB
58
- // So we must ensure adding DD context won't go over that (512b is an estimate)
59
- if (byteSize >= 1048576) {
60
- log.info('Payload size too large to pass context')
61
- return
62
- }
63
- injectPath.Data = finalData
64
- } else {
65
- log.error('Unable to parse payload, unable to pass trace context')
66
- }
194
+ params.Data = finalData
67
195
  }
68
196
  }
197
+
198
+ setDSMCheckpoint (span, parsedData, stream) {
199
+ // get payload size of request data
200
+ const payloadSize = Buffer.from(JSON.stringify(parsedData)).byteLength
201
+ const dataStreamsContext = this.tracer
202
+ .setCheckpoint(['direction:out', `topic:${stream}`, 'type:kinesis'], span, payloadSize)
203
+ return dataStreamsContext
204
+ }
69
205
  }
70
206
 
71
207
  module.exports = Kinesis
@@ -1,4 +1,6 @@
1
1
  'use strict'
2
+ const { getHeadersSize } = require('../../../dd-trace/src/datastreams/processor')
3
+ const { DsmPathwayCodec } = 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(':')
@@ -52,17 +55,17 @@ class Sns extends BaseAwsSdkPlugin {
52
55
 
53
56
  switch (operation) {
54
57
  case 'publish':
55
- this._injectMessageAttributes(span, params)
58
+ this.injectToMessage(span, params, params.TopicArn, true)
56
59
  break
57
60
  case 'publishBatch':
58
- if (params.PublishBatchRequestEntries && params.PublishBatchRequestEntries.length > 0) {
59
- this._injectMessageAttributes(span, params.PublishBatchRequestEntries[0])
61
+ for (let i = 0; i < params.PublishBatchRequestEntries.length; i++) {
62
+ this.injectToMessage(span, params.PublishBatchRequestEntries[i], params.TopicArn, i === 0)
60
63
  }
61
64
  break
62
65
  }
63
66
  }
64
67
 
65
- _injectMessageAttributes (span, params) {
68
+ injectToMessage (span, params, topicArn, injectTraceContext) {
66
69
  if (!params.MessageAttributes) {
67
70
  params.MessageAttributes = {}
68
71
  }
@@ -70,11 +73,46 @@ class Sns extends BaseAwsSdkPlugin {
70
73
  log.info('Message attributes full, skipping trace context injection')
71
74
  return
72
75
  }
76
+
73
77
  const ddInfo = {}
74
- this.tracer.inject(span, 'text_map', ddInfo)
75
- params.MessageAttributes._datadog = {
76
- DataType: 'Binary',
77
- BinaryValue: Buffer.from(JSON.stringify(ddInfo)) // BINARY types are automatically base64 encoded
78
+ // for now, we only want to inject to the first message, this may change for batches in the future
79
+ if (injectTraceContext) {
80
+ this.tracer.inject(span, 'text_map', ddInfo)
81
+ // add ddInfo before checking DSM so we can include DD attributes in payload size
82
+ params.MessageAttributes._datadog = {
83
+ DataType: 'Binary',
84
+ BinaryValue: ddInfo
85
+ }
86
+ }
87
+
88
+ if (this.config.dsmEnabled) {
89
+ if (!params.MessageAttributes._datadog) {
90
+ params.MessageAttributes._datadog = {
91
+ DataType: 'Binary',
92
+ BinaryValue: ddInfo
93
+ }
94
+ }
95
+
96
+ const dataStreamsContext = this.setDSMCheckpoint(span, params, topicArn)
97
+ DsmPathwayCodec.encode(dataStreamsContext, ddInfo)
98
+ }
99
+
100
+ if (Object.keys(ddInfo).length !== 0) {
101
+ // BINARY types are automatically base64 encoded
102
+ params.MessageAttributes._datadog.BinaryValue = Buffer.from(JSON.stringify(ddInfo))
103
+ } else if (params.MessageAttributes._datadog) {
104
+ // let's avoid adding any additional information to payload if we failed to inject
105
+ delete params.MessageAttributes._datadog
106
+ }
107
+ }
108
+
109
+ setDSMCheckpoint (span, params, topicArn) {
110
+ // only set a checkpoint if publishing to a topic
111
+ if (topicArn) {
112
+ const payloadSize = getHeadersSize(params)
113
+ const dataStreamsContext = this.tracer
114
+ .setCheckpoint(['direction:out', `topic:${topicArn}`, 'type:sns'], span, payloadSize)
115
+ return dataStreamsContext
78
116
  }
79
117
  }
80
118
  }
@@ -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 { getHeadersSize } = require('../../../dd-trace/src/datastreams/processor')
7
+ const { DsmPathwayCodec } = 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,38 +142,136 @@ 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
 
149
- requestInject (span, request) {
150
- const operation = request.operation
151
- if (operation === 'sendMessage') {
152
- if (!request.params) {
153
- request.params = {}
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
+ }
154
194
  }
155
- if (!request.params.MessageAttributes) {
156
- request.params.MessageAttributes = {}
157
- } else if (Object.keys(request.params.MessageAttributes).length >= 10) { // SQS quota
158
- // TODO: add test when the test suite is fixed
159
- return
195
+ if (parsedAttributes && DsmPathwayCodec.contextExists(parsedAttributes)) {
196
+ const payloadSize = getHeadersSize({
197
+ Body: message.Body,
198
+ MessageAttributes: message.MessageAttributes
199
+ })
200
+ const queue = params.QueueUrl.split('/').pop()
201
+ this.tracer.decodeDataStreamsContext(parsedAttributes)
202
+ this.tracer
203
+ .setCheckpoint(['direction:in', `topic:${queue}`, 'type:sqs'], span, payloadSize)
160
204
  }
161
- const ddInfo = {}
205
+ })
206
+ }
207
+
208
+ requestInject (span, request) {
209
+ const { operation, params } = request
210
+
211
+ if (!params) return
212
+
213
+ switch (operation) {
214
+ case 'sendMessage':
215
+ this.injectToMessage(span, params, params.QueueUrl, true)
216
+ break
217
+ case 'sendMessageBatch':
218
+ for (let i = 0; i < params.Entries.length; i++) {
219
+ this.injectToMessage(span, params.Entries[i], params.QueueUrl, i === 0)
220
+ }
221
+ break
222
+ }
223
+ }
224
+
225
+ injectToMessage (span, params, queueUrl, injectTraceContext) {
226
+ if (!params) {
227
+ params = {}
228
+ }
229
+ if (!params.MessageAttributes) {
230
+ params.MessageAttributes = {}
231
+ } else if (Object.keys(params.MessageAttributes).length >= 10) { // SQS quota
232
+ // TODO: add test when the test suite is fixed
233
+ return
234
+ }
235
+ const ddInfo = {}
236
+ // for now, we only want to inject to the first message, this may change for batches in the future
237
+ if (injectTraceContext) {
162
238
  this.tracer.inject(span, 'text_map', ddInfo)
163
- request.params.MessageAttributes._datadog = {
239
+ params.MessageAttributes._datadog = {
164
240
  DataType: 'String',
165
241
  StringValue: JSON.stringify(ddInfo)
166
242
  }
167
243
  }
244
+
245
+ if (this.config.dsmEnabled) {
246
+ if (!params.MessageAttributes._datadog) {
247
+ params.MessageAttributes._datadog = {
248
+ DataType: 'String',
249
+ StringValue: JSON.stringify(ddInfo)
250
+ }
251
+ }
252
+
253
+ const dataStreamsContext = this.setDSMCheckpoint(span, params, queueUrl)
254
+ if (dataStreamsContext) {
255
+ DsmPathwayCodec.encode(dataStreamsContext, ddInfo)
256
+ params.MessageAttributes._datadog.StringValue = JSON.stringify(ddInfo)
257
+ }
258
+ }
259
+
260
+ if (params.MessageAttributes._datadog && Object.keys(ddInfo).length === 0) {
261
+ // let's avoid adding any additional information to payload if we failed to inject
262
+ delete params.MessageAttributes._datadog
263
+ }
264
+ }
265
+
266
+ setDSMCheckpoint (span, params, queueUrl) {
267
+ const payloadSize = getHeadersSize({
268
+ Body: params.MessageBody,
269
+ MessageAttributes: params.MessageAttributes
270
+ })
271
+ const queue = queueUrl.split('/').pop()
272
+ const dataStreamsContext = this.tracer
273
+ .setCheckpoint(['direction:out', `topic:${queue}`, 'type:sqs'], span, payloadSize)
274
+ return dataStreamsContext
168
275
  }
169
276
  }
170
277
 
@@ -0,0 +1,91 @@
1
+ 'use strict'
2
+
3
+ const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
4
+ const scrubChildProcessCmd = require('./scrub-cmd-params')
5
+
6
+ const MAX_ARG_SIZE = 4096 // 4kB
7
+
8
+ function truncateCommand (cmdFields) {
9
+ let size = cmdFields[0].length
10
+ let truncated = false
11
+ for (let i = 1; i < cmdFields.length; i++) {
12
+ if (size >= MAX_ARG_SIZE) {
13
+ truncated = true
14
+ cmdFields[i] = ''
15
+ continue
16
+ }
17
+
18
+ const argLen = cmdFields[i].length
19
+ if (size < MAX_ARG_SIZE && size + argLen > MAX_ARG_SIZE) {
20
+ cmdFields[i] = cmdFields[i].substring(0, 2)
21
+ truncated = true
22
+ }
23
+
24
+ size += argLen
25
+ }
26
+
27
+ return truncated
28
+ }
29
+
30
+ class ChildProcessPlugin extends TracingPlugin {
31
+ static get id () { return 'child_process' }
32
+ static get prefix () { return 'tracing:datadog:child_process:execution' }
33
+
34
+ get tracer () {
35
+ return this._tracer
36
+ }
37
+
38
+ start ({ command, shell }) {
39
+ if (typeof command !== 'string') {
40
+ return
41
+ }
42
+
43
+ const cmdFields = scrubChildProcessCmd(command)
44
+ const truncated = truncateCommand(cmdFields)
45
+ const property = (shell === true) ? 'cmd.shell' : 'cmd.exec'
46
+
47
+ const meta = {
48
+ 'component': 'subprocess',
49
+ [property]: (shell === true) ? cmdFields.join(' ') : JSON.stringify(cmdFields)
50
+ }
51
+
52
+ if (truncated) {
53
+ meta['cmd.truncated'] = `${truncated}`
54
+ }
55
+
56
+ this.startSpan('command_execution', {
57
+ service: this.config.service,
58
+ resource: (shell === true) ? 'sh' : cmdFields[0],
59
+ type: 'system',
60
+ meta
61
+ })
62
+ }
63
+
64
+ end ({ result, error }) {
65
+ let exitCode
66
+
67
+ if (result !== undefined) {
68
+ exitCode = result?.status || 0
69
+ } else if (error !== undefined) {
70
+ exitCode = error?.status || error?.code || 0
71
+ } else {
72
+ // TracingChannels call start, end synchronously. Later when the promise is resolved then asyncStart asyncEnd.
73
+ // Therefore in the case of calling end with neither result nor error means that they will come in the asyncEnd.
74
+ return
75
+ }
76
+
77
+ this.activeSpan?.setTag('cmd.exit_code', `${exitCode}`)
78
+ this.activeSpan?.finish()
79
+ }
80
+
81
+ error (error) {
82
+ this.addError(error)
83
+ }
84
+
85
+ asyncEnd ({ result }) {
86
+ this.activeSpan?.setTag('cmd.exit_code', `${result}`)
87
+ this.activeSpan?.finish()
88
+ }
89
+ }
90
+
91
+ module.exports = ChildProcessPlugin