dd-trace 5.44.0 → 5.46.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/ci/init.js +8 -0
- package/ext/exporters.d.ts +2 -1
- package/ext/exporters.js +2 -1
- package/package.json +3 -3
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +41 -1
- package/packages/datadog-instrumentations/src/mariadb.js +19 -0
- package/packages/datadog-instrumentations/src/playwright.js +321 -46
- package/packages/datadog-instrumentations/src/router.js +1 -7
- package/packages/datadog-plugin-mongodb-core/src/index.js +20 -0
- package/packages/datadog-plugin-playwright/src/index.js +115 -8
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +39 -15
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -1
- package/packages/dd-trace/src/appsec/rasp/command_injection.js +1 -1
- package/packages/dd-trace/src/appsec/rasp/index.js +4 -2
- package/packages/dd-trace/src/appsec/rasp/lfi.js +1 -1
- package/packages/dd-trace/src/appsec/rasp/sql_injection.js +1 -1
- package/packages/dd-trace/src/appsec/rasp/ssrf.js +1 -1
- package/packages/dd-trace/src/appsec/rasp/utils.js +12 -7
- package/packages/dd-trace/src/appsec/recommended.json +256 -84
- package/packages/dd-trace/src/appsec/reporter.js +6 -4
- package/packages/dd-trace/src/appsec/sdk/track_event.js +7 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +35 -4
- package/packages/dd-trace/src/appsec/telemetry/rasp.js +70 -6
- package/packages/dd-trace/src/appsec/telemetry/user.js +9 -1
- package/packages/dd-trace/src/appsec/telemetry/waf.js +0 -30
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +8 -3
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +6 -4
- package/packages/dd-trace/src/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +102 -22
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +263 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +69 -36
- package/packages/dd-trace/src/debugger/devtools_client/lock.js +8 -0
- package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +1 -7
- package/packages/dd-trace/src/debugger/devtools_client/send.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +15 -10
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +3 -3
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +69 -62
- package/packages/dd-trace/src/debugger/devtools_client/state.js +3 -2
- package/packages/dd-trace/src/debugger/index.js +3 -0
- package/packages/dd-trace/src/dogstatsd.js +94 -77
- package/packages/dd-trace/src/encode/0.4.js +24 -17
- package/packages/dd-trace/src/exporter.js +1 -0
- package/packages/dd-trace/src/format.js +58 -60
- package/packages/dd-trace/src/histogram.js +12 -23
- package/packages/dd-trace/src/llmobs/index.js +3 -0
- package/packages/dd-trace/src/llmobs/telemetry.js +27 -1
- package/packages/dd-trace/src/llmobs/writers/base.js +4 -0
- package/packages/dd-trace/src/llmobs/writers/spans/base.js +3 -3
- package/packages/dd-trace/src/opentelemetry/span.js +4 -4
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/util/test.js +4 -0
- package/packages/dd-trace/src/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/config.js +6 -0
- package/packages/dd-trace/src/profiling/profiler.js +4 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +10 -8
- package/packages/dd-trace/src/remote_config/manager.js +5 -0
- package/packages/dd-trace/src/tagger.js +38 -26
- package/packages/dd-trace/src/util.js +1 -7
|
@@ -6,6 +6,7 @@ const { URL, format } = require('url')
|
|
|
6
6
|
const logger = require('../../log')
|
|
7
7
|
|
|
8
8
|
const { encodeUnicode } = require('../util')
|
|
9
|
+
const telemetry = require('../telemetry')
|
|
9
10
|
const log = require('../../log')
|
|
10
11
|
|
|
11
12
|
class BaseLLMObsWriter {
|
|
@@ -45,6 +46,7 @@ class BaseLLMObsWriter {
|
|
|
45
46
|
append (event, byteLength) {
|
|
46
47
|
if (this._buffer.length >= this._bufferLimit) {
|
|
47
48
|
logger.warn(`${this.constructor.name} event buffer full (limit is ${this._bufferLimit}), dropping event`)
|
|
49
|
+
telemetry.recordDroppedPayload(1, this._eventType, 'buffer_full')
|
|
48
50
|
return
|
|
49
51
|
}
|
|
50
52
|
|
|
@@ -76,10 +78,12 @@ class BaseLLMObsWriter {
|
|
|
76
78
|
logger.error(
|
|
77
79
|
'Error sending %d LLMObs %s events to %s: %s', events.length, this._eventType, this._url, err.message, err
|
|
78
80
|
)
|
|
81
|
+
telemetry.recordDroppedPayload(events.length, this._eventType, 'request_error')
|
|
79
82
|
} else if (code >= 300) {
|
|
80
83
|
logger.error(
|
|
81
84
|
'Error sending %d LLMObs %s events to %s: %s', events.length, this._eventType, this._url, code
|
|
82
85
|
)
|
|
86
|
+
telemetry.recordDroppedPayload(events.length, this._eventType, 'http_error')
|
|
83
87
|
} else {
|
|
84
88
|
logger.debug(`Sent ${events.length} LLMObs ${this._eventType} events to ${this._url}`)
|
|
85
89
|
}
|
|
@@ -41,12 +41,12 @@ class LLMObsSpanWriter extends BaseWriter {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
makePayload (events) {
|
|
44
|
-
return {
|
|
44
|
+
return events.map(event => ({
|
|
45
45
|
'_dd.stage': 'raw',
|
|
46
46
|
'_dd.tracer_version': tracerVersion,
|
|
47
47
|
event_type: this._eventType,
|
|
48
|
-
spans:
|
|
49
|
-
}
|
|
48
|
+
spans: [event]
|
|
49
|
+
}))
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
_truncateSpanEvent (event) {
|
|
@@ -9,7 +9,7 @@ const { timeInputToHrTime } = require('@opentelemetry/core')
|
|
|
9
9
|
|
|
10
10
|
const tracer = require('../../')
|
|
11
11
|
const DatadogSpan = require('../opentracing/span')
|
|
12
|
-
const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../constants')
|
|
12
|
+
const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK, IGNORE_OTEL_ERROR } = require('../constants')
|
|
13
13
|
const { SERVICE_NAME, RESOURCE_NAME } = require('../../../../ext/tags')
|
|
14
14
|
const kinds = require('../../../../ext/kinds')
|
|
15
15
|
|
|
@@ -237,7 +237,8 @@ class Span {
|
|
|
237
237
|
this._hasStatus = true
|
|
238
238
|
if (code === 2) {
|
|
239
239
|
this._ddSpan.addTags({
|
|
240
|
-
[ERROR_MESSAGE]: message
|
|
240
|
+
[ERROR_MESSAGE]: message,
|
|
241
|
+
[IGNORE_OTEL_ERROR]: false
|
|
241
242
|
})
|
|
242
243
|
}
|
|
243
244
|
}
|
|
@@ -278,12 +279,11 @@ class Span {
|
|
|
278
279
|
}
|
|
279
280
|
|
|
280
281
|
recordException (exception, timeInput) {
|
|
281
|
-
// HACK: identifier is added so that trace.error remains unchanged after a call to otel.recordException
|
|
282
282
|
this._ddSpan.addTags({
|
|
283
283
|
[ERROR_TYPE]: exception.name,
|
|
284
284
|
[ERROR_MESSAGE]: exception.message,
|
|
285
285
|
[ERROR_STACK]: exception.stack,
|
|
286
|
-
|
|
286
|
+
[IGNORE_OTEL_ERROR]: this._ddSpan.context()._tags[IGNORE_OTEL_ERROR] ?? true
|
|
287
287
|
})
|
|
288
288
|
const attributes = {}
|
|
289
289
|
if (exception.message) attributes['exception.message'] = exception.message
|
|
@@ -133,6 +133,7 @@ module.exports = class PluginManager {
|
|
|
133
133
|
dbmPropagationMode,
|
|
134
134
|
dsmEnabled,
|
|
135
135
|
clientIpEnabled,
|
|
136
|
+
clientIpHeader,
|
|
136
137
|
memcachedCommandEnabled,
|
|
137
138
|
ciVisibilityTestSessionName,
|
|
138
139
|
ciVisAgentlessLogSubmissionEnabled,
|
|
@@ -148,6 +149,7 @@ module.exports = class PluginManager {
|
|
|
148
149
|
site,
|
|
149
150
|
url,
|
|
150
151
|
headers: headerTags || [],
|
|
152
|
+
clientIpHeader,
|
|
151
153
|
ciVisibilityTestSessionName,
|
|
152
154
|
ciVisAgentlessLogSubmissionEnabled,
|
|
153
155
|
isTestDynamicInstrumentationEnabled,
|
|
@@ -96,6 +96,9 @@ const CUCUMBER_WORKER_TRACE_PAYLOAD_CODE = 70
|
|
|
96
96
|
// mocha worker variables
|
|
97
97
|
const MOCHA_WORKER_TRACE_PAYLOAD_CODE = 80
|
|
98
98
|
|
|
99
|
+
// playwright worker variables
|
|
100
|
+
const PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE = 90
|
|
101
|
+
|
|
99
102
|
// Early flake detection util strings
|
|
100
103
|
const EFD_STRING = "Retried by Datadog's Early Flake Detection"
|
|
101
104
|
const EFD_TEST_NAME_REGEX = new RegExp(EFD_STRING + ' \\(#\\d+\\): ', 'g')
|
|
@@ -162,6 +165,7 @@ module.exports = {
|
|
|
162
165
|
JEST_WORKER_LOGS_PAYLOAD_CODE,
|
|
163
166
|
CUCUMBER_WORKER_TRACE_PAYLOAD_CODE,
|
|
164
167
|
MOCHA_WORKER_TRACE_PAYLOAD_CODE,
|
|
168
|
+
PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE,
|
|
165
169
|
TEST_SOURCE_START,
|
|
166
170
|
TEST_SKIPPED_BY_ITR,
|
|
167
171
|
TEST_IS_NEW,
|
|
@@ -14,7 +14,7 @@ module.exports = {
|
|
|
14
14
|
debug: (message) => log.debug(message),
|
|
15
15
|
info: (message) => log.info(message),
|
|
16
16
|
warn: (message) => log.warn(message),
|
|
17
|
-
error: (
|
|
17
|
+
error: (...args) => log.error(...args)
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const libraryInjected = injectionEnabled.length > 0
|
|
@@ -15,6 +15,7 @@ const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../plugins/util/tags')
|
|
|
15
15
|
const { tagger } = require('./tagger')
|
|
16
16
|
const { isFalse, isTrue } = require('../util')
|
|
17
17
|
const { getAzureTagsFromMetadata, getAzureAppMetadata } = require('../azure_metadata')
|
|
18
|
+
const satisfies = require('semifies')
|
|
18
19
|
|
|
19
20
|
class Config {
|
|
20
21
|
constructor (options = {}) {
|
|
@@ -22,6 +23,7 @@ class Config {
|
|
|
22
23
|
DD_AGENT_HOST,
|
|
23
24
|
DD_ENV,
|
|
24
25
|
DD_INTERNAL_PROFILING_TIMELINE_SAMPLING_ENABLED, // used for testing
|
|
26
|
+
DD_PROFILING_ASYNC_ID_ENABLED,
|
|
25
27
|
DD_PROFILING_CODEHOTSPOTS_ENABLED,
|
|
26
28
|
DD_PROFILING_CPU_ENABLED,
|
|
27
29
|
DD_PROFILING_DEBUG_SOURCE_MAPS,
|
|
@@ -179,6 +181,10 @@ class Config {
|
|
|
179
181
|
this.timelineSamplingEnabled = isTrue(coalesce(options.timelineSamplingEnabled,
|
|
180
182
|
DD_INTERNAL_PROFILING_TIMELINE_SAMPLING_ENABLED, true))
|
|
181
183
|
|
|
184
|
+
// Async ID gathering only works reliably on Node >= 22.10.0
|
|
185
|
+
this.asyncIdEnabled = isTrue(coalesce(options.asyncIdEnabled,
|
|
186
|
+
DD_PROFILING_ASYNC_ID_ENABLED, this.timelineEnabled && satisfies(process.versions.node, '>=22.10.0')))
|
|
187
|
+
|
|
182
188
|
this.codeHotspotsEnabled = isTrue(coalesce(options.codeHotspotsEnabled,
|
|
183
189
|
DD_PROFILING_CODEHOTSPOTS_ENABLED,
|
|
184
190
|
DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED, samplingContextsAvailable))
|
|
@@ -18,9 +18,9 @@ function maybeSourceMap (sourceMap, SourceMapper, debug) {
|
|
|
18
18
|
], debug)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
function logError (logger,
|
|
21
|
+
function logError (logger, ...args) {
|
|
22
22
|
if (logger) {
|
|
23
|
-
logger.error(
|
|
23
|
+
logger.error(...args)
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -52,7 +52,8 @@ class Profiler extends EventEmitter {
|
|
|
52
52
|
|
|
53
53
|
start (options) {
|
|
54
54
|
return this._start(options).catch((err) => {
|
|
55
|
-
logError(options.logger,
|
|
55
|
+
logError(options.logger, 'Error starting profiler. For troubleshooting tips, see ' +
|
|
56
|
+
'<https://dtdg.co/nodejs-profiler-troubleshooting>', err)
|
|
56
57
|
return false
|
|
57
58
|
})
|
|
58
59
|
}
|
|
@@ -70,12 +70,14 @@ function ensureChannelsActivated () {
|
|
|
70
70
|
class NativeWallProfiler {
|
|
71
71
|
constructor (options = {}) {
|
|
72
72
|
this.type = 'wall'
|
|
73
|
-
this.
|
|
74
|
-
this._flushIntervalMillis = options.flushInterval || 60 * 1e3 // 60 seconds
|
|
73
|
+
this._asyncIdEnabled = !!options.asyncIdEnabled
|
|
75
74
|
this._codeHotspotsEnabled = !!options.codeHotspotsEnabled
|
|
75
|
+
this._cpuProfilingEnabled = !!options.cpuProfilingEnabled
|
|
76
76
|
this._endpointCollectionEnabled = !!options.endpointCollectionEnabled
|
|
77
|
+
this._flushIntervalMillis = options.flushInterval || 60 * 1e3 // 60 seconds
|
|
78
|
+
this._samplingIntervalMicros = options.samplingInterval || 1e6 / 99 // 99hz
|
|
77
79
|
this._timelineEnabled = !!options.timelineEnabled
|
|
78
|
-
this.
|
|
80
|
+
this._v8ProfilerBugWorkaroundEnabled = !!options.v8ProfilerBugWorkaroundEnabled
|
|
79
81
|
// We need to capture span data into the sample context for either code hotspots
|
|
80
82
|
// or endpoint collection.
|
|
81
83
|
this._captureSpanData = this._codeHotspotsEnabled || this._endpointCollectionEnabled
|
|
@@ -84,7 +86,6 @@ class NativeWallProfiler {
|
|
|
84
86
|
// timestamps require the sample contexts feature in the pprof wall profiler), or
|
|
85
87
|
// cpu profiling is enabled.
|
|
86
88
|
this._withContexts = this._captureSpanData || this._timelineEnabled || this._cpuProfilingEnabled
|
|
87
|
-
this._v8ProfilerBugWorkaroundEnabled = !!options.v8ProfilerBugWorkaroundEnabled
|
|
88
89
|
this._mapper = undefined
|
|
89
90
|
this._pprof = undefined
|
|
90
91
|
|
|
@@ -127,13 +128,14 @@ class NativeWallProfiler {
|
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
this._pprof.time.start({
|
|
130
|
-
|
|
131
|
+
collectAsyncId: this._asyncIdEnabled,
|
|
132
|
+
collectCpuTime: this._cpuProfilingEnabled,
|
|
131
133
|
durationMillis: this._flushIntervalMillis,
|
|
134
|
+
intervalMicros: this._samplingIntervalMicros,
|
|
135
|
+
lineNumbers: false,
|
|
132
136
|
sourceMapper: this._mapper,
|
|
133
137
|
withContexts: this._withContexts,
|
|
134
|
-
|
|
135
|
-
workaroundV8Bug: this._v8ProfilerBugWorkaroundEnabled,
|
|
136
|
-
collectCpuTime: this._cpuProfilingEnabled
|
|
138
|
+
workaroundV8Bug: this._v8ProfilerBugWorkaroundEnabled
|
|
137
139
|
})
|
|
138
140
|
|
|
139
141
|
if (this._withContexts) {
|
|
@@ -10,6 +10,7 @@ const { getExtraServices } = require('../service-naming/extra-services')
|
|
|
10
10
|
const { UNACKNOWLEDGED, ACKNOWLEDGED, ERROR } = require('./apply_states')
|
|
11
11
|
const Scheduler = require('./scheduler')
|
|
12
12
|
const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../plugins/util/tags')
|
|
13
|
+
const tagger = require('../tagger')
|
|
13
14
|
|
|
14
15
|
const clientId = uuid()
|
|
15
16
|
|
|
@@ -34,6 +35,10 @@ class RemoteConfigManager extends EventEmitter {
|
|
|
34
35
|
port: config.port
|
|
35
36
|
}))
|
|
36
37
|
|
|
38
|
+
tagger.add(config.tags, {
|
|
39
|
+
'_dd.rc.client_id': clientId
|
|
40
|
+
})
|
|
41
|
+
|
|
37
42
|
const tags = config.repositoryUrl
|
|
38
43
|
? {
|
|
39
44
|
...config.tags,
|
|
@@ -1,43 +1,55 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const constants = require('./constants')
|
|
4
3
|
const log = require('./log')
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
|
|
5
|
+
function addNonEmpty (carrier, key, value) {
|
|
6
|
+
if (key !== '') {
|
|
7
|
+
carrier[key] = value
|
|
8
|
+
}
|
|
9
|
+
}
|
|
8
10
|
|
|
9
11
|
function add (carrier, keyValuePairs) {
|
|
10
|
-
if (!carrier
|
|
12
|
+
if (!carrier) return
|
|
11
13
|
|
|
12
|
-
if (Array.isArray(keyValuePairs)) {
|
|
13
|
-
return keyValuePairs.forEach(tags => add(carrier, tags))
|
|
14
|
-
}
|
|
15
14
|
try {
|
|
16
15
|
if (typeof keyValuePairs === 'string') {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
let valueStart = 0
|
|
17
|
+
let keyStart = 0
|
|
18
|
+
|
|
19
|
+
for (let i = 0; i < keyValuePairs.length; i++) {
|
|
20
|
+
const char = keyValuePairs[i]
|
|
21
|
+
|
|
22
|
+
if (char === ':') {
|
|
23
|
+
if (valueStart === 0) {
|
|
24
|
+
valueStart = i
|
|
25
|
+
}
|
|
26
|
+
} else if (char === ',') {
|
|
27
|
+
valueStart ||= i
|
|
28
|
+
addNonEmpty(
|
|
29
|
+
carrier,
|
|
30
|
+
keyValuePairs.slice(keyStart, valueStart).trim(),
|
|
31
|
+
keyValuePairs.slice(valueStart + 1, i).trim()
|
|
32
|
+
)
|
|
33
|
+
keyStart = i + 1
|
|
34
|
+
valueStart = 0
|
|
26
35
|
}
|
|
36
|
+
}
|
|
27
37
|
|
|
28
|
-
|
|
38
|
+
if (keyValuePairs.at(-1) !== ',') {
|
|
39
|
+
valueStart ||= keyValuePairs.length
|
|
40
|
+
addNonEmpty(
|
|
41
|
+
carrier,
|
|
42
|
+
keyValuePairs.slice(keyStart, valueStart).trim(),
|
|
43
|
+
keyValuePairs.slice(valueStart + 1).trim()
|
|
44
|
+
)
|
|
29
45
|
}
|
|
46
|
+
} else if (Array.isArray(keyValuePairs)) {
|
|
47
|
+
return keyValuePairs.forEach(tags => add(carrier, tags))
|
|
30
48
|
} else {
|
|
31
|
-
// HACK: to ensure otel.recordException does not influence trace.error
|
|
32
|
-
if (ERROR_MESSAGE in keyValuePairs || ERROR_STACK in keyValuePairs || ERROR_TYPE in keyValuePairs) {
|
|
33
|
-
if (!('doNotSetTraceError' in keyValuePairs)) {
|
|
34
|
-
carrier.setTraceError = true
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
49
|
Object.assign(carrier, keyValuePairs)
|
|
38
50
|
}
|
|
39
|
-
} catch (
|
|
40
|
-
log.error('Error adding tags',
|
|
51
|
+
} catch (error) {
|
|
52
|
+
log.error('Error adding tags', error)
|
|
41
53
|
}
|
|
42
54
|
}
|
|
43
55
|
|
|
@@ -13,13 +13,7 @@ function isFalse (str) {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
function isError (value) {
|
|
16
|
-
|
|
17
|
-
return true
|
|
18
|
-
}
|
|
19
|
-
if (value && value.message) {
|
|
20
|
-
return true
|
|
21
|
-
}
|
|
22
|
-
return false
|
|
16
|
+
return Boolean(value?.message || value instanceof Error)
|
|
23
17
|
}
|
|
24
18
|
|
|
25
19
|
// Matches a glob pattern to a given subject string
|