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
@@ -2,21 +2,19 @@
2
2
 
3
3
  const { storage } = require('../../../../datadog-core')
4
4
 
5
- const dc = require('../../../../diagnostics_channel')
5
+ const dc = require('dc-polyfill')
6
6
  const { HTTP_METHOD, HTTP_ROUTE, RESOURCE_NAME, SPAN_TYPE } = require('../../../../../ext/tags')
7
7
  const { WEB } = require('../../../../../ext/types')
8
8
  const runtimeMetrics = require('../../runtime_metrics')
9
9
  const telemetryMetrics = require('../../telemetry/metrics')
10
+ const { END_TIMESTAMP_LABEL, getNonJSThreadsLabels, getThreadLabels } = require('./shared')
10
11
 
11
12
  const beforeCh = dc.channel('dd-trace:storage:before')
12
13
  const enterCh = dc.channel('dd-trace:storage:enter')
14
+ const spanFinishCh = dc.channel('dd-trace:span:finish')
13
15
  const profilerTelemetryMetrics = telemetryMetrics.manager.namespace('profilers')
14
16
 
15
- const threadName = (function () {
16
- const { isMainThread, threadId } = require('node:worker_threads')
17
- const name = isMainThread ? 'Main' : `Worker #${threadId}`
18
- return `${name} Event Loop`
19
- })()
17
+ const MemoizedWebTags = Symbol('NativeWallProfiler.MemoizedWebTags')
20
18
 
21
19
  let kSampleCount
22
20
 
@@ -29,30 +27,6 @@ function getStartedSpans (context) {
29
27
  return context._trace.started
30
28
  }
31
29
 
32
- function generateLabels ({ context: { spanId, rootSpanId, webTags, endpoint }, timestamp }) {
33
- const labels = { 'thread name': threadName }
34
- if (spanId) {
35
- labels['span id'] = spanId
36
- }
37
- if (rootSpanId) {
38
- labels['local root span id'] = rootSpanId
39
- }
40
- if (webTags && Object.keys(webTags).length !== 0) {
41
- labels['trace endpoint'] = endpointNameFromTags(webTags)
42
- } else if (endpoint) {
43
- // fallback to endpoint computed when sample was taken
44
- labels['trace endpoint'] = endpoint
45
- }
46
- // Incoming timestamps are in microseconds, we emit nanos.
47
- labels['end_timestamp_ns'] = timestamp * 1000n
48
-
49
- return labels
50
- }
51
-
52
- function getSpanContextTags (span) {
53
- return span.context()._tags
54
- }
55
-
56
30
  function isWebServerSpan (tags) {
57
31
  return tags[SPAN_TYPE] === WEB
58
32
  }
@@ -64,6 +38,38 @@ function endpointNameFromTags (tags) {
64
38
  ].filter(v => v).join(' ')
65
39
  }
66
40
 
