dd-trace 4.14.0 → 4.16.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 -0
- package/ext/tags.d.ts +1 -0
- package/ext/tags.js +1 -0
- package/index.d.ts +5 -4
- package/package.json +7 -5
- package/packages/datadog-esbuild/index.js +30 -25
- package/packages/datadog-instrumentations/src/body-parser.js +2 -2
- package/packages/datadog-instrumentations/src/cookie-parser.js +37 -0
- package/packages/datadog-instrumentations/src/express.js +1 -1
- package/packages/datadog-instrumentations/src/graphql.js +10 -4
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
- package/packages/datadog-instrumentations/src/http/server.js +1 -1
- package/packages/datadog-instrumentations/src/mysql.js +39 -1
- package/packages/datadog-instrumentations/src/next.js +22 -80
- package/packages/datadog-instrumentations/src/pg.js +14 -15
- package/packages/datadog-instrumentations/src/playwright.js +15 -3
- package/packages/datadog-plugin-graphql/src/resolve.js +27 -2
- 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/addresses.js +1 -0
- package/packages/dd-trace/src/appsec/channels.js +2 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +7 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +28 -32
- package/packages/dd-trace/src/appsec/index.js +42 -7
- 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/reporter.js +26 -0
- package/packages/dd-trace/src/appsec/telemetry.js +132 -0
- package/packages/dd-trace/src/appsec/waf/index.js +1 -1
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +13 -5
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +12 -14
- 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/datastreams/processor.js +6 -2
- package/packages/dd-trace/src/format.js +6 -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 +1 -2
- 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/tracing.js +1 -1
- package/packages/dd-trace/src/telemetry/index.js +10 -1
- package/packages/dd-trace/src/util.js +1 -1
|
@@ -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,6 +3,12 @@
|
|
|
3
3
|
const Limiter = require('../rate_limiter')
|
|
4
4
|
const { storage } = require('../../../datadog-core')
|
|
5
5
|
const web = require('../plugins/util/web')
|
|
6
|
+
const {
|
|
7
|
+
incrementWafInitMetric,
|
|
8
|
+
updateWafRequestsMetricTags,
|
|
9
|
+
incrementWafUpdatesMetric,
|
|
10
|
+
incrementWafRequestsMetric
|
|
11
|
+
} = require('./telemetry')
|
|
6
12
|
|
|
7
13
|
// default limiter, configurable with setRateLimit()
|
|
8
14
|
let limiter = new Limiter(100)
|
|
@@ -63,6 +69,20 @@ function formatHeaderName (name) {
|
|
|
63
69
|
.toLowerCase()
|
|
64
70
|
}
|
|
65
71
|
|
|
72
|
+
function reportWafInit (wafVersion, rulesVersion, diagnosticsRules = {}) {
|
|
73
|
+
metricsQueue.set('_dd.appsec.waf.version', wafVersion)
|
|
74
|
+
|
|
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
|
+
}
|
|
80
|
+
|
|
81
|
+
metricsQueue.set('manual.keep', 'true')
|
|
82
|
+
|
|
83
|
+
incrementWafInitMetric(wafVersion, rulesVersion)
|
|
84
|
+
}
|
|
85
|
+
|
|
66
86
|
function reportMetrics (metrics) {
|
|
67
87
|
// TODO: metrics should be incremental, there already is an RFC to report metrics
|
|
68
88
|
const store = storage.getStore()
|
|
@@ -80,6 +100,8 @@ function reportMetrics (metrics) {
|
|
|
80
100
|
if (metrics.rulesVersion) {
|
|
81
101
|
rootSpan.setTag('_dd.appsec.event_rules.version', metrics.rulesVersion)
|
|
82
102
|
}
|
|
103
|
+
|
|
104
|
+
updateWafRequestsMetricTags(metrics, store.req)
|
|
83
105
|
}
|
|
84
106
|
|
|
85
107
|
function reportAttack (attackData) {
|
|
@@ -132,6 +154,8 @@ function finishRequest (req, res) {
|
|
|
132
154
|
metricsQueue.clear()
|
|
133
155
|
}
|
|
134
156
|
|
|
157
|
+
incrementWafRequestsMetric(req)
|
|
158
|
+
|
|
135
159
|
if (!rootSpan.context()._tags['appsec.event']) return
|
|
136
160
|
|
|
137
161
|
const newTags = filterHeaders(res.getHeaders(), RESPONSE_HEADERS_PASSLIST, 'http.response.headers.')
|
|
@@ -151,8 +175,10 @@ module.exports = {
|
|
|
151
175
|
metricsQueue,
|
|
152
176
|
filterHeaders,
|
|
153
177
|
formatHeaderName,
|
|
178
|
+
reportWafInit,
|
|
154
179
|
reportMetrics,
|
|
155
180
|
reportAttack,
|
|
181
|
+
reportWafUpdate: incrementWafUpdatesMetric,
|
|
156
182
|
finishRequest,
|
|
157
183
|
setRateLimit
|
|
158
184
|
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const telemetryMetrics = require('../telemetry/metrics')
|
|
4
|
+
|
|
5
|
+
const appsecMetrics = telemetryMetrics.manager.namespace('appsec')
|
|
6
|
+
|
|
7
|
+
const DD_TELEMETRY_WAF_RESULT_TAGS = Symbol('_dd.appsec.telemetry.waf.result.tags')
|
|
8
|
+
|
|
9
|
+
const tags = {
|
|
10
|
+
REQUEST_BLOCKED: 'request_blocked',
|
|
11
|
+
RULE_TRIGGERED: 'rule_triggered',
|
|
12
|
+
WAF_TIMEOUT: 'waf_timeout',
|
|
13
|
+
WAF_VERSION: 'waf_version',
|
|
14
|
+
EVENT_RULES_VERSION: 'event_rules_version'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const metricsStoreMap = new WeakMap()
|
|
18
|
+
|
|
19
|
+
let enabled = false
|
|
20
|
+
|
|
21
|
+
function enable (telemetryConfig) {
|
|
22
|
+
enabled = telemetryConfig?.enabled && telemetryConfig.metrics
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function disable () {
|
|
26
|
+
enabled = false
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getStore (req) {
|
|
30
|
+
let store = metricsStoreMap.get(req)
|
|
31
|
+
if (!store) {
|
|
32
|
+
store = {}
|
|
33
|
+
metricsStoreMap.set(req, store)
|
|
34
|
+
}
|
|
35
|
+
return store
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getVersionsTags (wafVersion, rulesVersion) {
|
|
39
|
+
return {
|
|
40
|
+
[tags.WAF_VERSION]: wafVersion,
|
|
41
|
+
[tags.EVENT_RULES_VERSION]: rulesVersion
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function trackWafDurations (metrics, versionsTags) {
|
|
46
|
+
if (metrics.duration) {
|
|
47
|
+
appsecMetrics.distribution('waf.duration', versionsTags).track(metrics.duration)
|
|
48
|
+
}
|
|
49
|
+
if (metrics.durationExt) {
|
|
50
|
+
appsecMetrics.distribution('waf.duration_ext', versionsTags).track(metrics.durationExt)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getOrCreateMetricTags (req, versionsTags) {
|
|
55
|
+
const store = getStore(req)
|
|
56
|
+
|
|
57
|
+
let metricTags = store[DD_TELEMETRY_WAF_RESULT_TAGS]
|
|
58
|
+
if (!metricTags) {
|
|
59
|
+
metricTags = {
|
|
60
|
+
[tags.REQUEST_BLOCKED]: false,
|
|
61
|
+
[tags.RULE_TRIGGERED]: false,
|
|
62
|
+
[tags.WAF_TIMEOUT]: false,
|
|
63
|
+
|
|
64
|
+
...versionsTags
|
|
65
|
+
}
|
|
66
|
+
store[DD_TELEMETRY_WAF_RESULT_TAGS] = metricTags
|
|
67
|
+
}
|
|
68
|
+
return metricTags
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function updateWafRequestsMetricTags (metrics, req) {
|
|
72
|
+
if (!req || !enabled) return
|
|
73
|
+
|
|
74
|
+
const versionsTags = getVersionsTags(metrics.wafVersion, metrics.rulesVersion)
|
|
75
|
+
|
|
76
|
+
trackWafDurations(metrics, versionsTags)
|
|
77
|
+
|
|
78
|
+
const metricTags = getOrCreateMetricTags(req, versionsTags)
|
|
79
|
+
|
|
80
|
+
const { blockTriggered, ruleTriggered, wafTimeout } = metrics
|
|
81
|
+
|
|
82
|
+
if (blockTriggered) {
|
|
83
|
+
metricTags[tags.REQUEST_BLOCKED] = blockTriggered
|
|
84
|
+
}
|
|
85
|
+
if (ruleTriggered) {
|
|
86
|
+
metricTags[tags.RULE_TRIGGERED] = ruleTriggered
|
|
87
|
+
}
|
|
88
|
+
if (wafTimeout) {
|
|
89
|
+
metricTags[tags.WAF_TIMEOUT] = wafTimeout
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return metricTags
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function incrementWafInitMetric (wafVersion, rulesVersion) {
|
|
96
|
+
if (!enabled) return
|
|
97
|
+
|
|
98
|
+
const versionsTags = getVersionsTags(wafVersion, rulesVersion)
|
|
99
|
+
|
|
100
|
+
appsecMetrics.count('waf.init', versionsTags).inc()
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function incrementWafUpdatesMetric (wafVersion, rulesVersion) {
|
|
104
|
+
if (!enabled) return
|
|
105
|
+
|
|
106
|
+
const versionsTags = getVersionsTags(wafVersion, rulesVersion)
|
|
107
|
+
|
|
108
|
+
appsecMetrics.count('waf.updates', versionsTags).inc()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function incrementWafRequestsMetric (req) {
|
|
112
|
+
if (!req || !enabled) return
|
|
113
|
+
|
|
114
|
+
const store = getStore(req)
|
|
115
|
+
|
|
116
|
+
const metricTags = store[DD_TELEMETRY_WAF_RESULT_TAGS]
|
|
117
|
+
if (metricTags) {
|
|
118
|
+
appsecMetrics.count('waf.requests', metricTags).inc()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
metricsStoreMap.delete(req)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = {
|
|
125
|
+
enable,
|
|
126
|
+
disable,
|
|
127
|
+
|
|
128
|
+
updateWafRequestsMetricTags,
|
|
129
|
+
incrementWafInitMetric,
|
|
130
|
+
incrementWafUpdatesMetric,
|
|
131
|
+
incrementWafRequestsMetric
|
|
132
|
+
}
|
|
@@ -39,7 +39,7 @@ function update (newRules) {
|
|
|
39
39
|
if (!waf.wafManager) throw new Error('Cannot update disabled WAF')
|
|
40
40
|
|
|
41
41
|
try {
|
|
42
|
-
waf.wafManager.
|
|
42
|
+
waf.wafManager.update(newRules)
|
|
43
43
|
} catch (err) {
|
|
44
44
|
log.error('Could not apply rules from remote config')
|
|
45
45
|
throw err
|
|
@@ -4,11 +4,12 @@ const log = require('../../log')
|
|
|
4
4
|
const Reporter = require('../reporter')
|
|
5
5
|
|
|
6
6
|
class WAFContextWrapper {
|
|
7
|
-
constructor (ddwafContext, requiredAddresses, wafTimeout,
|
|
7
|
+
constructor (ddwafContext, requiredAddresses, wafTimeout, wafVersion, rulesVersion) {
|
|
8
8
|
this.ddwafContext = ddwafContext
|
|
9
9
|
this.requiredAddresses = requiredAddresses
|
|
10
10
|
this.wafTimeout = wafTimeout
|
|
11
|
-
this.
|
|
11
|
+
this.wafVersion = wafVersion
|
|
12
|
+
this.rulesVersion = rulesVersion
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
run (params) {
|
|
@@ -32,14 +33,21 @@ class WAFContextWrapper {
|
|
|
32
33
|
|
|
33
34
|
const end = process.hrtime.bigint()
|
|
34
35
|
|
|
36
|
+
const ruleTriggered = !!result.events?.length
|
|
37
|
+
const blockTriggered = result.actions?.includes('block')
|
|
38
|
+
|
|
35
39
|
Reporter.reportMetrics({
|
|
36
40
|
duration: result.totalRuntime / 1e3,
|
|
37
41
|
durationExt: parseInt(end - start) / 1e3,
|
|
38
|
-
rulesVersion: this.
|
|
42
|
+
rulesVersion: this.rulesVersion,
|
|
43
|
+
ruleTriggered,
|
|
44
|
+
blockTriggered,
|
|
45
|
+
wafVersion: this.wafVersion,
|
|
46
|
+
wafTimeout: result.timeout
|
|
39
47
|
})
|
|
40
48
|
|
|
41
|
-
if (
|
|
42
|
-
Reporter.reportAttack(result.
|
|
49
|
+
if (ruleTriggered) {
|
|
50
|
+
Reporter.reportAttack(JSON.stringify(result.events))
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
return result.actions
|
|
@@ -11,7 +11,10 @@ class WAFManager {
|
|
|
11
11
|
this.config = config
|
|
12
12
|
this.wafTimeout = config.wafTimeout
|
|
13
13
|
this.ddwaf = this._loadDDWAF(rules)
|
|
14
|
-
this.
|
|
14
|
+
this.ddwafVersion = this.ddwaf.constructor.version()
|
|
15
|
+
this.rulesVersion = this.ddwaf.diagnostics.ruleset_version
|
|
16
|
+
|
|
17
|
+
Reporter.reportWafInit(this.ddwafVersion, this.rulesVersion, this.ddwaf.diagnostics.rules)
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
_loadDDWAF (rules) {
|
|
@@ -28,18 +31,6 @@ class WAFManager {
|
|
|
28
31
|
}
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
_reportMetrics () {
|
|
32
|
-
Reporter.metricsQueue.set('_dd.appsec.waf.version', this.ddwaf.constructor.version())
|
|
33
|
-
|
|
34
|
-
const { loaded, failed, errors } = this.ddwaf.rulesInfo
|
|
35
|
-
|
|
36
|
-
Reporter.metricsQueue.set('_dd.appsec.event_rules.loaded', loaded)
|
|
37
|
-
Reporter.metricsQueue.set('_dd.appsec.event_rules.error_count', failed)
|
|
38
|
-
if (failed) Reporter.metricsQueue.set('_dd.appsec.event_rules.errors', JSON.stringify(errors))
|
|
39
|
-
|
|
40
|
-
Reporter.metricsQueue.set('manual.keep', 'true')
|
|
41
|
-
}
|
|
42
|
-
|
|
43
34
|
getWAFContext (req) {
|
|
44
35
|
let wafContext = contexts.get(req)
|
|
45
36
|
|
|
@@ -48,7 +39,8 @@ class WAFManager {
|
|
|
48
39
|
this.ddwaf.createContext(),
|
|
49
40
|
this.ddwaf.requiredAddresses,
|
|
50
41
|
this.wafTimeout,
|
|
51
|
-
this.
|
|
42
|
+
this.ddwafVersion,
|
|
43
|
+
this.rulesVersion
|
|
52
44
|
)
|
|
53
45
|
contexts.set(req, wafContext)
|
|
54
46
|
}
|
|
@@ -56,6 +48,12 @@ class WAFManager {
|
|
|
56
48
|
return wafContext
|
|
57
49
|
}
|
|
58
50
|
|
|
51
|
+
update (newRules) {
|
|
52
|
+
this.ddwaf.update(newRules)
|
|
53
|
+
|
|
54
|
+
Reporter.reportWafUpdate(this.ddwafVersion, this.rulesVersion)
|
|
55
|
+
}
|
|
56
|
+
|
|
59
57
|
destroy () {
|
|
60
58
|
if (this.ddwaf) {
|
|
61
59
|
this.ddwaf.dispose()
|
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({
|
|
@@ -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)
|
|
@@ -13,7 +13,7 @@ const SPAN_SAMPLING_MECHANISM = constants.SPAN_SAMPLING_MECHANISM
|
|
|
13
13
|
const SPAN_SAMPLING_RULE_RATE = constants.SPAN_SAMPLING_RULE_RATE
|
|
14
14
|
const SPAN_SAMPLING_MAX_PER_SECOND = constants.SPAN_SAMPLING_MAX_PER_SECOND
|
|
15
15
|
const SAMPLING_MECHANISM_SPAN = constants.SAMPLING_MECHANISM_SPAN
|
|
16
|
-
const MEASURED = tags
|
|
16
|
+
const { MEASURED, BASE_SERVICE } = tags
|
|
17
17
|
const ORIGIN_KEY = constants.ORIGIN_KEY
|
|
18
18
|
const HOSTNAME_KEY = constants.HOSTNAME_KEY
|
|
19
19
|
const TOP_LEVEL_KEY = constants.TOP_LEVEL_KEY
|
|
@@ -73,6 +73,11 @@ function extractTags (trace, span) {
|
|
|
73
73
|
addTag({}, trace.metrics, MEASURED, 1)
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
const tracerService = span.tracer()._service.toLowerCase()
|
|
77
|
+
if (tags['service.name']?.toLowerCase() !== tracerService) {
|
|
78
|
+
span.setTag(BASE_SERVICE, tracerService)
|
|
79
|
+
}
|
|
80
|
+
|
|
76
81
|
for (const tag in tags) {
|
|
77
82
|
switch (tag) {
|
|
78
83
|
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
|
}
|
|
@@ -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))
|
|
@@ -57,11 +57,20 @@ function appStarted () {
|
|
|
57
57
|
return {
|
|
58
58
|
integrations: getIntegrations(),
|
|
59
59
|
dependencies: [],
|
|
60
|
-
configuration: flatten(config),
|
|
60
|
+
configuration: flatten(formatConfig(config)),
|
|
61
61
|
additional_payload: []
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function formatConfig (config) {
|
|
66
|
+
// format peerServiceMapping from an object to a string map in order for
|
|
67
|
+
// telemetry intake to accept the configuration
|
|
68
|
+
config.peerServiceMapping = config.peerServiceMapping
|
|
69
|
+
? Object.entries(config.peerServiceMapping).map(([key, value]) => `${key}:${value}`).join(',')
|
|
70
|
+
: ''
|
|
71
|
+
return config
|
|
72
|
+
}
|
|
73
|
+
|
|
65
74
|
function onBeforeExit () {
|
|
66
75
|
process.removeListener('beforeExit', onBeforeExit)
|
|
67
76
|
sendData(config, application, host, 'app-closing')
|
|
@@ -66,7 +66,7 @@ function globMatch (pattern, subject) {
|
|
|
66
66
|
function calculateDDBasePath (dirname) {
|
|
67
67
|
const dirSteps = dirname.split(path.sep)
|
|
68
68
|
const packagesIndex = dirSteps.lastIndexOf('packages')
|
|
69
|
-
return dirSteps.slice(0, packagesIndex).join(path.sep) + path.sep
|
|
69
|
+
return dirSteps.slice(0, packagesIndex + 1).join(path.sep) + path.sep
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
module.exports = {
|