dd-trace 2.17.0 → 2.19.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/index.d.ts +40 -2
- package/package.json +1 -1
- package/packages/datadog-instrumentations/src/cucumber.js +0 -2
- package/packages/datadog-instrumentations/src/elasticsearch.js +51 -47
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/mariadb.js +43 -69
- package/packages/datadog-instrumentations/src/mocha.js +14 -18
- package/packages/datadog-instrumentations/src/opensearch.js +10 -0
- package/packages/datadog-instrumentations/src/pg.js +2 -1
- package/packages/datadog-instrumentations/src/restify.js +1 -0
- package/packages/datadog-instrumentations/src/rhea.js +20 -17
- package/packages/datadog-plugin-amqp10/src/consumer.js +32 -0
- package/packages/datadog-plugin-amqp10/src/index.js +11 -101
- package/packages/datadog-plugin-amqp10/src/producer.js +34 -0
- package/packages/datadog-plugin-amqp10/src/util.js +15 -0
- package/packages/datadog-plugin-amqplib/src/client.js +38 -0
- package/packages/datadog-plugin-amqplib/src/consumer.js +40 -0
- package/packages/datadog-plugin-amqplib/src/index.js +14 -102
- package/packages/datadog-plugin-amqplib/src/producer.js +37 -0
- package/packages/datadog-plugin-amqplib/src/util.js +14 -0
- package/packages/datadog-plugin-dns/src/index.js +16 -91
- package/packages/datadog-plugin-dns/src/lookup.js +40 -0
- package/packages/datadog-plugin-dns/src/lookup_service.js +24 -0
- package/packages/datadog-plugin-dns/src/resolve.js +24 -0
- package/packages/datadog-plugin-dns/src/reverse.js +21 -0
- package/packages/datadog-plugin-elasticsearch/src/index.js +7 -7
- package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +25 -0
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +42 -0
- package/packages/datadog-plugin-google-cloud-pubsub/src/index.js +14 -99
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +34 -0
- package/packages/datadog-plugin-graphql/src/execute.js +73 -0
- package/packages/datadog-plugin-graphql/src/index.js +14 -176
- package/packages/datadog-plugin-graphql/src/parse.js +32 -0
- package/packages/datadog-plugin-graphql/src/resolve.js +70 -76
- package/packages/datadog-plugin-graphql/src/validate.js +28 -0
- package/packages/datadog-plugin-grpc/src/client.js +46 -55
- package/packages/datadog-plugin-grpc/src/index.js +7 -24
- package/packages/datadog-plugin-grpc/src/server.js +50 -52
- package/packages/datadog-plugin-grpc/src/util.js +15 -14
- package/packages/datadog-plugin-http/src/index.js +7 -22
- package/packages/datadog-plugin-http2/src/index.js +8 -26
- package/packages/datadog-plugin-jest/src/index.js +3 -0
- package/packages/datadog-plugin-kafkajs/src/consumer.js +42 -0
- package/packages/datadog-plugin-kafkajs/src/index.js +11 -87
- package/packages/datadog-plugin-kafkajs/src/producer.js +31 -0
- package/packages/datadog-plugin-mocha/src/index.js +2 -2
- package/packages/datadog-plugin-moleculer/src/client.js +22 -36
- package/packages/datadog-plugin-moleculer/src/index.js +8 -26
- package/packages/datadog-plugin-moleculer/src/server.js +18 -30
- package/packages/datadog-plugin-net/src/ipc.js +21 -0
- package/packages/datadog-plugin-net/src/tcp.js +46 -0
- package/packages/datadog-plugin-opensearch/src/index.js +11 -0
- package/packages/datadog-plugin-pg/src/index.js +2 -1
- package/packages/datadog-plugin-rhea/src/consumer.js +55 -0
- package/packages/datadog-plugin-rhea/src/index.js +11 -99
- package/packages/datadog-plugin-rhea/src/producer.js +45 -0
- package/packages/datadog-plugin-sharedb/src/index.js +22 -39
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/index.js +4 -6
- package/packages/dd-trace/src/appsec/iast/path-line.js +3 -0
- package/packages/dd-trace/src/appsec/recommended.json +60 -46
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +2 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -5
- package/packages/dd-trace/src/config.js +31 -6
- package/packages/dd-trace/src/constants.js +3 -0
- package/packages/dd-trace/src/dogstatsd.js +42 -10
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +10 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +2 -9
- package/packages/dd-trace/src/exporters/common/request.js +12 -0
- package/packages/dd-trace/src/format.js +13 -0
- package/packages/dd-trace/src/metrics.js +10 -2
- package/packages/dd-trace/src/plugin_manager.js +6 -1
- package/packages/dd-trace/src/plugins/client.js +3 -1
- package/packages/dd-trace/src/plugins/composite.js +26 -0
- package/packages/dd-trace/src/plugins/consumer.js +9 -0
- package/packages/dd-trace/src/plugins/incoming.js +7 -0
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/outgoing.js +1 -1
- package/packages/dd-trace/src/plugins/producer.js +9 -0
- package/packages/dd-trace/src/plugins/server.js +9 -0
- package/packages/dd-trace/src/plugins/storage.js +0 -4
- package/packages/dd-trace/src/plugins/tracing.js +10 -9
- package/packages/dd-trace/src/plugins/util/web.js +49 -3
- package/packages/dd-trace/src/profiling/profiler.js +8 -1
- package/packages/dd-trace/src/span_processor.js +3 -0
- package/packages/dd-trace/src/span_sampler.js +80 -0
- package/packages/dd-trace/src/tracer.js +6 -2
- package/packages/dd-trace/src/util.js +43 -1
|
@@ -24,6 +24,12 @@ class TracingPlugin extends Plugin {
|
|
|
24
24
|
})
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
get activeSpan () {
|
|
28
|
+
const store = storage.getStore()
|
|
29
|
+
|
|
30
|
+
return store && store.span
|
|
31
|
+
}
|
|
32
|
+
|
|
27
33
|
configure (config) {
|
|
28
34
|
return super.configure({
|
|
29
35
|
...config,
|
|
@@ -37,7 +43,7 @@ class TracingPlugin extends Plugin {
|
|
|
37
43
|
start () {} // implemented by individual plugins
|
|
38
44
|
|
|
39
45
|
finish () {
|
|
40
|
-
this.activeSpan
|
|
46
|
+
this.activeSpan.finish()
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
error (error) {
|
|
@@ -49,7 +55,7 @@ class TracingPlugin extends Plugin {
|
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
addError (error) {
|
|
52
|
-
const span = this.activeSpan
|
|
58
|
+
const span = this.activeSpan
|
|
53
59
|
|
|
54
60
|
if (!span._spanContext._tags['error']) {
|
|
55
61
|
span.setTag('error', error || 1)
|
|
@@ -64,8 +70,9 @@ class TracingPlugin extends Plugin {
|
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
const span = this.tracer.startSpan(name, {
|
|
73
|
+
childOf,
|
|
67
74
|
tags: {
|
|
68
|
-
'service.name': service,
|
|
75
|
+
'service.name': service || this.tracer._service,
|
|
69
76
|
'resource.name': resource,
|
|
70
77
|
'span.kind': kind,
|
|
71
78
|
'span.type': type,
|
|
@@ -80,12 +87,6 @@ class TracingPlugin extends Plugin {
|
|
|
80
87
|
|
|
81
88
|
return span
|
|
82
89
|
}
|
|
83
|
-
|
|
84
|
-
activeSpan () {
|
|
85
|
-
const store = storage.getStore()
|
|
86
|
-
|
|
87
|
-
return store && store.span
|
|
88
|
-
}
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
module.exports = TracingPlugin
|
|
@@ -58,13 +58,15 @@ const web = {
|
|
|
58
58
|
const hooks = getHooks(config)
|
|
59
59
|
const filter = urlFilter.getFilter(config)
|
|
60
60
|
const middleware = getMiddlewareSetting(config)
|
|
61
|
+
const queryStringObfuscation = getQsObfuscator(config)
|
|
61
62
|
|
|
62
63
|
return Object.assign({}, config, {
|
|
63
64
|
headers,
|
|
64
65
|
validateStatus,
|
|
65
66
|
hooks,
|
|
66
67
|
filter,
|
|
67
|
-
middleware
|
|
68
|
+
middleware,
|
|
69
|
+
queryStringObfuscation
|
|
68
70
|
})
|
|
69
71
|
},
|
|
70
72
|
|
|
@@ -368,6 +370,24 @@ const web = {
|
|
|
368
370
|
return req.socket && req.socket.remoteAddress
|
|
369
371
|
},
|
|
370
372
|
|
|
373
|
+
obfuscateQs (config, url) {
|
|
374
|
+
const { queryStringObfuscation } = config
|
|
375
|
+
|
|
376
|
+
if (queryStringObfuscation === false) return url
|
|
377
|
+
|
|
378
|
+
const i = url.indexOf('?')
|
|
379
|
+
if (i === -1) return url
|
|
380
|
+
|
|
381
|
+
const path = url.slice(0, i)
|
|
382
|
+
if (queryStringObfuscation === true) return path
|
|
383
|
+
|
|
384
|
+
let qs = url.slice(i + 1)
|
|
385
|
+
|
|
386
|
+
qs = qs.replace(queryStringObfuscation, '<redacted>')
|
|
387
|
+
|
|
388
|
+
return `${path}?${qs}`
|
|
389
|
+
},
|
|
390
|
+
|
|
371
391
|
wrapWriteHead (context) {
|
|
372
392
|
const { req, res } = context
|
|
373
393
|
const writeHead = res.writeHead
|
|
@@ -458,11 +478,11 @@ function reactivate (req, fn) {
|
|
|
458
478
|
}
|
|
459
479
|
|
|
460
480
|
function addRequestTags (context) {
|
|
461
|
-
const { req, span } = context
|
|
481
|
+
const { req, span, config } = context
|
|
462
482
|
const url = extractURL(req)
|
|
463
483
|
|
|
464
484
|
span.addTags({
|
|
465
|
-
[HTTP_URL]:
|
|
485
|
+
[HTTP_URL]: web.obfuscateQs(config, url),
|
|
466
486
|
[HTTP_METHOD]: req.method,
|
|
467
487
|
[SPAN_KIND]: SERVER,
|
|
468
488
|
[SPAN_TYPE]: WEB,
|
|
@@ -619,4 +639,30 @@ function getMiddlewareSetting (config) {
|
|
|
619
639
|
return true
|
|
620
640
|
}
|
|
621
641
|
|
|
642
|
+
function getQsObfuscator (config) {
|
|
643
|
+
const obfuscator = config.queryStringObfuscation
|
|
644
|
+
|
|
645
|
+
if (typeof obfuscator === 'boolean') {
|
|
646
|
+
return obfuscator
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (typeof obfuscator === 'string') {
|
|
650
|
+
if (obfuscator === '') return false // disable obfuscator
|
|
651
|
+
|
|
652
|
+
if (obfuscator === '.*') return true // optimize full redact
|
|
653
|
+
|
|
654
|
+
try {
|
|
655
|
+
return new RegExp(obfuscator, 'gi')
|
|
656
|
+
} catch (err) {
|
|
657
|
+
log.error(err)
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (config.hasOwnProperty('queryStringObfuscation')) {
|
|
662
|
+
log.error('Expected `queryStringObfuscation` to be a regex string or boolean.')
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return true
|
|
666
|
+
}
|
|
667
|
+
|
|
622
668
|
module.exports = web
|
|
@@ -36,9 +36,16 @@ class Profiler extends EventEmitter {
|
|
|
36
36
|
this._logger = config.logger
|
|
37
37
|
this._enabled = true
|
|
38
38
|
|
|
39
|
+
// Log errors if the source map finder fails, but don't prevent the rest
|
|
40
|
+
// of the profiler from running without source maps.
|
|
41
|
+
let mapper
|
|
39
42
|
try {
|
|
40
|
-
|
|
43
|
+
mapper = await maybeSourceMap(config.sourceMap)
|
|
44
|
+
} catch (err) {
|
|
45
|
+
this._logger.error(err)
|
|
46
|
+
}
|
|
41
47
|
|
|
48
|
+
try {
|
|
42
49
|
for (const profiler of config.profilers) {
|
|
43
50
|
// TODO: move this out of Profiler when restoring sourcemap support
|
|
44
51
|
profiler.start({ mapper })
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const log = require('./log')
|
|
4
4
|
const format = require('./format')
|
|
5
|
+
const SpanSampler = require('./span_sampler')
|
|
5
6
|
|
|
6
7
|
const { SpanStatsProcessor } = require('./span_stats')
|
|
7
8
|
|
|
@@ -15,6 +16,7 @@ class SpanProcessor {
|
|
|
15
16
|
this._config = config
|
|
16
17
|
|
|
17
18
|
this._stats = new SpanStatsProcessor(config)
|
|
19
|
+
this._spanSampler = new SpanSampler(config)
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
process (span) {
|
|
@@ -27,6 +29,7 @@ class SpanProcessor {
|
|
|
27
29
|
|
|
28
30
|
if (started.length === finished.length || finished.length >= flushMinSpans) {
|
|
29
31
|
this._prioritySampler.sample(spanContext)
|
|
32
|
+
this._spanSampler.sample(spanContext)
|
|
30
33
|
|
|
31
34
|
for (const span of started) {
|
|
32
35
|
if (span._duration !== undefined) {
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const { globMatch } = require('../src/util')
|
|
3
|
+
const {
|
|
4
|
+
USER_KEEP,
|
|
5
|
+
AUTO_KEEP
|
|
6
|
+
} = require('../../../ext').priority
|
|
7
|
+
const RateLimiter = require('./rate_limiter')
|
|
8
|
+
|
|
9
|
+
class SpanSampler {
|
|
10
|
+
constructor ({ spanSamplingRules = [] }) {
|
|
11
|
+
this._rules = spanSamplingRules
|
|
12
|
+
this._limiters = {}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
sample (spanContext) {
|
|
16
|
+
const decision = spanContext._sampling.priority
|
|
17
|
+
if (decision === USER_KEEP || decision === AUTO_KEEP) return
|
|
18
|
+
|
|
19
|
+
const { started } = spanContext._trace
|
|
20
|
+
for (const span of started) {
|
|
21
|
+
const service = span.tracer()._service
|
|
22
|
+
const name = span._name
|
|
23
|
+
const rule = findRule(this._rules, service, name)
|
|
24
|
+
if (!rule) continue
|
|
25
|
+
|
|
26
|
+
const sampleRate = getSampleRate(rule.sampleRate)
|
|
27
|
+
const maxPerSecond = getMaxPerSecond(rule.maxPerSecond)
|
|
28
|
+
const sampled = sample(sampleRate)
|
|
29
|
+
if (!sampled) continue
|
|
30
|
+
|
|
31
|
+
const key = `${service}:${name}`
|
|
32
|
+
const limiter = getLimiter(this._limiters, key, maxPerSecond)
|
|
33
|
+
if (limiter.isAllowed()) {
|
|
34
|
+
span.context()._sampling.spanSampling = {
|
|
35
|
+
sampleRate,
|
|
36
|
+
maxPerSecond
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function findRule (rules, service, name) {
|
|
44
|
+
for (const rule of rules) {
|
|
45
|
+
const servicePattern = getService(rule.service)
|
|
46
|
+
const namePattern = getName(rule.name)
|
|
47
|
+
if (globMatch(servicePattern, service) && globMatch(namePattern, name)) {
|
|
48
|
+
return rule
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getLimiter (list, key, maxPerSecond) {
|
|
54
|
+
if (typeof list[key] === 'undefined') {
|
|
55
|
+
list[key] = new RateLimiter(maxPerSecond)
|
|
56
|
+
}
|
|
57
|
+
return list[key]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function sample (sampleRate) {
|
|
61
|
+
return Math.random() < sampleRate
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getService (service) {
|
|
65
|
+
return service || '*'
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function getName (name) {
|
|
69
|
+
return name || '*'
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getSampleRate (sampleRate) {
|
|
73
|
+
return sampleRate || 1.0
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getMaxPerSecond (maxPerSecond) {
|
|
77
|
+
return maxPerSecond || Infinity
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = SpanSampler
|
|
@@ -44,11 +44,15 @@ class DatadogTracer extends Tracer {
|
|
|
44
44
|
const result = this.scope().activate(span, () => fn(span))
|
|
45
45
|
|
|
46
46
|
if (result && typeof result.then === 'function') {
|
|
47
|
-
result.then(
|
|
48
|
-
|
|
47
|
+
return result.then(
|
|
48
|
+
value => {
|
|
49
|
+
span.finish()
|
|
50
|
+
return value
|
|
51
|
+
},
|
|
49
52
|
err => {
|
|
50
53
|
addError(span, err)
|
|
51
54
|
span.finish()
|
|
55
|
+
throw err
|
|
52
56
|
}
|
|
53
57
|
)
|
|
54
58
|
} else {
|
|
@@ -20,8 +20,50 @@ function isError (value) {
|
|
|
20
20
|
return false
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// Matches a glob pattern to a given subject string
|
|
24
|
+
function globMatch (pattern, subject) {
|
|
25
|
+
let px = 0 // [p]attern inde[x]
|
|
26
|
+
let sx = 0 // [s]ubject inde[x]
|
|
27
|
+
let nextPx = 0
|
|
28
|
+
let nextSx = 0
|
|
29
|
+
while (px < pattern.length || sx < subject.length) {
|
|
30
|
+
if (px < pattern.length) {
|
|
31
|
+
const c = pattern[px]
|
|
32
|
+
switch (c) {
|
|
33
|
+
default: // ordinary character
|
|
34
|
+
if (sx < subject.length && subject[sx] === c) {
|
|
35
|
+
px++
|
|
36
|
+
sx++
|
|
37
|
+
continue
|
|
38
|
+
}
|
|
39
|
+
break
|
|
40
|
+
case '?':
|
|
41
|
+
if (sx < subject.length) {
|
|
42
|
+
px++
|
|
43
|
+
sx++
|
|
44
|
+
continue
|
|
45
|
+
}
|
|
46
|
+
break
|
|
47
|
+
case '*':
|
|
48
|
+
nextPx = px
|
|
49
|
+
nextSx = sx + 1
|
|
50
|
+
px++
|
|
51
|
+
continue
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (nextSx > 0 && nextSx <= subject.length) {
|
|
55
|
+
px = nextPx
|
|
56
|
+
sx = nextSx
|
|
57
|
+
continue
|
|
58
|
+
}
|
|
59
|
+
return false
|
|
60
|
+
}
|
|
61
|
+
return true
|
|
62
|
+
}
|
|
63
|
+
|
|
23
64
|
module.exports = {
|
|
24
65
|
isTrue,
|
|
25
66
|
isFalse,
|
|
26
|
-
isError
|
|
67
|
+
isError,
|
|
68
|
+
globMatch
|
|
27
69
|
}
|