41
+ function getWebTags (startedSpans, i, span) {
42
+ // Are web tags for this span already memoized?
43
+ const memoizedWebTags = span[MemoizedWebTags]
44
+ if (memoizedWebTags !== undefined) {
45
+ return memoizedWebTags
46
+ }
47
+ // No, we'll have to memoize a new value
48
+ function memoize (tags) {
49
+ span[MemoizedWebTags] = tags
50
+ return tags
51
+ }
52
+ // Is this span itself a web span?
53
+ const context = span.context()
54
+ const tags = context._tags
55
+ if (isWebServerSpan(tags)) {
56
+ return memoize(tags)
57
+ }
58
+ // It isn't. Get parent's web tags (memoize them too recursively.)
59
+ // There might be several webspans, for example with next.js, http plugin creates the first span
60
+ // and then next.js plugin creates a child span, and this child span has the correct endpoint
61
+ // information. That's why we always use the tags of the closest ancestor web span.
62
+ const parentId = context._parentId
63
+ while (--i >= 0) {
64
+ const ispan = startedSpans[i]
65
+ if (ispan.context()._spanId === parentId) {
66
+ return memoize(getWebTags(startedSpans, i, ispan))
67
+ }
68
+ }
69
+ // Local root span with no web span
70
+ return memoize(null)
71
+ }
72
+
67
73
  class NativeWallProfiler {
68
74
  constructor (options = {}) {
69
75
  this.type = 'wall'
@@ -71,13 +77,29 @@ class NativeWallProfiler {
71
77
  this._flushIntervalMillis = options.flushInterval || 60 * 1e3 // 60 seconds
72
78
  this._codeHotspotsEnabled = !!options.codeHotspotsEnabled
73
79
  this._endpointCollectionEnabled = !!options.endpointCollectionEnabled
74
- this._withContexts = this._codeHotspotsEnabled || this._endpointCollectionEnabled
80
+ this._timelineEnabled = !!options.timelineEnabled
81
+ this._cpuProfilingEnabled = !!options.cpuProfilingEnabled
82
+ // We need to capture span data into the sample context for either code hotspots
83
+ // or endpoint collection.
84
+ this._captureSpanData = this._codeHotspotsEnabled || this._endpointCollectionEnabled
85
+ // We need to run the pprof wall profiler with sample contexts if we're either
86
+ // capturing span data or timeline is enabled (so we need sample timestamps, and for now
87
+ // timestamps require the sample contexts feature in the pprof wall profiler), or
88
+ // cpu profiling is enabled.
89
+ this._withContexts = this._captureSpanData || this._timelineEnabled || this._cpuProfilingEnabled
75
90
  this._v8ProfilerBugWorkaroundEnabled = !!options.v8ProfilerBugWorkaroundEnabled
76
91
  this._mapper = undefined
77
92
  this._pprof = undefined
78
93
 
79
- // Bind to this so the same value can be used to unsubscribe later
80
- this._enter = this._enter.bind(this)
94
+ // Bind these to this so they can be used as callbacks
95
+ if (this._withContexts) {
96
+ if (this._captureSpanData) {
97
+ this._enter = this._enter.bind(this)
98
+ this._spanFinished = this._spanFinished.bind(this)
99
+ }
100
+ }
101
+ this._generateLabels = this._generateLabels.bind(this)
102
+
81
103
  this._logger = options.logger
82
104
  this._started = false
83
105
  }
@@ -111,19 +133,25 @@ class NativeWallProfiler {
111
133
  sourceMapper: this._mapper,
112
134
  withContexts: this._withContexts,
113
135
  lineNumbers: false,
114
- workaroundV8Bug: this._v8ProfilerBugWorkaroundEnabled
136
+ workaroundV8Bug: this._v8ProfilerBugWorkaroundEnabled,
137
+ collectCpuTime: this._cpuProfilingEnabled
115
138
  })
116
139
 
117
140
  if (this._withContexts) {
118
- this._profilerState = this._pprof.time.getState()
119
141
  this._currentContext = {}
120
142
  this._pprof.time.setContext(this._currentContext)
121
- this._lastSpan = undefined
122
- this._lastStartedSpans = undefined
123
- this._lastSampleCount = 0
124
143
 
125
- beforeCh.subscribe(this._enter)
126
- enterCh.subscribe(this._enter)
144
+ if (this._captureSpanData) {
145
+ this._profilerState = this._pprof.time.getState()
146
+ this._lastSpan = undefined
147
+ this._lastStartedSpans = undefined
148
+ this._lastWebTags = undefined
149
+ this._lastSampleCount = 0
150
+
151
+ beforeCh.subscribe(this._enter)
152
+ enterCh.subscribe(this._enter)
153
+ spanFinishCh.subscribe(this._spanFinished)
154
+ }
127
155
  }
128
156
 
129
157
  this._started = true
@@ -144,11 +172,17 @@ class NativeWallProfiler {
144
172
 
145
173
  const span = getActiveSpan()
146
174
  if (span) {
175
+ const context = span.context()
147
176
  this._lastSpan = span
148
- this._lastStartedSpans = getStartedSpans(span.context())
177
+ const startedSpans = getStartedSpans(context)
178
+ this._lastStartedSpans = startedSpans
179
+ if (this._endpointCollectionEnabled) {
180
+ this._lastWebTags = getWebTags(startedSpans, startedSpans.length, span)
181
+ }
149
182
  } else {
150
183
  this._lastStartedSpans = undefined
151
184
  this._lastSpan = undefined
185
+ this._lastWebTags = undefined
152
186
  }
153
187
  }
154
188
 
@@ -163,21 +197,17 @@ class NativeWallProfiler {
163
197
  context.rootSpanId = rootSpan.context().toSpanId()
164
198
  }
165
199
  }
