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.
- package/LICENSE-3rdparty.csv +3 -2
- package/README.md +3 -3
- package/ext/kinds.d.ts +1 -0
- package/ext/kinds.js +2 -1
- package/ext/tags.d.ts +2 -1
- package/ext/tags.js +6 -1
- package/index.d.ts +29 -0
- package/package.json +12 -11
- package/packages/datadog-core/src/storage/async_resource.js +1 -1
- package/packages/datadog-esbuild/index.js +1 -20
- package/packages/datadog-instrumentations/src/aerospike.js +47 -0
- package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
- package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
- package/packages/datadog-instrumentations/src/child-process.js +4 -5
- package/packages/datadog-instrumentations/src/couchbase.js +5 -4
- package/packages/datadog-instrumentations/src/crypto.js +2 -1
- package/packages/datadog-instrumentations/src/dns.js +2 -1
- package/packages/datadog-instrumentations/src/graphql.js +18 -4
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +10 -2
- package/packages/datadog-instrumentations/src/helpers/instrument.js +9 -4
- package/packages/datadog-instrumentations/src/helpers/register.js +19 -3
- package/packages/datadog-instrumentations/src/http/client.js +12 -2
- package/packages/datadog-instrumentations/src/http/server.js +7 -4
- package/packages/datadog-instrumentations/src/http2/client.js +3 -1
- package/packages/datadog-instrumentations/src/http2/server.js +3 -1
- package/packages/datadog-instrumentations/src/jest.js +12 -6
- package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
- package/packages/datadog-instrumentations/src/net.js +10 -2
- package/packages/datadog-instrumentations/src/next.js +18 -6
- package/packages/datadog-instrumentations/src/restify.js +14 -1
- package/packages/datadog-instrumentations/src/rhea.js +15 -9
- package/packages/datadog-plugin-aerospike/src/index.js +113 -0
- package/packages/datadog-plugin-cucumber/src/index.js +34 -2
- package/packages/datadog-plugin-cypress/src/plugin.js +60 -8
- package/packages/datadog-plugin-graphql/src/resolve.js +26 -18
- package/packages/datadog-plugin-http/src/client.js +19 -2
- package/packages/datadog-plugin-jest/src/index.js +38 -4
- package/packages/datadog-plugin-kafkajs/src/consumer.js +59 -6
- package/packages/datadog-plugin-kafkajs/src/producer.js +64 -6
- package/packages/datadog-plugin-mocha/src/index.js +32 -1
- package/packages/datadog-plugin-next/src/index.js +40 -14
- package/packages/datadog-plugin-playwright/src/index.js +17 -1
- package/packages/dd-trace/src/appsec/activation.js +29 -0
- package/packages/dd-trace/src/appsec/addresses.js +3 -1
- package/packages/dd-trace/src/appsec/api_security_sampler.js +48 -0
- package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
- package/packages/dd-trace/src/appsec/blocking.js +95 -43
- package/packages/dd-trace/src/appsec/channels.js +5 -2
- package/packages/dd-trace/src/appsec/graphql.js +146 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +105 -0
- package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
- package/packages/dd-trace/src/appsec/iast/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/path-line.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/constants.js +7 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +12 -19
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +20 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +6 -10
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +18 -25
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +79 -85
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +27 -36
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +14 -11
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
- package/packages/dd-trace/src/appsec/index.js +33 -32
- package/packages/dd-trace/src/appsec/recommended.json +1737 -120
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +6 -1
- package/packages/dd-trace/src/appsec/remote_config/index.js +40 -15
- package/packages/dd-trace/src/appsec/reporter.js +50 -34
- package/packages/dd-trace/src/appsec/rule_manager.js +9 -6
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +28 -13
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +0 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +30 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +30 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +17 -1
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +110 -59
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +40 -7
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +26 -1
- package/packages/dd-trace/src/ci-visibility/telemetry.js +130 -0
- package/packages/dd-trace/src/config.js +145 -63
- package/packages/dd-trace/src/datastreams/processor.js +166 -26
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +14 -1
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +14 -0
- package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +4 -0
- package/packages/dd-trace/src/exporters/common/form-data.js +4 -0
- package/packages/dd-trace/src/format.js +6 -1
- package/packages/dd-trace/src/id.js +12 -0
- package/packages/dd-trace/src/iitm.js +1 -1
- package/packages/dd-trace/src/log/channels.js +1 -1
- package/packages/dd-trace/src/noop/proxy.js +4 -0
- package/packages/dd-trace/src/opentelemetry/span.js +95 -2
- package/packages/dd-trace/src/opentelemetry/tracer.js +9 -10
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +14 -5
- package/packages/dd-trace/src/opentracing/span.js +6 -0
- package/packages/dd-trace/src/opentracing/span_context.js +5 -2
- package/packages/dd-trace/src/opentracing/tracer.js +2 -2
- package/packages/dd-trace/src/plugin_manager.js +1 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +46 -9
- package/packages/dd-trace/src/plugins/database.js +1 -1
- package/packages/dd-trace/src/plugins/index.js +6 -0
- package/packages/dd-trace/src/plugins/plugin.js +1 -1
- package/packages/dd-trace/src/plugins/util/ci.js +6 -19
- package/packages/dd-trace/src/plugins/util/exec.js +23 -2
- package/packages/dd-trace/src/plugins/util/git.js +98 -22
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +7 -6
- package/packages/dd-trace/src/plugins/util/test.js +3 -2
- package/packages/dd-trace/src/plugins/util/url.js +26 -0
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +4 -16
- package/packages/dd-trace/src/priority_sampler.js +30 -38
- package/packages/dd-trace/src/profiler.js +5 -3
- package/packages/dd-trace/src/profiling/config.js +26 -2
- package/packages/dd-trace/src/profiling/exporters/agent.js +1 -0
- package/packages/dd-trace/src/profiling/profiler.js +17 -10
- package/packages/dd-trace/src/profiling/profilers/events.js +264 -0
- package/packages/dd-trace/src/profiling/profilers/shared.js +39 -0
- package/packages/dd-trace/src/profiling/profilers/space.js +2 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +121 -58
- package/packages/dd-trace/src/proxy.js +25 -1
- package/packages/dd-trace/src/ritm.js +1 -1
- package/packages/dd-trace/src/sampling_rule.js +130 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +5 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
- package/packages/dd-trace/src/span_processor.js +4 -0
- package/packages/dd-trace/src/span_sampler.js +6 -64
- package/packages/dd-trace/src/spanleak.js +98 -0
- package/packages/dd-trace/src/startup-log.js +7 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +56 -10
- package/packages/dd-trace/src/telemetry/index.js +171 -41
- package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
- package/packages/dd-trace/src/telemetry/send-data.js +47 -5
- package/packages/dd-trace/src/tracer.js +8 -2
- package/scripts/install_plugin_modules.js +11 -3
- package/packages/diagnostics_channel/index.js +0 -3
- 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
|
|
@@ -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
|
|
@@ -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
|
-
|
|
50
|
-
|
|
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(
|
|
8
|
+
this._rules = spanSamplingRules.map(SamplingRule.from)
|
|
60
9
|
}
|
|
61
10
|
|
|
62
|
-
findRule (
|
|
11
|
+
findRule (context) {
|
|
63
12
|
for (const rule of this._rules) {
|
|
64
|
-
if (rule.match(
|
|
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
|
|
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('
|
|
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())
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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) {
|