dd-trace 4.15.0 → 4.17.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 +2 -0
- package/ext/tags.d.ts +1 -0
- package/ext/tags.js +1 -0
- package/index.d.ts +1 -0
- package/package.json +9 -6
- package/packages/datadog-esbuild/index.js +30 -25
- package/packages/datadog-instrumentations/src/body-parser.js +4 -3
- package/packages/datadog-instrumentations/src/cookie-parser.js +37 -0
- package/packages/datadog-instrumentations/src/cucumber.js +24 -4
- package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +45 -0
- package/packages/datadog-instrumentations/src/express.js +3 -2
- package/packages/datadog-instrumentations/src/graphql.js +5 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +5 -1
- package/packages/datadog-instrumentations/src/http/server.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +20 -11
- package/packages/datadog-instrumentations/src/knex.js +62 -1
- package/packages/datadog-instrumentations/src/mocha.js +19 -4
- package/packages/datadog-instrumentations/src/mongodb.js +63 -0
- package/packages/datadog-instrumentations/src/mongoose.js +140 -1
- package/packages/datadog-instrumentations/src/next.js +62 -80
- package/packages/datadog-instrumentations/src/pg.js +14 -15
- package/packages/datadog-instrumentations/src/playwright.js +26 -5
- package/packages/datadog-plugin-cucumber/src/index.js +17 -5
- package/packages/datadog-plugin-cypress/src/plugin.js +38 -8
- package/packages/datadog-plugin-jest/src/index.js +19 -4
- package/packages/datadog-plugin-jest/src/util.js +45 -2
- package/packages/datadog-plugin-memcached/src/index.js +10 -5
- package/packages/datadog-plugin-mocha/src/index.js +19 -6
- package/packages/datadog-plugin-mysql/src/index.js +2 -2
- package/packages/datadog-plugin-next/src/index.js +14 -5
- package/packages/datadog-plugin-pg/src/index.js +2 -2
- package/packages/dd-trace/src/appsec/channels.js +4 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +166 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +21 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +3 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +1 -2
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +4 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +25 -12
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +4 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/secure-marks-generator.js +13 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +2 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +16 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +3 -4
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +9 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +13 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +169 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
- package/packages/dd-trace/src/appsec/index.js +45 -14
- package/packages/dd-trace/src/appsec/recommended.json +549 -24
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
- package/packages/dd-trace/src/appsec/remote_config/index.js +2 -0
- package/packages/dd-trace/src/appsec/remote_config/manager.js +11 -3
- package/packages/dd-trace/src/appsec/reporter.js +7 -5
- package/packages/dd-trace/src/appsec/telemetry.js +2 -2
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +18 -5
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +5 -4
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +1 -14
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +1 -13
- package/packages/dd-trace/src/config.js +8 -0
- package/packages/dd-trace/src/datastreams/processor.js +6 -2
- package/packages/dd-trace/src/format.js +9 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +2 -2
- package/packages/dd-trace/src/opentracing/tracer.js +0 -2
- package/packages/dd-trace/src/plugin_manager.js +4 -3
- package/packages/dd-trace/src/plugins/database.js +14 -4
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/outbound.js +4 -3
- package/packages/dd-trace/src/plugins/util/ci.js +17 -0
- package/packages/dd-trace/src/plugins/util/git.js +26 -4
- package/packages/dd-trace/src/plugins/util/test.js +16 -1
- package/packages/dd-trace/src/profiling/config.js +36 -5
- package/packages/dd-trace/src/profiling/profilers/wall.js +7 -1
- package/packages/dd-trace/src/service-naming/extra-services.js +24 -0
- package/packages/dd-trace/src/telemetry/index.js +10 -1
- package/packages/dd-trace/src/telemetry/metrics.js +0 -5
|
@@ -46,6 +46,7 @@ function enableWafUpdate (appsecConfig) {
|
|
|
46
46
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_REQUEST_BLOCKING, true)
|
|
47
47
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_RULES, true)
|
|
48
48
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_BLOCKING_RESPONSE, true)
|
|
49
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_TRUSTED_IPS, true)
|
|
49
50
|
|
|
50
51
|
rc.on('ASM_DATA', noop)
|
|
51
52
|
rc.on('ASM_DD', noop)
|
|
@@ -66,6 +67,7 @@ function disableWafUpdate () {
|
|
|
66
67
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_REQUEST_BLOCKING, false)
|
|
67
68
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_RULES, false)
|
|
68
69
|
rc.updateCapabilities(RemoteConfigCapabilities.ASM_CUSTOM_BLOCKING_RESPONSE, false)
|
|
70
|
+
rc.updateCapabilities(RemoteConfigCapabilities.ASM_TRUSTED_IPS, false)
|
|
69
71
|
|
|
70
72
|
rc.off('ASM_DATA', noop)
|
|
71
73
|
rc.off('ASM_DD', noop)
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
const { URL, format } = require('url')
|
|
4
4
|
const uuid = require('crypto-randomuuid')
|
|
5
5
|
const { EventEmitter } = require('events')
|
|
6
|
-
const Scheduler = require('./scheduler')
|
|
7
6
|
const tracerVersion = require('../../../../../package.json').version
|
|
8
7
|
const request = require('../../exporters/common/request')
|
|
9
8
|
const log = require('../../log')
|
|
9
|
+
const { getExtraServices } = require('../../service-naming/extra-services')
|
|
10
10
|
const { UNACKNOWLEDGED, ACKNOWLEDGED, ERROR } = require('./apply_states')
|
|
11
|
+
const Scheduler = require('./scheduler')
|
|
11
12
|
|
|
12
13
|
const clientId = uuid()
|
|
13
14
|
|
|
@@ -57,7 +58,8 @@ class RemoteConfigManager extends EventEmitter {
|
|
|
57
58
|
tracer_version: tracerVersion,
|
|
58
59
|
service: config.service,
|
|
59
60
|
env: config.env,
|
|
60
|
-
app_version: config.version
|
|
61
|
+
app_version: config.version,
|
|
62
|
+
extra_services: []
|
|
61
63
|
},
|
|
62
64
|
capabilities: DEFAULT_CAPABILITY // updated by `updateCapabilities()`
|
|
63
65
|
},
|
|
@@ -113,8 +115,14 @@ class RemoteConfigManager extends EventEmitter {
|
|
|
113
115
|
this.state.client.products = this.eventNames().filter(e => typeof e === 'string')
|
|
114
116
|
}
|
|
115
117
|
|
|
118
|
+
getPayload () {
|
|
119
|
+
this.state.client.client_tracer.extra_services = getExtraServices()
|
|
120
|
+
|
|
121
|
+
return JSON.stringify(this.state)
|
|
122
|
+
}
|
|
123
|
+
|
|
116
124
|
poll (cb) {
|
|
117
|
-
request(
|
|
125
|
+
request(this.getPayload(), this.requestOptions, (err, data, statusCode) => {
|
|
118
126
|
// 404 means RC is disabled, ignore it
|
|
119
127
|
if (statusCode === 404) return cb()
|
|
120
128
|
|
|
@@ -69,16 +69,18 @@ function formatHeaderName (name) {
|
|
|
69
69
|
.toLowerCase()
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
function reportWafInit (wafVersion,
|
|
72
|
+
function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}) {
|
|
73
73
|
metricsQueue.set('_dd.appsec.waf.version', wafVersion)
|
|
74
74
|
|
|
75
|
-
metricsQueue.set('_dd.appsec.event_rules.loaded',
|
|
76
|
-
metricsQueue.set('_dd.appsec.event_rules.error_count',
|
|
77
|
-
if (
|
|
75
|
+
metricsQueue.set('_dd.appsec.event_rules.loaded', diagnosticsRules.loaded?.length || 0)
|
|
76
|
+
metricsQueue.set('_dd.appsec.event_rules.error_count', diagnosticsRules.failed?.length || 0)
|
|
77
|
+
if (diagnosticsRules.failed?.length) {
|
|
78
|
+
metricsQueue.set('_dd.appsec.event_rules.errors', JSON.stringify(diagnosticsRules.errors))
|
|
79
|
+
}
|
|
78
80
|
|
|
79
81
|
metricsQueue.set('manual.keep', 'true')
|
|
80
82
|
|
|
81
|
-
incrementWafInitMetric(wafVersion,
|
|
83
|
+
incrementWafInitMetric(wafVersion, rulesVersion)
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
function reportMetrics (metrics) {
|
|
@@ -51,7 +51,7 @@ function trackWafDurations (metrics, versionsTags) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
function getOrCreateMetricTags (
|
|
54
|
+
function getOrCreateMetricTags (req, versionsTags) {
|
|
55
55
|
const store = getStore(req)
|
|
56
56
|
|
|
57
57
|
let metricTags = store[DD_TELEMETRY_WAF_RESULT_TAGS]
|
|
@@ -75,7 +75,7 @@ function updateWafRequestsMetricTags (metrics, req) {
|
|
|
75
75
|
|
|
76
76
|
trackWafDurations(metrics, versionsTags)
|
|
77
77
|
|
|
78
|
-
const metricTags = getOrCreateMetricTags(
|
|
78
|
+
const metricTags = getOrCreateMetricTags(req, versionsTags)
|
|
79
79
|
|
|
80
80
|
const { blockTriggered, ruleTriggered, wafTimeout } = metrics
|
|
81
81
|
|
|
@@ -2,24 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
const log = require('../../log')
|
|
4
4
|
const Reporter = require('../reporter')
|
|
5
|
+
const addresses = require('../addresses')
|
|
6
|
+
|
|
7
|
+
// TODO: remove once ephemeral addresses are implemented
|
|
8
|
+
const preventDuplicateAddresses = new Set([
|
|
9
|
+
addresses.HTTP_INCOMING_QUERY
|
|
10
|
+
])
|
|
5
11
|
|
|
6
12
|
class WAFContextWrapper {
|
|
7
|
-
constructor (ddwafContext, requiredAddresses, wafTimeout,
|
|
13
|
+
constructor (ddwafContext, requiredAddresses, wafTimeout, wafVersion, rulesVersion) {
|
|
8
14
|
this.ddwafContext = ddwafContext
|
|
9
15
|
this.requiredAddresses = requiredAddresses
|
|
10
16
|
this.wafTimeout = wafTimeout
|
|
11
|
-
this.rulesVersion = rulesInfo.version
|
|
12
17
|
this.wafVersion = wafVersion
|
|
18
|
+
this.rulesVersion = rulesVersion
|
|
19
|
+
this.addressesToSkip = new Set()
|
|
13
20
|
}
|
|
14
21
|
|
|
15
22
|
run (params) {
|
|
16
23
|
const inputs = {}
|
|
17
24
|
let someInputAdded = false
|
|
25
|
+
const newAddressesToSkip = new Set(this.addressesToSkip)
|
|
18
26
|
|
|
19
27
|
// TODO: possible optimizaion: only send params that haven't already been sent with same value to this wafContext
|
|
20
28
|
for (const key of Object.keys(params)) {
|
|
21
|
-
if (this.requiredAddresses.has(key)) {
|
|
29
|
+
if (this.requiredAddresses.has(key) && !this.addressesToSkip.has(key)) {
|
|
22
30
|
inputs[key] = params[key]
|
|
31
|
+
if (preventDuplicateAddresses.has(key)) {
|
|
32
|
+
newAddressesToSkip.add(key)
|
|
33
|
+
}
|
|
23
34
|
someInputAdded = true
|
|
24
35
|
}
|
|
25
36
|
}
|
|
@@ -33,7 +44,9 @@ class WAFContextWrapper {
|
|
|
33
44
|
|
|
34
45
|
const end = process.hrtime.bigint()
|
|
35
46
|
|
|
36
|
-
|
|
47
|
+
this.addressesToSkip = newAddressesToSkip
|
|
48
|
+
|
|
49
|
+
const ruleTriggered = !!result.events?.length
|
|
37
50
|
const blockTriggered = result.actions?.includes('block')
|
|
38
51
|
|
|
39
52
|
Reporter.reportMetrics({
|
|
@@ -47,7 +60,7 @@ class WAFContextWrapper {
|
|
|
47
60
|
})
|
|
48
61
|
|
|
49
62
|
if (ruleTriggered) {
|
|
50
|
-
Reporter.reportAttack(result.
|
|
63
|
+
Reporter.reportAttack(JSON.stringify(result.events))
|
|
51
64
|
}
|
|
52
65
|
|
|
53
66
|
return result.actions
|
|
@@ -12,8 +12,9 @@ class WAFManager {
|
|
|
12
12
|
this.wafTimeout = config.wafTimeout
|
|
13
13
|
this.ddwaf = this._loadDDWAF(rules)
|
|
14
14
|
this.ddwafVersion = this.ddwaf.constructor.version()
|
|
15
|
+
this.rulesVersion = this.ddwaf.diagnostics.ruleset_version
|
|
15
16
|
|
|
16
|
-
Reporter.reportWafInit(this.ddwafVersion, this.ddwaf.
|
|
17
|
+
Reporter.reportWafInit(this.ddwafVersion, this.rulesVersion, this.ddwaf.diagnostics.rules)
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
_loadDDWAF (rules) {
|
|
@@ -38,8 +39,8 @@ class WAFManager {
|
|
|
38
39
|
this.ddwaf.createContext(),
|
|
39
40
|
this.ddwaf.requiredAddresses,
|
|
40
41
|
this.wafTimeout,
|
|
41
|
-
this.
|
|
42
|
-
this.
|
|
42
|
+
this.ddwafVersion,
|
|
43
|
+
this.rulesVersion
|
|
43
44
|
)
|
|
44
45
|
contexts.set(req, wafContext)
|
|
45
46
|
}
|
|
@@ -50,7 +51,7 @@ class WAFManager {
|
|
|
50
51
|
update (newRules) {
|
|
51
52
|
this.ddwaf.update(newRules)
|
|
52
53
|
|
|
53
|
-
Reporter.reportWafUpdate(this.ddwafVersion, this.
|
|
54
|
+
Reporter.reportWafUpdate(this.ddwafVersion, this.rulesVersion)
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
destroy () {
|
package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js
CHANGED
|
@@ -28,25 +28,12 @@ function getItrConfiguration ({
|
|
|
28
28
|
if (isEvpProxy) {
|
|
29
29
|
options.path = '/evp_proxy/v2/api/v2/libraries/tests/services/setting'
|
|
30
30
|
options.headers['X-Datadog-EVP-Subdomain'] = 'api'
|
|
31
|
-
options.headers['X-Datadog-NeedsAppKey'] = 'true'
|
|
32
31
|
} else {
|
|
33
32
|
const apiKey = process.env.DATADOG_API_KEY || process.env.DD_API_KEY
|
|
34
|
-
const appKey = process.env.DATADOG_APP_KEY ||
|
|
35
|
-
process.env.DD_APP_KEY ||
|
|
36
|
-
process.env.DATADOG_APPLICATION_KEY ||
|
|
37
|
-
process.env.DD_APPLICATION_KEY
|
|
38
|
-
|
|
39
|
-
const messagePrefix = 'Request to settings endpoint was not done because Datadog'
|
|
40
|
-
|
|
41
|
-
if (!appKey) {
|
|
42
|
-
return done(new Error(`${messagePrefix} application key is not defined.`))
|
|
43
|
-
}
|
|
44
33
|
if (!apiKey) {
|
|
45
|
-
return done(new Error(
|
|
34
|
+
return done(new Error('Request to settings endpoint was not done because Datadog API key is not defined.'))
|
|
46
35
|
}
|
|
47
|
-
|
|
48
36
|
options.headers['dd-api-key'] = apiKey
|
|
49
|
-
options.headers['dd-application-key'] = appKey
|
|
50
37
|
}
|
|
51
38
|
|
|
52
39
|
const data = JSON.stringify({
|
|
@@ -28,25 +28,13 @@ function getSkippableSuites ({
|
|
|
28
28
|
if (isEvpProxy) {
|
|
29
29
|
options.path = '/evp_proxy/v2/api/v2/ci/tests/skippable'
|
|
30
30
|
options.headers['X-Datadog-EVP-Subdomain'] = 'api'
|
|
31
|
-
options.headers['X-Datadog-NeedsAppKey'] = 'true'
|
|
32
31
|
} else {
|
|
33
32
|
const apiKey = process.env.DATADOG_API_KEY || process.env.DD_API_KEY
|
|
34
|
-
const appKey = process.env.DATADOG_APP_KEY ||
|
|
35
|
-
process.env.DD_APP_KEY ||
|
|
36
|
-
process.env.DATADOG_APPLICATION_KEY ||
|
|
37
|
-
process.env.DD_APPLICATION_KEY
|
|
38
|
-
|
|
39
|
-
const messagePrefix = 'Skippable suites were not fetched because Datadog'
|
|
40
|
-
|
|
41
|
-
if (!appKey) {
|
|
42
|
-
return done(new Error(`${messagePrefix} application key is not defined.`))
|
|
43
|
-
}
|
|
44
33
|
if (!apiKey) {
|
|
45
|
-
return done(new Error(
|
|
34
|
+
return done(new Error('Skippable suites were not fetched because Datadog API key is not defined.'))
|
|
46
35
|
}
|
|
47
36
|
|
|
48
37
|
options.headers['dd-api-key'] = apiKey
|
|
49
|
-
options.headers['dd-application-key'] = appKey
|
|
50
38
|
}
|
|
51
39
|
|
|
52
40
|
const data = JSON.stringify({
|
|
@@ -179,6 +179,11 @@ class Config {
|
|
|
179
179
|
false
|
|
180
180
|
)
|
|
181
181
|
|
|
182
|
+
const DD_TRACE_MEMCACHED_COMMAND_ENABLED = coalesce(
|
|
183
|
+
process.env.DD_TRACE_MEMCACHED_COMMAND_ENABLED,
|
|
184
|
+
false
|
|
185
|
+
)
|
|
186
|
+
|
|
182
187
|
const DD_SERVICE = options.service ||
|
|
183
188
|
process.env.DD_SERVICE ||
|
|
184
189
|
process.env.DD_SERVICE_NAME ||
|
|
@@ -629,6 +634,9 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
|
|
|
629
634
|
|
|
630
635
|
this.openaiSpanCharLimit = DD_OPENAI_SPAN_CHAR_LIMIT
|
|
631
636
|
|
|
637
|
+
// Requires an accompanying DD_APM_OBFUSCATION_MEMCACHED_KEEP_COMMAND=true in the agent
|
|
638
|
+
this.memcachedCommandEnabled = isTrue(DD_TRACE_MEMCACHED_COMMAND_ENABLED)
|
|
639
|
+
|
|
632
640
|
if (this.gitMetadataEnabled) {
|
|
633
641
|
this.repositoryUrl = coalesce(
|
|
634
642
|
process.env.DD_GIT_REPOSITORY_URL,
|
|
@@ -66,7 +66,9 @@ class DataStreamsProcessor {
|
|
|
66
66
|
port,
|
|
67
67
|
url,
|
|
68
68
|
env,
|
|
69
|
-
tags
|
|
69
|
+
tags,
|
|
70
|
+
version,
|
|
71
|
+
service
|
|
70
72
|
} = {}) {
|
|
71
73
|
this.writer = new DataStreamsWriter({
|
|
72
74
|
hostname,
|
|
@@ -79,7 +81,8 @@ class DataStreamsProcessor {
|
|
|
79
81
|
this.enabled = dsmEnabled
|
|
80
82
|
this.env = env
|
|
81
83
|
this.tags = tags || {}
|
|
82
|
-
this.service =
|
|
84
|
+
this.service = service || 'unnamed-nodejs-service'
|
|
85
|
+
this.version = version || ''
|
|
83
86
|
this.sequence = 0
|
|
84
87
|
|
|
85
88
|
if (this.enabled) {
|
|
@@ -96,6 +99,7 @@ class DataStreamsProcessor {
|
|
|
96
99
|
Service: this.service,
|
|
97
100
|
Stats: serialized,
|
|
98
101
|
TracerVersion: pkg.version,
|
|
102
|
+
Version: this.version,
|
|
99
103
|
Lang: 'javascript'
|
|
100
104
|
}
|
|
101
105
|
this.writer.flush(payload)
|
|
@@ -4,6 +4,7 @@ const constants = require('./constants')
|
|
|
4
4
|
const tags = require('../../../ext/tags')
|
|
5
5
|
const id = require('./id')
|
|
6
6
|
const { isError } = require('./util')
|
|
7
|
+
const { registerExtraService } = require('./service-naming/extra-services')
|
|
7
8
|
|
|
8
9
|
const SAMPLING_PRIORITY_KEY = constants.SAMPLING_PRIORITY_KEY
|
|
9
10
|
const SAMPLING_RULE_DECISION = constants.SAMPLING_RULE_DECISION
|
|
@@ -13,7 +14,7 @@ const SPAN_SAMPLING_MECHANISM = constants.SPAN_SAMPLING_MECHANISM
|
|
|
13
14
|
const SPAN_SAMPLING_RULE_RATE = constants.SPAN_SAMPLING_RULE_RATE
|
|
14
15
|
const SPAN_SAMPLING_MAX_PER_SECOND = constants.SPAN_SAMPLING_MAX_PER_SECOND
|
|
15
16
|
const SAMPLING_MECHANISM_SPAN = constants.SAMPLING_MECHANISM_SPAN
|
|
16
|
-
const MEASURED = tags
|
|
17
|
+
const { MEASURED, BASE_SERVICE } = tags
|
|
17
18
|
const ORIGIN_KEY = constants.ORIGIN_KEY
|
|
18
19
|
const HOSTNAME_KEY = constants.HOSTNAME_KEY
|
|
19
20
|
const TOP_LEVEL_KEY = constants.TOP_LEVEL_KEY
|
|
@@ -73,6 +74,13 @@ function extractTags (trace, span) {
|
|
|
73
74
|
addTag({}, trace.metrics, MEASURED, 1)
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
const tracerService = span.tracer()._service.toLowerCase()
|
|
78
|
+
if (tags['service.name']?.toLowerCase() !== tracerService) {
|
|
79
|
+
span.setTag(BASE_SERVICE, tracerService)
|
|
80
|
+
|
|
81
|
+
registerExtraService(tags['service.name'])
|
|
82
|
+
}
|
|
83
|
+
|
|
76
84
|
for (const tag in tags) {
|
|
77
85
|
switch (tag) {
|
|
78
86
|
case 'service.name':
|
|
@@ -343,10 +343,10 @@ class TextMapPropagator {
|
|
|
343
343
|
spanContext._trace.origin = value
|
|
344
344
|
break
|
|
345
345
|
case 't.dm': {
|
|
346
|
-
const mechanism =
|
|
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'] =
|
|
349
|
+
spanContext._trace.tags['_dd.p.dm'] = `-${mechanism}`
|
|
350
350
|
}
|
|
351
351
|
break
|
|
352
352
|
}
|
|
@@ -26,8 +26,6 @@ class DatadogTracer {
|
|
|
26
26
|
this._version = config.version
|
|
27
27
|
this._env = config.env
|
|
28
28
|
this._tags = config.tags
|
|
29
|
-
this._computePeerService = config.spanComputePeerService
|
|
30
|
-
this._peerServiceMapping = config.peerServiceMapping
|
|
31
29
|
this._logInjection = config.logInjection
|
|
32
30
|
this._debug = config.debug
|
|
33
31
|
this._prioritySampler = new PrioritySampler(config.env, config.sampler)
|
|
@@ -72,11 +72,10 @@ module.exports = class PluginManager {
|
|
|
72
72
|
const Plugin = pluginClasses[name]
|
|
73
73
|
|
|
74
74
|
if (!Plugin) return
|
|
75
|
+
if (!this._tracerConfig) return // TODO: don't wait for tracer to be initialized
|
|
75
76
|
if (!this._pluginsByName[name]) {
|
|
76
77
|
this._pluginsByName[name] = new Plugin(this._tracer, this._tracerConfig)
|
|
77
78
|
}
|
|
78
|
-
if (!this._tracerConfig) return // TODO: don't wait for tracer to be initialized
|
|
79
|
-
|
|
80
79
|
const pluginConfig = this._configsByName[name] || {
|
|
81
80
|
enabled: this._tracerConfig.plugins !== false
|
|
82
81
|
}
|
|
@@ -137,7 +136,8 @@ module.exports = class PluginManager {
|
|
|
137
136
|
headerTags,
|
|
138
137
|
dbmPropagationMode,
|
|
139
138
|
dsmEnabled,
|
|
140
|
-
clientIpEnabled
|
|
139
|
+
clientIpEnabled,
|
|
140
|
+
memcachedCommandEnabled
|
|
141
141
|
} = this._tracerConfig
|
|
142
142
|
|
|
143
143
|
const sharedConfig = {}
|
|
@@ -152,6 +152,7 @@ module.exports = class PluginManager {
|
|
|
152
152
|
|
|
153
153
|
sharedConfig.dbmPropagationMode = dbmPropagationMode
|
|
154
154
|
sharedConfig.dsmEnabled = dsmEnabled
|
|
155
|
+
sharedConfig.memcachedCommandEnabled = memcachedCommandEnabled
|
|
155
156
|
|
|
156
157
|
if (serviceMapping && serviceMapping[name]) {
|
|
157
158
|
sharedConfig.service = serviceMapping[name]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const StoragePlugin = require('./storage')
|
|
4
|
+
const { PEER_SERVICE_KEY } = require('../constants')
|
|
4
5
|
|
|
5
6
|
class DatabasePlugin extends StoragePlugin {
|
|
6
7
|
static get operation () { return 'query' }
|
|
@@ -38,20 +39,29 @@ class DatabasePlugin extends StoragePlugin {
|
|
|
38
39
|
`ddps='${encodedDdps}',ddpv='${encodedDdpv}'`
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
getDbmServiceName (span, tracerService) {
|
|
43
|
+
if (this._tracerConfig.spanComputePeerService) {
|
|
44
|
+
const peerData = this.getPeerService(span.context()._tags)
|
|
45
|
+
return this.getPeerServiceRemap(peerData)[PEER_SERVICE_KEY] || tracerService
|
|
46
|
+
}
|
|
47
|
+
return tracerService
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
injectDbmQuery (span, query, serviceName, isPreparedStatement = false) {
|
|
42
51
|
const mode = this.config.dbmPropagationMode
|
|
52
|
+
const dbmService = this.getDbmServiceName(span, serviceName)
|
|
43
53
|
|
|
44
54
|
if (mode === 'disabled') {
|
|
45
55
|
return query
|
|
46
56
|
}
|
|
47
57
|
|
|
48
|
-
const servicePropagation = this.createDBMPropagationCommentService(
|
|
58
|
+
const servicePropagation = this.createDBMPropagationCommentService(dbmService)
|
|
49
59
|
|
|
50
60
|
if (isPreparedStatement || mode === 'service') {
|
|
51
61
|
return `/*${servicePropagation}*/ ${query}`
|
|
52
62
|
} else if (mode === 'full') {
|
|
53
|
-
|
|
54
|
-
const traceparent =
|
|
63
|
+
span.setTag('_dd.dbm_trace_injected', 'true')
|
|
64
|
+
const traceparent = span._spanContext.toTraceparent()
|
|
55
65
|
return `/*${servicePropagation},traceparent='${traceparent}'*/ ${query}`
|
|
56
66
|
}
|
|
57
67
|
}
|
|
@@ -64,6 +64,7 @@ module.exports = {
|
|
|
64
64
|
get 'pg' () { return require('../../../datadog-plugin-pg/src') },
|
|
65
65
|
get 'pino' () { return require('../../../datadog-plugin-pino/src') },
|
|
66
66
|
get 'pino-pretty' () { return require('../../../datadog-plugin-pino/src') },
|
|
67
|
+
get 'playwright' () { return require('../../../datadog-plugin-playwright/src') },
|
|
67
68
|
get 'redis' () { return require('../../../datadog-plugin-redis/src') },
|
|
68
69
|
get 'restify' () { return require('../../../datadog-plugin-restify/src') },
|
|
69
70
|
get 'rhea' () { return require('../../../datadog-plugin-rhea/src') },
|
|
@@ -64,10 +64,11 @@ class OutboundPlugin extends TracingPlugin {
|
|
|
64
64
|
* peer service and add the value we overrode.
|
|
65
65
|
*/
|
|
66
66
|
const peerService = peerData[PEER_SERVICE_KEY]
|
|
67
|
-
|
|
67
|
+
const mappedService = this._tracerConfig.peerServiceMapping?.[peerService]
|
|
68
|
+
if (peerService && mappedService) {
|
|
68
69
|
return {
|
|
69
70
|
...peerData,
|
|
70
|
-
[PEER_SERVICE_KEY]:
|
|
71
|
+
[PEER_SERVICE_KEY]: mappedService,
|
|
71
72
|
[PEER_SERVICE_REMAP_KEY]: peerService
|
|
72
73
|
}
|
|
73
74
|
}
|
|
@@ -80,7 +81,7 @@ class OutboundPlugin extends TracingPlugin {
|
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
tagPeerService (span) {
|
|
83
|
-
if (this.
|
|
84
|
+
if (this._tracerConfig.spanComputePeerService) {
|
|
84
85
|
const peerData = this.getPeerService(span.context()._tags)
|
|
85
86
|
if (peerData !== undefined) {
|
|
86
87
|
span.addTags(this.getPeerServiceRemap(peerData))
|
|
@@ -602,6 +602,23 @@ module.exports = {
|
|
|
602
602
|
tags[refKey] = ref
|
|
603
603
|
}
|
|
604
604
|
|
|
605
|
+
if (env.CODEBUILD_INITIATOR?.startsWith('codepipeline/')) {
|
|
606
|
+
const {
|
|
607
|
+
CODEBUILD_BUILD_ARN,
|
|
608
|
+
DD_ACTION_EXECUTION_ID,
|
|
609
|
+
DD_PIPELINE_EXECUTION_ID
|
|
610
|
+
} = env
|
|
611
|
+
tags = {
|
|
612
|
+
[CI_PROVIDER_NAME]: 'awscodepipeline',
|
|
613
|
+
[CI_PIPELINE_ID]: DD_PIPELINE_EXECUTION_ID,
|
|
614
|
+
[CI_ENV_VARS]: JSON.stringify({
|
|
615
|
+
CODEBUILD_BUILD_ARN,
|
|
616
|
+
DD_PIPELINE_EXECUTION_ID,
|
|
617
|
+
DD_ACTION_EXECUTION_ID
|
|
618
|
+
})
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
605
622
|
normalizeTag(tags, CI_WORKSPACE_PATH, resolveTilde)
|
|
606
623
|
normalizeTag(tags, GIT_REPOSITORY_URL, filterSensitiveInfoFromRepository)
|
|
607
624
|
normalizeTag(tags, GIT_BRANCH, normalizeRef)
|
|
@@ -61,15 +61,37 @@ function unshallowRepository () {
|
|
|
61
61
|
}
|
|
62
62
|
const defaultRemoteName = sanitizedExec('git', ['config', '--default', 'origin', '--get', 'clone.defaultRemoteName'])
|
|
63
63
|
const revParseHead = sanitizedExec('git', ['rev-parse', 'HEAD'])
|
|
64
|
-
|
|
64
|
+
|
|
65
|
+
const baseGitOptions = [
|
|
65
66
|
'fetch',
|
|
66
67
|
'--shallow-since="1 month ago"',
|
|
67
68
|
'--update-shallow',
|
|
68
69
|
'--filter=blob:none',
|
|
69
70
|
'--recurse-submodules=no',
|
|
70
|
-
defaultRemoteName
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
defaultRemoteName
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
execFileSync('git', [
|
|
76
|
+
...baseGitOptions,
|
|
77
|
+
revParseHead
|
|
78
|
+
], { stdio: 'pipe' })
|
|
79
|
+
} catch (e) {
|
|
80
|
+
// If the local HEAD is a commit that has not been pushed to the remote, the above command will fail.
|
|
81
|
+
log.error(e)
|
|
82
|
+
const upstreamRemote = sanitizedExec('git', ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}'])
|
|
83
|
+
try {
|
|
84
|
+
execFileSync('git', [
|
|
85
|
+
...baseGitOptions,
|
|
86
|
+
upstreamRemote
|
|
87
|
+
], { stdio: 'pipe' })
|
|
88
|
+
} catch (e) {
|
|
89
|
+
// If the CI is working on a detached HEAD or branch tracking hasn’t been set up, the above command will fail.
|
|
90
|
+
log.error(e)
|
|
91
|
+
// We use sanitizedExec here because if this last option fails, we'll give up.
|
|
92
|
+
sanitizedExec('git', baseGitOptions)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
73
95
|
}
|
|
74
96
|
|
|
75
97
|
function getRepositoryUrl () {
|
|
@@ -58,6 +58,8 @@ const TEST_ITR_SKIPPING_ENABLED = 'test.itr.tests_skipping.enabled'
|
|
|
58
58
|
const TEST_ITR_SKIPPING_TYPE = 'test.itr.tests_skipping.type'
|
|
59
59
|
const TEST_ITR_SKIPPING_COUNT = 'test.itr.tests_skipping.count'
|
|
60
60
|
const TEST_CODE_COVERAGE_ENABLED = 'test.code_coverage.enabled'
|
|
61
|
+
const TEST_ITR_UNSKIPPABLE = 'test.itr.unskippable'
|
|
62
|
+
const TEST_ITR_FORCED_RUN = 'test.itr.forced_run'
|
|
61
63
|
|
|
62
64
|
const TEST_CODE_COVERAGE_LINES_PCT = 'test.code_coverage.lines_pct'
|
|
63
65
|
|
|
@@ -107,6 +109,8 @@ module.exports = {
|
|
|
107
109
|
TEST_ITR_SKIPPING_COUNT,
|
|
108
110
|
TEST_CODE_COVERAGE_ENABLED,
|
|
109
111
|
TEST_CODE_COVERAGE_LINES_PCT,
|
|
112
|
+
TEST_ITR_UNSKIPPABLE,
|
|
113
|
+
TEST_ITR_FORCED_RUN,
|
|
110
114
|
addIntelligentTestRunnerSpanTags,
|
|
111
115
|
getCoveredFilenamesFromCoverage,
|
|
112
116
|
resetCoverage,
|
|
@@ -366,7 +370,9 @@ function addIntelligentTestRunnerSpanTags (
|
|
|
366
370
|
isCodeCoverageEnabled,
|
|
367
371
|
testCodeCoverageLinesTotal,
|
|
368
372
|
skippingCount,
|
|
369
|
-
skippingType = 'suite'
|
|
373
|
+
skippingType = 'suite',
|
|
374
|
+
hasUnskippableSuites,
|
|
375
|
+
hasForcedToRunSuites
|
|
370
376
|
}
|
|
371
377
|
) {
|
|
372
378
|
testSessionSpan.setTag(TEST_ITR_TESTS_SKIPPED, isSuitesSkipped ? 'true' : 'false')
|
|
@@ -381,6 +387,15 @@ function addIntelligentTestRunnerSpanTags (
|
|
|
381
387
|
testModuleSpan.setTag(TEST_ITR_SKIPPING_COUNT, skippingCount)
|
|
382
388
|
testModuleSpan.setTag(TEST_CODE_COVERAGE_ENABLED, isCodeCoverageEnabled ? 'true' : 'false')
|
|
383
389
|
|
|
390
|
+
if (hasUnskippableSuites) {
|
|
391
|
+
testSessionSpan.setTag(TEST_ITR_UNSKIPPABLE, 'true')
|
|
392
|
+
testModuleSpan.setTag(TEST_ITR_UNSKIPPABLE, 'true')
|
|
393
|
+
}
|
|
394
|
+
if (hasForcedToRunSuites) {
|
|
395
|
+
testSessionSpan.setTag(TEST_ITR_FORCED_RUN, 'true')
|
|
396
|
+
testModuleSpan.setTag(TEST_ITR_FORCED_RUN, 'true')
|
|
397
|
+
}
|
|
398
|
+
|
|
384
399
|
// If suites have been skipped we don't want to report the total coverage, as it will be wrong
|
|
385
400
|
if (testCodeCoverageLinesTotal !== undefined && !isSuitesSkipped) {
|
|
386
401
|
testSessionSpan.setTag(TEST_CODE_COVERAGE_LINES_PCT, testCodeCoverageLinesTotal)
|
|
@@ -37,6 +37,8 @@ class Config {
|
|
|
37
37
|
DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE,
|
|
38
38
|
DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT,
|
|
39
39
|
DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES,
|
|
40
|
+
DD_PROFILING_CODEHOTSPOTS_ENABLED,
|
|
41
|
+
DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
|
|
40
42
|
DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED,
|
|
41
43
|
DD_PROFILING_EXPERIMENTAL_ENDPOINT_COLLECTION_ENABLED
|
|
42
44
|
} = process.env
|
|
@@ -53,8 +55,6 @@ class Config {
|
|
|
53
55
|
Number(DD_PROFILING_UPLOAD_TIMEOUT), 60 * 1000)
|
|
54
56
|
const sourceMap = coalesce(options.sourceMap,
|
|
55
57
|
DD_PROFILING_SOURCE_MAP, true)
|
|
56
|
-
const endpointCollectionEnabled = coalesce(options.endpointCollection,
|
|
57
|
-
DD_PROFILING_EXPERIMENTAL_ENDPOINT_COLLECTION_ENABLED, false)
|
|
58
58
|
const pprofPrefix = coalesce(options.pprofPrefix,
|
|
59
59
|
DD_PROFILING_PPROF_PREFIX, '')
|
|
60
60
|
|
|
@@ -71,11 +71,25 @@ class Config {
|
|
|
71
71
|
tagger.parse({ env, host, service, version, functionname })
|
|
72
72
|
)
|
|
73
73
|
this.logger = ensureLogger(options.logger)
|
|
74
|
+
const logger = this.logger
|
|
75
|
+
function logExperimentalVarDeprecation (shortVarName) {
|
|
76
|
+
const deprecatedEnvVarName = `DD_PROFILING_EXPERIMENTAL_${shortVarName}`
|
|
77
|
+
const v = process.env[deprecatedEnvVarName]
|
|
78
|
+
// not null, undefined, or NaN -- same logic as koalas.hasValue
|
|
79
|
+
// eslint-disable-next-line no-self-compare
|
|
80
|
+
if (v != null && v === v) {
|
|
81
|
+
logger.warn(`${deprecatedEnvVarName} is deprecated. Use DD_PROFILING_${shortVarName} instead.`)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
74
84
|
this.flushInterval = flushInterval
|
|
75
85
|
this.uploadTimeout = uploadTimeout
|
|
76
86
|
this.sourceMap = sourceMap
|
|
77
87
|
this.debugSourceMaps = isTrue(coalesce(options.debugSourceMaps, DD_PROFILING_DEBUG_SOURCE_MAPS, false))
|
|
78
|
-
this.endpointCollectionEnabled =
|
|
88
|
+
this.endpointCollectionEnabled = isTrue(coalesce(options.endpointCollection,
|
|
89
|
+
DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
|
|
90
|
+
DD_PROFILING_EXPERIMENTAL_ENDPOINT_COLLECTION_ENABLED, false))
|
|
91
|
+
logExperimentalVarDeprecation('ENDPOINT_COLLECTION_ENABLED')
|
|
92
|
+
|
|
79
93
|
this.pprofPrefix = pprofPrefix
|
|
80
94
|
this.v8ProfilerBugWorkaroundEnabled = isTrue(coalesce(options.v8ProfilerBugWorkaround,
|
|
81
95
|
DD_PROFILING_V8_PROFILER_BUG_WORKAROUND, true))
|
|
@@ -113,8 +127,25 @@ class Config {
|
|
|
113
127
|
const profilers = options.profilers
|
|
114
128
|
? options.profilers
|
|
115
129
|
: getProfilers({ DD_PROFILING_HEAP_ENABLED, DD_PROFILING_WALLTIME_ENABLED, DD_PROFILING_PROFILERS })
|
|
116
|
-
|
|
117
|
-
|
|
130
|
+
|
|
131
|
+
function getCodeHotspotsOptionsOr (defvalue) {
|
|
132
|
+
return coalesce(options.codeHotspotsEnabled,
|
|
133
|
+
DD_PROFILING_CODEHOTSPOTS_ENABLED,
|
|
134
|
+
DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED, defvalue)
|
|
135
|
+
}
|
|
136
|
+
this.codeHotspotsEnabled = isTrue(getCodeHotspotsOptionsOr(false))
|
|
137
|
+
logExperimentalVarDeprecation('CODEHOTSPOTS_ENABLED')
|
|
138
|
+
if (this.endpointCollectionEnabled && !this.codeHotspotsEnabled) {
|
|
139
|
+
if (getCodeHotspotsOptionsOr(undefined) !== undefined) {
|
|
140
|
+
this.logger.warn(
|
|
141
|
+
'Endpoint collection is enabled, but Code Hotspots are disabled. ' +
|
|
142
|
+
'Enable Code Hotspots too for endpoint collection to work.')
|
|
143
|
+
this.endpointCollectionEnabled = false
|
|
144
|
+
} else {
|
|
145
|
+
this.logger.info('Code Hotspots are implicitly enabled by endpoint collection.')
|
|
146
|
+
this.codeHotspotsEnabled = true
|
|
147
|
+
}
|
|
148
|
+
}
|
|
118
149
|
|
|
119
150
|
this.profilers = ensureProfilers(profilers, this)
|
|
120
151
|
}
|