166
- if (this._endpointCollectionEnabled) {
167
- const startedSpans = this._lastStartedSpans
168
- // Find the first webspan starting from the end:
169
- // There might be several webspans, for example with next.js, http plugin creates a first span
170
- // and then next.js plugin creates a child span, and this child span haves the correct endpoint information.
171
- for (let i = startedSpans.length - 1; i >= 0; i--) {
172
- const tags = getSpanContextTags(startedSpans[i])
173
- if (isWebServerSpan(tags)) {
174
- context.webTags = tags
175
- // endpoint may not be determined yet, but keep it as fallback
176
- // if tags are not available anymore during serialization
177
- context.endpoint = endpointNameFromTags(tags)
178
- break
179
- }
180
- }
200
+ if (this._lastWebTags) {
201
+ context.webTags = this._lastWebTags
202
+ // endpoint may not be determined yet, but keep it as fallback
203
+ // if tags are not available anymore during serialization
204
+ context.endpoint = endpointNameFromTags(this._lastWebTags)
205
+ }
206
+ }
207
+
208
+ _spanFinished (span) {
209
+ if (span[MemoizedWebTags]) {
210
+ span[MemoizedWebTags] = undefined
181
211
  }
182
212
  }
183
213
 
@@ -193,23 +223,75 @@ class NativeWallProfiler {
193
223
 
194
224
  _stop (restart) {
195
225
  if (!this._started) return
196
- if (this._withContexts) {
226
+
227
+ if (this._captureSpanData) {
197
228
  // update last sample context if needed
198
229
  this._enter()
199
230
  this._lastSampleCount = 0
200
231
  }
201
- const profile = this._pprof.time.stop(restart, this._withContexts ? generateLabels : undefined)
232
+ const profile = this._pprof.time.stop(restart, this._generateLabels)
233
+
202
234
  if (restart) {
203
235
  const v8BugDetected = this._pprof.time.v8ProfilerStuckEventLoopDetected()
204
236
  if (v8BugDetected !== 0) {
205
237
  this._reportV8bug(v8BugDetected === 1)
206
238
  }
239
+ } else {
240
+ if (this._captureSpanData) {
241
+ beforeCh.unsubscribe(this._enter)
242
+ enterCh.unsubscribe(this._enter)
243
+ spanFinishCh.unsubscribe(this._spanFinished)
244
+ this._profilerState = undefined
245
+ this._lastSpan = undefined
246
+ this._lastStartedSpans = undefined
247
+ this._lastWebTags = undefined
248
+ }
249
+ this._started = false
207
250
  }
251
+
208
252
  return profile
209
253
  }
210
254
 
211
- profile () {
212
- return this._stop(true)
255
+ _generateLabels ({ node, context }) {
256
+ // check for special node that represents CPU time all non-JS threads.
257
+ // In that case only return a special thread name label since we cannot associate any timestamp/span/endpoint to it.
258
+ if (node.name === this._pprof.time.constants.NON_JS_THREADS_FUNCTION_NAME) {
259
+ return getNonJSThreadsLabels()
260
+ }
261
+
262
+ if (context == null) {
263
+ // generateLabels is also called for samples without context.
264
+ // In that case just return thread labels.
265
+ return getThreadLabels()
266
+ }
267
+
268
+ const labels = { ...getThreadLabels() }
269
+
270
+ const { context: { spanId, rootSpanId, webTags, endpoint }, timestamp } = context
271
+
272
+ if (this._timelineEnabled) {
273
+ // Incoming timestamps are in microseconds, we emit nanos.
274
+ labels[END_TIMESTAMP_LABEL] = timestamp * 1000n
275
+ }
276
+
277
+ if (spanId) {
278
+ labels['span id'] = spanId
279
+ }
280
+ if (rootSpanId) {
281
+ labels['local root span id'] = rootSpanId
282
+ }
283
+ if (webTags && Object.keys(webTags).length !== 0) {
284
+ labels['trace endpoint'] = endpointNameFromTags(webTags)
285
+ } else if (endpoint) {
286
+ // fallback to endpoint computed when sample was taken
287
+ labels['trace endpoint'] = endpoint
288
+ }
289
+
290
+ return labels
291
+ }
292
+
293
+ profile (restart) {
294
+ return this._stop(restart)
213
295
  }
214
296
 
215
297
  encode (profile) {
@@ -217,17 +299,11 @@ class NativeWallProfiler {
217
299
  }
218
300
 
219
301
  stop () {
220
- if (!this._started) return
221
-
222
- const profile = this._stop(false)
223
- if (this._withContexts) {
224
- beforeCh.unsubscribe(this._enter)
225
- enterCh.unsubscribe(this._enter)
226
- this._profilerState = undefined
227
- }
302
+ this._stop(false)
303
+ }
228
304
 
229
- this._started = false
230
- return profile
305
+ isStarted () {
306
+ return this._started
231
307
  }
232
308
  }
233
309
 
@@ -10,6 +10,7 @@ const PluginManager = require('./plugin_manager')
10
10
  const remoteConfig = require('./appsec/remote_config')
11
11
  const AppsecSdk = require('./appsec/sdk')
12
12
  const dogstatsd = require('./dogstatsd')
13
+ const spanleak = require('./spanleak')
13
14
 
14
15
  class Tracer extends NoopProxy {
15
16
  constructor () {
@@ -18,6 +19,7 @@ class Tracer extends NoopProxy {
18
19
  this._initialized = false
19
20
  this._pluginManager = new PluginManager(this)
20
21
  this.dogstatsd = new dogstatsd.NoopDogStatsDClient()
22
+ this._tracingInitialized = false
21
23
  }
22
24
 
23
25
  init (options) {
@@ -27,6 +29,7 @@ class Tracer extends NoopProxy {
27
29
 
28
30
  try {
29
31
  const config = new Config(options) // TODO: support dynamic code config
32
+ telemetry.start(config, this._pluginManager)
30
33
 
31
34
  if (config.dogstatsd) {
32
35
  // Custom Metrics
@@ -35,6 +38,19 @@ class Tracer extends NoopProxy {
35
38
  setInterval(() => {
36
39
  this.dogstatsd.flush()
37
40
  }, 10 * 1000).unref()
41
+
42
+ process.once('beforeExit', () => {
43
+ this.dogstatsd.flush()
44
+ })
45
+ }
46
+
47
+ if (config.spanLeakDebug > 0) {
48
+ if (config.spanLeakDebug === spanleak.MODES.LOG) {
49
+ spanleak.enableLogging()
50
+ } else if (config.spanLeakDebug === spanleak.MODES.GC_AND_LOG) {
51
+ spanleak.enableGarbageCollection()
52
+ }
53
+ spanleak.startScrubber()
38
54
  }
39
55
 
40
56
  if (config.remoteConfig.enabled && !config.isCiVisibility) {
@@ -46,11 +62,7 @@ class Tracer extends NoopProxy {
46
62
  } else {
47
63
  config.configure(conf.lib_config, true)
48
64
  }
49
-
50
- if (config.tracing) {
51
- this._tracer.configure(config)
52
- this._pluginManager.configure(config)
53
- }
65
+ this._enableOrDisableTracing(config)
54
66
  })
55
67
  }
56
68
 
@@ -62,35 +74,22 @@ class Tracer extends NoopProxy {
62
74
  // do not stop tracer initialization if the profiler fails to be imported
63
75
  try {
64
76
  const profiler = require('./profiler')
65
- profiler.start(config)
77
+ this._profilerStarted = profiler.start(config)
66
78
  } catch (e) {
67
79
  log.error(e)
68
80
  }
69
81
  }
82
+ if (!this._profilerStarted) {
83
+ this._profilerStarted = Promise.resolve(false)
84
+ }
70
85
 
71
86
  if (config.runtimeMetrics) {
72
87
  runtimeMetrics.start(config)
73
88
  }
74
89
 
75
- if (config.tracing) {
76
- // TODO: This should probably not require tracing to be enabled.
77
- telemetry.start(config, this._pluginManager)
78
-
79
- // dirty require for now so zero appsec code is executed unless explicitly enabled
80
- if (config.appsec.enabled) {
81
- require('./appsec').enable(config)
82
- }
83
-
84
- this._tracer = new DatadogTracer(config)
85
- this.appsec = new AppsecSdk(this._tracer, config)
86
-
87
- if (config.iast.enabled) {
88
- require('./appsec/iast').enable(config, this._tracer)
89
- }
90
-
91
- this._pluginManager.configure(config)
92
- setStartupLogPluginManager(this._pluginManager)
90
+ this._enableOrDisableTracing(config)
93
91
 
92
+ if (config.tracing) {
94
93
  if (config.isManualApiEnabled) {
95
94
  const TestApiManualPlugin = require('./ci-visibility/test-api-manual/test-api-manual-plugin')
96
95
  this._testApiManualPlugin = new TestApiManualPlugin(this)
@@ -104,6 +103,39 @@ class Tracer extends NoopProxy {
104
103
  return this
105
104
  }
106
105
 
106
+ _enableOrDisableTracing (config) {
107
+ if (config.tracing !== false) {
108
+ // dirty require for now so zero appsec code is executed unless explicitly enabled
109
+ if (config.appsec.enabled) {
110
+ require('./appsec').enable(config)
111
+ }
112
+ if (!this._tracingInitialized) {
113
+ this._tracer = new DatadogTracer(config)
114
+ this.appsec = new AppsecSdk(this._tracer, config)
115
+ this._tracingInitialized = true
116
+ }
117
+ if (config.iast.enabled) {
118
+ require('./appsec/iast').enable(config, this._tracer)
119
+ }
120
+ } else if (this._tracingInitialized) {
121
+ require('./appsec').disable()
122
+ require('./appsec/iast').disable()
123
+ }
124
+
125
+ if (this._tracingInitialized) {
126
+ this._tracer.configure(config)
127
+ this._pluginManager.configure(config)
128
+ setStartupLogPluginManager(this._pluginManager)
129
+ }
130
+ }
131
+
132
+ profilerStarted () {
133
+ if (!this._profilerStarted) {
134
+ throw new Error('profilerStarted() must be called after init()')
135
+ }
136
+ return this._profilerStarted
137
+ }
138
+
107
139
  use () {
108
140
  this._pluginManager.configurePlugin(...arguments)
109
141
  return this
@@ -3,7 +3,7 @@
3
3
  const path = require('path')
4
4
  const Module = require('module')
5
5
  const parse = require('module-details-from-path')
6
- const dc = require('../../diagnostics_channel')
6
+ const dc = require('dc-polyfill')
7
7
 
8
8
  const origRequire = Module.prototype.require
9
9
 
@@ -0,0 +1,130 @@
1
+ 'use strict'
2
+
3
+ const { globMatch } = require('../src/util')
4
+ const RateLimiter = require('./rate_limiter')
5
+ const Sampler = require('./sampler')
6
+
7
+ class AlwaysMatcher {
8
+ match () {
9
+ return true
10
+ }
11
+ }
12
+
13
+ class GlobMatcher {
14
+ constructor (pattern, locator) {
15
+ this.pattern = pattern
16
+ this.locator = locator
17
+ }
18
+
19
+ match (span) {
20
+ const subject = this.locator(span)
21
+ if (!subject) return false
22
+ return globMatch(this.pattern, subject)
23
+ }
24
+ }
25
+
26
+ class RegExpMatcher {
27
+ constructor (pattern, locator) {
28
+ this.pattern = pattern
29
+ this.locator = locator
30
+ }
31
+
32
+ match (span) {
33
+ const subject = this.locator(span)
34
+ if (!subject) return false
35
+ return this.pattern.test(subject)
36
+ }
37
+ }
38
+
39
+ function matcher (pattern, locator) {
40
+ if (pattern instanceof RegExp) {
41
+ return new RegExpMatcher(pattern, locator)
42
+ }
43
+
44
+ if (typeof pattern === 'string' && pattern !== '*') {
45
+ return new GlobMatcher(pattern, locator)
46
+ }
47
+
48
+ return new AlwaysMatcher()
49
+ }
50
+
51
+ function makeTagLocator (tag) {
52
+ return (span) => span.context()._tags[tag]
53
+ }
54
+
55
+ function nameLocator (span) {
56
+ return span.context()._name
57
+ }
58
+
59
+ function serviceLocator (span) {
60
+ const { _tags: tags } = span.context()
61
+ return tags.service ||
62
+ tags['service.name'] ||
63
+ span.tracer()._service
64
+ }
65
+
66
+ class SamplingRule {
67
+ constructor ({ name, service, resource, tags, sampleRate = 1.0, maxPerSecond } = {}) {
68
+ this.matchers = []
69
+
70
+ if (name) {
71
+ this.matchers.push(matcher(name, nameLocator))
72
+ }
73
+ if (service) {
74
+ this.matchers.push(matcher(service, serviceLocator))
75
+ }
76
+ if (resource) {
77
+ this.matchers.push(matcher(resource, makeTagLocator('resource.name')))
78
+ }
79
+ for (const [key, value] of Object.entries(tags || {})) {
80
+ this.matchers.push(matcher(value, makeTagLocator(key)))
81
+ }
82
+
83
+ this._sampler = new Sampler(sampleRate)
84
+ this._limiter = undefined
85
+
86
+ if (Number.isFinite(maxPerSecond)) {
87
+ this._limiter = new RateLimiter(maxPerSecond)
88
+ }
89
+ }
90
+
91
+ static from (config) {
92
+ return new SamplingRule(config)
93
+ }
94
+
95
+ get sampleRate () {
96
+ return this._sampler.rate()
97
+ }
98
+
99
+ get effectiveRate () {
100
+ return this._limiter && this._limiter.effectiveRate()
101
+ }
102
+
103
+ get maxPerSecond () {
104
+ return this._limiter && this._limiter._rateLimit
105
+ }
106
+
107
+ match (span) {
108
+ for (const matcher of this.matchers) {
109
+ if (!matcher.match(span)) {
110
+ return false
111
+ }
112
+ }
113
+
114
+ return true
115
+ }
116
+
117
+ sample () {
118
+ if (!this._sampler.isSampled()) {
119
+ return false
120
+ }
121
+
122
+ if (this._limiter) {
123
+ return this._limiter.isAllowed()
124
+ }
125
+
126
+ return true
127
+ }
128
+ }
129
+
130
+ module.exports = SamplingRule
@@ -37,6 +37,11 @@ const redisConfig = {
37
37
 
38
38
  const storage = {
39
39
  client: {
40
+ aerospike: {
41
+ opName: () => 'aerospike.command',
42
+ serviceName: ({ tracerService, pluginConfig }) =>
43
+ pluginConfig.service || `${tracerService}-aerospike`
44
+ },
40
45
  'cassandra-driver': {
41
46
  opName: () => 'cassandra.query',
42
47
  serviceName: ({ tracerService, pluginConfig, system }) =>
@@ -22,6 +22,10 @@ function withFunction ({ tracerService, pluginConfig, params }) {
22
22
 
23
23
  const storage = {
24
24
  client: {
25
+ aerospike: {
26
+ opName: () => 'aerospike.command',
27
+ serviceName: configWithFallback
28
+ },
25
29
  'cassandra-driver': {
26
30
  opName: () => 'cassandra.query',
27
31
  serviceName: configWithFallback
@@ -27,10 +27,14 @@ class SpanProcessor {
27
27
  const active = []
28
28
  const formatted = []
29
29
  const trace = spanContext._trace
30
- const { flushMinSpans } = this._config
30
+ const { flushMinSpans, tracing } = this._config
31
31
  const { started, finished } = trace
32
32
 
33
33
  if (trace.record === false) return
34
+ if (tracing === false) {
35
+ this._erase(trace, active)
36
+ return
37
+ }
34
38
  if (started.length === finished.length || finished.length >= flushMinSpans) {
35
39
  this._prioritySampler.sample(spanContext)
36
40
  this._spanSampler.sample(spanContext)
@@ -138,6 +142,10 @@ class SpanProcessor {
138
142
  }
139
143
  }
140
144
 
145
+ for (const span of trace.finished) {
146
+ span.context()._tags = {}
147
+ }
148
+
141
149
  trace.started = active
142
150
  trace.finished = []
143
151
  }