dd-trace 4.18.0 → 4.23.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 (137) hide show
  1. package/LICENSE-3rdparty.csv +3 -2
  2. package/README.md +3 -3
  3. package/ext/kinds.d.ts +1 -0
  4. package/ext/kinds.js +2 -1
  5. package/ext/tags.d.ts +2 -1
  6. package/ext/tags.js +6 -1
  7. package/index.d.ts +29 -0
  8. package/package.json +12 -11
  9. package/packages/datadog-core/src/storage/async_resource.js +1 -1
  10. package/packages/datadog-esbuild/index.js +1 -20
  11. package/packages/datadog-instrumentations/src/aerospike.js +47 -0
  12. package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
  13. package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
  14. package/packages/datadog-instrumentations/src/child-process.js +4 -5
  15. package/packages/datadog-instrumentations/src/couchbase.js +5 -4
  16. package/packages/datadog-instrumentations/src/crypto.js +2 -1
  17. package/packages/datadog-instrumentations/src/dns.js +2 -1
  18. package/packages/datadog-instrumentations/src/graphql.js +18 -4
  19. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +1 -2
  20. package/packages/datadog-instrumentations/src/helpers/hooks.js +10 -2
  21. package/packages/datadog-instrumentations/src/helpers/instrument.js +9 -4
  22. package/packages/datadog-instrumentations/src/helpers/register.js +19 -3
  23. package/packages/datadog-instrumentations/src/http/client.js +12 -2
  24. package/packages/datadog-instrumentations/src/http/server.js +7 -4
  25. package/packages/datadog-instrumentations/src/http2/client.js +3 -1
  26. package/packages/datadog-instrumentations/src/http2/server.js +3 -1
  27. package/packages/datadog-instrumentations/src/jest.js +12 -6
  28. package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
  29. package/packages/datadog-instrumentations/src/net.js +10 -2
  30. package/packages/datadog-instrumentations/src/next.js +18 -6
  31. package/packages/datadog-instrumentations/src/restify.js +14 -1
  32. package/packages/datadog-instrumentations/src/rhea.js +15 -9
  33. package/packages/datadog-plugin-aerospike/src/index.js +113 -0
  34. package/packages/datadog-plugin-cucumber/src/index.js +34 -2
  35. package/packages/datadog-plugin-cypress/src/plugin.js +60 -8
  36. package/packages/datadog-plugin-graphql/src/resolve.js +26 -18
  37. package/packages/datadog-plugin-http/src/client.js +19 -2
  38. package/packages/datadog-plugin-jest/src/index.js +38 -4
  39. package/packages/datadog-plugin-kafkajs/src/consumer.js +59 -6
  40. package/packages/datadog-plugin-kafkajs/src/producer.js +64 -6
  41. package/packages/datadog-plugin-mocha/src/index.js +32 -1
  42. package/packages/datadog-plugin-next/src/index.js +40 -14
  43. package/packages/datadog-plugin-playwright/src/index.js +17 -1
  44. package/packages/dd-trace/src/appsec/activation.js +29 -0
  45. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  46. package/packages/dd-trace/src/appsec/api_security_sampler.js +48 -0
  47. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
  48. package/packages/dd-trace/src/appsec/blocking.js +95 -43
  49. package/packages/dd-trace/src/appsec/channels.js +5 -2
  50. package/packages/dd-trace/src/appsec/graphql.js +146 -0
  51. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  52. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +105 -0
  53. package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
  54. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
  55. package/packages/dd-trace/src/appsec/iast/index.js +1 -1
  56. package/packages/dd-trace/src/appsec/iast/path-line.js +1 -1
  57. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -1
  58. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/constants.js +7 -0
  59. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +12 -19
  60. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +20 -0
  61. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +6 -10
  62. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +18 -25
  63. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +79 -85
  64. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +27 -36
  65. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +14 -11
  66. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  67. package/packages/dd-trace/src/appsec/index.js +33 -32
  68. package/packages/dd-trace/src/appsec/recommended.json +1737 -120
  69. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +6 -1
  70. package/packages/dd-trace/src/appsec/remote_config/index.js +40 -15
  71. package/packages/dd-trace/src/appsec/reporter.js +50 -34
  72. package/packages/dd-trace/src/appsec/rule_manager.js +9 -6
  73. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  74. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +28 -13
  75. package/packages/dd-trace/src/appsec/waf/waf_manager.js +0 -1
  76. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +30 -1
  77. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +30 -1
  78. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +17 -1
  79. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +110 -59
  80. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +40 -7
  81. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +26 -1
  82. package/packages/dd-trace/src/ci-visibility/telemetry.js +130 -0
  83. package/packages/dd-trace/src/config.js +145 -63
  84. package/packages/dd-trace/src/datastreams/processor.js +166 -26
  85. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +14 -1
  86. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +14 -0
  87. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +4 -0
  88. package/packages/dd-trace/src/exporters/common/form-data.js +4 -0
  89. package/packages/dd-trace/src/format.js +6 -1
  90. package/packages/dd-trace/src/id.js +12 -0
  91. package/packages/dd-trace/src/iitm.js +1 -1
  92. package/packages/dd-trace/src/log/channels.js +1 -1
  93. package/packages/dd-trace/src/noop/proxy.js +4 -0
  94. package/packages/dd-trace/src/opentelemetry/span.js +95 -2
  95. package/packages/dd-trace/src/opentelemetry/tracer.js +9 -10
  96. package/packages/dd-trace/src/opentracing/propagation/text_map.js +14 -5
  97. package/packages/dd-trace/src/opentracing/span.js +6 -0
  98. package/packages/dd-trace/src/opentracing/span_context.js +5 -2
  99. package/packages/dd-trace/src/opentracing/tracer.js +2 -2
  100. package/packages/dd-trace/src/plugin_manager.js +1 -1
  101. package/packages/dd-trace/src/plugins/ci_plugin.js +46 -9
  102. package/packages/dd-trace/src/plugins/database.js +1 -1
  103. package/packages/dd-trace/src/plugins/index.js +6 -0
  104. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  105. package/packages/dd-trace/src/plugins/util/ci.js +6 -19
  106. package/packages/dd-trace/src/plugins/util/exec.js +23 -2
  107. package/packages/dd-trace/src/plugins/util/git.js +98 -22
  108. package/packages/dd-trace/src/plugins/util/ip_extractor.js +7 -6
  109. package/packages/dd-trace/src/plugins/util/test.js +3 -2
  110. package/packages/dd-trace/src/plugins/util/url.js +26 -0
  111. package/packages/dd-trace/src/plugins/util/user-provided-git.js +4 -16
  112. package/packages/dd-trace/src/priority_sampler.js +30 -38
  113. package/packages/dd-trace/src/profiler.js +5 -3
  114. package/packages/dd-trace/src/profiling/config.js +26 -2
  115. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -0
  116. package/packages/dd-trace/src/profiling/profiler.js +17 -10
  117. package/packages/dd-trace/src/profiling/profilers/events.js +264 -0
  118. package/packages/dd-trace/src/profiling/profilers/shared.js +39 -0
  119. package/packages/dd-trace/src/profiling/profilers/space.js +2 -1
  120. package/packages/dd-trace/src/profiling/profilers/wall.js +121 -58
  121. package/packages/dd-trace/src/proxy.js +25 -1
  122. package/packages/dd-trace/src/ritm.js +1 -1
  123. package/packages/dd-trace/src/sampling_rule.js +130 -0
  124. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +5 -0
  125. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  126. package/packages/dd-trace/src/span_processor.js +4 -0
  127. package/packages/dd-trace/src/span_sampler.js +6 -64
  128. package/packages/dd-trace/src/spanleak.js +98 -0
  129. package/packages/dd-trace/src/startup-log.js +7 -1
  130. package/packages/dd-trace/src/telemetry/dependencies.js +56 -10
  131. package/packages/dd-trace/src/telemetry/index.js +171 -41
  132. package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
  133. package/packages/dd-trace/src/telemetry/send-data.js +47 -5
  134. package/packages/dd-trace/src/tracer.js +8 -2
  135. package/scripts/install_plugin_modules.js +11 -3
  136. package/packages/diagnostics_channel/index.js +0 -3
  137. package/packages/diagnostics_channel/src/index.js +0 -121
