dd-trace 5.51.0 → 5.53.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 -6
- package/README.md +5 -0
- package/index.d.ts +88 -6
- package/package.json +3 -9
- package/packages/datadog-instrumentations/src/amqplib.js +8 -5
- package/packages/datadog-instrumentations/src/child_process.js +2 -1
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +406 -0
- package/packages/datadog-instrumentations/src/couchbase.js +2 -1
- package/packages/datadog-instrumentations/src/cucumber.js +43 -45
- package/packages/datadog-instrumentations/src/dns.js +16 -14
- package/packages/datadog-instrumentations/src/express.js +2 -6
- package/packages/datadog-instrumentations/src/fs.js +43 -51
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +17 -12
- package/packages/datadog-instrumentations/src/http/client.js +2 -1
- package/packages/datadog-instrumentations/src/iovalkey.js +51 -0
- package/packages/datadog-instrumentations/src/jest.js +53 -40
- package/packages/datadog-instrumentations/src/kafkajs.js +21 -8
- package/packages/datadog-instrumentations/src/mocha/main.js +33 -46
- package/packages/datadog-instrumentations/src/mocha/utils.js +76 -74
- package/packages/datadog-instrumentations/src/mysql2.js +3 -1
- package/packages/datadog-instrumentations/src/net.js +27 -29
- package/packages/datadog-instrumentations/src/next.js +6 -14
- package/packages/datadog-instrumentations/src/pg.js +15 -7
- package/packages/datadog-instrumentations/src/playwright.js +64 -67
- package/packages/datadog-instrumentations/src/url.js +9 -17
- package/packages/datadog-instrumentations/src/vitest.js +66 -72
- package/packages/datadog-plugin-confluentinc-kafka-javascript/src/batch-consumer.js +11 -0
- package/packages/datadog-plugin-confluentinc-kafka-javascript/src/consumer.js +11 -0
- package/packages/datadog-plugin-confluentinc-kafka-javascript/src/index.js +19 -0
- package/packages/datadog-plugin-confluentinc-kafka-javascript/src/producer.js +11 -0
- package/packages/datadog-plugin-cucumber/src/index.js +32 -18
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +3 -0
- package/packages/datadog-plugin-dns/src/lookup.js +10 -5
- package/packages/datadog-plugin-dns/src/lookup_service.js +6 -2
- package/packages/datadog-plugin-dns/src/resolve.js +5 -2
- package/packages/datadog-plugin-dns/src/reverse.js +6 -2
- package/packages/datadog-plugin-fs/src/index.js +9 -2
- package/packages/datadog-plugin-iovalkey/src/index.js +18 -0
- package/packages/datadog-plugin-jest/src/index.js +17 -8
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -1
- package/packages/datadog-plugin-kafkajs/src/consumer.js +12 -21
- package/packages/datadog-plugin-kafkajs/src/producer.js +12 -5
- package/packages/datadog-plugin-kafkajs/src/utils.js +27 -0
- package/packages/datadog-plugin-langchain/src/index.js +0 -1
- package/packages/datadog-plugin-mocha/src/index.js +58 -35
- package/packages/datadog-plugin-net/src/ipc.js +6 -4
- package/packages/datadog-plugin-net/src/tcp.js +15 -9
- package/packages/datadog-plugin-pg/src/index.js +5 -1
- package/packages/datadog-plugin-playwright/src/index.js +29 -20
- package/packages/datadog-plugin-redis/src/index.js +8 -3
- package/packages/datadog-plugin-vitest/src/index.js +67 -44
- package/packages/datadog-shimmer/src/shimmer.js +164 -33
- package/packages/dd-trace/src/appsec/api_security_sampler.js +20 -12
- package/packages/dd-trace/src/appsec/graphql.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +14 -9
- package/packages/dd-trace/src/appsec/index.js +15 -12
- package/packages/dd-trace/src/appsec/rasp/index.js +4 -2
- package/packages/dd-trace/src/appsec/rasp/utils.js +11 -6
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -2
- package/packages/dd-trace/src/appsec/telemetry/index.js +1 -2
- package/packages/dd-trace/src/appsec/telemetry/rasp.js +0 -9
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +6 -6
- package/packages/dd-trace/src/baggage.js +36 -0
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +4 -2
- package/packages/dd-trace/src/config.js +14 -2
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +61 -7
- package/packages/dd-trace/src/debugger/devtools_client/index.js +10 -26
- package/packages/dd-trace/src/debugger/devtools_client/send.js +8 -7
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +15 -7
- package/packages/dd-trace/src/debugger/devtools_client/state.js +22 -2
- package/packages/dd-trace/src/dogstatsd.js +2 -0
- package/packages/dd-trace/src/exporters/common/docker.js +13 -31
- package/packages/dd-trace/src/guardrails/telemetry.js +2 -5
- package/packages/dd-trace/src/llmobs/tagger.js +3 -3
- package/packages/dd-trace/src/llmobs/writers/base.js +33 -12
- package/packages/dd-trace/src/noop/proxy.js +5 -0
- package/packages/dd-trace/src/opentelemetry/context_manager.js +2 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +17 -9
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/index.js +4 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +9 -20
- package/packages/dd-trace/src/plugins/outbound.js +11 -3
- package/packages/dd-trace/src/plugins/tracing.js +8 -4
- package/packages/dd-trace/src/plugins/util/test.js +1 -1
- package/packages/dd-trace/src/profiling/exporter_cli.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookup.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookupservice.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_resolve.js +2 -2
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_reverse.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +15 -14
- package/packages/dd-trace/src/proxy.js +12 -4
- package/packages/dd-trace/src/serverless.js +0 -48
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +8 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +8 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
- package/packages/dd-trace/src/standalone/product.js +3 -5
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { storage } = require('../../datadog-core')
|
|
4
|
+
const baggageStorage = storage('baggage')
|
|
5
|
+
|
|
6
|
+
function setBaggageItem (key, value) {
|
|
7
|
+
storage('baggage').enterWith({ ...baggageStorage.getStore(), [key]: value })
|
|
8
|
+
return storage('baggage').getStore()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getBaggageItem (key) {
|
|
12
|
+
return storage('baggage').getStore()?.[key]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getAllBaggageItems () {
|
|
16
|
+
return storage('baggage').getStore()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function removeBaggageItem (keyToRemove) {
|
|
20
|
+
const { [keyToRemove]: _, ...newBaggage } = storage('baggage').getStore()
|
|
21
|
+
storage('baggage').enterWith(newBaggage)
|
|
22
|
+
return newBaggage
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function removeAllBaggageItems () {
|
|
26
|
+
storage('baggage').enterWith({})
|
|
27
|
+
return storage('baggage').getStore()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = {
|
|
31
|
+
setBaggageItem,
|
|
32
|
+
getBaggageItem,
|
|
33
|
+
getAllBaggageItems,
|
|
34
|
+
removeBaggageItem,
|
|
35
|
+
removeAllBaggageItems
|
|
36
|
+
}
|
|
@@ -7,7 +7,8 @@ function getTestManagementTests ({
|
|
|
7
7
|
evpProxyPrefix,
|
|
8
8
|
isGzipCompatible,
|
|
9
9
|
repositoryUrl,
|
|
10
|
-
commitMessage
|
|
10
|
+
commitMessage,
|
|
11
|
+
sha
|
|
11
12
|
}, done) {
|
|
12
13
|
const options = {
|
|
13
14
|
path: '/api/v2/test/libraries/test-management/tests',
|
|
@@ -41,7 +42,8 @@ function getTestManagementTests ({
|
|
|
41
42
|
type: 'ci_app_libraries_tests_request',
|
|
42
43
|
attributes: {
|
|
43
44
|
repository_url: repositoryUrl,
|
|
44
|
-
commit_message: commitMessage
|
|
45
|
+
commit_message: commitMessage,
|
|
46
|
+
sha
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
})
|
|
@@ -139,7 +139,7 @@ const qsRegex = '(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private
|
|
|
139
139
|
// eslint-disable-next-line @stylistic/js/max-len
|
|
140
140
|
const defaultWafObfuscatorKeyRegex = '(?i)pass|pw(?:or)?d|secret|(?:api|private|public|access)[_-]?key|token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)|bearer|authorization|jsessionid|phpsessid|asp\\.net[_-]sessionid|sid|jwt'
|
|
141
141
|
// eslint-disable-next-line @stylistic/js/max-len
|
|
142
|
-
const defaultWafObfuscatorValueRegex = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:[_-]?phrase)?|secret(?:[_-]?key)?|(?:(?:api|private|public|access)[_-]?)key(?:[_-]?id)?|(?:(?:auth|access|id|refresh)[_-]?)?token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?|jsessionid|phpsessid|asp\\.net(?:[_-]|-)sessionid|sid|jwt)(?:\\s*=[
|
|
142
|
+
const defaultWafObfuscatorValueRegex = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:[_-]?phrase)?|secret(?:[_-]?key)?|(?:(?:api|private|public|access)[_-]?)key(?:[_-]?id)?|(?:(?:auth|access|id|refresh)[_-]?)?token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?|jsessionid|phpsessid|asp\\.net(?:[_-]|-)sessionid|sid|jwt)(?:\\s*=([^;&]+)|"\\s*:\\s*("[^"]+"|\\d+))|bearer\\s+([a-z0-9\\._\\-]+)|token\\s*:\\s*([a-z0-9]{13})|gh[opsu]_([0-9a-zA-Z]{36})|ey[I-L][\\w=-]+\\.(ey[I-L][\\w=-]+(?:\\.[\\w.+\\/=-]+)?)|[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}([^\\-]+)[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY|ssh-rsa\\s*([a-z0-9\\/\\.+]{100,})'
|
|
143
143
|
const runtimeId = uuid()
|
|
144
144
|
|
|
145
145
|
function maybeFile (filepath) {
|
|
@@ -481,7 +481,8 @@ class Config {
|
|
|
481
481
|
this._setValue(defaults, 'clientIpEnabled', false)
|
|
482
482
|
this._setValue(defaults, 'clientIpHeader', null)
|
|
483
483
|
this._setValue(defaults, 'crashtracking.enabled', true)
|
|
484
|
-
this._setValue(defaults, 'codeOriginForSpans.enabled',
|
|
484
|
+
this._setValue(defaults, 'codeOriginForSpans.enabled', true)
|
|
485
|
+
this._setValue(defaults, 'codeOriginForSpans.experimental.exit_spans.enabled', false)
|
|
485
486
|
this._setValue(defaults, 'dbmPropagationMode', 'disabled')
|
|
486
487
|
this._setValue(defaults, 'dogstatsd.hostname', '127.0.0.1')
|
|
487
488
|
this._setValue(defaults, 'dogstatsd.port', '8125')
|
|
@@ -660,6 +661,7 @@ class Config {
|
|
|
660
661
|
DD_APPSEC_WAF_TIMEOUT,
|
|
661
662
|
DD_CRASHTRACKING_ENABLED,
|
|
662
663
|
DD_CODE_ORIGIN_FOR_SPANS_ENABLED,
|
|
664
|
+
DD_CODE_ORIGIN_FOR_SPANS_EXPERIMENTAL_EXIT_SPANS_ENABLED,
|
|
663
665
|
DD_DATA_STREAMS_ENABLED,
|
|
664
666
|
DD_DBM_PROPAGATION_MODE,
|
|
665
667
|
DD_DOGSTATSD_HOSTNAME,
|
|
@@ -825,6 +827,11 @@ class Config {
|
|
|
825
827
|
!this._isInServerlessEnvironment()
|
|
826
828
|
))
|
|
827
829
|
this._setBoolean(env, 'codeOriginForSpans.enabled', DD_CODE_ORIGIN_FOR_SPANS_ENABLED)
|
|
830
|
+
this._setBoolean(
|
|
831
|
+
env,
|
|
832
|
+
'codeOriginForSpans.experimental.exit_spans.enabled',
|
|
833
|
+
DD_CODE_ORIGIN_FOR_SPANS_EXPERIMENTAL_EXIT_SPANS_ENABLED
|
|
834
|
+
)
|
|
828
835
|
this._setString(env, 'dbmPropagationMode', DD_DBM_PROPAGATION_MODE)
|
|
829
836
|
this._setString(env, 'dogstatsd.hostname', DD_DOGSTATSD_HOST || DD_DOGSTATSD_HOSTNAME)
|
|
830
837
|
this._setString(env, 'dogstatsd.port', DD_DOGSTATSD_PORT)
|
|
@@ -1029,6 +1036,11 @@ class Config {
|
|
|
1029
1036
|
this._setValue(opts, 'baggageMaxBytes', options.baggageMaxBytes)
|
|
1030
1037
|
this._setValue(opts, 'baggageMaxItems', options.baggageMaxItems)
|
|
1031
1038
|
this._setBoolean(opts, 'codeOriginForSpans.enabled', options.codeOriginForSpans?.enabled)
|
|
1039
|
+
this._setBoolean(
|
|
1040
|
+
opts,
|
|
1041
|
+
'codeOriginForSpans.experimental.exit_spans.enabled',
|
|
1042
|
+
options.codeOriginForSpans?.experimental?.exit_spans?.enabled
|
|
1043
|
+
)
|
|
1032
1044
|
this._setString(opts, 'dbmPropagationMode', options.dbmPropagationMode)
|
|
1033
1045
|
if (options.dogstatsd) {
|
|
1034
1046
|
this._setString(opts, 'dogstatsd.hostname', options.dogstatsd.hostname)
|
|
@@ -5,10 +5,35 @@ const { getGeneratedPosition } = require('./source-maps')
|
|
|
5
5
|
const session = require('./session')
|
|
6
6
|
const { compile: compileCondition, compileSegments, templateRequiresEvaluation } = require('./condition')
|
|
7
7
|
const { MAX_SNAPSHOTS_PER_SECOND_PER_PROBE, MAX_NON_SNAPSHOTS_PER_SECOND_PER_PROBE } = require('./defaults')
|
|
8
|
-
const {
|
|
8
|
+
const {
|
|
9
|
+
findScriptFromPartialPath,
|
|
10
|
+
clearState,
|
|
11
|
+
locationToBreakpoint,
|
|
12
|
+
breakpointToProbes,
|
|
13
|
+
probeToLocation
|
|
14
|
+
} = require('./state')
|
|
9
15
|
const log = require('../../log')
|
|
10
16
|
|
|
11
17
|
let sessionStarted = false
|
|
18
|
+
const probes = new Map()
|
|
19
|
+
let scriptLoadingStabilizedResolve
|
|
20
|
+
const scriptLoadingStabilized = new Promise((resolve) => { scriptLoadingStabilizedResolve = resolve })
|
|
21
|
+
|
|
22
|
+
// There's a race condition when a probe is first added, where the actual script that the probe is supposed to match
|
|
23
|
+
// hasn't been loaded yet. This will result in either the probe not being added at all, or an incorrect script being
|
|
24
|
+
// matched as the probe target.
|
|
25
|
+
//
|
|
26
|
+
// Therefore, once new scripts has been loaded, all probes are re-evaluated. If the matched `scriptId` has changed, we
|
|
27
|
+
// simply remove the old probe (if it was added to the wrong script) and apply it again.
|
|
28
|
+
session.on('scriptLoadingStabilized', () => {
|
|
29
|
+
log.debug('[debugger:devtools_client] Re-evaluating probes')
|
|
30
|
+
scriptLoadingStabilizedResolve()
|
|
31
|
+
for (const probe of probes.values()) {
|
|
32
|
+
reEvaluateProbe(probe).catch(err => {
|
|
33
|
+
log.error('[debugger:devtools_client] Error re-evaluating probe %s', probe.id, err)
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
})
|
|
12
37
|
|
|
13
38
|
module.exports = {
|
|
14
39
|
addBreakpoint,
|
|
@@ -18,13 +43,14 @@ module.exports = {
|
|
|
18
43
|
async function addBreakpoint (probe) {
|
|
19
44
|
if (!sessionStarted) await start()
|
|
20
45
|
|
|
46
|
+
probes.set(probe.id, probe)
|
|
47
|
+
|
|
21
48
|
const file = probe.where.sourceFile
|
|
22
49
|
let lineNumber = Number(probe.where.lines[0]) // Tracer doesn't support multiple-line breakpoints
|
|
23
50
|
let columnNumber = 0 // Probes do not contain/support column information
|
|
24
51
|
|
|
25
52
|
// Optimize for sending data to /debugger/v1/input endpoint
|
|
26
53
|
probe.location = { file, lines: [String(lineNumber)] }
|
|
27
|
-
delete probe.where
|
|
28
54
|
|
|
29
55
|
// Optimize for fast calculations when probe is hit
|
|
30
56
|
probe.templateRequiresEvaluation = templateRequiresEvaluation(probe.segments)
|
|
@@ -47,6 +73,8 @@ async function addBreakpoint (probe) {
|
|
|
47
73
|
if (!script) throw new Error(`No loaded script found for ${file} (probe: ${probe.id}, version: ${probe.version})`)
|
|
48
74
|
const { url, scriptId, sourceMapURL, source } = script
|
|
49
75
|
|
|
76
|
+
probe.scriptId = scriptId // Needed for detecting script changes during re-evaluation
|
|
77
|
+
|
|
50
78
|
if (sourceMapURL) {
|
|
51
79
|
log.debug(
|
|
52
80
|
'[debugger:devtools_client] Translating location using source map for %s:%d:%d (probe: %s, version: %d)',
|
|
@@ -109,6 +137,8 @@ async function removeBreakpoint ({ id }) {
|
|
|
109
137
|
throw Error(`Unknown probe id: ${id}`)
|
|
110
138
|
}
|
|
111
139
|
|
|
140
|
+
probes.delete(id)
|
|
141
|
+
|
|
112
142
|
const release = await lock()
|
|
113
143
|
|
|
114
144
|
try {
|
|
@@ -173,23 +203,47 @@ async function updateBreakpoint (breakpoint, probe) {
|
|
|
173
203
|
}
|
|
174
204
|
}
|
|
175
205
|
|
|
176
|
-
function
|
|
206
|
+
async function reEvaluateProbe (probe) {
|
|
207
|
+
const script = findScriptFromPartialPath(probe.where.sourceFile)
|
|
208
|
+
log.debug('[debugger:devtools_client] re-evaluating probe %s: %s => %s', probe.id, probe.scriptId, script?.scriptId)
|
|
209
|
+
|
|
210
|
+
if (probe.scriptId !== script?.scriptId) {
|
|
211
|
+
log.debug('[debugger:devtools_client] Better match found for probe %s, re-evaluating', probe.id)
|
|
212
|
+
if (probeToLocation.has(probe.id)) {
|
|
213
|
+
await removeBreakpoint(probe)
|
|
214
|
+
}
|
|
215
|
+
await addBreakpoint(probe)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function start () {
|
|
177
220
|
sessionStarted = true
|
|
178
|
-
|
|
221
|
+
log.debug('[debugger:devtools_client] Starting debugger')
|
|
222
|
+
await session.post('Debugger.enable')
|
|
223
|
+
|
|
224
|
+
// Wait until there's a pause in script-loading to avoid accidentally adding probes to incorrect scripts. This is not
|
|
225
|
+
// a guarantee, but best effort.
|
|
226
|
+
log.debug('[debugger:devtools_client] Waiting for script-loading to stabilize')
|
|
227
|
+
await scriptLoadingStabilized
|
|
228
|
+
log.debug('[debugger:devtools_client] Script loading stabilized')
|
|
179
229
|
}
|
|
180
230
|
|
|
181
231
|
function stop () {
|
|
182
232
|
sessionStarted = false
|
|
233
|
+
clearState()
|
|
234
|
+
log.debug('[debugger:devtools_client] Stopping debugger')
|
|
183
235
|
return session.post('Debugger.disable')
|
|
184
236
|
}
|
|
185
237
|
|
|
186
238
|
// Only if all probes have a condition can we use a compound condition.
|
|
187
239
|
// Otherwise, we need to evaluate each probe individually once the breakpoint is hit.
|
|
188
|
-
// TODO: Handle errors - if there's 2 conditons, and one fails but the other returns true, we should still pause the
|
|
189
|
-
// breakpoint
|
|
190
240
|
function compileCompoundCondition (probes) {
|
|
241
|
+
if (probes.length === 1) return probes[0].condition
|
|
242
|
+
|
|
191
243
|
return probes.every(p => p.condition)
|
|
192
|
-
? probes
|
|
244
|
+
? probes
|
|
245
|
+
.map((p) => `(() => { try { return ${p.condition} } catch { return false } })()`)
|
|
246
|
+
.join(' || ')
|
|
193
247
|
: undefined
|
|
194
248
|
}
|
|
195
249
|
|
|
@@ -6,7 +6,7 @@ const session = require('./session')
|
|
|
6
6
|
const { getLocalStateForCallFrame } = require('./snapshot')
|
|
7
7
|
const send = require('./send')
|
|
8
8
|
const { getStackFromCallFrames } = require('./state')
|
|
9
|
-
const { ackEmitting
|
|
9
|
+
const { ackEmitting } = require('./status')
|
|
10
10
|
const { parentThreadId } = require('./config')
|
|
11
11
|
const { MAX_SNAPSHOTS_PER_SECOND_GLOBALLY } = require('./defaults')
|
|
12
12
|
const log = require('../../log')
|
|
@@ -36,7 +36,6 @@ const getDDTagsExpression = `(() => {
|
|
|
36
36
|
const threadId = parentThreadId === 0 ? `pid:${process.pid}` : `pid:${process.pid};tid:${parentThreadId}`
|
|
37
37
|
const threadName = parentThreadId === 0 ? 'MainThread' : `WorkerThread:${parentThreadId}`
|
|
38
38
|
|
|
39
|
-
const SUPPORT_ITERATOR_METHODS = NODE_MAJOR >= 22
|
|
40
39
|
const SUPPORT_ARRAY_BUFFER_RESIZE = NODE_MAJOR >= 20
|
|
41
40
|
const oneSecondNs = 1_000_000_000n
|
|
42
41
|
let globalSnapshotSamplingRateWindowStart = 0n
|
|
@@ -77,14 +76,6 @@ session.on('Debugger.paused', async ({ params }) => {
|
|
|
77
76
|
}
|
|
78
77
|
}
|
|
79
78
|
|
|
80
|
-
// If all the probes have a condition, we know that it triggered. If at least one probe doesn't have a condition, we
|
|
81
|
-
// need to verify which conditions are met.
|
|
82
|
-
const shouldVerifyConditions = (
|
|
83
|
-
SUPPORT_ITERATOR_METHODS
|
|
84
|
-
? probesAtLocation.values()
|
|
85
|
-
: Array.from(probesAtLocation.values())
|
|
86
|
-
).some((probe) => probe.condition === undefined)
|
|
87
|
-
|
|
88
79
|
for (const probe of probesAtLocation.values()) {
|
|
89
80
|
if (start - probe.lastCaptureNs < probe.nsBetweenSampling) {
|
|
90
81
|
continue
|
|
@@ -109,7 +100,7 @@ session.on('Debugger.paused', async ({ params }) => {
|
|
|
109
100
|
maxLength = highestOrUndefined(probe.capture.maxLength, maxLength)
|
|
110
101
|
}
|
|
111
102
|
|
|
112
|
-
if (
|
|
103
|
+
if (probe.condition !== undefined) {
|
|
113
104
|
// TODO: Bundle all conditions and evaluate them in a single call
|
|
114
105
|
// TODO: Handle errors
|
|
115
106
|
const { result } = await session.post('Debugger.evaluateOnCallFrame', {
|
|
@@ -153,20 +144,11 @@ session.on('Debugger.paused', async ({ params }) => {
|
|
|
153
144
|
evalResults = result?.value ?? []
|
|
154
145
|
}
|
|
155
146
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
params.callFrames[0],
|
|
162
|
-
{ maxReferenceDepth, maxCollectionSize, maxFieldCount, maxLength }
|
|
163
|
-
)
|
|
164
|
-
} catch (err) {
|
|
165
|
-
for (let i = 0; i < numberOfProbesWithSnapshots; i++) {
|
|
166
|
-
ackError(err, probes[snapshotProbeIndex[i]]) // TODO: Ok to continue after sending ackError?
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
147
|
+
// TODO: Create unique states for each affected probe based on that probes unique `capture` settings (DEBUG-2863)
|
|
148
|
+
const processLocalState = numberOfProbesWithSnapshots !== 0 && await getLocalStateForCallFrame(
|
|
149
|
+
params.callFrames[0],
|
|
150
|
+
{ maxReferenceDepth, maxCollectionSize, maxFieldCount, maxLength }
|
|
151
|
+
)
|
|
170
152
|
|
|
171
153
|
await session.post('Debugger.resume')
|
|
172
154
|
const diff = process.hrtime.bigint() - start // TODO: Recored as telemetry (DEBUG-2858)
|
|
@@ -206,7 +188,9 @@ session.on('Debugger.paused', async ({ params }) => {
|
|
|
206
188
|
|
|
207
189
|
if (probe.captureSnapshot) {
|
|
208
190
|
const state = processLocalState()
|
|
209
|
-
if (state) {
|
|
191
|
+
if (state instanceof Error) {
|
|
192
|
+
snapshot.captureError = state.message
|
|
193
|
+
} else if (state) {
|
|
210
194
|
snapshot.captures = {
|
|
211
195
|
lines: { [probe.location.lines[0]]: { locals: state } }
|
|
212
196
|
}
|
|
@@ -13,7 +13,8 @@ const { version } = require('../../../../../package.json')
|
|
|
13
13
|
module.exports = send
|
|
14
14
|
|
|
15
15
|
const MAX_MESSAGE_LENGTH = 8 * 1024 // 8KB
|
|
16
|
-
const
|
|
16
|
+
const MAX_LOG_PAYLOAD_SIZE_MB = 1
|
|
17
|
+
const MAX_LOG_PAYLOAD_SIZE_BYTES = MAX_LOG_PAYLOAD_SIZE_MB * 1024 * 1024
|
|
17
18
|
|
|
18
19
|
const ddsource = 'dd_debugger'
|
|
19
20
|
const hostname = getHostname()
|
|
@@ -48,13 +49,13 @@ function send (message, logger, dd, snapshot) {
|
|
|
48
49
|
let json = JSON.stringify(payload)
|
|
49
50
|
let size = Buffer.byteLength(json)
|
|
50
51
|
|
|
51
|
-
if (size >
|
|
52
|
+
if (size > MAX_LOG_PAYLOAD_SIZE_BYTES) {
|
|
52
53
|
// TODO: This is a very crude way to handle large payloads. Proper pruning will be implemented later (DEBUG-2624)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
delete payload.debugger.snapshot.captures
|
|
55
|
+
payload.debugger.snapshot.captureError =
|
|
56
|
+
`Snapshot was too large (max allowed size is ${MAX_LOG_PAYLOAD_SIZE_MB} MiB). ` +
|
|
57
|
+
'Consider reducing the capture depth or turn off "Capture Variables" completely, ' +
|
|
58
|
+
'and instead include the variables of interest directly in the message template.'
|
|
58
59
|
json = JSON.stringify(payload)
|
|
59
60
|
size = Buffer.byteLength(json)
|
|
60
61
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { getRuntimeObject } = require('./collector')
|
|
4
4
|
const { processRawState } = require('./processor')
|
|
5
|
+
const log = require('../../../log')
|
|
5
6
|
|
|
6
7
|
const DEFAULT_MAX_REFERENCE_DEPTH = 3
|
|
7
8
|
const DEFAULT_MAX_COLLECTION_SIZE = 100
|
|
@@ -24,13 +25,20 @@ async function getLocalStateForCallFrame (
|
|
|
24
25
|
const rawState = []
|
|
25
26
|
let processedState = null
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
try {
|
|
29
|
+
await Promise.all(callFrame.scopeChain.map(async (scope) => {
|
|
30
|
+
if (scope.type === 'global') return // The global scope is too noisy
|
|
31
|
+
rawState.push(...await getRuntimeObject(
|
|
32
|
+
scope.object.objectId,
|
|
33
|
+
{ maxReferenceDepth, maxCollectionSize, maxFieldCount }
|
|
34
|
+
))
|
|
35
|
+
}))
|
|
36
|
+
} catch (err) {
|
|
37
|
+
// TODO: We might be able to get part of the scope chain.
|
|
38
|
+
// Consider if we could set errors just for the part of the scope chain that throws during collection.
|
|
39
|
+
log.error('[debugger:devtools_client] Error getting local state for call frame', err)
|
|
40
|
+
return () => new Error('Error getting local state')
|
|
41
|
+
}
|
|
34
42
|
|
|
35
43
|
// Deplay calling `processRawState` so the caller gets a chance to resume the main thread before processing `rawState`
|
|
36
44
|
return () => {
|
|
@@ -9,12 +9,16 @@ const WINDOWS_DRIVE_LETTER_REGEX = /[a-zA-Z]/
|
|
|
9
9
|
|
|
10
10
|
const loadedScripts = []
|
|
11
11
|
const scriptUrls = new Map()
|
|
12
|
+
let reEvaluateProbesTimer = null
|
|
12
13
|
|
|
13
14
|
module.exports = {
|
|
14
15
|
locationToBreakpoint: new Map(),
|
|
15
16
|
breakpointToProbes: new Map(),
|
|
16
17
|
probeToLocation: new Map(),
|
|
17
18
|
|
|
19
|
+
_loadedScripts: loadedScripts, // Only exposed for testing
|
|
20
|
+
_scriptUrls: scriptUrls, // Only exposed for testing
|
|
21
|
+
|
|
18
22
|
/**
|
|
19
23
|
* Find the script to inspect based on a partial or absolute path. Handles both Windows and POSIX paths.
|
|
20
24
|
*
|
|
@@ -88,11 +92,15 @@ module.exports = {
|
|
|
88
92
|
}
|
|
89
93
|
}
|
|
90
94
|
|
|
91
|
-
return maxMatchLength
|
|
95
|
+
return maxMatchLength !== -1 ? bestMatch : null
|
|
92
96
|
},
|
|
93
97
|
|
|
94
98
|
getStackFromCallFrames (callFrames) {
|
|
95
99
|
return callFrames.map((frame) => {
|
|
100
|
+
// TODO: Possible race condition: If the breakpoint is in the process of being removed, and this is the last
|
|
101
|
+
// breakpoint, it will also stop the debugging session, which in turn will clear the state, which means clearing
|
|
102
|
+
// the `scriptUrls` map. That might result in this the `scriptUrls.get` call above returning `undefined`, which
|
|
103
|
+
// will throw when `startsWith` is called on it.
|
|
96
104
|
let fileName = scriptUrls.get(frame.location.scriptId)
|
|
97
105
|
if (fileName.startsWith('file://')) fileName = fileName.substr(7) // TODO: This might not be required
|
|
98
106
|
return {
|
|
@@ -102,6 +110,14 @@ module.exports = {
|
|
|
102
110
|
columnNumber: frame.location.columnNumber + 1 // Beware! columnNumber is zero-indexed
|
|
103
111
|
}
|
|
104
112
|
})
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// The maps locationToBreakpoint, breakpointToProbes, and probeToLocation are always updated when breakpoints are
|
|
116
|
+
// removed. Therefore they do not need to get manually cleared. Only the state internal to this file needs to be
|
|
117
|
+
// cleared.
|
|
118
|
+
clearState () {
|
|
119
|
+
loadedScripts.length = 0
|
|
120
|
+
scriptUrls.clear()
|
|
105
121
|
}
|
|
106
122
|
}
|
|
107
123
|
|
|
@@ -112,7 +128,6 @@ module.exports = {
|
|
|
112
128
|
// Unknown params.url values:
|
|
113
129
|
// - `structured-stack` - Not sure what this is, but should just be ignored
|
|
114
130
|
// - `` - Not sure what this is, but should just be ignored
|
|
115
|
-
// TODO: Event fired for all files, every time debugger is enabled. So when we disable it, we need to reset the state
|
|
116
131
|
session.on('Debugger.scriptParsed', ({ params }) => {
|
|
117
132
|
scriptUrls.set(params.scriptId, params.url)
|
|
118
133
|
if (params.url.startsWith('file:')) {
|
|
@@ -142,5 +157,10 @@ session.on('Debugger.scriptParsed', ({ params }) => {
|
|
|
142
157
|
} else {
|
|
143
158
|
loadedScripts.push(params)
|
|
144
159
|
}
|
|
160
|
+
|
|
161
|
+
clearTimeout(reEvaluateProbesTimer)
|
|
162
|
+
reEvaluateProbesTimer = setTimeout(() => {
|
|
163
|
+
session.emit('scriptLoadingStabilized')
|
|
164
|
+
}, 500)
|
|
145
165
|
}
|
|
146
166
|
})
|
|
@@ -10,43 +10,25 @@ const uuidSource =
|
|
|
10
10
|
'[0-9a-f]{8}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{12}|[0-9a-f]{8}(?:-[0-9a-f]{4}){4}$'
|
|
11
11
|
const containerSource = '[0-9a-f]{64}'
|
|
12
12
|
const taskSource = '[0-9a-f]{32}-\\d+'
|
|
13
|
-
const lineReg = /^(\d+):([^:]*):(.+)$/
|
|
13
|
+
const lineReg = /^(\d+):([^:]*):(.+)$/m
|
|
14
14
|
const entityReg = new RegExp(`.*(${uuidSource}|${containerSource}|${taskSource})(?:\\.scope)?$`, 'm')
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
let inode = 0
|
|
17
|
+
let cgroup = ''
|
|
18
|
+
let entityId
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
try {
|
|
21
|
+
cgroup = fs.readFileSync('/proc/self/cgroup', 'utf8').trim()
|
|
22
|
+
entityId = cgroup.match(entityReg)?.[1]
|
|
23
|
+
} catch { /* Ignore error */ }
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
function getInode () {
|
|
27
|
-
const match = cgroup.match(lineReg) || []
|
|
28
|
-
|
|
29
|
-
return readInode(match[3])
|
|
30
|
-
}
|
|
25
|
+
const inodePath = cgroup.match(lineReg)?.[3]
|
|
26
|
+
if (inodePath) {
|
|
27
|
+
const strippedPath = inodePath.replace(/^\/|\/$/g, '')
|
|
31
28
|
|
|
32
|
-
function readControlGroup () {
|
|
33
29
|
try {
|
|
34
|
-
|
|
35
|
-
} catch
|
|
36
|
-
return ''
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function readInode (path) {
|
|
41
|
-
if (!path) return 0
|
|
42
|
-
|
|
43
|
-
const strippedPath = path.replace(/^\//, '').replace(/\/$/, '')
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
return fs.statSync(`/sys/fs/cgroup/${strippedPath}`).ino
|
|
47
|
-
} catch (err) {
|
|
48
|
-
return 0
|
|
49
|
-
}
|
|
30
|
+
inode = fs.statSync(`/sys/fs/cgroup/${strippedPath}`).ino
|
|
31
|
+
} catch { /* Ignore error */ }
|
|
50
32
|
}
|
|
51
33
|
|
|
52
34
|
module.exports = {
|
|
@@ -14,11 +14,8 @@ if (!process.env.DD_INJECTION_ENABLED) {
|
|
|
14
14
|
module.exports = function () {}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (!fs.existsSync(process.env.DD_TELEMETRY_FORWARDER_PATH)) {
|
|
17
|
+
var telemetryForwarderPath = process.env.DD_TELEMETRY_FORWARDER_PATH
|
|
18
|
+
if (typeof telemetryForwarderPath !== 'string' || !fs.existsSync(telemetryForwarderPath)) {
|
|
22
19
|
module.exports = function () {}
|
|
23
20
|
}
|
|
24
21
|
|
|
@@ -148,12 +148,12 @@ class LLMObsTagger {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
tagSpanTags (span, tags) {
|
|
151
|
-
// new tags will be merged with existing tags
|
|
152
151
|
const currentTags = registry.get(span)?.[TAGS]
|
|
153
152
|
if (currentTags) {
|
|
154
|
-
Object.assign(
|
|
153
|
+
Object.assign(currentTags, tags)
|
|
154
|
+
} else {
|
|
155
|
+
this._setTag(span, TAGS, tags)
|
|
155
156
|
}
|
|
156
|
-
this._setTag(span, TAGS, tags)
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
changeKind (span, newKind) {
|
|
@@ -41,6 +41,18 @@ class BaseLLMObsWriter {
|
|
|
41
41
|
this._destroyed = false
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
get url () {
|
|
45
|
+
if (this._agentless == null) return null
|
|
46
|
+
|
|
47
|
+
const baseUrl = this._baseUrl.href
|
|
48
|
+
const endpoint = this._endpoint
|
|
49
|
+
|
|
50
|
+
// Split on protocol separator to preserve it
|
|
51
|
+
// path.join will remove some slashes unnecessarily
|
|
52
|
+
const [protocol, rest] = baseUrl.split('://')
|
|
53
|
+
return protocol + '://' + path.join(rest, endpoint)
|
|
54
|
+
}
|
|
55
|
+
|
|
44
56
|
append (event, byteLength) {
|
|
45
57
|
if (this._buffer.length >= this._bufferLimit) {
|
|
46
58
|
logger.warn(`${this.constructor.name} event buffer full (limit is ${this._bufferLimit}), dropping event`)
|
|
@@ -69,7 +81,7 @@ class BaseLLMObsWriter {
|
|
|
69
81
|
const options = this._getOptions()
|
|
70
82
|
|
|
71
83
|
request(payload, options, (err, resp, code) => {
|
|
72
|
-
parseResponseAndLog(err, code, events.length,
|
|
84
|
+
parseResponseAndLog(err, code, events.length, this.url, this._eventType)
|
|
73
85
|
})
|
|
74
86
|
}
|
|
75
87
|
|
|
@@ -87,17 +99,23 @@ class BaseLLMObsWriter {
|
|
|
87
99
|
|
|
88
100
|
setAgentless (agentless) {
|
|
89
101
|
this._agentless = agentless
|
|
90
|
-
|
|
91
|
-
|
|
102
|
+
const { url, endpoint } = this._getUrlAndPath()
|
|
103
|
+
|
|
104
|
+
this._baseUrl = url
|
|
105
|
+
this._endpoint = endpoint
|
|
106
|
+
|
|
107
|
+
logger.debug(`Configuring ${this.constructor.name} to ${this.url}`)
|
|
92
108
|
}
|
|
93
109
|
|
|
94
|
-
|
|
110
|
+
_getUrlAndPath () {
|
|
95
111
|
if (this._agentless) {
|
|
96
|
-
return
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
112
|
+
return {
|
|
113
|
+
url: new URL(format({
|
|
114
|
+
protocol: 'https:',
|
|
115
|
+
hostname: `${this._intake}.${this._config.site}`
|
|
116
|
+
})),
|
|
117
|
+
endpoint: this._endpoint
|
|
118
|
+
}
|
|
101
119
|
}
|
|
102
120
|
|
|
103
121
|
const { hostname, port } = this._config
|
|
@@ -107,8 +125,10 @@ class BaseLLMObsWriter {
|
|
|
107
125
|
port
|
|
108
126
|
}))
|
|
109
127
|
|
|
110
|
-
|
|
111
|
-
|
|
128
|
+
return {
|
|
129
|
+
url: base,
|
|
130
|
+
endpoint: path.join(EVP_PROXY_AGENT_BASE_PATH, this._endpoint)
|
|
131
|
+
}
|
|
112
132
|
}
|
|
113
133
|
|
|
114
134
|
_getOptions () {
|
|
@@ -118,7 +138,8 @@ class BaseLLMObsWriter {
|
|
|
118
138
|
},
|
|
119
139
|
method: 'POST',
|
|
120
140
|
timeout: this._timeout,
|
|
121
|
-
url: this.
|
|
141
|
+
url: this._baseUrl,
|
|
142
|
+
path: this._endpoint
|
|
122
143
|
}
|
|
123
144
|
|
|
124
145
|
if (this._agentless) {
|
|
@@ -16,6 +16,11 @@ class NoopProxy {
|
|
|
16
16
|
this.appsec = noopAppsec
|
|
17
17
|
this.dogstatsd = noopDogStatsDClient
|
|
18
18
|
this.llmobs = noopLLMObs
|
|
19
|
+
this.setBaggageItem = () => {}
|
|
20
|
+
this.getBaggageItem = () => {}
|
|
21
|
+
this.getAllBaggageItems = () => {}
|
|
22
|
+
this.removeBaggageItem = () => {}
|
|
23
|
+
this.removeAllBaggageItems = () => {}
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
init () {
|
|
@@ -12,6 +12,7 @@ class ContextManager {
|
|
|
12
12
|
this._store = storage('opentelemetry')
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
// converts dd to otel
|
|
15
16
|
active () {
|
|
16
17
|
const activeSpan = tracer.scope().active()
|
|
17
18
|
const store = this._store.getStore()
|
|
@@ -54,6 +55,7 @@ class ContextManager {
|
|
|
54
55
|
: wrappedContext
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
// converts otel to dd
|
|
57
59
|
with (context, fn, thisArg, ...args) {
|
|
58
60
|
const span = trace.getSpan(context)
|
|
59
61
|
const ddScope = tracer.scope()
|