dd-trace 3.17.1 → 3.18.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 +1 -1
- package/esbuild.js +3 -0
- package/index.d.ts +10 -9
- package/package.json +7 -7
- package/packages/datadog-core/src/storage/async_resource.js +1 -1
- package/packages/datadog-esbuild/index.js +9 -2
- package/packages/datadog-instrumentations/src/cucumber.js +11 -1
- package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +6 -3
- package/packages/datadog-instrumentations/src/mocha.js +19 -2
- package/packages/datadog-instrumentations/src/playwright.js +2 -2
- package/packages/datadog-plugin-cucumber/src/index.js +6 -4
- package/packages/datadog-plugin-cypress/src/plugin.js +5 -1
- package/packages/datadog-plugin-cypress/src/support.js +4 -0
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +10 -5
- package/packages/datadog-plugin-mocha/src/index.js +9 -4
- package/packages/datadog-plugin-playwright/src/index.js +6 -5
- package/packages/datadog-plugin-redis/src/index.js +16 -5
- package/packages/dd-trace/src/appsec/gateway/channels.js +1 -1
- package/packages/dd-trace/src/appsec/iast/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +21 -13
- package/packages/dd-trace/src/appsec/iast/telemetry/logs.js +1 -1
- package/packages/dd-trace/src/appsec/remote_config/index.js +1 -1
- package/packages/dd-trace/src/config.js +1 -0
- package/packages/dd-trace/src/dcitm.js +2 -0
- package/packages/dd-trace/src/encode/tags-processors.js +40 -68
- package/packages/dd-trace/src/exporters/common/request.js +2 -2
- package/packages/dd-trace/src/format.js +2 -1
- package/packages/dd-trace/src/iitm.js +1 -1
- package/packages/dd-trace/src/log/channels.js +11 -12
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +2 -2
- package/packages/dd-trace/src/plugin_manager.js +1 -1
- package/packages/dd-trace/src/plugins/plugin.js +1 -1
- package/packages/dd-trace/src/plugins/util/test.js +19 -1
- package/packages/dd-trace/src/profiling/profilers/cpu.js +1 -1
- package/packages/dd-trace/src/ritm.js +1 -1
- package/packages/dd-trace/src/span_stats.js +1 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/index.js +1 -1
- package/packages/diagnostics_channel/index.js +3 -0
- package/packages/diagnostics_channel/src/index.js +57 -0
- package/packages/dd-trace/src/instrumenter.js +0 -203
- package/packages/dd-trace/src/loader.js +0 -131
|
@@ -480,6 +480,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
480
480
|
this.appsec = {
|
|
481
481
|
enabled: DD_APPSEC_ENABLED,
|
|
482
482
|
rules: DD_APPSEC_RULES ? safeJsonParse(maybeFile(DD_APPSEC_RULES)) : require('./appsec/recommended.json'),
|
|
483
|
+
customRulesProvided: !!DD_APPSEC_RULES,
|
|
483
484
|
rateLimit: DD_APPSEC_TRACE_RATE_LIMIT,
|
|
484
485
|
wafTimeout: DD_APPSEC_WAF_TIMEOUT,
|
|
485
486
|
obfuscatorKeyRegex: DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP,
|
|
@@ -9,8 +9,6 @@ const MAX_META_KEY_LENGTH = 200
|
|
|
9
9
|
const MAX_META_VALUE_LENGTH = 25000
|
|
10
10
|
// MAX_METRIC_KEY_LENGTH the maximum length of a metric name key
|
|
11
11
|
const MAX_METRIC_KEY_LENGTH = MAX_META_KEY_LENGTH
|
|
12
|
-
// MAX_METRIC_VALUE_LENGTH the maximum length of a metric name value
|
|
13
|
-
const MAX_METRIC_VALUE_LENGTH = MAX_META_VALUE_LENGTH
|
|
14
12
|
|
|
15
13
|
// From agent normalizer:
|
|
16
14
|
// https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/traceutil/normalize.go
|
|
@@ -25,80 +23,55 @@ const MAX_SERVICE_LENGTH = 100
|
|
|
25
23
|
// MAX_TYPE_LENGTH the maximum length a span type can have
|
|
26
24
|
const MAX_TYPE_LENGTH = 100
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
function truncateToLength (value, maxLength) {
|
|
32
|
-
if (!value) {
|
|
33
|
-
return value
|
|
34
|
-
}
|
|
35
|
-
if (value.length > maxLength) {
|
|
36
|
-
return `${value.slice(0, maxLength)}...`
|
|
37
|
-
}
|
|
38
|
-
return value
|
|
39
|
-
}
|
|
26
|
+
// TODO (bengl) Pretty much everything in this file should happen in
|
|
27
|
+
// `format.js`, so that we're not iterating over all the spans and modifying
|
|
28
|
+
// them yet again.
|
|
40
29
|
|
|
41
30
|
// normally the agent truncates the resource and parses it in certain scenarios (e.g. SQL Queries)
|
|
42
31
|
function truncateSpan (span, shouldTruncateResourceName = true) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
32
|
+
if (shouldTruncateResourceName && span.resource && span.resource.length > MAX_RESOURCE_NAME_LENGTH) {
|
|
33
|
+
span.resource = `${span.resource.slice(0, MAX_RESOURCE_NAME_LENGTH)}...`
|
|
34
|
+
}
|
|
35
|
+
for (let metaKey in span.meta) {
|
|
36
|
+
const val = span.meta[metaKey]
|
|
37
|
+
if (metaKey.length > MAX_META_KEY_LENGTH) {
|
|
38
|
+
delete span.meta[metaKey]
|
|
39
|
+
metaKey = `${metaKey.slice(0, MAX_META_KEY_LENGTH)}...`
|
|
40
|
+
span.metrics[metaKey] = val
|
|
41
|
+
}
|
|
42
|
+
if (val && val.length > MAX_META_VALUE_LENGTH) {
|
|
43
|
+
span.meta[metaKey] = `${val.slice(0, MAX_META_VALUE_LENGTH)}...`
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
for (let metricsKey in span.metrics) {
|
|
47
|
+
const val = span.metrics[metricsKey]
|
|
48
|
+
if (metricsKey.length > MAX_METRIC_KEY_LENGTH) {
|
|
49
|
+
delete span.metrics[metricsKey]
|
|
50
|
+
metricsKey = `${metricsKey.slice(0, MAX_METRIC_KEY_LENGTH)}...`
|
|
51
|
+
span.metrics[metricsKey] = val
|
|
57
52
|
}
|
|
58
|
-
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return span
|
|
59
56
|
}
|
|
60
57
|
|
|
61
58
|
function normalizeSpan (span) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (!value) {
|
|
66
|
-
return [key, DEFAULT_SERVICE_NAME]
|
|
67
|
-
}
|
|
68
|
-
if (value.length > MAX_SERVICE_LENGTH) {
|
|
69
|
-
return [key, value.slice(0, MAX_SERVICE_LENGTH)]
|
|
70
|
-
}
|
|
71
|
-
break
|
|
72
|
-
case 'name':
|
|
73
|
-
if (!value) {
|
|
74
|
-
return [key, DEFAULT_SPAN_NAME]
|
|
75
|
-
}
|
|
76
|
-
if (value.length > MAX_NAME_LENGTH) {
|
|
77
|
-
return [key, value.slice(0, MAX_NAME_LENGTH)]
|
|
78
|
-
}
|
|
79
|
-
break
|
|
80
|
-
case 'resource':
|
|
81
|
-
if (!value) {
|
|
82
|
-
return [key, span.name || DEFAULT_SPAN_NAME]
|
|
83
|
-
}
|
|
84
|
-
break
|
|
85
|
-
case 'type':
|
|
86
|
-
if (!value) {
|
|
87
|
-
return [key, value]
|
|
88
|
-
}
|
|
89
|
-
if (value.length > MAX_TYPE_LENGTH) {
|
|
90
|
-
return [key, value.slice(0, MAX_TYPE_LENGTH)]
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return [key, value]
|
|
94
|
-
}))
|
|
95
|
-
if (!normalizedSpan.service) {
|
|
96
|
-
normalizedSpan.service = DEFAULT_SERVICE_NAME
|
|
59
|
+
span.service = span.service || DEFAULT_SERVICE_NAME
|
|
60
|
+
if (span.service.length > MAX_SERVICE_LENGTH) {
|
|
61
|
+
span.service = span.service.slice(0, MAX_SERVICE_LENGTH)
|
|
97
62
|
}
|
|
98
|
-
|
|
99
|
-
|
|
63
|
+
span.name = span.name || DEFAULT_SPAN_NAME
|
|
64
|
+
if (span.name.length > MAX_NAME_LENGTH) {
|
|
65
|
+
span.name = span.name.slice(0, MAX_NAME_LENGTH)
|
|
100
66
|
}
|
|
101
|
-
|
|
67
|
+
if (!span.resource) {
|
|
68
|
+
span.resource = span.name
|
|
69
|
+
}
|
|
70
|
+
if (span.type && span.type.length > MAX_TYPE_LENGTH) {
|
|
71
|
+
span.type = span.type.slice(0, MAX_TYPE_LENGTH)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return span
|
|
102
75
|
}
|
|
103
76
|
|
|
104
77
|
module.exports = {
|
|
@@ -107,7 +80,6 @@ module.exports = {
|
|
|
107
80
|
MAX_META_KEY_LENGTH,
|
|
108
81
|
MAX_META_VALUE_LENGTH,
|
|
109
82
|
MAX_METRIC_KEY_LENGTH,
|
|
110
|
-
MAX_METRIC_VALUE_LENGTH,
|
|
111
83
|
MAX_NAME_LENGTH,
|
|
112
84
|
MAX_SERVICE_LENGTH,
|
|
113
85
|
MAX_TYPE_LENGTH,
|
|
@@ -54,13 +54,13 @@ function request (data, options, callback) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
if (options.url) {
|
|
57
|
-
const url = typeof options.url === 'object' ? options.url : fromUrlString(options.url)
|
|
57
|
+
const url = typeof options.url === 'object' ? urlToOptions(options.url) : fromUrlString(options.url)
|
|
58
58
|
if (url.protocol === 'unix:') {
|
|
59
59
|
options.socketPath = url.pathname
|
|
60
60
|
} else {
|
|
61
61
|
if (!options.path) options.path = url.path
|
|
62
62
|
options.protocol = url.protocol
|
|
63
|
-
options.hostname = url.hostname
|
|
63
|
+
options.hostname = url.hostname // for IPv6 this should be '::1' and not '[::1]'
|
|
64
64
|
options.port = url.port
|
|
65
65
|
}
|
|
66
66
|
}
|
|
@@ -146,7 +146,8 @@ function extractError (trace, error) {
|
|
|
146
146
|
trace.error = 1
|
|
147
147
|
|
|
148
148
|
if (isError(error)) {
|
|
149
|
-
|
|
149
|
+
// AggregateError only has a code and no message.
|
|
150
|
+
addTag(trace.meta, trace.metrics, ERROR_MESSAGE, error.message || error.code)
|
|
150
151
|
addTag(trace.meta, trace.metrics, ERROR_TYPE, error.name)
|
|
151
152
|
addTag(trace.meta, trace.metrics, ERROR_STACK, error.stack)
|
|
152
153
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const semver = require('semver')
|
|
4
4
|
const logger = require('./log')
|
|
5
5
|
const { addHook } = require('import-in-the-middle')
|
|
6
|
-
const dc = require('diagnostics_channel')
|
|
6
|
+
const dc = require('../../diagnostics_channel')
|
|
7
7
|
|
|
8
8
|
if (semver.satisfies(process.versions.node, '>=14.13.1')) {
|
|
9
9
|
const moduleLoadStartChannel = dc.channel('dd-trace:moduleLoadStart')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const { channel } = require('../../../diagnostics_channel')
|
|
4
4
|
|
|
5
5
|
const Level = {
|
|
6
6
|
Debug: 'debug',
|
|
@@ -11,19 +11,18 @@ const Level = {
|
|
|
11
11
|
|
|
12
12
|
const defaultLevel = Level.Debug
|
|
13
13
|
|
|
14
|
-
class LogChannel extends dc.Channel {
|
|
15
|
-
constructor (name, logLevel) {
|
|
16
|
-
super(`datadog:log:${name}`)
|
|
17
|
-
this.logLevel = logLevel
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
14
|
// based on: https://github.com/trentm/node-bunyan#levels
|
|
22
15
|
const logChannels = {
|
|
23
|
-
[Level.Debug]:
|
|
24
|
-
[Level.Info]:
|
|
25
|
-
[Level.Warn]:
|
|
26
|
-
[Level.Error]:
|
|
16
|
+
[Level.Debug]: createLogChannel(Level.Debug, 20),
|
|
17
|
+
[Level.Info]: createLogChannel(Level.Info, 30),
|
|
18
|
+
[Level.Warn]: createLogChannel(Level.Warn, 40),
|
|
19
|
+
[Level.Error]: createLogChannel(Level.Error, 50)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function createLogChannel (name, logLevel) {
|
|
23
|
+
const logChannel = channel(`datadog:log:${name}`)
|
|
24
|
+
logChannel.logLevel = logLevel
|
|
25
|
+
return logChannel
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
function getChannelLogLevel (level) {
|
|
@@ -343,10 +343,10 @@ class TextMapPropagator {
|
|
|
343
343
|
spanContext._trace.origin = value
|
|
344
344
|
break
|
|
345
345
|
case 't.dm': {
|
|
346
|
-
const mechanism = parseInt(value, 10)
|
|
346
|
+
const mechanism = -Math.abs(parseInt(value, 10))
|
|
347
347
|
if (Number.isInteger(mechanism)) {
|
|
348
348
|
spanContext._sampling.mechanism = mechanism
|
|
349
|
-
spanContext._trace.tags['_dd.p.dm'] = mechanism
|
|
349
|
+
spanContext._trace.tags['_dd.p.dm'] = String(mechanism)
|
|
350
350
|
}
|
|
351
351
|
break
|
|
352
352
|
}
|
|
@@ -36,6 +36,7 @@ const TEST_SKIP_REASON = 'test.skip_reason'
|
|
|
36
36
|
const TEST_IS_RUM_ACTIVE = 'test.is_rum_active'
|
|
37
37
|
const TEST_CODE_OWNERS = 'test.codeowners'
|
|
38
38
|
const TEST_SOURCE_FILE = 'test.source.file'
|
|
39
|
+
const TEST_SOURCE_START = 'test.source.start'
|
|
39
40
|
const LIBRARY_VERSION = 'library_version'
|
|
40
41
|
const TEST_COMMAND = 'test.command'
|
|
41
42
|
const TEST_MODULE = 'test.module'
|
|
@@ -77,6 +78,7 @@ module.exports = {
|
|
|
77
78
|
LIBRARY_VERSION,
|
|
78
79
|
JEST_WORKER_TRACE_PAYLOAD_CODE,
|
|
79
80
|
JEST_WORKER_COVERAGE_PAYLOAD_CODE,
|
|
81
|
+
TEST_SOURCE_START,
|
|
80
82
|
getTestEnvironmentMetadata,
|
|
81
83
|
getTestParametersString,
|
|
82
84
|
finishAllTraceSpans,
|
|
@@ -104,7 +106,8 @@ module.exports = {
|
|
|
104
106
|
getCoveredFilenamesFromCoverage,
|
|
105
107
|
resetCoverage,
|
|
106
108
|
mergeCoverage,
|
|
107
|
-
fromCoverageMapToCoverage
|
|
109
|
+
fromCoverageMapToCoverage,
|
|
110
|
+
getTestLineStart
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
// Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19
|
|
@@ -381,3 +384,18 @@ function fromCoverageMapToCoverage (coverageMap) {
|
|
|
381
384
|
return acc
|
|
382
385
|
}, {})
|
|
383
386
|
}
|
|
387
|
+
|
|
388
|
+
// Get the start line of a test by inspecting a given error's stack trace
|
|
389
|
+
function getTestLineStart (err, testSuitePath) {
|
|
390
|
+
if (!err.stack) {
|
|
391
|
+
return null
|
|
392
|
+
}
|
|
393
|
+
// From https://github.com/felixge/node-stack-trace/blob/ba06dcdb50d465cd440d84a563836e293b360427/index.js#L40
|
|
394
|
+
const testFileLine = err.stack.split('\n').find(line => line.includes(testSuitePath))
|
|
395
|
+
try {
|
|
396
|
+
const testFileLineMatch = testFileLine.match(/at (?:(.+?)\s+\()?(?:(.+?):(\d+)(?::(\d+))?|([^)]+))\)?/)
|
|
397
|
+
return parseInt(testFileLineMatch[3], 10) || null
|
|
398
|
+
} catch (e) {
|
|
399
|
+
return null
|
|
400
|
+
}
|
|
401
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { storage } = require('../../../../datadog-core')
|
|
4
4
|
|
|
5
|
-
const dc = require('diagnostics_channel')
|
|
5
|
+
const dc = require('../../../../diagnostics_channel')
|
|
6
6
|
|
|
7
7
|
const beforeCh = dc.channel('dd-trace:storage:before')
|
|
8
8
|
const afterCh = dc.channel('dd-trace:storage:after')
|
|
@@ -4,7 +4,7 @@ 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('../../../diagnostics_channel')
|
|
8
8
|
const { fileURLToPath } = require('url')
|
|
9
9
|
|
|
10
10
|
const savedDependencies = new Set()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const tracerVersion = require('../../../../package.json').version
|
|
4
|
-
const dc = require('diagnostics_channel')
|
|
4
|
+
const dc = require('../../../diagnostics_channel')
|
|
5
5
|
const os = require('os')
|
|
6
6
|
const dependencies = require('./dependencies')
|
|
7
7
|
const { sendData } = require('./send-data')
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
Channel,
|
|
5
|
+
channel
|
|
6
|
+
} = require('diagnostics_channel') // eslint-disable-line n/no-restricted-require
|
|
7
|
+
|
|
8
|
+
const [major, minor] = process.versions.node.split('.')
|
|
9
|
+
const channels = new WeakSet()
|
|
10
|
+
|
|
11
|
+
// Our own DC with a limited subset of functionality stable across Node versions.
|
|
12
|
+
// TODO: Move the rest of the polyfill here.
|
|
13
|
+
// TODO: Switch to using global subscribe/unsubscribe/hasSubscribers.
|
|
14
|
+
const dc = { channel }
|
|
15
|
+
|
|
16
|
+
// Prevent going to 0 subscribers to avoid bug in Node.
|
|
17
|
+
// See https://github.com/nodejs/node/pull/47520
|
|
18
|
+
if (major === '19' && minor === '9') {
|
|
19
|
+
dc.channel = function () {
|
|
20
|
+
const ch = channel.apply(this, arguments)
|
|
21
|
+
|
|
22
|
+
if (!channels.has(ch)) {
|
|
23
|
+
const subscribe = ch.subscribe
|
|
24
|
+
const unsubscribe = ch.unsubscribe
|
|
25
|
+
|
|
26
|
+
ch.subscribe = function () {
|
|
27
|
+
delete ch.subscribe
|
|
28
|
+
delete ch.unsubscribe
|
|
29
|
+
|
|
30
|
+
const result = subscribe.apply(this, arguments)
|
|
31
|
+
|
|
32
|
+
this.subscribe(() => {}) // Keep it active forever.
|
|
33
|
+
|
|
34
|
+
return result
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (ch.unsubscribe === Channel.prototype.unsubscribe) {
|
|
38
|
+
// Needed because another subscriber could have subscribed to something
|
|
39
|
+
// that we unsubscribe to before the library is loaded.
|
|
40
|
+
ch.unsubscribe = function () {
|
|
41
|
+
delete ch.subscribe
|
|
42
|
+
delete ch.unsubscribe
|
|
43
|
+
|
|
44
|
+
this.subscribe(() => {}) // Keep it active forever.
|
|
45
|
+
|
|
46
|
+
return unsubscribe.apply(this, arguments)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
channels.add(ch)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return ch
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = dc
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const shimmer = require('../../datadog-shimmer')
|
|
4
|
-
const log = require('./log')
|
|
5
|
-
const metrics = require('./metrics')
|
|
6
|
-
const Loader = require('./loader')
|
|
7
|
-
const { isTrue } = require('./util')
|
|
8
|
-
const plugins = require('./plugins')
|
|
9
|
-
const Plugin = require('./plugins/plugin')
|
|
10
|
-
const telemetry = require('./telemetry')
|
|
11
|
-
|
|
12
|
-
const disabledPlugins = process.env.DD_TRACE_DISABLED_PLUGINS
|
|
13
|
-
|
|
14
|
-
const collectDisabledPlugins = () => {
|
|
15
|
-
return new Set(disabledPlugins && disabledPlugins.split(',').map(plugin => plugin.trim()))
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function cleanEnv (name) {
|
|
19
|
-
return process.env[`DD_TRACE_${name.toUpperCase()}`.replace(/[^a-z0-9_]/ig, '_')]
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function getConfig (name, config = {}) {
|
|
23
|
-
if (!name) {
|
|
24
|
-
return config
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const enabled = cleanEnv(`${name}_ENABLED`)
|
|
28
|
-
if (enabled !== undefined) {
|
|
29
|
-
config.enabled = isTrue(enabled)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return config
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
class Instrumenter {
|
|
36
|
-
constructor (tracer) {
|
|
37
|
-
this._tracer = tracer
|
|
38
|
-
this._loader = new Loader(this)
|
|
39
|
-
this._enabled = false
|
|
40
|
-
this._names = new Set()
|
|
41
|
-
this._plugins = new Map()
|
|
42
|
-
this._instrumented = new Map()
|
|
43
|
-
this._disabledPlugins = collectDisabledPlugins()
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
use (name, config) {
|
|
47
|
-
if (typeof name !== 'string') return
|
|
48
|
-
const plugin = plugins[name.toLowerCase()]
|
|
49
|
-
if (plugin && plugin.prototype instanceof Plugin) return
|
|
50
|
-
if (typeof config === 'boolean') {
|
|
51
|
-
config = { enabled: config }
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
config = getConfig(name, config)
|
|
55
|
-
|
|
56
|
-
try {
|
|
57
|
-
this._set(plugin, { name, config })
|
|
58
|
-
telemetry.updateIntegrations()
|
|
59
|
-
} catch (e) {
|
|
60
|
-
log.debug(`Could not find a plugin named "${name}".`)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (this._enabled) {
|
|
64
|
-
this._loader.reload(this._plugins)
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
enable (config) {
|
|
69
|
-
config = config || {}
|
|
70
|
-
const serviceMapping = config.serviceMapping
|
|
71
|
-
|
|
72
|
-
this._enabled = true
|
|
73
|
-
|
|
74
|
-
if (config.plugins !== false) {
|
|
75
|
-
Object.keys(plugins)
|
|
76
|
-
.filter(name => !this._plugins.has(plugins[name]))
|
|
77
|
-
.forEach(name => {
|
|
78
|
-
if (plugins[name].prototype instanceof Plugin) return
|
|
79
|
-
const pluginConfig = {}
|
|
80
|
-
if (serviceMapping && serviceMapping[name]) {
|
|
81
|
-
pluginConfig.service = serviceMapping[name]
|
|
82
|
-
}
|
|
83
|
-
this._set(plugins[name], { name, config: getConfig(name, pluginConfig) })
|
|
84
|
-
})
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
this._loader.reload(this._plugins)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
disable () {
|
|
91
|
-
for (const instrumentation of this._instrumented.keys()) {
|
|
92
|
-
this.unpatch(instrumentation)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
this._plugins.clear()
|
|
96
|
-
this._enabled = false
|
|
97
|
-
this._loader.reload(this._plugins)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
wrap (nodules, names, wrapper) {
|
|
101
|
-
shimmer.massWrap(nodules, names, wrapper)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
unwrap (nodules, names, wrapper) {
|
|
105
|
-
shimmer.massUnwrap(nodules, names, wrapper)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
wrapExport (moduleExports, wrapper) {
|
|
109
|
-
return shimmer.wrap(moduleExports, wrapper)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
unwrapExport (moduleExports) {
|
|
113
|
-
return shimmer.unwrap(moduleExports)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
load (plugin, meta) {
|
|
117
|
-
if (!this._enabled) return
|
|
118
|
-
|
|
119
|
-
const instrumentations = [].concat(plugin)
|
|
120
|
-
const enabled = meta.config.enabled !== false
|
|
121
|
-
|
|
122
|
-
metrics.boolean(`datadog.tracer.node.plugin.enabled.by.name`, enabled, `name:${meta.name}`)
|
|
123
|
-
|
|
124
|
-
try {
|
|
125
|
-
instrumentations
|
|
126
|
-
.forEach(instrumentation => {
|
|
127
|
-
this._loader.load(instrumentation, meta.config)
|
|
128
|
-
})
|
|
129
|
-
} catch (e) {
|
|
130
|
-
log.error(e)
|
|
131
|
-
this.unload(plugin)
|
|
132
|
-
log.debug(`Error while trying to patch ${meta.name}. The plugin has been disabled.`)
|
|
133
|
-
|
|
134
|
-
metrics.increment(`datadog.tracer.node.plugin.errors`, true)
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
unload (plugin) {
|
|
139
|
-
[].concat(plugin)
|
|
140
|
-
.forEach(instrumentation => {
|
|
141
|
-
this.unpatch(instrumentation)
|
|
142
|
-
this._instrumented.delete(instrumentation)
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
const meta = this._plugins.get(plugin)
|
|
146
|
-
|
|
147
|
-
if (meta) {
|
|
148
|
-
this._plugins.delete(plugin)
|
|
149
|
-
|
|
150
|
-
metrics.boolean(`datadog.tracer.node.plugin.enabled.by.name`, false, `name:${meta.name}`)
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
patch (instrumentation, moduleExports, config) {
|
|
155
|
-
let instrumented = this._instrumented.get(instrumentation)
|
|
156
|
-
|
|
157
|
-
if (!instrumented) {
|
|
158
|
-
this._instrumented.set(instrumentation, instrumented = new Set())
|
|
159
|
-
telemetry.updateIntegrations()
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (!instrumented.has(this._defaultExport(moduleExports))) {
|
|
163
|
-
try {
|
|
164
|
-
moduleExports = instrumentation.patch.call(this, moduleExports, this._tracer._tracer, config) || moduleExports
|
|
165
|
-
return moduleExports
|
|
166
|
-
} finally {
|
|
167
|
-
// add even on error since `unpatch` will take care of removing it.
|
|
168
|
-
instrumented.add(this._defaultExport(moduleExports))
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
unpatch (instrumentation) {
|
|
174
|
-
const instrumented = this._instrumented.get(instrumentation)
|
|
175
|
-
|
|
176
|
-
if (instrumented) {
|
|
177
|
-
instrumented.forEach(moduleExports => {
|
|
178
|
-
try {
|
|
179
|
-
instrumentation.unpatch.call(this, moduleExports, this._tracer)
|
|
180
|
-
} catch (e) {
|
|
181
|
-
log.error(e)
|
|
182
|
-
}
|
|
183
|
-
})
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
_set (plugin, meta) {
|
|
188
|
-
if (this._disabledPlugins.has(meta.name)) {
|
|
189
|
-
log.debug(`Plugin "${meta.name}" was disabled via configuration option.`)
|
|
190
|
-
} else {
|
|
191
|
-
this._plugins.set(plugin, meta)
|
|
192
|
-
this.load(plugin, meta)
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// ESM modules have a different export between `import` and `require` so we
|
|
197
|
-
// use the default export instead when available.
|
|
198
|
-
_defaultExport (moduleExports) {
|
|
199
|
-
return moduleExports && (moduleExports.default || moduleExports)
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
module.exports = Instrumenter
|