dd-trace 5.75.0 → 5.77.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 +1 -0
- package/package.json +15 -15
- package/packages/datadog-instrumentations/src/express.js +18 -7
- package/packages/datadog-instrumentations/src/jest.js +16 -16
- package/packages/datadog-instrumentations/src/openai.js +8 -0
- package/packages/datadog-instrumentations/src/playwright.js +47 -0
- package/packages/datadog-instrumentations/src/router.js +9 -8
- package/packages/datadog-instrumentations/src/vitest.js +94 -8
- package/packages/datadog-plugin-cucumber/src/index.js +2 -1
- package/packages/datadog-plugin-grpc/src/util.js +5 -1
- package/packages/datadog-plugin-http/src/client.js +5 -1
- package/packages/datadog-plugin-http2/src/client.js +5 -1
- package/packages/datadog-plugin-openai/src/stream-helpers.js +26 -1
- package/packages/datadog-plugin-openai/src/tracing.js +46 -1
- package/packages/datadog-plugin-vitest/src/index.js +5 -1
- package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
- package/packages/dd-trace/src/appsec/channels.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +7 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -3
- package/packages/dd-trace/src/appsec/rasp/lfi.js +23 -9
- package/packages/dd-trace/src/appsec/stack_trace.js +6 -6
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +6 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +30 -7
- package/packages/dd-trace/src/config.js +10 -1
- package/packages/dd-trace/src/debugger/devtools_client/index.js +8 -1
- package/packages/dd-trace/src/debugger/devtools_client/log.js +15 -4
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +5 -1
- package/packages/dd-trace/src/exporters/common/docker.js +1 -1
- package/packages/dd-trace/src/exporters/span-stats/writer.js +4 -5
- package/packages/dd-trace/src/format.js +4 -5
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +9 -6
- package/packages/dd-trace/src/llmobs/plugins/anthropic.js +3 -6
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +3 -2
- package/packages/dd-trace/src/llmobs/plugins/openai.js +216 -12
- package/packages/dd-trace/src/llmobs/span_processor.js +4 -3
- package/packages/dd-trace/src/llmobs/tagger.js +9 -3
- package/packages/dd-trace/src/llmobs/util.js +1 -1
- package/packages/dd-trace/src/openfeature/writers/exposures.js +2 -2
- package/packages/dd-trace/src/opentelemetry/span.js +3 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +3 -1
- package/packages/dd-trace/src/plugins/util/serverless.js +1 -2
- package/packages/dd-trace/src/plugins/util/test.js +16 -1
- package/packages/dd-trace/src/plugins/util/web.js +5 -1
- package/packages/dd-trace/src/profiling/config.js +32 -10
- package/packages/dd-trace/src/proxy.js +2 -2
- package/packages/dd-trace/src/remote_config/capabilities.js +2 -0
- package/packages/dd-trace/src/remote_config/index.js +4 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +2 -2
- package/packages/dd-trace/src/span_processor.js +4 -1
- package/packages/dd-trace/src/startup-log.js +24 -38
- package/packages/dd-trace/src/supported-configurations.json +1 -0
|
@@ -11,7 +11,8 @@ const { MEASURED } = require('../../../ext/tags')
|
|
|
11
11
|
const {
|
|
12
12
|
convertBuffersToObjects,
|
|
13
13
|
constructCompletionResponseFromStreamedChunks,
|
|
14
|
-
constructChatCompletionResponseFromStreamedChunks
|
|
14
|
+
constructChatCompletionResponseFromStreamedChunks,
|
|
15
|
+
constructResponseResponseFromStreamedChunks
|
|
15
16
|
} = require('./stream-helpers')
|
|
16
17
|
|
|
17
18
|
const { DD_MAJOR } = require('../../../version')
|
|
@@ -59,6 +60,8 @@ class OpenAiTracingPlugin extends TracingPlugin {
|
|
|
59
60
|
response = constructCompletionResponseFromStreamedChunks(chunks, n)
|
|
60
61
|
} else if (methodName === 'createChatCompletion') {
|
|
61
62
|
response = constructChatCompletionResponseFromStreamedChunks(chunks, n)
|
|
63
|
+
} else if (methodName === 'createResponse') {
|
|
64
|
+
response = constructResponseResponseFromStreamedChunks(chunks)
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
ctx.result = { data: response }
|
|
@@ -134,6 +137,10 @@ class OpenAiTracingPlugin extends TracingPlugin {
|
|
|
134
137
|
case 'createEdit':
|
|
135
138
|
createEditRequestExtraction(tags, payload, openaiStore)
|
|
136
139
|
break
|
|
140
|
+
|
|
141
|
+
case 'createResponse':
|
|
142
|
+
createResponseRequestExtraction(tags, payload, openaiStore)
|
|
143
|
+
break
|
|
137
144
|
}
|
|
138
145
|
|
|
139
146
|
span.addTags(tags)
|
|
@@ -313,6 +320,10 @@ function normalizeMethodName (methodName) {
|
|
|
313
320
|
case 'embeddings.create':
|
|
314
321
|
return 'createEmbedding'
|
|
315
322
|
|
|
323
|
+
// responses
|
|
324
|
+
case 'responses.create':
|
|
325
|
+
return 'createResponse'
|
|
326
|
+
|
|
316
327
|
// files
|
|
317
328
|
case 'files.create':
|
|
318
329
|
return 'createFile'
|
|
@@ -376,6 +387,16 @@ function createEditRequestExtraction (tags, payload, openaiStore) {
|
|
|
376
387
|
openaiStore.instruction = instruction
|
|
377
388
|
}
|
|
378
389
|
|
|
390
|
+
function createResponseRequestExtraction (tags, payload, openaiStore) {
|
|
391
|
+
// Extract model information
|
|
392
|
+
if (payload.model) {
|
|
393
|
+
tags['openai.request.model'] = payload.model
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Store the full payload for response extraction
|
|
397
|
+
openaiStore.responseData = payload
|
|
398
|
+
}
|
|
399
|
+
|
|
379
400
|
function retrieveModelRequestExtraction (tags, payload) {
|
|
380
401
|
tags['openai.request.id'] = payload.id
|
|
381
402
|
}
|
|
@@ -410,6 +431,10 @@ function responseDataExtractionByMethod (methodName, tags, body, openaiStore) {
|
|
|
410
431
|
commonCreateResponseExtraction(tags, body, openaiStore, methodName)
|
|
411
432
|
break
|
|
412
433
|
|
|
434
|
+
case 'createResponse':
|
|
435
|
+
createResponseResponseExtraction(tags, body, openaiStore)
|
|
436
|
+
break
|
|
437
|
+
|
|
413
438
|
case 'listFiles':
|
|
414
439
|
case 'listFineTunes':
|
|
415
440
|
case 'listFineTuneEvents':
|
|
@@ -513,6 +538,26 @@ function commonCreateResponseExtraction (tags, body, openaiStore, methodName) {
|
|
|
513
538
|
openaiStore.choices = body.choices
|
|
514
539
|
}
|
|
515
540
|
|
|
541
|
+
function createResponseResponseExtraction (tags, body, openaiStore) {
|
|
542
|
+
// Extract response ID if available
|
|
543
|
+
if (body.id) {
|
|
544
|
+
tags['openai.response.id'] = body.id
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Extract status if available
|
|
548
|
+
if (body.status) {
|
|
549
|
+
tags['openai.response.status'] = body.status
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Extract model from response if available
|
|
553
|
+
if (body.model) {
|
|
554
|
+
tags['openai.response.model'] = body.model
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Store the full response for potential future use
|
|
558
|
+
openaiStore.response = body
|
|
559
|
+
}
|
|
560
|
+
|
|
516
561
|
// The server almost always responds with JSON
|
|
517
562
|
function coerceResponseBody (body, methodName) {
|
|
518
563
|
switch (methodName) {
|
|
@@ -6,6 +6,7 @@ const { getEnvironmentVariable } = require('../../dd-trace/src/config-helper')
|
|
|
6
6
|
|
|
7
7
|
const {
|
|
8
8
|
TEST_STATUS,
|
|
9
|
+
VITEST_POOL,
|
|
9
10
|
finishAllTraceSpans,
|
|
10
11
|
getTestSuitePath,
|
|
11
12
|
getTestSuiteCommonTags,
|
|
@@ -344,7 +345,6 @@ class VitestPlugin extends CiPlugin {
|
|
|
344
345
|
finishAllTraceSpans(testSuiteSpan)
|
|
345
346
|
}
|
|
346
347
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
|
|
347
|
-
// TODO: too frequent flush - find for method in worker to decrease frequency
|
|
348
348
|
this.tracer._exporter.flush(onFinish)
|
|
349
349
|
if (this.runningTestProbe) {
|
|
350
350
|
this.removeDiProbe(this.runningTestProbe)
|
|
@@ -373,6 +373,7 @@ class VitestPlugin extends CiPlugin {
|
|
|
373
373
|
isEarlyFlakeDetectionEnabled,
|
|
374
374
|
isEarlyFlakeDetectionFaulty,
|
|
375
375
|
isTestManagementTestsEnabled,
|
|
376
|
+
vitestPool,
|
|
376
377
|
onFinish
|
|
377
378
|
}) => {
|
|
378
379
|
this.testSessionSpan.setTag(TEST_STATUS, status)
|
|
@@ -394,6 +395,9 @@ class VitestPlugin extends CiPlugin {
|
|
|
394
395
|
if (isTestManagementTestsEnabled) {
|
|
395
396
|
this.testSessionSpan.setTag(TEST_MANAGEMENT_ENABLED, 'true')
|
|
396
397
|
}
|
|
398
|
+
if (vitestPool) {
|
|
399
|
+
this.testSessionSpan.setTag(VITEST_POOL, vitestPool)
|
|
400
|
+
}
|
|
397
401
|
this.testModuleSpan.finish()
|
|
398
402
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
|
|
399
403
|
this.testSessionSpan.finish()
|
|
@@ -12,6 +12,7 @@ module.exports = {
|
|
|
12
12
|
cookieParser: dc.channel('datadog:cookie-parser:read:finish'),
|
|
13
13
|
expressMiddlewareError: dc.channel('apm:express:middleware:error'),
|
|
14
14
|
expressProcessParams: dc.channel('datadog:express:process_params:start'),
|
|
15
|
+
expressResponseRenderStart: dc.channel('tracing:datadog:express:response:render:start'),
|
|
15
16
|
expressSession: dc.channel('datadog:express-session:middleware:finish'),
|
|
16
17
|
fastifyBodyParser: dc.channel('datadog:fastify:body-parser:finish'),
|
|
17
18
|
fastifyCookieParser: dc.channel('datadog:fastify-cookie:read:finish'),
|
|
@@ -68,6 +68,13 @@ class PathTraversalAnalyzer extends InjectionAnalyzer {
|
|
|
68
68
|
}
|
|
69
69
|
this.analyze(pathArguments)
|
|
70
70
|
})
|
|
71
|
+
|
|
72
|
+
this.addSub('tracing:datadog:express:response:render:start', (ctx) => {
|
|
73
|
+
const store = storage('legacy').getStore()
|
|
74
|
+
if (!store) return
|
|
75
|
+
|
|
76
|
+
this.analyze([ctx.view])
|
|
77
|
+
})
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
_isExcluded (location) {
|
|
@@ -8,8 +8,8 @@ const STRINGIFY_SENSITIVE_KEY = STRINGIFY_RANGE_KEY + 'SENSITIVE'
|
|
|
8
8
|
const STRINGIFY_SENSITIVE_NOT_STRING_KEY = STRINGIFY_SENSITIVE_KEY + 'NOTSTRING'
|
|
9
9
|
|
|
10
10
|
// eslint-disable-next-line @stylistic/max-len
|
|
11
|
-
const KEYS_REGEX_WITH_SENSITIVE_RANGES = new RegExp(`(?:"(${STRINGIFY_RANGE_KEY}_
|
|
12
|
-
const KEYS_REGEX_WITHOUT_SENSITIVE_RANGES = new RegExp(`"(${STRINGIFY_RANGE_KEY}_
|
|
11
|
+
const KEYS_REGEX_WITH_SENSITIVE_RANGES = new RegExp(String.raw`(?:"(${STRINGIFY_RANGE_KEY}_\d+_))|(?:"(${STRINGIFY_SENSITIVE_KEY}_\d+_(\d+)_))|("${STRINGIFY_SENSITIVE_NOT_STRING_KEY}_\d+_([\s0-9.a-zA-Z]*)")`, 'gm')
|
|
12
|
+
const KEYS_REGEX_WITHOUT_SENSITIVE_RANGES = new RegExp(String.raw`"(${STRINGIFY_RANGE_KEY}_\d+_)`, 'gm')
|
|
13
13
|
|
|
14
14
|
const sensitiveValueRegex = new RegExp(DEFAULT_IAST_REDACTION_VALUE_PATTERN, 'gmi')
|
|
15
15
|
|
|
@@ -84,9 +84,10 @@ function sendVulnerabilities (vulnerabilities, span) {
|
|
|
84
84
|
const jsonToSend = vulnerabilitiesFormatter.toJson(validatedVulnerabilities)
|
|
85
85
|
|
|
86
86
|
if (jsonToSend.vulnerabilities.length > 0) {
|
|
87
|
-
const tags = {
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
const tags = {
|
|
88
|
+
// TODO: Store this outside of the span and set the tag in the exporter.
|
|
89
|
+
[IAST_JSON_TAG_KEY]: JSON.stringify(jsonToSend)
|
|
90
|
+
}
|
|
90
91
|
span.addTags(tags)
|
|
91
92
|
}
|
|
92
93
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { isAbsolute } = require('path')
|
|
4
|
+
|
|
5
|
+
const { fsOperationStart, incomingHttpRequestStart, expressResponseRenderStart } = require('../channels')
|
|
4
6
|
const { storage } = require('../../../../datadog-core')
|
|
5
7
|
const { enable: enableFsPlugin, disable: disableFsPlugin, RASP_MODULE } = require('./fs-plugin')
|
|
6
8
|
const { FS_OPERATION_PATH } = require('../addresses')
|
|
7
9
|
const waf = require('../waf')
|
|
8
10
|
const { RULE_TYPES, handleResult } = require('./utils')
|
|
9
|
-
const { isAbsolute } = require('path')
|
|
10
11
|
|
|
11
12
|
let config
|
|
12
13
|
let enabled
|
|
@@ -25,6 +26,7 @@ function enable (_config) {
|
|
|
25
26
|
function disable () {
|
|
26
27
|
if (fsOperationStart.hasSubscribers) fsOperationStart.unsubscribe(analyzeLfi)
|
|
27
28
|
if (incomingHttpRequestStart.hasSubscribers) incomingHttpRequestStart.unsubscribe(onFirstReceivedRequest)
|
|
29
|
+
if (expressResponseRenderStart.hasSubscribers) expressResponseRenderStart.unsubscribe(analyzeLfiInResponseRender)
|
|
28
30
|
|
|
29
31
|
disableFsPlugin(RASP_MODULE)
|
|
30
32
|
|
|
@@ -42,10 +44,18 @@ function onFirstReceivedRequest () {
|
|
|
42
44
|
|
|
43
45
|
if (!analyzeSubscribed) {
|
|
44
46
|
fsOperationStart.subscribe(analyzeLfi)
|
|
47
|
+
expressResponseRenderStart.subscribe(analyzeLfiInResponseRender)
|
|
45
48
|
analyzeSubscribed = true
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
51
|
|
|
52
|
+
function analyzeLfiInResponseRender (ctx) {
|
|
53
|
+
const store = storage('legacy').getStore()
|
|
54
|
+
if (!store) return
|
|
55
|
+
|
|
56
|
+
analyzeLfiPath(ctx.view, ctx.req, store.res, ctx.abortController)
|
|
57
|
+
}
|
|
58
|
+
|
|
49
59
|
function analyzeLfi (ctx) {
|
|
50
60
|
const store = storage('legacy').getStore()
|
|
51
61
|
if (!store) return
|
|
@@ -54,15 +64,19 @@ function analyzeLfi (ctx) {
|
|
|
54
64
|
if (!req || !fs) return
|
|
55
65
|
|
|
56
66
|
getPaths(ctx, fs).forEach(path => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
analyzeLfiPath(path, req, res, ctx.abortController)
|
|
68
|
+
})
|
|
69
|
+
}
|
|
60
70
|
|
|
61
|
-
|
|
71
|
+
function analyzeLfiPath (path, req, res, abortController) {
|
|
72
|
+
const ephemeral = {
|
|
73
|
+
[FS_OPERATION_PATH]: path
|
|
74
|
+
}
|
|
62
75
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
})
|
|
76
|
+
const raspRule = { type: RULE_TYPES.LFI }
|
|
77
|
+
|
|
78
|
+
const result = waf.run({ ephemeral }, req, raspRule)
|
|
79
|
+
handleResult(result, req, res, abortController, config, raspRule)
|
|
66
80
|
}
|
|
67
81
|
|
|
68
82
|
function getPaths (ctx, fs) {
|
|
@@ -42,9 +42,9 @@ function filterOutFramesFromLibrary (callSiteList) {
|
|
|
42
42
|
if (globalThis.__DD_ESBUILD_IAST_WITH_SM) {
|
|
43
43
|
// bundled with SourceMap, get original file and line to discriminate if comes from dd-trace or not
|
|
44
44
|
const callSiteLocation = {
|
|
45
|
-
path: callSite.getFileName(),
|
|
46
|
-
line: callSite.getLineNumber(),
|
|
47
|
-
column: callSite.getColumnNumber()
|
|
45
|
+
path: callSite.getTranslatedFileName?.() ?? callSite.getFileName(),
|
|
46
|
+
line: callSite.getTranslatedLineNumber?.() ?? callSite.getLineNumber(),
|
|
47
|
+
column: callSite.getTranslatedColumnNumber?.() ?? callSite.getColumnNumber()
|
|
48
48
|
}
|
|
49
49
|
const { path } = getOriginalPathAndLineFromSourceMap(callSiteLocation)
|
|
50
50
|
return !path?.startsWith(ddBasePath)
|
|
@@ -68,9 +68,9 @@ function getCallsiteFrames (maxDepth = 32, constructorOpt = getCallsiteFrames, c
|
|
|
68
68
|
const callSite = filteredFrames[index]
|
|
69
69
|
indexedFrames.push({
|
|
70
70
|
id: index,
|
|
71
|
-
file: callSite.getFileName(),
|
|
72
|
-
line: callSite.getLineNumber(),
|
|
73
|
-
column: callSite.getColumnNumber(),
|
|
71
|
+
file: callSite.getTranslatedFileName?.() ?? callSite.getFileName(),
|
|
72
|
+
line: callSite.getTranslatedLineNumber?.() ?? callSite.getLineNumber(),
|
|
73
|
+
column: callSite.getTranslatedColumnNumber?.() ?? callSite.getColumnNumber(),
|
|
74
74
|
function: callSite.getFunctionName(),
|
|
75
75
|
class_name: callSite.getTypeName(),
|
|
76
76
|
isNative: callSite.isNative()
|
|
@@ -29,6 +29,9 @@ function getInterprocessTraceCode () {
|
|
|
29
29
|
if (getEnvironmentVariable('TINYPOOL_WORKER_ID')) {
|
|
30
30
|
return VITEST_WORKER_TRACE_PAYLOAD_CODE
|
|
31
31
|
}
|
|
32
|
+
if (getEnvironmentVariable('DD_VITEST_WORKER')) {
|
|
33
|
+
return VITEST_WORKER_TRACE_PAYLOAD_CODE
|
|
34
|
+
}
|
|
32
35
|
return null
|
|
33
36
|
}
|
|
34
37
|
|
|
@@ -47,6 +50,9 @@ function getInterprocessLogsCode () {
|
|
|
47
50
|
if (getEnvironmentVariable('TINYPOOL_WORKER_ID')) {
|
|
48
51
|
return VITEST_WORKER_LOGS_PAYLOAD_CODE
|
|
49
52
|
}
|
|
53
|
+
if (getEnvironmentVariable('DD_VITEST_WORKER')) {
|
|
54
|
+
return VITEST_WORKER_LOGS_PAYLOAD_CODE
|
|
55
|
+
}
|
|
50
56
|
return null
|
|
51
57
|
}
|
|
52
58
|
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const { JSONEncoder } = require('../../encode/json-encoder')
|
|
3
3
|
const { getEnvironmentVariable } = require('../../../config-helper')
|
|
4
|
+
const log = require('../../../log')
|
|
5
|
+
const {
|
|
6
|
+
VITEST_WORKER_TRACE_PAYLOAD_CODE,
|
|
7
|
+
VITEST_WORKER_LOGS_PAYLOAD_CODE
|
|
8
|
+
} = require('../../../plugins/util/test')
|
|
4
9
|
|
|
5
10
|
class Writer {
|
|
6
11
|
constructor (interprocessCode) {
|
|
@@ -26,24 +31,42 @@ class Writer {
|
|
|
26
31
|
_sendPayload (data, onDone = () => {}) {
|
|
27
32
|
// ## Jest
|
|
28
33
|
// Only available when `child_process` is used for the jest worker.
|
|
29
|
-
// https://github.com/facebook/jest/blob/bb39cb2c617a3334bf18daeca66bd87b7ccab28b/packages/jest-worker/README.md#experimental-worker
|
|
30
34
|
// If worker_threads is used, this will not work
|
|
31
|
-
// TODO: make
|
|
35
|
+
// TODO: make `jest` instrumentation compatible with worker_threads
|
|
36
|
+
// https://github.com/facebook/jest/blob/bb39cb2c617a3334bf18daeca66bd87b7ccab28b/packages/jest-worker/README.md#experimental-worker
|
|
32
37
|
|
|
33
38
|
// ## Cucumber
|
|
34
39
|
// This reports to the test's main process the same way test data is reported by Cucumber
|
|
35
40
|
// See cucumber code:
|
|
36
41
|
// https://github.com/cucumber/cucumber-js/blob/5ce371870b677fe3d1a14915dc535688946f734c/src/runtime/parallel/run_worker.ts#L13
|
|
37
|
-
if (process.send) { // it only works if process.send is available
|
|
38
|
-
const isVitestWorker = !!getEnvironmentVariable('TINYPOOL_WORKER_ID')
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
// Old because vitest@>=4 uses `DD_VITEST_WORKER` and reports arrays just like other frameworks
|
|
44
|
+
// Before vitest@>=4, we need the `__tinypool_worker_message__` property, or tinypool will crash
|
|
45
|
+
const isVitestWorkerOld = !!getEnvironmentVariable('TINYPOOL_WORKER_ID')
|
|
46
|
+
const payload = isVitestWorkerOld
|
|
47
|
+
? { __tinypool_worker_message__: true, interprocessCode: this._interprocessCode, data }
|
|
48
|
+
: [this._interprocessCode, data]
|
|
43
49
|
|
|
50
|
+
const isVitestTestWorker =
|
|
51
|
+
this._interprocessCode === VITEST_WORKER_TRACE_PAYLOAD_CODE ||
|
|
52
|
+
this._interprocessCode === VITEST_WORKER_LOGS_PAYLOAD_CODE
|
|
53
|
+
|
|
54
|
+
if (process.send) {
|
|
44
55
|
process.send(payload, () => {
|
|
45
56
|
onDone()
|
|
46
57
|
})
|
|
58
|
+
} else if (isVitestTestWorker) { // TODO: worker_threads are only supported in vitest right now
|
|
59
|
+
const { isMainThread, parentPort } = require('worker_threads')
|
|
60
|
+
if (isMainThread) {
|
|
61
|
+
return onDone()
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
parentPort.postMessage(payload)
|
|
65
|
+
} catch (error) {
|
|
66
|
+
log.error('Error posting message to parent port', error)
|
|
67
|
+
} finally {
|
|
68
|
+
onDone()
|
|
69
|
+
}
|
|
47
70
|
} else {
|
|
48
71
|
onDone()
|
|
49
72
|
}
|
|
@@ -4,6 +4,7 @@ const fs = require('fs')
|
|
|
4
4
|
const os = require('os')
|
|
5
5
|
const uuid = require('crypto-randomuuid') // we need to keep the old uuid dep because of cypress
|
|
6
6
|
const { URL } = require('url')
|
|
7
|
+
|
|
7
8
|
const log = require('./log')
|
|
8
9
|
const tagger = require('./tagger')
|
|
9
10
|
const set = require('../../datadog-core/src/utils/src/set')
|
|
@@ -1507,4 +1508,12 @@ function getAgentUrl (url, options) {
|
|
|
1507
1508
|
}
|
|
1508
1509
|
}
|
|
1509
1510
|
|
|
1510
|
-
|
|
1511
|
+
let configInstance = null
|
|
1512
|
+
function getConfig (options) {
|
|
1513
|
+
if (!configInstance) {
|
|
1514
|
+
configInstance = new Config(options)
|
|
1515
|
+
}
|
|
1516
|
+
return configInstance
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
module.exports = getConfig
|
|
@@ -58,6 +58,11 @@ if (SUPPORT_ARRAY_BUFFER_RESIZE) {
|
|
|
58
58
|
session.on('Debugger.paused', async ({ params }) => {
|
|
59
59
|
const start = process.hrtime.bigint()
|
|
60
60
|
|
|
61
|
+
if (params.reason !== 'other') {
|
|
62
|
+
// This error should not be caught, and should exit the worker thread, effectively stopping the debugging session
|
|
63
|
+
throw new Error(`Unexpected Debugger.paused reason: ${params.reason}`)
|
|
64
|
+
}
|
|
65
|
+
|
|
61
66
|
let maxReferenceDepth, maxCollectionSize, maxFieldCount, maxLength
|
|
62
67
|
let sampled = false
|
|
63
68
|
let numberOfProbesWithSnapshots = 0
|
|
@@ -168,7 +173,9 @@ session.on('Debugger.paused', async ({ params }) => {
|
|
|
168
173
|
await session.post('Debugger.resume')
|
|
169
174
|
const diff = process.hrtime.bigint() - start // TODO: Recored as telemetry (DEBUG-2858)
|
|
170
175
|
|
|
171
|
-
|
|
176
|
+
// This doesn't measure the overhead of the CDP protocol. The actual pause time is slightly larger.
|
|
177
|
+
// On my machine I'm seeing around 1.7ms of overhead.
|
|
178
|
+
log.debug(() => `[debugger:devtools_client] Finished processing breakpoints - main thread paused for: ~${
|
|
172
179
|
Number(diff) / 1_000_000
|
|
173
180
|
} ms`)
|
|
174
181
|
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
/** @typedef {'error'|'warn'|'info'|'debug'} Level */
|
|
4
|
+
/** @typedef {(...args: unknown[]) => void} LogFn */
|
|
5
|
+
/** @typedef {Record<Level, LogFn>} Logger */
|
|
6
|
+
|
|
3
7
|
const { workerData } = require('node:worker_threads')
|
|
4
8
|
|
|
5
9
|
// For testing purposes, we allow `workerData` to be undefined and fallback to a default config
|
|
6
|
-
const { config: { debug, logLevel }, logPort } = workerData ?? {
|
|
10
|
+
const { config: { debug = false, logLevel } = {}, logPort } = workerData ?? {}
|
|
7
11
|
|
|
12
|
+
/** @type {Level[]} */
|
|
8
13
|
const LEVELS = ['error', 'warn', 'info', 'debug']
|
|
9
14
|
const on = (level, ...args) => {
|
|
10
15
|
if (typeof args[0] === 'function') {
|
|
@@ -14,6 +19,12 @@ const on = (level, ...args) => {
|
|
|
14
19
|
}
|
|
15
20
|
const off = () => {}
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
22
|
+
const threshold = LEVELS.indexOf(logLevel)
|
|
23
|
+
|
|
24
|
+
/** @type {Logger} */
|
|
25
|
+
module.exports = Object.fromEntries(
|
|
26
|
+
LEVELS.map(level => [
|
|
27
|
+
level,
|
|
28
|
+
debug && threshold >= LEVELS.indexOf(level) ? on.bind(null, level) : off
|
|
29
|
+
])
|
|
30
|
+
)
|
|
@@ -13,6 +13,10 @@ module.exports = {
|
|
|
13
13
|
getLocalStateForCallFrame
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
function returnError () {
|
|
17
|
+
return new Error('Error getting local state')
|
|
18
|
+
}
|
|
19
|
+
|
|
16
20
|
async function getLocalStateForCallFrame (
|
|
17
21
|
callFrame,
|
|
18
22
|
{
|
|
@@ -37,7 +41,7 @@ async function getLocalStateForCallFrame (
|
|
|
37
41
|
// TODO: We might be able to get part of the scope chain.
|
|
38
42
|
// Consider if we could set errors just for the part of the scope chain that throws during collection.
|
|
39
43
|
log.error('[debugger:devtools_client] Error getting local state for call frame', err)
|
|
40
|
-
return
|
|
44
|
+
return returnError
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
// Delay calling `processRawState` so the caller gets a chance to resume the main thread before processing `rawState`
|
|
@@ -12,7 +12,7 @@ const uuidSource =
|
|
|
12
12
|
const containerSource = '[0-9a-f]{64}'
|
|
13
13
|
const taskSource = String.raw`[0-9a-f]{32}-\d+`
|
|
14
14
|
const lineReg = /^(\d+):([^:]*):(.+)$/m
|
|
15
|
-
const entityReg = new RegExp(`.*(${uuidSource}|${containerSource}|${taskSource})(
|
|
15
|
+
const entityReg = new RegExp(String.raw`.*(${uuidSource}|${containerSource}|${taskSource})(?:\.scope)?$`, 'm')
|
|
16
16
|
|
|
17
17
|
let inode = 0
|
|
18
18
|
let cgroup = ''
|
|
@@ -36,13 +36,12 @@ function makeRequest (data, url, cb) {
|
|
|
36
36
|
'Datadog-Meta-Lang': 'javascript',
|
|
37
37
|
'Datadog-Meta-Tracer-Version': pkg.version,
|
|
38
38
|
'Content-Type': 'application/msgpack'
|
|
39
|
-
}
|
|
39
|
+
},
|
|
40
|
+
protocol: url.protocol,
|
|
41
|
+
hostname: url.hostname,
|
|
42
|
+
port: url.port
|
|
40
43
|
}
|
|
41
44
|
|
|
42
|
-
options.protocol = url.protocol
|
|
43
|
-
options.hostname = url.hostname
|
|
44
|
-
options.port = url.port
|
|
45
|
-
|
|
46
45
|
log.debug('Request to the intake: %j', options)
|
|
47
46
|
|
|
48
47
|
request(data, options, (err, res) => {
|
|
@@ -32,13 +32,13 @@ const map = {
|
|
|
32
32
|
'resource.name': 'resource'
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
function format (span) {
|
|
35
|
+
function format (span, isChunkRoot) {
|
|
36
36
|
const formatted = formatSpan(span)
|
|
37
37
|
|
|
38
38
|
extractSpanLinks(formatted, span)
|
|
39
39
|
extractSpanEvents(formatted, span)
|
|
40
40
|
extractRootTags(formatted, span)
|
|
41
|
-
extractChunkTags(formatted, span)
|
|
41
|
+
extractChunkTags(formatted, span, isChunkRoot)
|
|
42
42
|
extractTags(formatted, span)
|
|
43
43
|
|
|
44
44
|
return formatted
|
|
@@ -192,11 +192,10 @@ function extractRootTags (formattedSpan, span) {
|
|
|
192
192
|
addTag({}, formattedSpan.metrics, TOP_LEVEL_KEY, 1)
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
function extractChunkTags (formattedSpan, span) {
|
|
195
|
+
function extractChunkTags (formattedSpan, span, isChunkRoot) {
|
|
196
196
|
const context = span.context()
|
|
197
|
-
const isLocalRoot = span === context._trace.started[0]
|
|
198
197
|
|
|
199
|
-
if (!
|
|
198
|
+
if (!isChunkRoot) return
|
|
200
199
|
|
|
201
200
|
for (const [key, value] of Object.entries(context._trace.tags)) {
|
|
202
201
|
addTag(formattedSpan.meta, formattedSpan.metrics, key, value)
|
|
@@ -82,7 +82,9 @@ class VercelAILLMObsPlugin extends BaseLLMObsPlugin {
|
|
|
82
82
|
* @param {string} toolDescription
|
|
83
83
|
* @returns {string | undefined}
|
|
84
84
|
*/
|
|
85
|
-
findToolName (toolDescription) {
|
|
85
|
+
findToolName (toolName, toolDescription) {
|
|
86
|
+
if (Number.isNaN(Number.parseInt(toolName))) return toolName
|
|
87
|
+
|
|
86
88
|
for (const availableTool of this.#availableTools) {
|
|
87
89
|
const description = availableTool.description
|
|
88
90
|
if (description === toolDescription && availableTool.id) {
|
|
@@ -260,16 +262,17 @@ class VercelAILLMObsPlugin extends BaseLLMObsPlugin {
|
|
|
260
262
|
|
|
261
263
|
const formattedToolCalls = []
|
|
262
264
|
for (const toolCall of outputMessageToolCalls) {
|
|
263
|
-
const
|
|
265
|
+
const toolArgs = toolCall.args ?? toolCall.input
|
|
266
|
+
const toolCallArgs = typeof toolArgs === 'string' ? getJsonStringValue(toolArgs, {}) : toolArgs
|
|
264
267
|
const toolDescription = toolsForModel?.find(tool => toolCall.toolName === tool.name)?.description
|
|
265
|
-
const name = this.findToolName(toolDescription)
|
|
268
|
+
const name = this.findToolName(toolCall.toolName, toolDescription)
|
|
266
269
|
this.#toolCallIdsToName[toolCall.toolCallId] = name
|
|
267
270
|
|
|
268
271
|
formattedToolCalls.push({
|
|
269
272
|
arguments: toolCallArgs,
|
|
270
273
|
name,
|
|
271
274
|
toolId: toolCall.toolCallId,
|
|
272
|
-
type: 'function'
|
|
275
|
+
type: toolCall.toolCallType ?? 'function'
|
|
273
276
|
})
|
|
274
277
|
}
|
|
275
278
|
|
|
@@ -317,10 +320,10 @@ class VercelAILLMObsPlugin extends BaseLLMObsPlugin {
|
|
|
317
320
|
finalContent += part.text ?? part.data
|
|
318
321
|
} else if (type === 'tool-call') {
|
|
319
322
|
const toolDescription = toolsForModel?.find(tool => part.toolName === tool.name)?.description
|
|
320
|
-
const name = this.findToolName(toolDescription)
|
|
323
|
+
const name = this.findToolName(part.toolName, toolDescription)
|
|
321
324
|
|
|
322
325
|
toolCalls.push({
|
|
323
|
-
arguments: part.args,
|
|
326
|
+
arguments: part.args ?? part.input,
|
|
324
327
|
name,
|
|
325
328
|
toolId: part.toolCallId,
|
|
326
329
|
type: 'function'
|
|
@@ -242,12 +242,9 @@ class AnthropicLLMObsPlugin extends LLMObsPlugin {
|
|
|
242
242
|
const cacheWriteTokens = usage.cache_creation_input_tokens
|
|
243
243
|
const cacheReadTokens = usage.cache_read_input_tokens
|
|
244
244
|
|
|
245
|
-
const metrics = {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
(inputTokens ?? 0) +
|
|
249
|
-
(cacheWriteTokens ?? 0) +
|
|
250
|
-
(cacheReadTokens ?? 0)
|
|
245
|
+
const metrics = {
|
|
246
|
+
inputTokens: (inputTokens ?? 0) + (cacheWriteTokens ?? 0) + (cacheReadTokens ?? 0)
|
|
247
|
+
}
|
|
251
248
|
|
|
252
249
|
if (outputTokens) metrics.outputTokens = outputTokens
|
|
253
250
|
const totalTokens = metrics.inputTokens + (outputTokens ?? 0)
|
|
@@ -37,8 +37,9 @@ class LangChainLLMObsHandler {
|
|
|
37
37
|
return message
|
|
38
38
|
}
|
|
39
39
|
try {
|
|
40
|
-
const messageContent = {
|
|
41
|
-
|
|
40
|
+
const messageContent = {
|
|
41
|
+
content: message.content || ''
|
|
42
|
+
}
|
|
42
43
|
|
|
43
44
|
const role = this.getRole(message)
|
|
44
45
|
if (role) messageContent.role = role
|