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
@@ -6,7 +6,7 @@ function getDataStreamsContext () {
6
6
  }
7
7
 
8
8
  function setDataStreamsContext (dataStreamsContext) {
9
- storage.enterWith({ ...(storage.getStore()), dataStreamsContext })
9
+ if (dataStreamsContext) storage.enterWith({ ...(storage.getStore()), dataStreamsContext })
10
10
  }
11
11
 
12
12
  module.exports = {
@@ -8,6 +8,9 @@ const LRUCache = require('lru-cache')
8
8
  const options = { max: 500 }
9
9
  const cache = new LRUCache(options)
10
10
 
11
+ const CONTEXT_PROPAGATION_KEY = 'dd-pathway-ctx'
12
+ const CONTEXT_PROPAGATION_KEY_BASE64 = 'dd-pathway-ctx-base64'
13
+
11
14
  function shaHash (checkpointString) {
12
15
  const hash = crypto.createHash('md5').update(checkpointString).digest('hex').slice(0, 16)
13
16
  return Buffer.from(hash, 'hex')
@@ -33,6 +36,11 @@ function encodePathwayContext (dataStreamsContext) {
33
36
  ], 20)
34
37
  }
35
38
 
39
+ function encodePathwayContextBase64 (dataStreamsContext) {
40
+ const encodedPathway = encodePathwayContext(dataStreamsContext)
41
+ return encodedPathway.toString('base64')
42
+ }
43
+
36
44
  function decodePathwayContext (pathwayContext) {
37
45
  if (pathwayContext == null || pathwayContext.length < 8) {
38
46
  return null
@@ -51,8 +59,57 @@ function decodePathwayContext (pathwayContext) {
51
59
  return { hash: pathwayHash, pathwayStartNs: pathwayStartMs * 1e6, edgeStartNs: edgeStartMs * 1e6 }
52
60
  }
53
61
 
62
+ function decodePathwayContextBase64 (pathwayContext) {
63
+ if (pathwayContext == null || pathwayContext.length < 8) {
64
+ return
65
+ }
66
+ if (Buffer.isBuffer(pathwayContext)) {
67
+ pathwayContext = pathwayContext.toString()
68
+ }
69
+ const encodedPathway = Buffer.from(pathwayContext, 'base64')
70
+ return decodePathwayContext(encodedPathway)
71
+ }
72
+
73
+ class DsmPathwayCodec {
74
+ // we use a class for encoding / decoding in case we update our encoding/decoding. A class will make updates easier
75
+ // instead of using individual functions.
76
+ static encode (dataStreamsContext, carrier) {
77
+ if (!dataStreamsContext || !dataStreamsContext.hash) {
78
+ return
79
+ }
80
+ carrier[CONTEXT_PROPAGATION_KEY_BASE64] = encodePathwayContextBase64(dataStreamsContext)
81
+ }
82
+
83
+ static decode (carrier) {
84
+ if (carrier == null) return
85
+
86
+ let ctx
87
+ if (CONTEXT_PROPAGATION_KEY_BASE64 in carrier) {
88
+ // decode v2 encoding of base64
89
+ ctx = decodePathwayContextBase64(carrier[CONTEXT_PROPAGATION_KEY_BASE64])
90
+ } else if (CONTEXT_PROPAGATION_KEY in carrier) {
91
+ try {
92
+ // decode v1 encoding
93
+ ctx = decodePathwayContext(carrier[CONTEXT_PROPAGATION_KEY])
94
+ } catch {
95
+ // pass
96
+ }
97
+ // cover case where base64 context was received under wrong key
98
+ if (!ctx) ctx = decodePathwayContextBase64(carrier[CONTEXT_PROPAGATION_KEY])
99
+ }
100
+ return ctx
101
+ }
102
+
103
+ static contextExists (carrier) {
104
+ return CONTEXT_PROPAGATION_KEY_BASE64 in carrier || CONTEXT_PROPAGATION_KEY in carrier
105
+ }
106
+ }
107
+
54
108
  module.exports = {
55
109
  computePathwayHash: computeHash,
56
110
  encodePathwayContext,
57
- decodePathwayContext
111
+ decodePathwayContext,
112
+ encodePathwayContextBase64,
113
+ decodePathwayContextBase64,
114
+ DsmPathwayCodec
58
115
  }
@@ -4,9 +4,12 @@ const pkg = require('../../../../package.json')
4
4
  const Uint64 = require('int64-buffer').Uint64BE
5
5
 
6
6
  const { LogCollapsingLowestDenseDDSketch } = require('@datadog/sketches-js')
7
-
7
+ const { DsmPathwayCodec } = require('./pathway')
8
8
  const { DataStreamsWriter } = require('./writer')
9
9
  const { computePathwayHash } = require('./pathway')
10
+ const { types } = require('util')
11
+ const { PATHWAY_HASH } = require('../../../../ext/tags')
12
+
10
13
  const ENTRY_PARENT_HASH = Buffer.from('0000000000000000', 'hex')
11
14
 
12
15
  const HIGH_ACCURACY_DISTRIBUTION = 0.0075
@@ -18,6 +21,7 @@ class StatsPoint {
18
21
  this.edgeTags = edgeTags
19
22
  this.edgeLatency = new LogCollapsingLowestDenseDDSketch(HIGH_ACCURACY_DISTRIBUTION)
20
23
  this.pathwayLatency = new LogCollapsingLowestDenseDDSketch(HIGH_ACCURACY_DISTRIBUTION)
24
+ this.payloadSize = new LogCollapsingLowestDenseDDSketch(HIGH_ACCURACY_DISTRIBUTION)
21
25
  }
22
26
 
23
27
  addLatencies (checkpoint) {
@@ -25,6 +29,7 @@ class StatsPoint {
25
29
  const pathwayLatencySec = checkpoint.pathwayLatencyNs / 1e9
26
30
  this.edgeLatency.accept(edgeLatencySec)
27
31
  this.pathwayLatency.accept(pathwayLatencySec)
32
+ this.payloadSize.accept(checkpoint.payloadSize)
28
33
  }
29
34
 
30
35
  encode () {
@@ -33,20 +38,123 @@ class StatsPoint {
33
38
  ParentHash: this.parentHash,
34
39
  EdgeTags: this.edgeTags,
35
40
  EdgeLatency: this.edgeLatency.toProto(),
36
- PathwayLatency: this.pathwayLatency.toProto()
41
+ PathwayLatency: this.pathwayLatency.toProto(),
42
+ PayloadSize: this.payloadSize.toProto()
37
43
  }
38
44
  }
39
45
  }
40
46
 
41
- class StatsBucket extends Map {
47
+ class Backlog {
48
+ constructor ({ offset, ...tags }) {
49
+ this._tags = Object.keys(tags).sort().map(key => `${key}:${tags[key]}`)
50
+ this._hash = this._tags.join(',')
51
+ this._offset = offset
52
+ }
53
+
54
+ get hash () { return this._hash }
55
+
56
+ get offset () { return this._offset }
57
+
58
+ get tags () { return this._tags }
59
+
60
+ encode () {
61
+ return {
62
+ Tags: this.tags,
63
+ Value: this.offset
64
+ }
65
+ }
66
+ }
67
+
68
+ class StatsBucket {
69
+ constructor () {
70
+ this._checkpoints = new Map()
71
+ this._backlogs = new Map()
72
+ }
73
+
74
+ get checkpoints () {
75
+ return this._checkpoints
76
+ }
77
+
78
+ get backlogs () {
79
+ return this._backlogs
80
+ }
81
+
42
82
  forCheckpoint (checkpoint) {
43
83
  const key = checkpoint.hash
44
- if (!this.has(key)) {
45
- this.set(key, new StatsPoint(checkpoint.hash, checkpoint.parentHash, checkpoint.edgeTags)) // StatsPoint
84
+ if (!this._checkpoints.has(key)) {
85
+ this._checkpoints.set(
86
+ key, new StatsPoint(checkpoint.hash, checkpoint.parentHash, checkpoint.edgeTags)
87
+ )
46
88
  }
47
89
 
48
- return this.get(key)
90
+ return this._checkpoints.get(key)
91
+ }
92
+
93
+ /**
94
+ * Conditionally add a backlog to the bucket. If there is currently an offset
95
+ * matching the backlog's tags, overwrite the offset IFF the backlog's offset
96
+ * is greater than the recorded offset.
97
+ *
98
+ * @typedef {{[key: string]: string}} BacklogData
99
+ * @property {number} offset
100
+ *
101
+ * @param {BacklogData} backlogData
102
+ * @returns {Backlog}
103
+ */
104
+ forBacklog (backlogData) {
105
+ const backlog = new Backlog(backlogData)
106
+ const existingBacklog = this._backlogs.get(backlog.hash)
107
+ if (existingBacklog !== undefined) {
108
+ if (existingBacklog.offset > backlog.offset) {
109
+ return existingBacklog
110
+ }
111
+ }
112
+ this._backlogs.set(backlog.hash, backlog)
113
+ return backlog
114
+ }
115
+ }
116
+
117
+ function getSizeOrZero (obj) {
118
+ if (typeof obj === 'string') {
119
+ return Buffer.from(obj, 'utf-8').length
49
120
  }
121
+ if (types.isArrayBuffer(obj)) {
122
+ return obj.byteLength
123
+ }
124
+ if (Buffer.isBuffer(obj)) {
125
+ return obj.length
126
+ }
127
+ if (Array.isArray(obj) && obj.length > 0) {
128
+ if (typeof obj[0] === 'number') return Buffer.from(obj).length
129
+ let payloadSize = 0
130
+ obj.forEach(item => {
131
+ payloadSize += getSizeOrZero(item)
132
+ })
133
+ return payloadSize
134
+ }
135
+ if (typeof obj === 'object') {
136
+ try {
137
+ return getHeadersSize(obj)
138
+ } catch {
139
+ // pass
140
+ }
141
+ }
142
+ return 0
143
+ }
144
+
145
+ function getHeadersSize (headers) {
146
+ if (headers === undefined) return 0
147
+ return Object.entries(headers).reduce((prev, [key, val]) => getSizeOrZero(key) + getSizeOrZero(val) + prev, 0)
148
+ }
149
+
150
+ function getMessageSize (message) {
151
+ const { key, value, headers } = message
152
+ return getSizeOrZero(key) + getSizeOrZero(value) + getHeadersSize(headers)
153
+ }
154
+
155
+ function getAmqpMessageSize (message) {
156
+ const { headers, content } = message
157
+ return getSizeOrZero(content) + getHeadersSize(headers)
50
158
  }
51
159
 
52
160
  class TimeBuckets extends Map {
@@ -68,7 +176,8 @@ class DataStreamsProcessor {
68
176
  env,
69
177
  tags,
70
178
  version,
71
- service
179
+ service,
180
+ flushInterval
72
181
  } = {}) {
73
182
  this.writer = new DataStreamsWriter({
74
183
  hostname,
@@ -84,20 +193,22 @@ class DataStreamsProcessor {
84
193
  this.service = service || 'unnamed-nodejs-service'
85
194
  this.version = version || ''
86
195
  this.sequence = 0
196
+ this.flushInterval = flushInterval
87
197
 
88
198
  if (this.enabled) {
89
- this.timer = setInterval(this.onInterval.bind(this), 10000)
199
+ this.timer = setInterval(this.onInterval.bind(this), flushInterval)
90
200
  this.timer.unref()
91
201
  }
202
+ process.once('beforeExit', () => this.onInterval())
92
203
  }
93
204
 
94
205
  onInterval () {
95
- const serialized = this._serializeBuckets()
96
- if (!serialized) return
206
+ const { Stats } = this._serializeBuckets()
207
+ if (Stats.length === 0) return
97
208
  const payload = {
98
209
  Env: this.env,
99
210
  Service: this.service,
100
- Stats: serialized,
211
+ Stats,
101
212
  TracerVersion: pkg.version,
102
213
  Version: this.version,
103
214
  Lang: 'javascript'
@@ -105,15 +216,29 @@ class DataStreamsProcessor {
105
216
  this.writer.flush(payload)
106
217
  }
107
218
 
108
- recordCheckpoint (checkpoint) {
219
+ /**
220
+ * Given a timestamp in nanoseconds, compute and return the closest TimeBucket
221
+ * @param {number} timestamp
222
+ * @returns {StatsBucket}
223
+ */
224
+ bucketFromTimestamp (timestamp) {
225
+ const bucketTime = Math.round(timestamp - (timestamp % this.bucketSizeNs))
226
+ const bucket = this.buckets.forTime(bucketTime)
227
+ return bucket
228
+ }
229
+
230
+ recordCheckpoint (checkpoint, span = null) {
109
231
  if (!this.enabled) return
110
- const bucketTime = Math.round(checkpoint.currentTimestamp - (checkpoint.currentTimestamp % this.bucketSizeNs))
111
- this.buckets.forTime(bucketTime)
232
+ this.bucketFromTimestamp(checkpoint.currentTimestamp)
112
233
  .forCheckpoint(checkpoint)
113
234
  .addLatencies(checkpoint)
235
+ // set DSM pathway hash on span to enable related traces feature on DSM tab, convert from buffer to uint64
236
+ if (span) {
237
+ span.setTag(PATHWAY_HASH, checkpoint.hash.readBigUInt64BE(0).toString())
238
+ }
114
239
  }
115
240
 
116
- setCheckpoint (edgeTags, ctx = null) {
241
+ setCheckpoint (edgeTags, span, ctx = null, payloadSize = 0) {
117
242
  if (!this.enabled) return null
118
243
  const nowNs = Date.now() * 1e6
119
244
  const direction = edgeTags.find(t => t.startsWith('direction:'))
@@ -147,45 +272,84 @@ class DataStreamsProcessor {
147
272
  const hash = computePathwayHash(this.service, this.env, edgeTags, parentHash)
148
273
  const edgeLatencyNs = nowNs - edgeStartNs
149
274
  const pathwayLatencyNs = nowNs - pathwayStartNs
275
+ const dataStreamsContext = {
276
+ hash: hash,
277
+ edgeStartNs: edgeStartNs,
278
+ pathwayStartNs: pathwayStartNs,
279
+ previousDirection: direction,
280
+ closestOppositeDirectionHash: closestOppositeDirectionHash,
281
+ closestOppositeDirectionEdgeStart: closestOppositeDirectionEdgeStart
282
+ }
283
+ if (direction === 'direction:out') {
284
+ // Add the header for this now, as the callee doesn't have access to context when producing
285
+ // - 1 to account for extra byte for {
286
+ const ddInfoContinued = {}
287
+ DsmPathwayCodec.encode(dataStreamsContext, ddInfoContinued)
288
+ payloadSize += getSizeOrZero(JSON.stringify(ddInfoContinued)) - 1
289
+ }
150
290
  const checkpoint = {
151
291
  currentTimestamp: nowNs,
152
292
  parentHash: parentHash,
153
293
  hash: hash,
154
294
  edgeTags: edgeTags,
155
295
  edgeLatencyNs: edgeLatencyNs,
156
- pathwayLatencyNs: pathwayLatencyNs
296
+ pathwayLatencyNs: pathwayLatencyNs,
297
+ payloadSize: payloadSize
157
298
  }
158
- this.recordCheckpoint(checkpoint)
159
- return {
160
- hash: hash,
161
- edgeStartNs: edgeStartNs,
162
- pathwayStartNs: pathwayStartNs,
163
- previousDirection: direction,
164
- closestOppositeDirectionHash: closestOppositeDirectionHash,
165
- closestOppositeDirectionEdgeStart: closestOppositeDirectionEdgeStart
299
+ this.recordCheckpoint(checkpoint, span)
300
+ return dataStreamsContext
301
+ }
302
+
303
+ recordOffset ({ timestamp, ...backlogData }) {
304
+ if (!this.enabled) return
305
+ return this.bucketFromTimestamp(timestamp)
306
+ .forBacklog(backlogData)
307
+ }
308
+
309
+ setOffset (offsetObj) {
310
+ if (!this.enabled) return
311
+ const nowNs = Date.now() * 1e6
312
+ const backlogData = {
313
+ ...offsetObj,
314
+ timestamp: nowNs
166
315
  }
316
+ this.recordOffset(backlogData)
167
317
  }
168
318
 
169
319
  _serializeBuckets () {
320
+ // TimeBuckets
170
321
  const serializedBuckets = []
171
322
 
172
323
  for (const [ timeNs, bucket ] of this.buckets.entries()) {
173
324
  const points = []
174
325
 
175
- for (const stats of bucket.values()) {
326
+ // bucket: StatsBucket
327
+ // stats: StatsPoint
328
+ for (const stats of bucket._checkpoints.values()) {
176
329
  points.push(stats.encode())
177
330
  }
178
331
 
332
+ const backlogs = []
333
+ for (const backlog of bucket._backlogs.values()) {
334
+ backlogs.push(backlog.encode())
335
+ }
179
336
  serializedBuckets.push({
180
337
  Start: new Uint64(timeNs),
181
338
  Duration: new Uint64(this.bucketSizeNs),
182
- Stats: points
339
+ Stats: points,
340
+ Backlogs: backlogs
183
341
  })
184
342
  }
185
343
 
186
344
  this.buckets.clear()
187
345
 
188
- return serializedBuckets
346
+ return {
347
+ Stats: serializedBuckets
348
+ }
349
+ }
350
+
351
+ setUrl (url) {
352
+ this.writer.setUrl(url)
189
353
  }
190
354
  }
191
355
 
@@ -193,6 +357,11 @@ module.exports = {
193
357
  DataStreamsProcessor: DataStreamsProcessor,
194
358
  StatsPoint: StatsPoint,
195
359
  StatsBucket: StatsBucket,
360
+ Backlog,
196
361
  TimeBuckets,
362
+ getMessageSize,
363
+ getHeadersSize,
364
+ getSizeOrZero,
365
+ getAmqpMessageSize,
197
366
  ENTRY_PARENT_HASH
198
367
  }
@@ -15,13 +15,10 @@ function makeRequest (data, url, cb) {
15
15
  'Datadog-Meta-Tracer-Version': pkg.version,
16
16
  'Content-Type': 'application/msgpack',
17
17
  'Content-Encoding': 'gzip'
18
- }
18
+ },
19
+ url
19
20
  }
20
21
 
21
- options.protocol = url.protocol
22
- options.hostname = url.hostname
23
- options.port = url.port
24
-
25
22
  log.debug(() => `Request to the intake: ${JSON.stringify(options)}`)
26
23
 
27
24
  request(data, options, (err, res) => {
@@ -59,6 +56,15 @@ class DataStreamsWriter {
59
56
  })
60
57
  })
61
58
  }
59
+
60
+ setUrl (url) {
61
+ try {
62
+ url = new URL(url)
63
+ this._url = url
64
+ } catch (e) {
65
+ log.warn(e.stack)
66
+ }
67
+ }
62
68
  }
63
69
 
64
70
  module.exports = {
@@ -67,16 +67,14 @@ class DogStatsDClient {
67
67
  request(buffer, this._httpOptions, (err) => {
68
68
  if (err) {
69
69
  log.error('HTTP error from agent: ' + err.stack)
70
- if (err.status) {
70
+ if (err.status === 404) {
71
71
  // Inside this if-block, we have connectivity to the agent, but
72
72
  // we're not getting a 200 from the proxy endpoint. If it's a 404,
73
73
  // then we know we'll never have the endpoint, so just clear out the
74
74
  // options. Either way, we can give UDP a try.
75
- if (err.status === 404) {
76
- this._httpOptions = null
77
- }
78
- this._sendUdp(queue)
75
+ this._httpOptions = null
79
76
  }
77
+ this._sendUdp(queue)
80
78
  }
81
79
  })
82
80
  }
@@ -2,14 +2,21 @@
2
2
  const { truncateSpan, normalizeSpan } = require('./tags-processors')
3
3
  const { AgentEncoder } = require('./0.4')
4
4
  const { version: ddTraceVersion } = require('../../../../package.json')
5
- const id = require('../../../dd-trace/src/id')
6
- const ENCODING_VERSION = 1
5
+ const { ITR_CORRELATION_ID } = require('../../src/plugins/util/test')
6
+ const id = require('../../src/id')
7
+ const {
8
+ distributionMetric,
9
+ TELEMETRY_ENDPOINT_PAYLOAD_SERIALIZATION_MS,
10
+ TELEMETRY_ENDPOINT_PAYLOAD_EVENTS_COUNT
11
+ } = require('../ci-visibility/telemetry')
7
12
 
13
+ const ENCODING_VERSION = 1
8
14
  const ALLOWED_CONTENT_TYPES = ['test_session_end', 'test_module_end', 'test_suite_end', 'test']
9
15
 
10
16
  const TEST_SUITE_KEYS_LENGTH = 12
11
17
  const TEST_MODULE_KEYS_LENGTH = 11
12
18
  const TEST_SESSION_KEYS_LENGTH = 10
19
+ const TEST_AND_SPAN_KEYS_LENGTH = 11
13
20
 
14
21
  const INTAKE_SOFT_LIMIT = 2 * 1024 * 1024 // 2MB
15
22
 
@@ -40,7 +47,13 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
40
47
  }
41
48
 
42
49
  _encodeTestSuite (bytes, content) {
43
- this._encodeMapPrefix(bytes, TEST_SUITE_KEYS_LENGTH)
50
+ let keysLength = TEST_SUITE_KEYS_LENGTH
51
+ const itrCorrelationId = content.meta[ITR_CORRELATION_ID]
52
+ if (itrCorrelationId) {
53
+ keysLength++
54
+ }
55
+
56
+ this._encodeMapPrefix(bytes, keysLength)
44
57
  this._encodeString(bytes, 'type')
45
58
  this._encodeString(bytes, content.type)
46
59
 
@@ -53,6 +66,12 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
53
66
  this._encodeString(bytes, 'test_suite_id')
54
67
  this._encodeId(bytes, content.span_id)
55
68
 
69
+ if (itrCorrelationId) {
70
+ this._encodeString(bytes, ITR_CORRELATION_ID)
71
+ this._encodeString(bytes, itrCorrelationId)
72
+ delete content.meta[ITR_CORRELATION_ID]
73
+ }
74
+
56
75
  this._encodeString(bytes, 'error')
57
76
  this._encodeNumber(bytes, content.error)
58
77
  this._encodeString(bytes, 'name')
@@ -127,9 +146,7 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
127
146
  }
128
147
 
129
148
  _encodeEventContent (bytes, content) {
130
- const keysLength = Object.keys(content).length
131
-
132
- let totalKeysLength = keysLength
149
+ let totalKeysLength = TEST_AND_SPAN_KEYS_LENGTH
133
150
  if (content.meta.test_session_id) {
134
151
  totalKeysLength = totalKeysLength + 1
135
152
  }
@@ -139,6 +156,13 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
139
156
  if (content.meta.test_suite_id) {
140
157
  totalKeysLength = totalKeysLength + 1
141
158
  }
159
+ const itrCorrelationId = content.meta[ITR_CORRELATION_ID]
160
+ if (itrCorrelationId) {
161
+ totalKeysLength = totalKeysLength + 1
162
+ }
163
+ if (content.type) {
164
+ totalKeysLength = totalKeysLength + 1
165
+ }
142
166
  this._encodeMapPrefix(bytes, totalKeysLength)
143
167
  if (content.type) {
144
168
  this._encodeString(bytes, 'type')
@@ -189,6 +213,12 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
189
213
  delete content.meta.test_suite_id
190
214
  }
191
215
 
216
+ if (itrCorrelationId) {
217
+ this._encodeString(bytes, ITR_CORRELATION_ID)
218
+ this._encodeString(bytes, itrCorrelationId)
219
+ delete content.meta[ITR_CORRELATION_ID]
220
+ }
221
+
192
222
  this._encodeString(bytes, 'meta')
193
223
  this._encodeMap(bytes, content.meta)
194
224
  this._encodeString(bytes, 'metrics')
@@ -247,6 +277,8 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
247
277
  }
248
278
 
249
279
  _encode (bytes, trace) {
280
+ const startTime = Date.now()
281
+
250
282
  const rawEvents = trace.map(formatSpan)
251
283
 
252
284
  const testSessionEvents = rawEvents.filter(
@@ -261,9 +293,15 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
261
293
  for (const event of events) {
262
294
  this._encodeEvent(bytes, event)
263
295
  }
296
+ distributionMetric(
297
+ TELEMETRY_ENDPOINT_PAYLOAD_SERIALIZATION_MS,
298
+ { endpoint: 'test_cycle' },
299
+ Date.now() - startTime
300
+ )
264
301
  }
265
302
 
266
303
  makePayload () {
304
+ distributionMetric(TELEMETRY_ENDPOINT_PAYLOAD_EVENTS_COUNT, { endpoint: 'test_cycle' }, this._eventCount)
267
305
  const bytes = this._traceBytes
268
306
  const eventsOffset = this._eventsOffset
269
307
  const eventsCount = this._eventCount
@@ -2,6 +2,11 @@
2
2
  const { AgentEncoder } = require('./0.4')
3
3
  const Chunk = require('./chunk')
4
4
 
5
+ const {
6
+ distributionMetric,
7
+ TELEMETRY_ENDPOINT_PAYLOAD_SERIALIZATION_MS,
8
+ TELEMETRY_ENDPOINT_PAYLOAD_EVENTS_COUNT
9
+ } = require('../ci-visibility/telemetry')
5
10
  const FormData = require('../exporters/common/form-data')
6
11
 
7
12
  const COVERAGE_PAYLOAD_VERSION = 2
@@ -21,8 +26,16 @@ class CoverageCIVisibilityEncoder extends AgentEncoder {
21
26
  }
22
27
 
23
28
  encode (coverage) {
29
+ const startTime = Date.now()
30
+
24
31
  this._coveragesCount++
25
32
  this.encodeCodeCoverage(this._coverageBytes, coverage)
33
+
34
+ distributionMetric(
35
+ TELEMETRY_ENDPOINT_PAYLOAD_SERIALIZATION_MS,
36
+ { endpoint: 'code_coverage' },
37
+ Date.now() - startTime
38
+ )
26
39
  }
27
40
 
28
41
  encodeCodeCoverage (bytes, coverage) {
@@ -73,6 +86,7 @@ class CoverageCIVisibilityEncoder extends AgentEncoder {
73
86
  }
74
87
 
75
88
  makePayload () {
89
+ distributionMetric(TELEMETRY_ENDPOINT_PAYLOAD_EVENTS_COUNT, { endpoint: 'code_coverage' }, this._coveragesCount)
76
90
  const bytes = this._coverageBytes
77
91
 
78
92
  const coveragesOffset = this._coveragesOffset
@@ -1,6 +1,7 @@
1
1
  const { URL, format } = require('url')
2
2
 
3
3
  const request = require('./request')
4
+ const { incrementCountMetric, TELEMETRY_EVENTS_ENQUEUED_FOR_SERIALIZATION } = require('../../ci-visibility/telemetry')
4
5
 
5
6
  function fetchAgentInfo (url, callback) {
6
7
  request('', {
@@ -49,6 +50,9 @@ class AgentInfoExporter {
49
50
  }
50
51
 
51
52
  _export (payload, writer = this._writer, timerKey = '_timer') {
53
+ if (this._config.isCiVisibility) {
54
+ incrementCountMetric(TELEMETRY_EVENTS_ENQUEUED_FOR_SERIALIZATION, {}, payload.length)
55
+ }
52
56
  writer.append(payload)
53
57
 
54
58
  const { flushInterval } = this._config
@@ -21,6 +21,10 @@ class FormData extends Readable {
21
21
  }
22
22
  }
23
23
 
24
+ size () {
25
+ return this._data.reduce((size, chunk) => size + chunk.length, 0)
26
+ }
27
+
24
28
  getHeaders () {
25
29
  return { 'Content-Type': 'multipart/form-data; boundary=' + this._boundary }
26
30
  }