@@ -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 () {
@@ -35,6 +36,19 @@ class Tracer extends NoopProxy {
35
36
  setInterval(() => {
36
37
  this.dogstatsd.flush()
37
38
  }, 10 * 1000).unref()
39
+
40
+ process.once('beforeExit', () => {
41
+ this.dogstatsd.flush()
42
+ })
43
+ }
44
+
45
+ if (config.spanLeakDebug > 0) {
46
+ if (config.spanLeakDebug === spanleak.MODES.LOG) {
47
+ spanleak.enableLogging()
48
+ } else if (config.spanLeakDebug === spanleak.MODES.GC_AND_LOG) {
49
+ spanleak.enableGarbageCollection()
50
+ }
51
+ spanleak.startScrubber()
38
52
  }
39
53
 
40
54
  if (config.remoteConfig.enabled && !config.isCiVisibility) {
@@ -62,11 +76,14 @@ class Tracer extends NoopProxy {
62
76
  // do not stop tracer initialization if the profiler fails to be imported
63
77
  try {
64
78
  const profiler = require('./profiler')
65
- profiler.start(config)
79
+ this._profilerStarted = profiler.start(config)
66
80
  } catch (e) {
67
81
  log.error(e)
68
82
  }
69
83
  }
84
+ if (!this._profilerStarted) {
85
+ this._profilerStarted = Promise.resolve(false)
86
+ }
70
87
 
71
88
  if (config.runtimeMetrics) {
72
89
  runtimeMetrics.start(config)
@@ -104,6 +121,13 @@ class Tracer extends NoopProxy {
104
121
  return this
105
122
  }
106
123
 
124
+ profilerStarted () {
125
+ if (!this._profilerStarted) {
126
+ throw new Error('profilerStarted() must be called after init()')
127
+ }
128
+ return this._profilerStarted
129
+ }
130
+
107
131
  use () {
108
132
  this._pluginManager.configurePlugin(...arguments)
109
133
  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
@@ -138,6 +138,10 @@ class SpanProcessor {
138
138
  }
139
139
  }
140
140
 
141
+ for (const span of trace.finished) {
142
+ span.context()._tags = {}
143
+ }
144
+
141
145
  trace.started = active
142
146
  trace.finished = []
143
147
  }
@@ -1,67 +1,16 @@
1
1
  'use strict'
2
- const { globMatch } = require('../src/util')
3
- const { USER_KEEP, AUTO_KEEP } = require('../../../ext').priority
4
- const RateLimiter = require('./rate_limiter')
5
- const Sampler = require('./sampler')
6
-
7
- class SpanSamplingRule {
8
- constructor ({ service, name, sampleRate = 1.0, maxPerSecond } = {}) {
9
- this.service = service
10
- this.name = name
11
-
12
- this._sampler = new Sampler(sampleRate)
13
- this._limiter = undefined
14
-
15
- if (Number.isFinite(maxPerSecond)) {
16
- this._limiter = new RateLimiter(maxPerSecond)
17
- }
18
- }
19
-
20
- get sampleRate () {
21
- return this._sampler.rate()
22
- }
23
-
24
- get maxPerSecond () {
25
- return this._limiter && this._limiter._rateLimit
26
- }
27
-
28
- static from (config) {
29
- return new SpanSamplingRule(config)
30
- }
31
-
32
- match (service, name) {
33
- if (this.service && !globMatch(this.service, service)) {
34
- return false
35
- }
36
-
37
- if (this.name && !globMatch(this.name, name)) {
38
- return false
39
- }
40
-
41
- return true
42
- }
43
-
44
- sample () {
45
- if (!this._sampler.isSampled()) {
46
- return false
47
- }
48
2
 
49
- if (this._limiter) {
50
- return this._limiter.isAllowed()
51
- }
52
-
53
- return true
54
- }
55
- }
3
+ const { USER_KEEP, AUTO_KEEP } = require('../../../ext').priority
4
+ const SamplingRule = require('./sampling_rule')
56
5
 
57
6
  class SpanSampler {
58
7
  constructor ({ spanSamplingRules = [] } = {}) {
59
- this._rules = spanSamplingRules.map(SpanSamplingRule.from)
8
+ this._rules = spanSamplingRules.map(SamplingRule.from)
60
9
  }
61
10
 
62
- findRule (service, name) {
11
+ findRule (context) {
63
12
  for (const rule of this._rules) {
64
- if (rule.match(service, name)) {
13
+ if (rule.match(context)) {
65
14
  return rule
66
15
  }
67
16
  }
@@ -73,14 +22,7 @@ class SpanSampler {
73
22
 
74
23
  const { started } = spanContext._trace
75
24
  for (const span of started) {
76
- const context = span.context()
77
- const tags = context._tags || {}
78
- const name = context._name
79
- const service = tags.service ||
80
- tags['service.name'] ||
81
- span.tracer()._service
82
-
83
- const rule = this.findRule(service, name)
25
+ const rule = this.findRule(span)
84
26
  if (rule && rule.sample()) {
85
27
  span.context()._spanSampling = {
86
28
  sampleRate: rule.sampleRate,
@@ -0,0 +1,98 @@
1
+ 'use strict'
2
+
3
+ /* eslint-disable no-console */
4
+
5
+ const SortedSet = require('tlhunter-sorted-set')
6
+
7
+ const INTERVAL = 1000 // look for expired spans every 1s
8
+ const LIFETIME = 60 * 1000 // all spans have a max lifetime of 1m
9
+
10
+ const MODES = {
11
+ DISABLED: 0,
12
+ // METRICS_ONLY
13
+ LOG: 1,
14
+ GC_AND_LOG: 2
15
+ // GC
16
+ }
17
+
18
+ module.exports.MODES = MODES
19
+
20
+ const spans = new SortedSet()
21
+
22
+ // TODO: should these also be delivered as runtime metrics?
23
+
24
+ // const registry = new FinalizationRegistry(name => {
25
+ // spans.del(span) // there is no span
26
+ // })
27
+
28
+ let interval
29
+ let mode = MODES.DISABLED
30
+
31
+ module.exports.disable = function () {
32
+ mode = MODES.DISABLED
33
+ }
34
+
35
+ module.exports.enableLogging = function () {
36
+ mode = MODES.LOG
37
+ }
38
+
39
+ module.exports.enableGarbageCollection = function () {
40
+ mode = MODES.GC_AND_LOG
41
+ }
42
+
43
+ module.exports.startScrubber = function () {
44
+ if (!isEnabled()) return
45
+
46
+ interval = setInterval(() => {
47
+ const now = Date.now()
48
+ const expired = spans.rangeByScore(0, now)
49
+
50
+ if (!expired.length) return
51
+
52
+ const gc = isGarbageCollecting()
53
+
54
+ const expirationsByType = Object.create(null) // { [spanType]: count }
55
+
56
+ for (const wrapped of expired) {
57
+ spans.del(wrapped)
58
+ const span = wrapped.deref()
59
+
60
+ if (!span) continue // span has already been garbage collected
61
+
62
+ // TODO: Should we also do things like record the route to help users debug leaks?
63
+ if (!expirationsByType[span._name]) expirationsByType[span._name] = 0
64
+ expirationsByType[span._name]++
65
+
66
+ if (!gc) continue // everything after this point is related to manual GC
67
+
68
+ // TODO: what else can we do to alleviate memory usage
69
+ span.context()._tags = Object.create(null)
70
+ }
71
+
72
+ console.log('expired spans:' +
73
+ Object.keys(expirationsByType).reduce((a, c) => `${a} ${c}: ${expirationsByType[c]}`, ''))
74
+ }, INTERVAL)
75
+ }
76
+
77
+ module.exports.stopScrubber = function () {
78
+ clearInterval(interval)
79
+ }
80
+
81
+ module.exports.addSpan = function (span) {
82
+ if (!isEnabled()) return
83
+
84
+ const now = Date.now()
85
+ const expiration = now + LIFETIME
86
+ // eslint-disable-next-line no-undef
87
+ const wrapped = new WeakRef(span)
88
+ spans.add(wrapped, expiration)
89
+ // registry.register(span, span._name)
90
+ }
91
+
92
+ function isEnabled () {
93
+ return mode > MODES.DISABLED
94
+ }
95
+
96
+ function isGarbageCollecting () {
97
+ return mode >= MODES.GC_AND_LOG
98
+ }
@@ -6,6 +6,7 @@ const os = require('os')
6
6
  const { inspect } = require('util')
7
7
  const tracerVersion = require('../../../package.json').version
8
8
 
9
+ const errors = {}
9
10
  let config
10
11
  let pluginManager
11
12
  let samplingRules = []
@@ -89,6 +90,10 @@ function startupLog ({ agentError } = {}) {
89
90
  info('DATADOG TRACER CONFIGURATION - ' + out)
90
91
  if (agentError) {
91
92
  warn('DATADOG TRACER DIAGNOSTIC - Agent Error: ' + agentError.message)
93
+ errors.agentError = {
94
+ code: agentError.code ? agentError.code : '',
95
+ message: `Agent Error:${agentError.message}`
96
+ }
92
97
  }
93
98
 
94
99
  config = undefined
@@ -112,5 +117,6 @@ module.exports = {
112
117
  startupLog,
113
118
  setStartupLogConfig,
114
119
  setStartupLogPluginManager,
115
- setSamplingRules
120
+ setSamplingRules,
121
+ errors
116
122
  }
@@ -4,8 +4,9 @@ const path = require('path')
4
4
  const parse = require('module-details-from-path')
5
5
  const requirePackageJson = require('../require-package-json')
6
6
  const { sendData } = require('./send-data')
7
- const dc = require('../../../diagnostics_channel')
7
+ const dc = require('dc-polyfill')
8
8
  const { fileURLToPath } = require('url')
9
+ const { isTrue } = require('../../src/util')
9
10
 
10
11
  const savedDependenciesToSend = new Set()
11
12
  const detectedDependencyKeys = new Set()
@@ -14,20 +15,57 @@ const detectedDependencyVersions = new Set()
14
15
  const FILE_URI_START = `file://`
15
16
  const moduleLoadStartChannel = dc.channel('dd-trace:moduleLoadStart')
16
17
 
17
- let immediate, config, application, host
18
+ let immediate, config, application, host, initialLoad
18
19
  let isFirstModule = true
20
+ let getRetryData
21
+ let updateRetryData
19
22
 
23
+ function createBatchPayload (payload) {
24
+ const batchPayload = []
25
+ payload.map(item => {
26
+ batchPayload.push({
27
+ request_type: item.reqType,
28
+ payload: item.payload
29
+ })
30
+ })
31
+
32
+ return batchPayload
33
+ }
20
34
  function waitAndSend (config, application, host) {
21
35
  if (!immediate) {
22
36
  immediate = setImmediate(() => {
23
37
  immediate = null
24
38
  if (savedDependenciesToSend.size > 0) {
25
- const dependencies = Array.from(savedDependenciesToSend.values()).splice(0, 1000).map(pair => {
26
- savedDependenciesToSend.delete(pair)
27
- const [name, version] = pair.split(' ')
28
- return { name, version }
29
- })
30
- sendData(config, application, host, 'app-dependencies-loaded', { dependencies })
39
+ const dependencies = Array.from(savedDependenciesToSend.values())
40
+ // if a depencdency is from the initial load, *always* send the event
41
+ // Otherwise, only send if dependencyCollection is enabled
42
+ .filter(dep => {
43
+ const initialLoadModule = isTrue(dep.split(' ')[2])
44
+ const sendModule = initialLoadModule || (config.telemetry?.dependencyCollection)
45
+
46
+ if (!sendModule) savedDependenciesToSend.delete(dep) // we'll never send it
47
+ return sendModule
48
+ })
49
+ .splice(0, 2000) // v2 documentation specifies up to 2000 dependencies can be sent at once
50
+ .map(pair => {
51
+ savedDependenciesToSend.delete(pair)
52
+ const [name, version] = pair.split(' ')
53
+ return { name, version }
54
+ })
55
+ let currPayload
56
+ const retryData = getRetryData()
57
+ if (retryData) {
58
+ currPayload = { reqType: 'app-dependencies-loaded', payload: { dependencies } }
59
+ } else {
60
+ if (!dependencies.length) return // no retry data and no dependencies, nothing to send
61
+ currPayload = { dependencies }
62
+ }
63
+
64
+ const payload = retryData ? createBatchPayload([currPayload, retryData]) : currPayload
65
+ const reqType = retryData ? 'message-batch' : 'app-dependencies-loaded'
66
+
67
+ sendData(config, application, host, reqType, payload, updateRetryData)
68
+
31
69
  if (savedDependenciesToSend.size > 0) {
32
70
  waitAndSend(config, application, host)
33
71
  }
@@ -76,7 +114,7 @@ function onModuleLoad (data) {
76
114
  const dependencyAndVersion = `${name} ${version}`
77
115
 
78
116
  if (!detectedDependencyVersions.has(dependencyAndVersion)) {
79
- savedDependenciesToSend.add(dependencyAndVersion)
117
+ savedDependenciesToSend.add(`${dependencyAndVersion} ${initialLoad}`)
80
118
  detectedDependencyVersions.add(dependencyAndVersion)
81
119
 
82
120
  waitAndSend(config, application, host)
@@ -89,11 +127,19 @@ function onModuleLoad (data) {
89
127
  }
90
128
  }
91
129
  }
92
- function start (_config, _application, _host) {
130
+ function start (_config = {}, _application, _host, getRetryDataFunction, updateRetryDatafunction) {
93
131
  config = _config
94
132
  application = _application
95
133
  host = _host
134
+ initialLoad = true
135
+ getRetryData = getRetryDataFunction
136
+ updateRetryData = updateRetryDatafunction
96
137
  moduleLoadStartChannel.subscribe(onModuleLoad)
138
+
139
+ // try and capture intially loaded modules in the first tick
140
+ // since, ideally, the tracer (and this module) should be loaded first,
141
+ // this should capture any first-tick dependencies
142
+ queueMicrotask(() => { initialLoad = false })
97
143
  }
98
144
 
99
145
  function isDependency (filename, request) {