dd-trace 4.38.1 → 4.40.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 +0 -3
- package/README.md +8 -18
- package/ci/init.js +7 -0
- package/ext/exporters.d.ts +1 -0
- package/ext/exporters.js +2 -1
- package/ext/tags.d.ts +1 -0
- package/ext/tags.js +1 -0
- package/index.d.ts +18 -3
- package/initialize.mjs +52 -0
- package/package.json +9 -12
- package/packages/datadog-instrumentations/src/amqplib.js +5 -2
- package/packages/datadog-instrumentations/src/apollo-server-core.js +0 -1
- package/packages/datadog-instrumentations/src/apollo-server.js +0 -1
- package/packages/datadog-instrumentations/src/body-parser.js +0 -1
- package/packages/datadog-instrumentations/src/check_require_cache.js +67 -5
- package/packages/datadog-instrumentations/src/cookie-parser.js +0 -1
- package/packages/datadog-instrumentations/src/express.js +0 -1
- package/packages/datadog-instrumentations/src/graphql.js +0 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +5 -2
- package/packages/datadog-instrumentations/src/http/server.js +0 -1
- package/packages/datadog-instrumentations/src/jest.js +6 -3
- package/packages/datadog-instrumentations/src/mocha/common.js +48 -0
- package/packages/datadog-instrumentations/src/mocha/main.js +487 -0
- package/packages/datadog-instrumentations/src/mocha/utils.js +306 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +51 -0
- package/packages/datadog-instrumentations/src/mocha.js +4 -673
- package/packages/datadog-instrumentations/src/openai.js +188 -17
- package/packages/datadog-instrumentations/src/playwright.js +4 -3
- package/packages/datadog-instrumentations/src/router.js +1 -1
- package/packages/datadog-instrumentations/src/selenium.js +13 -6
- package/packages/datadog-plugin-graphql/src/resolve.js +4 -0
- package/packages/datadog-plugin-mocha/src/index.js +82 -8
- package/packages/datadog-plugin-next/src/index.js +1 -2
- package/packages/datadog-plugin-openai/src/index.js +219 -73
- package/packages/dd-trace/src/appsec/addresses.js +4 -2
- package/packages/dd-trace/src/appsec/blocking.js +19 -25
- package/packages/dd-trace/src/appsec/channels.js +2 -1
- package/packages/dd-trace/src/appsec/graphql.js +10 -3
- package/packages/dd-trace/src/appsec/index.js +11 -4
- package/packages/dd-trace/src/appsec/rasp.js +35 -0
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
- package/packages/dd-trace/src/appsec/remote_config/index.js +1 -0
- package/packages/dd-trace/src/appsec/rule_manager.js +15 -25
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -5
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +3 -1
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -1
- package/packages/dd-trace/src/config.js +97 -22
- package/packages/dd-trace/src/constants.js +2 -0
- package/packages/dd-trace/src/encode/0.4.js +47 -8
- package/packages/dd-trace/src/exporter.js +1 -0
- package/packages/dd-trace/src/flare/file.js +44 -0
- package/packages/dd-trace/src/flare/index.js +98 -0
- package/packages/dd-trace/src/log/channels.js +54 -29
- package/packages/dd-trace/src/log/writer.js +7 -49
- package/packages/dd-trace/src/opentelemetry/span.js +8 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +57 -12
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
- package/packages/dd-trace/src/plugins/util/test.js +6 -0
- package/packages/dd-trace/src/priority_sampler.js +8 -4
- package/packages/dd-trace/src/profiler.js +2 -1
- package/packages/dd-trace/src/profiling/config.js +1 -0
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/{ssi-telemetry.js → ssi-heuristics.js} +64 -36
- package/packages/dd-trace/src/profiling/ssi-telemetry-mock-profiler.js +4 -9
- package/packages/dd-trace/src/proxy.js +49 -15
- package/packages/dd-trace/src/ritm.js +13 -1
- package/packages/dd-trace/src/sampling_rule.js +2 -1
- package/packages/dd-trace/src/startup-log.js +19 -15
- package/packages/dd-trace/src/telemetry/index.js +6 -2
- package/packages/dd-trace/src/tracer.js +3 -0
- package/packages/dd-trace/src/plugins/util/ip_blocklist.js +0 -51
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { Writable } = require('stream')
|
|
4
|
+
|
|
5
|
+
const INITIAL_SIZE = 64 * 1024
|
|
6
|
+
|
|
7
|
+
class FlareFile extends Writable {
|
|
8
|
+
constructor () {
|
|
9
|
+
super()
|
|
10
|
+
|
|
11
|
+
this.length = 0
|
|
12
|
+
|
|
13
|
+
this._buffer = Buffer.alloc(INITIAL_SIZE)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get data () {
|
|
17
|
+
return this._buffer.subarray(0, this.length)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_write (chunk, encoding, callback) {
|
|
21
|
+
const length = Buffer.byteLength(chunk)
|
|
22
|
+
|
|
23
|
+
this._reserve(length)
|
|
24
|
+
|
|
25
|
+
if (Buffer.isBuffer(chunk)) {
|
|
26
|
+
this.length += chunk.copy(this._buffer, this.length)
|
|
27
|
+
} else {
|
|
28
|
+
this.length += this._buffer.write(chunk, encoding)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
callback()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_reserve (length) {
|
|
35
|
+
while (this.length + length > this._buffer.length) {
|
|
36
|
+
const buffer = Buffer.alloc(this.length * 2)
|
|
37
|
+
|
|
38
|
+
this._buffer.copy(buffer)
|
|
39
|
+
this._buffer = buffer
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = FlareFile
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const log = require('../log')
|
|
4
|
+
const startupLog = require('../startup-log')
|
|
5
|
+
const FlareFile = require('./file')
|
|
6
|
+
const { LogChannel } = require('../log/channels')
|
|
7
|
+
const request = require('../exporters/common/request')
|
|
8
|
+
const FormData = require('../exporters/common/form-data')
|
|
9
|
+
|
|
10
|
+
const MAX_LOG_SIZE = 12 * 1024 * 1024 // 12MB soft limit
|
|
11
|
+
const TIMEOUT = 20 * 1000 * 60
|
|
12
|
+
|
|
13
|
+
let logChannel = null
|
|
14
|
+
let tracerLogs = null
|
|
15
|
+
let timer
|
|
16
|
+
let tracerConfig = null
|
|
17
|
+
|
|
18
|
+
const logger = {
|
|
19
|
+
debug: (msg) => recordLog(msg),
|
|
20
|
+
info: (msg) => recordLog(msg),
|
|
21
|
+
warn: (msg) => recordLog(msg),
|
|
22
|
+
error: (err) => recordLog(err.stack)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const flare = {
|
|
26
|
+
enable (tracerConfig_) {
|
|
27
|
+
tracerConfig = tracerConfig_
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
disable () {
|
|
31
|
+
tracerConfig = null
|
|
32
|
+
|
|
33
|
+
flare.cleanup()
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
prepare (logLevel) {
|
|
37
|
+
if (!tracerConfig) return
|
|
38
|
+
|
|
39
|
+
logChannel?.unsubscribe(logger)
|
|
40
|
+
logChannel = new LogChannel(logLevel)
|
|
41
|
+
logChannel.subscribe(logger)
|
|
42
|
+
tracerLogs = tracerLogs || new FlareFile()
|
|
43
|
+
timer = timer || setTimeout(flare.cleanup, TIMEOUT)
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
send (task) {
|
|
47
|
+
if (!tracerConfig) return
|
|
48
|
+
|
|
49
|
+
const tracerInfo = new FlareFile()
|
|
50
|
+
|
|
51
|
+
tracerInfo.write(JSON.stringify(startupLog.tracerInfo(), null, 2))
|
|
52
|
+
|
|
53
|
+
flare._sendFile(task, tracerInfo, 'tracer_info.txt')
|
|
54
|
+
flare._sendFile(task, tracerLogs, 'tracer_logs.txt')
|
|
55
|
+
|
|
56
|
+
flare.cleanup()
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
cleanup () {
|
|
60
|
+
logChannel?.unsubscribe(logger)
|
|
61
|
+
timer = clearTimeout(timer)
|
|
62
|
+
logChannel = null
|
|
63
|
+
tracerLogs = null
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
_sendFile (task, file, filename) {
|
|
67
|
+
if (!file) return
|
|
68
|
+
|
|
69
|
+
const form = new FormData()
|
|
70
|
+
|
|
71
|
+
form.append('case_id', task.case_id)
|
|
72
|
+
form.append('hostname', task.hostname)
|
|
73
|
+
form.append('email', task.user_handle)
|
|
74
|
+
form.append('source', 'tracer_nodejs')
|
|
75
|
+
form.append('flare_file', file.data, { filename })
|
|
76
|
+
|
|
77
|
+
request(form, {
|
|
78
|
+
url: tracerConfig.url,
|
|
79
|
+
hostname: tracerConfig.hostname,
|
|
80
|
+
port: tracerConfig.port,
|
|
81
|
+
method: 'POST',
|
|
82
|
+
path: '/tracer_flare/v1',
|
|
83
|
+
headers: form.getHeaders()
|
|
84
|
+
}, (err) => {
|
|
85
|
+
if (err) {
|
|
86
|
+
log.error(err)
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function recordLog (msg) {
|
|
93
|
+
if (tracerLogs.length > MAX_LOG_SIZE) return
|
|
94
|
+
|
|
95
|
+
tracerLogs.write(`${msg}\n`) // TODO: gzip
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = flare
|
|
@@ -3,44 +3,69 @@
|
|
|
3
3
|
const { channel } = require('dc-polyfill')
|
|
4
4
|
|
|
5
5
|
const Level = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
trace: 20,
|
|
7
|
+
debug: 20,
|
|
8
|
+
info: 30,
|
|
9
|
+
warn: 40,
|
|
10
|
+
error: 50,
|
|
11
|
+
critical: 50,
|
|
12
|
+
off: 100
|
|
10
13
|
}
|
|
11
14
|
|
|
12
|
-
const
|
|
15
|
+
const debugChannel = channel('datadog:log:debug')
|
|
16
|
+
const infoChannel = channel('datadog:log:info')
|
|
17
|
+
const warnChannel = channel('datadog:log:warn')
|
|
18
|
+
const errorChannel = channel('datadog:log:error')
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
const logChannels = {
|
|
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
|
-
}
|
|
20
|
+
const defaultLevel = Level.debug
|
|
21
21
|
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
function getChannelLogLevel (level) {
|
|
23
|
+
return level && typeof level === 'string'
|
|
24
|
+
? Level[level.toLowerCase().trim()] || defaultLevel
|
|
25
|
+
: defaultLevel
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
class LogChannel {
|
|
29
|
+
constructor (level) {
|
|
30
|
+
this._level = getChannelLogLevel(level)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
subscribe (logger) {
|
|
34
|
+
if (Level.debug >= this._level) {
|
|
35
|
+
debugChannel.subscribe(logger.debug)
|
|
36
|
+
}
|
|
37
|
+
if (Level.info >= this._level) {
|
|
38
|
+
infoChannel.subscribe(logger.info)
|
|
39
|
+
}
|
|
40
|
+
if (Level.warn >= this._level) {
|
|
41
|
+
warnChannel.subscribe(logger.warn)
|
|
42
|
+
}
|
|
43
|
+
if (Level.error >= this._level) {
|
|
44
|
+
errorChannel.subscribe(logger.error)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
unsubscribe (logger) {
|
|
49
|
+
if (debugChannel.hasSubscribers) {
|
|
50
|
+
debugChannel.unsubscribe(logger.debug)
|
|
51
|
+
}
|
|
52
|
+
if (infoChannel.hasSubscribers) {
|
|
53
|
+
infoChannel.unsubscribe(logger.info)
|
|
54
|
+
}
|
|
55
|
+
if (warnChannel.hasSubscribers) {
|
|
56
|
+
warnChannel.unsubscribe(logger.warn)
|
|
57
|
+
}
|
|
58
|
+
if (errorChannel.hasSubscribers) {
|
|
59
|
+
errorChannel.unsubscribe(logger.error)
|
|
60
|
+
}
|
|
34
61
|
}
|
|
35
|
-
return logChannel.logLevel
|
|
36
62
|
}
|
|
37
63
|
|
|
38
64
|
module.exports = {
|
|
39
|
-
|
|
40
|
-
getChannelLogLevel,
|
|
65
|
+
LogChannel,
|
|
41
66
|
|
|
42
|
-
debugChannel
|
|
43
|
-
infoChannel
|
|
44
|
-
warnChannel
|
|
45
|
-
errorChannel
|
|
67
|
+
debugChannel,
|
|
68
|
+
infoChannel,
|
|
69
|
+
warnChannel,
|
|
70
|
+
errorChannel
|
|
46
71
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { storage } = require('../../../datadog-core')
|
|
4
|
-
const {
|
|
5
|
-
|
|
4
|
+
const { LogChannel } = require('./channels')
|
|
6
5
|
const defaultLogger = {
|
|
7
6
|
debug: msg => console.debug(msg), /* eslint-disable-line no-console */
|
|
8
7
|
info: msg => console.info(msg), /* eslint-disable-line no-console */
|
|
@@ -12,7 +11,7 @@ const defaultLogger = {
|
|
|
12
11
|
|
|
13
12
|
let enabled = false
|
|
14
13
|
let logger = defaultLogger
|
|
15
|
-
let
|
|
14
|
+
let logChannel = new LogChannel()
|
|
16
15
|
|
|
17
16
|
function withNoop (fn) {
|
|
18
17
|
const store = storage.getStore()
|
|
@@ -23,45 +22,21 @@ function withNoop (fn) {
|
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
function unsubscribeAll () {
|
|
26
|
-
|
|
27
|
-
debugChannel.unsubscribe(onDebug)
|
|
28
|
-
}
|
|
29
|
-
if (infoChannel.hasSubscribers) {
|
|
30
|
-
infoChannel.unsubscribe(onInfo)
|
|
31
|
-
}
|
|
32
|
-
if (warnChannel.hasSubscribers) {
|
|
33
|
-
warnChannel.unsubscribe(onWarn)
|
|
34
|
-
}
|
|
35
|
-
if (errorChannel.hasSubscribers) {
|
|
36
|
-
errorChannel.unsubscribe(onError)
|
|
37
|
-
}
|
|
25
|
+
logChannel.unsubscribe({ debug, info, warn, error })
|
|
38
26
|
}
|
|
39
27
|
|
|
40
|
-
function toggleSubscription (enable) {
|
|
28
|
+
function toggleSubscription (enable, level) {
|
|
41
29
|
unsubscribeAll()
|
|
42
30
|
|
|
43
31
|
if (enable) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
if (infoChannel.logLevel >= logLevel) {
|
|
48
|
-
infoChannel.subscribe(onInfo)
|
|
49
|
-
}
|
|
50
|
-
if (warnChannel.logLevel >= logLevel) {
|
|
51
|
-
warnChannel.subscribe(onWarn)
|
|
52
|
-
}
|
|
53
|
-
if (errorChannel.logLevel >= logLevel) {
|
|
54
|
-
errorChannel.subscribe(onError)
|
|
55
|
-
}
|
|
32
|
+
logChannel = new LogChannel(level)
|
|
33
|
+
logChannel.subscribe({ debug, info, warn, error })
|
|
56
34
|
}
|
|
57
35
|
}
|
|
58
36
|
|
|
59
37
|
function toggle (enable, level) {
|
|
60
|
-
if (level !== undefined) {
|
|
61
|
-
logLevel = getChannelLogLevel(level)
|
|
62
|
-
}
|
|
63
38
|
enabled = enable
|
|
64
|
-
toggleSubscription(enabled)
|
|
39
|
+
toggleSubscription(enabled, level)
|
|
65
40
|
}
|
|
66
41
|
|
|
67
42
|
function use (newLogger) {
|
|
@@ -73,26 +48,9 @@ function use (newLogger) {
|
|
|
73
48
|
function reset () {
|
|
74
49
|
logger = defaultLogger
|
|
75
50
|
enabled = false
|
|
76
|
-
logLevel = getChannelLogLevel()
|
|
77
51
|
toggleSubscription(false)
|
|
78
52
|
}
|
|
79
53
|
|
|
80
|
-
function onError (err) {
|
|
81
|
-
if (enabled) error(err)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function onWarn (message) {
|
|
85
|
-
if (enabled) warn(message)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function onInfo (message) {
|
|
89
|
-
if (enabled) info(message)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function onDebug (message) {
|
|
93
|
-
if (enabled) debug(message)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
54
|
function error (err) {
|
|
97
55
|
if (typeof err !== 'object' || !err) {
|
|
98
56
|
err = String(err)
|
|
@@ -179,11 +179,19 @@ class Span {
|
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
setAttribute (key, value) {
|
|
182
|
+
if (key === 'http.response.status_code') {
|
|
183
|
+
this._ddSpan.setTag('http.status_code', value.toString())
|
|
184
|
+
}
|
|
185
|
+
|
|
182
186
|
this._ddSpan.setTag(key, value)
|
|
183
187
|
return this
|
|
184
188
|
}
|
|
185
189
|
|
|
186
190
|
setAttributes (attributes) {
|
|
191
|
+
if ('http.response.status_code' in attributes) {
|
|
192
|
+
attributes['http.status_code'] = attributes['http.response.status_code'].toString()
|
|
193
|
+
}
|
|
194
|
+
|
|
187
195
|
this._ddSpan.addTags(attributes)
|
|
188
196
|
return this
|
|
189
197
|
}
|
|
@@ -5,6 +5,7 @@ const id = require('../../id')
|
|
|
5
5
|
const DatadogSpanContext = require('../span_context')
|
|
6
6
|
const log = require('../../log')
|
|
7
7
|
const TraceState = require('./tracestate')
|
|
8
|
+
const tags = require('../../../../../ext/tags')
|
|
8
9
|
|
|
9
10
|
const { AUTO_KEEP, AUTO_REJECT, USER_KEEP } = require('../../../../../ext/priority')
|
|
10
11
|
|
|
@@ -39,6 +40,7 @@ const tracestateTagKeyFilter = /[^\x21-\x2b\x2d-\x3c\x3e-\x7e]/g
|
|
|
39
40
|
// Tag values in tracestate replace ',', '~' and ';' with '_'
|
|
40
41
|
const tracestateTagValueFilter = /[^\x20-\x2b\x2d-\x3a\x3c-\x7d]/g
|
|
41
42
|
const invalidSegment = /^0+$/
|
|
43
|
+
const zeroTraceId = '0000000000000000'
|
|
42
44
|
|
|
43
45
|
class TextMapPropagator {
|
|
44
46
|
constructor (config) {
|
|
@@ -175,9 +177,9 @@ class TextMapPropagator {
|
|
|
175
177
|
// SpanContext was created by a ddtrace span.
|
|
176
178
|
// Last datadog span id should be set to the current span.
|
|
177
179
|
state.set('p', spanContext._spanId)
|
|
178
|
-
} else if (spanContext._trace.tags[
|
|
180
|
+
} else if (spanContext._trace.tags[tags.DD_PARENT_ID]) {
|
|
179
181
|
// Propagate the last Datadog span id set on the remote span.
|
|
180
|
-
state.set('p', spanContext._trace.tags[
|
|
182
|
+
state.set('p', spanContext._trace.tags[tags.DD_PARENT_ID])
|
|
181
183
|
}
|
|
182
184
|
state.set('s', priority)
|
|
183
185
|
if (mechanism) {
|
|
@@ -214,9 +216,56 @@ class TextMapPropagator {
|
|
|
214
216
|
return this._config.tracePropagationStyle[mode].includes(name)
|
|
215
217
|
}
|
|
216
218
|
|
|
219
|
+
_hasTraceIdConflict (w3cSpanContext, firstSpanContext) {
|
|
220
|
+
return w3cSpanContext !== null &&
|
|
221
|
+
firstSpanContext.toTraceId(true) === w3cSpanContext.toTraceId(true) &&
|
|
222
|
+
firstSpanContext.toSpanId() !== w3cSpanContext.toSpanId()
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
_hasParentIdInTags (spanContext) {
|
|
226
|
+
return tags.DD_PARENT_ID in spanContext._trace.tags &&
|
|
227
|
+
spanContext._trace.tags[tags.DD_PARENT_ID] !== zeroTraceId
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
_updateParentIdFromDdHeaders (carrier, firstSpanContext) {
|
|
231
|
+
const ddCtx = this._extractDatadogContext(carrier)
|
|
232
|
+
if (ddCtx !== null) {
|
|
233
|
+
firstSpanContext._trace.tags[tags.DD_PARENT_ID] = ddCtx._spanId.toString().padStart(16, '0')
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
_resolveTraceContextConflicts (w3cSpanContext, firstSpanContext, carrier) {
|
|
238
|
+
if (!this._hasTraceIdConflict(w3cSpanContext, firstSpanContext)) {
|
|
239
|
+
return firstSpanContext
|
|
240
|
+
}
|
|
241
|
+
if (this._hasParentIdInTags(w3cSpanContext)) {
|
|
242
|
+
// tracecontext headers contain a p value, ensure this value is sent to backend
|
|
243
|
+
firstSpanContext._trace.tags[tags.DD_PARENT_ID] = w3cSpanContext._trace.tags[tags.DD_PARENT_ID]
|
|
244
|
+
} else {
|
|
245
|
+
// if p value is not present in tracestate, use the parent id from the datadog headers
|
|
246
|
+
this._updateParentIdFromDdHeaders(carrier, firstSpanContext)
|
|
247
|
+
}
|
|
248
|
+
// the span_id in tracecontext takes precedence over the first extracted propagation style
|
|
249
|
+
firstSpanContext._spanId = w3cSpanContext._spanId
|
|
250
|
+
return firstSpanContext
|
|
251
|
+
}
|
|
252
|
+
|
|
217
253
|
_extractSpanContext (carrier) {
|
|
254
|
+
let spanContext = null
|
|
218
255
|
for (const extractor of this._config.tracePropagationStyle.extract) {
|
|
219
|
-
|
|
256
|
+
// add logic to ensure tracecontext headers takes precedence over other extracted headers
|
|
257
|
+
if (spanContext !== null) {
|
|
258
|
+
if (this._config.tracePropagationExtractFirst) {
|
|
259
|
+
return spanContext
|
|
260
|
+
}
|
|
261
|
+
if (extractor !== 'tracecontext') {
|
|
262
|
+
continue
|
|
263
|
+
}
|
|
264
|
+
spanContext = this._resolveTraceContextConflicts(
|
|
265
|
+
this._extractTraceparentContext(carrier), spanContext, carrier)
|
|
266
|
+
break
|
|
267
|
+
}
|
|
268
|
+
|
|
220
269
|
switch (extractor) {
|
|
221
270
|
case 'datadog':
|
|
222
271
|
spanContext = this._extractDatadogContext(carrier)
|
|
@@ -238,13 +287,9 @@ class TextMapPropagator {
|
|
|
238
287
|
default:
|
|
239
288
|
log.warn(`Unknown propagation style: ${extractor}`)
|
|
240
289
|
}
|
|
241
|
-
|
|
242
|
-
if (spanContext !== null) {
|
|
243
|
-
return spanContext
|
|
244
|
-
}
|
|
245
290
|
}
|
|
246
291
|
|
|
247
|
-
return this._extractSqsdContext(carrier)
|
|
292
|
+
return spanContext || this._extractSqsdContext(carrier)
|
|
248
293
|
}
|
|
249
294
|
|
|
250
295
|
_extractDatadogContext (carrier) {
|
|
@@ -354,7 +399,7 @@ class TextMapPropagator {
|
|
|
354
399
|
for (const [key, value] of state.entries()) {
|
|
355
400
|
switch (key) {
|
|
356
401
|
case 'p': {
|
|
357
|
-
spanContext._trace.tags[
|
|
402
|
+
spanContext._trace.tags[tags.DD_PARENT_ID] = value
|
|
358
403
|
break
|
|
359
404
|
}
|
|
360
405
|
case 's': {
|
|
@@ -387,8 +432,8 @@ class TextMapPropagator {
|
|
|
387
432
|
}
|
|
388
433
|
})
|
|
389
434
|
|
|
390
|
-
if (!spanContext._trace.tags[
|
|
391
|
-
spanContext._trace.tags[
|
|
435
|
+
if (!spanContext._trace.tags[tags.DD_PARENT_ID]) {
|
|
436
|
+
spanContext._trace.tags[tags.DD_PARENT_ID] = zeroTraceId
|
|
392
437
|
}
|
|
393
438
|
|
|
394
439
|
this._extractBaggageItems(carrier, spanContext)
|
|
@@ -531,7 +576,7 @@ class TextMapPropagator {
|
|
|
531
576
|
|
|
532
577
|
const tid = traceId.substring(0, 16)
|
|
533
578
|
|
|
534
|
-
if (tid ===
|
|
579
|
+
if (tid === zeroTraceId) return
|
|
535
580
|
|
|
536
581
|
spanContext._trace.tags['_dd.p.tid'] = tid
|
|
537
582
|
}
|
|
@@ -54,6 +54,7 @@ module.exports = {
|
|
|
54
54
|
get 'microgateway-core' () { return require('../../../datadog-plugin-microgateway-core/src') },
|
|
55
55
|
get mocha () { return require('../../../datadog-plugin-mocha/src') },
|
|
56
56
|
get 'mocha-each' () { return require('../../../datadog-plugin-mocha/src') },
|
|
57
|
+
get workerpool () { return require('../../../datadog-plugin-mocha/src') },
|
|
57
58
|
get moleculer () { return require('../../../datadog-plugin-moleculer/src') },
|
|
58
59
|
get mongodb () { return require('../../../datadog-plugin-mongodb-core/src') },
|
|
59
60
|
get 'mongodb-core' () { return require('../../../datadog-plugin-mongodb-core/src') },
|
|
@@ -62,6 +62,7 @@ const JEST_TEST_RUNNER = 'test.jest.test_runner'
|
|
|
62
62
|
const JEST_DISPLAY_NAME = 'test.jest.display_name'
|
|
63
63
|
|
|
64
64
|
const CUCUMBER_IS_PARALLEL = 'test.cucumber.is_parallel'
|
|
65
|
+
const MOCHA_IS_PARALLEL = 'test.mocha.is_parallel'
|
|
65
66
|
|
|
66
67
|
const TEST_ITR_TESTS_SKIPPED = '_dd.ci.itr.tests_skipped'
|
|
67
68
|
const TEST_ITR_SKIPPING_ENABLED = 'test.itr.tests_skipping.enabled'
|
|
@@ -87,6 +88,9 @@ const JEST_WORKER_COVERAGE_PAYLOAD_CODE = 61
|
|
|
87
88
|
// cucumber worker variables
|
|
88
89
|
const CUCUMBER_WORKER_TRACE_PAYLOAD_CODE = 70
|
|
89
90
|
|
|
91
|
+
// mocha worker variables
|
|
92
|
+
const MOCHA_WORKER_TRACE_PAYLOAD_CODE = 80
|
|
93
|
+
|
|
90
94
|
// Early flake detection util strings
|
|
91
95
|
const EFD_STRING = "Retried by Datadog's Early Flake Detection"
|
|
92
96
|
const EFD_TEST_NAME_REGEX = new RegExp(EFD_STRING + ' \\(#\\d+\\): ', 'g')
|
|
@@ -98,6 +102,7 @@ module.exports = {
|
|
|
98
102
|
JEST_TEST_RUNNER,
|
|
99
103
|
JEST_DISPLAY_NAME,
|
|
100
104
|
CUCUMBER_IS_PARALLEL,
|
|
105
|
+
MOCHA_IS_PARALLEL,
|
|
101
106
|
TEST_TYPE,
|
|
102
107
|
TEST_NAME,
|
|
103
108
|
TEST_SUITE,
|
|
@@ -111,6 +116,7 @@ module.exports = {
|
|
|
111
116
|
JEST_WORKER_TRACE_PAYLOAD_CODE,
|
|
112
117
|
JEST_WORKER_COVERAGE_PAYLOAD_CODE,
|
|
113
118
|
CUCUMBER_WORKER_TRACE_PAYLOAD_CODE,
|
|
119
|
+
MOCHA_WORKER_TRACE_PAYLOAD_CODE,
|
|
114
120
|
TEST_SOURCE_START,
|
|
115
121
|
TEST_SKIPPED_BY_ITR,
|
|
116
122
|
TEST_CONFIGURATION_BROWSER_NAME,
|
|
@@ -10,6 +10,8 @@ const {
|
|
|
10
10
|
SAMPLING_MECHANISM_AGENT,
|
|
11
11
|
SAMPLING_MECHANISM_RULE,
|
|
12
12
|
SAMPLING_MECHANISM_MANUAL,
|
|
13
|
+
SAMPLING_MECHANISM_REMOTE_USER,
|
|
14
|
+
SAMPLING_MECHANISM_REMOTE_DYNAMIC,
|
|
13
15
|
SAMPLING_RULE_DECISION,
|
|
14
16
|
SAMPLING_LIMIT_DECISION,
|
|
15
17
|
SAMPLING_AGENT_DECISION,
|
|
@@ -41,9 +43,9 @@ class PrioritySampler {
|
|
|
41
43
|
this.update({})
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
configure (env, { sampleRate, rateLimit = 100, rules = [] } = {}) {
|
|
46
|
+
configure (env, { sampleRate, provenance = undefined, rateLimit = 100, rules = [] } = {}) {
|
|
45
47
|
this._env = env
|
|
46
|
-
this._rules = this._normalizeRules(rules, sampleRate, rateLimit)
|
|
48
|
+
this._rules = this._normalizeRules(rules, sampleRate, rateLimit, provenance)
|
|
47
49
|
this._limiter = new RateLimiter(rateLimit)
|
|
48
50
|
|
|
49
51
|
setSamplingRules(this._rules)
|
|
@@ -137,6 +139,8 @@ class PrioritySampler {
|
|
|
137
139
|
_getPriorityByRule (context, rule) {
|
|
138
140
|
context._trace[SAMPLING_RULE_DECISION] = rule.sampleRate
|
|
139
141
|
context._sampling.mechanism = SAMPLING_MECHANISM_RULE
|
|
142
|
+
if (rule.provenance === 'customer') context._sampling.mechanism = SAMPLING_MECHANISM_REMOTE_USER
|
|
143
|
+
if (rule.provenance === 'dynamic') context._sampling.mechanism = SAMPLING_MECHANISM_REMOTE_DYNAMIC
|
|
140
144
|
|
|
141
145
|
return rule.sample() && this._isSampledByRateLimit(context)
|
|
142
146
|
? USER_KEEP
|
|
@@ -181,11 +185,11 @@ class PrioritySampler {
|
|
|
181
185
|
}
|
|
182
186
|
}
|
|
183
187
|
|
|
184
|
-
_normalizeRules (rules, sampleRate, rateLimit) {
|
|
188
|
+
_normalizeRules (rules, sampleRate, rateLimit, provenance) {
|
|
185
189
|
rules = [].concat(rules || [])
|
|
186
190
|
|
|
187
191
|
return rules
|
|
188
|
-
.concat({ sampleRate, maxPerSecond: rateLimit })
|
|
192
|
+
.concat({ sampleRate, maxPerSecond: rateLimit, provenance })
|
|
189
193
|
.map(rule => ({ ...rule, sampleRate: parseFloat(rule.sampleRate) }))
|
|
190
194
|
.filter(rule => !isNaN(rule.sampleRate))
|
|
191
195
|
.map(SamplingRule.from)
|
|
@@ -9,7 +9,7 @@ process.once('beforeExit', () => { profiler.stop() })
|
|
|
9
9
|
module.exports = {
|
|
10
10
|
start: config => {
|
|
11
11
|
const { service, version, env, url, hostname, port, tags, repositoryUrl, commitSHA } = config
|
|
12
|
-
const { enabled, sourceMap, exporters } = config.profiling
|
|
12
|
+
const { enabled, sourceMap, exporters, heuristicsEnabled } = config.profiling
|
|
13
13
|
const logger = {
|
|
14
14
|
debug: (message) => log.debug(message),
|
|
15
15
|
info: (message) => log.info(message),
|
|
@@ -19,6 +19,7 @@ module.exports = {
|
|
|
19
19
|
|
|
20
20
|
return profiler.start({
|
|
21
21
|
enabled,
|
|
22
|
+
heuristicsEnabled,
|
|
22
23
|
service,
|
|
23
24
|
version,
|
|
24
25
|
env,
|
|
@@ -55,7 +55,7 @@ class Profiler extends EventEmitter {
|
|
|
55
55
|
if (this._enabled) return true
|
|
56
56
|
|
|
57
57
|
const config = this._config = new Config(options)
|
|
58
|
-
if (!config.enabled) return false
|
|
58
|
+
if (!config.enabled && !config.heuristicsEnabled) return false
|
|
59
59
|
|
|
60
60
|
this._logger = config.logger
|
|
61
61
|
this._enabled = true
|