dd-trace 5.46.0 → 5.48.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 -2
- package/index.d.ts +39 -0
- package/package.json +8 -9
- package/packages/datadog-instrumentations/orchestrion.yml +52 -0
- package/packages/datadog-instrumentations/src/cucumber.js +2 -1
- package/packages/datadog-instrumentations/src/jest.js +11 -2
- package/packages/datadog-instrumentations/src/langchain.js +49 -53
- package/packages/datadog-instrumentations/src/mocha/main.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/utils.js +11 -3
- package/packages/datadog-instrumentations/src/orchestrion-config/index.js +5 -0
- package/packages/datadog-instrumentations/src/playwright.js +14 -2
- package/packages/datadog-instrumentations/src/vitest.js +11 -3
- package/packages/datadog-plugin-cucumber/src/index.js +11 -4
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +17 -5
- package/packages/datadog-plugin-jest/src/index.js +11 -4
- package/packages/datadog-plugin-langchain/src/index.js +18 -12
- package/packages/datadog-plugin-langchain/src/tracing.js +66 -6
- package/packages/datadog-plugin-mocha/src/index.js +17 -5
- package/packages/datadog-plugin-mongodb-core/src/index.js +5 -1
- package/packages/datadog-plugin-playwright/src/index.js +10 -3
- package/packages/datadog-plugin-vitest/src/index.js +13 -8
- package/packages/datadog-shimmer/src/shimmer.js +3 -42
- package/packages/dd-trace/src/appsec/iast/taint-tracking/filter.js +3 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +0 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +24 -11
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-telemetry.js +3 -32
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +98 -56
- package/packages/dd-trace/src/appsec/sdk/index.js +23 -1
- package/packages/dd-trace/src/appsec/sdk/noop.js +10 -0
- package/packages/dd-trace/src/appsec/sdk/set_user.js +2 -1
- package/packages/dd-trace/src/appsec/sdk/track_event.js +129 -8
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +0 -1
- package/packages/dd-trace/src/appsec/telemetry/index.js +2 -2
- package/packages/dd-trace/src/appsec/telemetry/user.js +2 -2
- package/packages/dd-trace/src/config.js +9 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +14 -1
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +36 -2
- package/packages/dd-trace/src/debugger/devtools_client/index.js +75 -33
- package/packages/dd-trace/src/debugger/devtools_client/send.js +4 -1
- package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
- package/packages/dd-trace/src/exporters/common/docker.js +37 -7
- package/packages/dd-trace/src/exporters/common/request.js +1 -4
- package/packages/dd-trace/src/llmobs/plugins/base.js +2 -2
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +62 -3
- package/packages/dd-trace/src/llmobs/plugins/openai.js +1 -0
- package/packages/dd-trace/src/llmobs/plugins/vertexai.js +2 -1
- package/packages/dd-trace/src/log/index.js +2 -0
- package/packages/dd-trace/src/log/writer.js +19 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +17 -3
- package/packages/dd-trace/src/opentracing/span.js +10 -0
- package/packages/dd-trace/src/plugins/util/test.js +7 -0
- package/packages/dd-trace/src/profiling/exporters/agent.js +1 -5
- package/packages/dd-trace/src/profiling/profilers/wall.js +3 -1
- package/packages/dd-trace/src/proxy.js +5 -1
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
module.exports =
|
|
3
|
+
module.exports = {
|
|
4
|
+
compile,
|
|
5
|
+
compileSegments,
|
|
6
|
+
templateRequiresEvaluation
|
|
7
|
+
}
|
|
4
8
|
|
|
5
9
|
const identifierRegex = /^[@a-zA-Z_$][\w$]*$/
|
|
6
10
|
|
|
@@ -35,7 +39,37 @@ const reservedWords = new Set([
|
|
|
35
39
|
|
|
36
40
|
const PRIMITIVE_TYPES = new Set(['string', 'number', 'bigint', 'boolean', 'undefined', 'symbol', 'null'])
|
|
37
41
|
|
|
38
|
-
|
|
42
|
+
function templateRequiresEvaluation (segments) {
|
|
43
|
+
if (segments === undefined) return false // There should always be segments, but just in case
|
|
44
|
+
for (const { str } of segments) {
|
|
45
|
+
if (str === undefined) return true
|
|
46
|
+
}
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function compileSegments (segments) {
|
|
51
|
+
let result = '['
|
|
52
|
+
for (let i = 0; i < segments.length; i++) {
|
|
53
|
+
const { str, dsl, json } = segments[i]
|
|
54
|
+
result += str !== undefined
|
|
55
|
+
? JSON.stringify(str)
|
|
56
|
+
: `(() => {
|
|
57
|
+
try {
|
|
58
|
+
const result = ${compile(json)}
|
|
59
|
+
return typeof result === 'string' ? result : $dd_inspect(result, $dd_segmentInspectOptions)
|
|
60
|
+
} catch (e) {
|
|
61
|
+
return { expr: ${JSON.stringify(dsl)}, message: \`\${e.name}: \${e.message}\` }
|
|
62
|
+
}
|
|
63
|
+
})()`
|
|
64
|
+
if (i !== segments.length - 1) {
|
|
65
|
+
result += ','
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return `${result}]`
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// TODO: Consider storing some of these functions that doesn't require closure access to the current scope on `process`
|
|
72
|
+
// so they can be reused across probes
|
|
39
73
|
function compile (node) {
|
|
40
74
|
if (node === null || typeof node === 'number' || typeof node === 'boolean') {
|
|
41
75
|
return node
|
|
@@ -16,10 +16,20 @@ const { NODE_MAJOR } = require('../../../../../version')
|
|
|
16
16
|
require('./remote_config')
|
|
17
17
|
|
|
18
18
|
// Expression to run on a call frame of the paused thread to get its active trace and span id.
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
19
|
+
const templateExpressionSetupCode = `
|
|
20
|
+
const $dd_inspect = global.require('node:util').inspect;
|
|
21
|
+
const $dd_segmentInspectOptions = {
|
|
22
|
+
depth: 0,
|
|
23
|
+
customInspect: false,
|
|
24
|
+
maxArrayLength: 3,
|
|
25
|
+
maxStringLength: 8 * 1024,
|
|
26
|
+
breakLength: Infinity
|
|
27
|
+
};
|
|
22
28
|
`
|
|
29
|
+
const getDDTagsExpression = `(() => {
|
|
30
|
+
const context = global.require('dd-trace').scope().active()?.context();
|
|
31
|
+
return { trace_id: context?.toTraceId(), span_id: context?.toSpanId() }
|
|
32
|
+
})()`
|
|
23
33
|
|
|
24
34
|
// There doesn't seem to be an official standard for the content of these fields, so we're just populating them with
|
|
25
35
|
// something that should be useful to a Node.js developer.
|
|
@@ -45,6 +55,7 @@ session.on('Debugger.paused', async ({ params }) => {
|
|
|
45
55
|
let sampled = false
|
|
46
56
|
let numberOfProbesWithSnapshots = 0
|
|
47
57
|
const probes = []
|
|
58
|
+
let templateExpressions = ''
|
|
48
59
|
|
|
49
60
|
// V8 doesn't allow setting more than one breakpoint at a specific location, however, it's possible to set two
|
|
50
61
|
// breakpoints just next to each other that will "snap" to the same logical location, which in turn will be hit at the
|
|
@@ -79,15 +90,6 @@ session.on('Debugger.paused', async ({ params }) => {
|
|
|
79
90
|
continue
|
|
80
91
|
}
|
|
81
92
|
|
|
82
|
-
if (shouldVerifyConditions && probe.condition !== undefined) {
|
|
83
|
-
const { result } = await session.post('Debugger.evaluateOnCallFrame', {
|
|
84
|
-
callFrameId: params.callFrames[0].callFrameId,
|
|
85
|
-
expression: probe.condition,
|
|
86
|
-
returnByValue: true
|
|
87
|
-
})
|
|
88
|
-
if (result.value !== true) continue
|
|
89
|
-
}
|
|
90
|
-
|
|
91
93
|
if (probe.captureSnapshot === true) {
|
|
92
94
|
// This algorithm to calculate number of sampled snapshots within the last second is not perfect, as it's not a
|
|
93
95
|
// sliding window. But it's quick and easy :)
|
|
@@ -107,9 +109,24 @@ session.on('Debugger.paused', async ({ params }) => {
|
|
|
107
109
|
maxLength = highestOrUndefined(probe.capture.maxLength, maxLength)
|
|
108
110
|
}
|
|
109
111
|
|
|
112
|
+
if (shouldVerifyConditions && probe.condition !== undefined) {
|
|
113
|
+
// TODO: Bundle all conditions and evaluate them in a single call
|
|
114
|
+
// TODO: Handle errors
|
|
115
|
+
const { result } = await session.post('Debugger.evaluateOnCallFrame', {
|
|
116
|
+
callFrameId: params.callFrames[0].callFrameId,
|
|
117
|
+
expression: probe.condition,
|
|
118
|
+
returnByValue: true
|
|
119
|
+
})
|
|
120
|
+
if (result.value !== true) continue
|
|
121
|
+
}
|
|
122
|
+
|
|
110
123
|
sampled = true
|
|
111
124
|
probe.lastCaptureNs = start
|
|
112
125
|
|
|
126
|
+
if (probe.templateRequiresEvaluation) {
|
|
127
|
+
templateExpressions += `,${probe.template}`
|
|
128
|
+
}
|
|
129
|
+
|
|
113
130
|
probes.push(probe)
|
|
114
131
|
}
|
|
115
132
|
}
|
|
@@ -119,7 +136,22 @@ session.on('Debugger.paused', async ({ params }) => {
|
|
|
119
136
|
}
|
|
120
137
|
|
|
121
138
|
const timestamp = Date.now()
|
|
122
|
-
|
|
139
|
+
|
|
140
|
+
let evalResults = null
|
|
141
|
+
const { result } = await session.post('Debugger.evaluateOnCallFrame', {
|
|
142
|
+
callFrameId: params.callFrames[0].callFrameId,
|
|
143
|
+
expression: templateExpressions.length === 0
|
|
144
|
+
? `[${getDDTagsExpression}]`
|
|
145
|
+
: `${templateExpressionSetupCode}[${getDDTagsExpression}${templateExpressions}]`,
|
|
146
|
+
returnByValue: true,
|
|
147
|
+
includeCommandLineAPI: true
|
|
148
|
+
})
|
|
149
|
+
if (result?.subtype === 'error') {
|
|
150
|
+
log.error('[debugger:devtools_client] Error evaluating code on call frame: %s', result?.description)
|
|
151
|
+
evalResults = []
|
|
152
|
+
} else {
|
|
153
|
+
evalResults = result?.value ?? []
|
|
154
|
+
}
|
|
123
155
|
|
|
124
156
|
let processLocalState
|
|
125
157
|
if (numberOfProbesWithSnapshots !== 0) {
|
|
@@ -155,6 +187,8 @@ session.on('Debugger.paused', async ({ params }) => {
|
|
|
155
187
|
}
|
|
156
188
|
|
|
157
189
|
const stack = getStackFromCallFrames(params.callFrames)
|
|
190
|
+
const dd = processDD(evalResults[0]) // the first result is the dd tags, the rest are the probe template results
|
|
191
|
+
let messageIndex = 1
|
|
158
192
|
|
|
159
193
|
// TODO: Send multiple probes in one HTTP request as an array (DEBUG-2848)
|
|
160
194
|
for (const probe of probes) {
|
|
@@ -179,9 +213,33 @@ session.on('Debugger.paused', async ({ params }) => {
|
|
|
179
213
|
}
|
|
180
214
|
}
|
|
181
215
|
|
|
216
|
+
let message = ''
|
|
217
|
+
if (probe.templateRequiresEvaluation) {
|
|
218
|
+
const results = evalResults[messageIndex++]
|
|
219
|
+
if (results === undefined) {
|
|
220
|
+
log.error('[debugger:devtools_client] No evaluation results for probe %s', probe.id)
|
|
221
|
+
} else {
|
|
222
|
+
for (const result of results) {
|
|
223
|
+
if (typeof result === 'string') {
|
|
224
|
+
message += result
|
|
225
|
+
} else {
|
|
226
|
+
// If `result` isn't a string, it's an evaluation error object
|
|
227
|
+
if (snapshot.evaluationErrors === undefined) {
|
|
228
|
+
snapshot.evaluationErrors = [result]
|
|
229
|
+
} else {
|
|
230
|
+
snapshot.evaluationErrors.push(result)
|
|
231
|
+
}
|
|
232
|
+
message += `{${result.message}}`
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
message = probe.template
|
|
238
|
+
}
|
|
239
|
+
|
|
182
240
|
ackEmitting(probe)
|
|
183
|
-
|
|
184
|
-
send(
|
|
241
|
+
|
|
242
|
+
send(message, logger, dd, snapshot)
|
|
185
243
|
}
|
|
186
244
|
})
|
|
187
245
|
|
|
@@ -189,22 +247,6 @@ function highestOrUndefined (num, max) {
|
|
|
189
247
|
return num === undefined ? max : Math.max(num, max ?? 0)
|
|
190
248
|
}
|
|
191
249
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
// `Runtime.releaseObjectGroup`
|
|
195
|
-
const { result } = await session.post('Debugger.evaluateOnCallFrame', {
|
|
196
|
-
callFrameId,
|
|
197
|
-
expression,
|
|
198
|
-
returnByValue: true,
|
|
199
|
-
includeCommandLineAPI: true
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
if (result?.value?.trace_id === undefined) {
|
|
203
|
-
if (result?.subtype === 'error') {
|
|
204
|
-
log.error('[debugger:devtools_client] Error getting trace/span id:', result.description)
|
|
205
|
-
}
|
|
206
|
-
return
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return result.value
|
|
250
|
+
function processDD (result) {
|
|
251
|
+
return result?.trace_id === undefined ? undefined : result
|
|
210
252
|
}
|
|
@@ -12,6 +12,7 @@ const { version } = require('../../../../../package.json')
|
|
|
12
12
|
|
|
13
13
|
module.exports = send
|
|
14
14
|
|
|
15
|
+
const MAX_MESSAGE_LENGTH = 8 * 1024 // 8KB
|
|
15
16
|
const MAX_LOG_PAYLOAD_SIZE = 1024 * 1024 // 1MB
|
|
16
17
|
|
|
17
18
|
const ddsource = 'dd_debugger'
|
|
@@ -36,7 +37,9 @@ function send (message, logger, dd, snapshot) {
|
|
|
36
37
|
ddsource,
|
|
37
38
|
hostname,
|
|
38
39
|
service,
|
|
39
|
-
message
|
|
40
|
+
message: message?.length > MAX_MESSAGE_LENGTH
|
|
41
|
+
? message.slice(0, MAX_MESSAGE_LENGTH) + '…'
|
|
42
|
+
: message,
|
|
40
43
|
logger,
|
|
41
44
|
dd,
|
|
42
45
|
debugger: { snapshot }
|
|
@@ -27,7 +27,7 @@ const self = module.exports = {
|
|
|
27
27
|
|
|
28
28
|
async getGeneratedPosition (url, source, line, sourceMapURL) {
|
|
29
29
|
const dir = dirname(new URL(url).pathname)
|
|
30
|
-
return
|
|
30
|
+
return SourceMapConsumer.with(
|
|
31
31
|
await self.loadSourceMap(dir, sourceMapURL),
|
|
32
32
|
null,
|
|
33
33
|
(consumer) => consumer.generatedPositionFor({ source, line, column: 0 })
|
|
@@ -2,34 +2,64 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs')
|
|
4
4
|
|
|
5
|
+
const { DD_EXTERNAL_ENV } = process.env
|
|
6
|
+
|
|
5
7
|
// The second part is the PCF / Garden regexp. We currently assume no suffix($) to avoid matching pod UIDs
|
|
6
8
|
// See https://github.com/DataDog/datadog-agent/blob/7.40.x/pkg/util/cgroups/reader.go#L50
|
|
7
9
|
const uuidSource =
|
|
8
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}$'
|
|
9
11
|
const containerSource = '[0-9a-f]{64}'
|
|
10
12
|
const taskSource = '[0-9a-f]{32}-\\d+'
|
|
13
|
+
const lineReg = /^(\d+):([^:]*):(.+)$/
|
|
11
14
|
const entityReg = new RegExp(`.*(${uuidSource}|${containerSource}|${taskSource})(?:\\.scope)?$`, 'm')
|
|
12
15
|
|
|
16
|
+
const cgroup = readControlGroup()
|
|
13
17
|
const entityId = getEntityId()
|
|
18
|
+
const inode = getInode()
|
|
14
19
|
|
|
15
20
|
function getEntityId () {
|
|
16
|
-
const
|
|
17
|
-
const match = cgroup.trim().match(entityReg) || []
|
|
21
|
+
const match = cgroup.match(entityReg) || []
|
|
18
22
|
|
|
19
23
|
return match[1]
|
|
20
24
|
}
|
|
21
25
|
|
|
26
|
+
function getInode () {
|
|
27
|
+
const match = cgroup.match(lineReg) || []
|
|
28
|
+
|
|
29
|
+
return readInode(match[3])
|
|
30
|
+
}
|
|
31
|
+
|
|
22
32
|
function readControlGroup () {
|
|
23
33
|
try {
|
|
24
|
-
return fs.readFileSync('/proc/self/cgroup').toString()
|
|
34
|
+
return fs.readFileSync('/proc/self/cgroup').toString().trim()
|
|
35
|
+
} catch (err) {
|
|
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
|
|
25
47
|
} catch (err) {
|
|
26
|
-
|
|
48
|
+
return 0
|
|
27
49
|
}
|
|
28
50
|
}
|
|
29
51
|
|
|
30
52
|
module.exports = {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
53
|
+
inject (carrier) {
|
|
54
|
+
if (entityId) {
|
|
55
|
+
carrier['Datadog-Container-Id'] = entityId
|
|
56
|
+
carrier['Datadog-Entity-ID'] = `ci-${entityId}`
|
|
57
|
+
} else if (inode) {
|
|
58
|
+
carrier['Datadog-Entity-ID'] = `in-${inode}`
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (DD_EXTERNAL_ENV) {
|
|
62
|
+
carrier['Datadog-External-Env'] = DD_EXTERNAL_ENV
|
|
63
|
+
}
|
|
34
64
|
}
|
|
35
65
|
}
|
|
@@ -15,7 +15,6 @@ const { storage } = require('../../../../datadog-core')
|
|
|
15
15
|
const log = require('../../log')
|
|
16
16
|
|
|
17
17
|
const maxActiveRequests = 8
|
|
18
|
-
const containerId = docker.id()
|
|
19
18
|
|
|
20
19
|
let activeRequests = 0
|
|
21
20
|
|
|
@@ -63,9 +62,7 @@ function request (data, options, callback) {
|
|
|
63
62
|
options.headers['Content-Length'] = byteLength(dataArray)
|
|
64
63
|
}
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
options.headers['Datadog-Container-ID'] = containerId
|
|
68
|
-
}
|
|
65
|
+
docker.inject(options.headers)
|
|
69
66
|
|
|
70
67
|
options.agent = isSecure ? httpsAgent : httpAgent
|
|
71
68
|
|
|
@@ -37,13 +37,13 @@ class LLMObsPlugin extends TracingPlugin {
|
|
|
37
37
|
// register options may not be set for operations we do not trace with llmobs
|
|
38
38
|
// ie OpenAI fine tuning jobs, file jobs, etc.
|
|
39
39
|
if (registerOptions) {
|
|
40
|
-
telemetry.incrementLLMObsSpanStartCount({ autoinstrumented: true, integration: this.constructor.
|
|
40
|
+
telemetry.incrementLLMObsSpanStartCount({ autoinstrumented: true, integration: this.constructor.integration })
|
|
41
41
|
|
|
42
42
|
ctx.llmobs = {} // initialize context-based namespace
|
|
43
43
|
llmobsStorage.enterWith({ span })
|
|
44
44
|
ctx.llmobs.parent = parent
|
|
45
45
|
|
|
46
|
-
this._tagger.registerLLMObsSpan(span, { parent, integration: this.constructor.
|
|
46
|
+
this._tagger.registerLLMObsSpan(span, { parent, integration: this.constructor.integration, ...registerOptions })
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -20,7 +20,8 @@ const ChatModelHandler = require('./handlers/chat_model')
|
|
|
20
20
|
const LlmHandler = require('./handlers/llm')
|
|
21
21
|
const EmbeddingHandler = require('./handlers/embedding')
|
|
22
22
|
|
|
23
|
-
class
|
|
23
|
+
class BaseLangChainLLMObsPlugin extends LLMObsPlugin {
|
|
24
|
+
static get integration () { return 'langchain' }
|
|
24
25
|
static get id () { return 'langchain' }
|
|
25
26
|
static get prefix () {
|
|
26
27
|
return 'tracing:apm:langchain:invoke'
|
|
@@ -55,8 +56,11 @@ class LangChainLLMObsPlugin extends LLMObsPlugin {
|
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
setLLMObsTags (ctx) {
|
|
59
|
+
ctx.args = ctx.arguments
|
|
60
|
+
ctx.instance = ctx.self
|
|
61
|
+
|
|
58
62
|
const span = ctx.currentStore?.span
|
|
59
|
-
const type = ctx.type // langchain operation type (oneof chain,chat_model,llm,embedding)
|
|
63
|
+
const type = ctx.type = this.constructor.lcType // langchain operation type (oneof chain,chat_model,llm,embedding)
|
|
60
64
|
|
|
61
65
|
if (!Object.keys(this._handlers).includes(type)) {
|
|
62
66
|
log.warn(`Unsupported LangChain operation type: ${type}`)
|
|
@@ -129,4 +133,59 @@ class LangChainLLMObsPlugin extends LLMObsPlugin {
|
|
|
129
133
|
}
|
|
130
134
|
}
|
|
131
135
|
|
|
132
|
-
|
|
136
|
+
class RunnableSequenceInvokePlugin extends BaseLangChainLLMObsPlugin {
|
|
137
|
+
static get id () { return 'llmobs_langchain_rs_invoke' }
|
|
138
|
+
static get lcType () { return 'chain' }
|
|
139
|
+
static get prefix () {
|
|
140
|
+
return 'tracing:orchestrion:@langchain/core:RunnableSequence_invoke'
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
class RunnableSequenceBatchPlugin extends BaseLangChainLLMObsPlugin {
|
|
145
|
+
static get id () { return 'llmobs_langchain_rs_batch' }
|
|
146
|
+
static get lcType () { return 'chain' }
|
|
147
|
+
static get prefix () {
|
|
148
|
+
return 'tracing:orchestrion:@langchain/core:RunnableSequence_batch'
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
class BaseChatModelGeneratePlugin extends BaseLangChainLLMObsPlugin {
|
|
153
|
+
static get id () { return 'llmobs_langchain_chat_model_generate' }
|
|
154
|
+
static get lcType () { return 'chat_model' }
|
|
155
|
+
static get prefix () {
|
|
156
|
+
return 'tracing:orchestrion:@langchain/core:BaseChatModel_generate'
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
class BaseLLMGeneratePlugin extends BaseLangChainLLMObsPlugin {
|
|
161
|
+
static get id () { return 'llmobs_langchain_llm_generate' }
|
|
162
|
+
static get lcType () { return 'llm' }
|
|
163
|
+
static get prefix () {
|
|
164
|
+
return 'tracing:orchestrion:@langchain/core:BaseLLM_generate'
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
class EmbeddingsEmbedQueryPlugin extends BaseLangChainLLMObsPlugin {
|
|
169
|
+
static get id () { return 'llmobs_langchain_embeddings_embed_query' }
|
|
170
|
+
static get lcType () { return 'embedding' }
|
|
171
|
+
static get prefix () {
|
|
172
|
+
return 'tracing:apm:@langchain/core:Embeddings_embedQuery'
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
class EmbeddingsEmbedDocumentsPlugin extends BaseLangChainLLMObsPlugin {
|
|
177
|
+
static get id () { return 'llmobs_langchain_embeddings_embed_documents' }
|
|
178
|
+
static get lcType () { return 'embedding' }
|
|
179
|
+
static get prefix () {
|
|
180
|
+
return 'tracing:apm:@langchain/core:Embeddings_embedDocuments'
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = [
|
|
185
|
+
RunnableSequenceInvokePlugin,
|
|
186
|
+
RunnableSequenceBatchPlugin,
|
|
187
|
+
BaseChatModelGeneratePlugin,
|
|
188
|
+
BaseLLMGeneratePlugin,
|
|
189
|
+
EmbeddingsEmbedQueryPlugin,
|
|
190
|
+
EmbeddingsEmbedDocumentsPlugin
|
|
191
|
+
]
|
|
@@ -7,7 +7,8 @@ const {
|
|
|
7
7
|
} = require('../../../../datadog-plugin-google-cloud-vertexai/src/utils')
|
|
8
8
|
|
|
9
9
|
class VertexAILLMObsPlugin extends LLMObsPlugin {
|
|
10
|
-
static get
|
|
10
|
+
static get integration () { return 'vertexai' } // used for llmobs telemetry
|
|
11
|
+
static get id () { return 'vertexai' }
|
|
11
12
|
static get prefix () {
|
|
12
13
|
return 'tracing:apm:vertexai:request'
|
|
13
14
|
}
|
|
@@ -13,6 +13,7 @@ const defaultLogger = {
|
|
|
13
13
|
let enabled = false
|
|
14
14
|
let logger = defaultLogger
|
|
15
15
|
let logChannel = new LogChannel()
|
|
16
|
+
let stackTraceLimitFunction = onError
|
|
16
17
|
|
|
17
18
|
function withNoop (fn) {
|
|
18
19
|
const store = storage('legacy').getStore()
|
|
@@ -61,12 +62,28 @@ function getErrorLog (err) {
|
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
function setStackTraceLimitFunction (fn) {
|
|
66
|
+
if (typeof fn !== 'function') {
|
|
67
|
+
throw new TypeError('stackTraceLimitFunction must be a function')
|
|
68
|
+
}
|
|
69
|
+
stackTraceLimitFunction = fn
|
|
70
|
+
}
|
|
71
|
+
|
|
64
72
|
function onError (err) {
|
|
65
73
|
const { formatted, cause } = getErrorLog(err)
|
|
66
74
|
|
|
67
75
|
// calling twice logger.error() because Error cause is only available in nodejs v16.9.0
|
|
68
76
|
// TODO: replace it with Error(message, { cause }) when cause has broad support
|
|
69
|
-
if (formatted)
|
|
77
|
+
if (formatted) {
|
|
78
|
+
withNoop(() => {
|
|
79
|
+
const l = Error.stackTraceLimit
|
|
80
|
+
Error.stackTraceLimit = 0
|
|
81
|
+
const e = new Error(formatted)
|
|
82
|
+
Error.stackTraceLimit = l
|
|
83
|
+
Error.captureStackTrace(e, stackTraceLimitFunction)
|
|
84
|
+
logger.error(e)
|
|
85
|
+
})
|
|
86
|
+
}
|
|
70
87
|
if (cause) withNoop(() => logger.error(cause))
|
|
71
88
|
}
|
|
72
89
|
|
|
@@ -122,4 +139,4 @@ function trace (...args) {
|
|
|
122
139
|
onTrace(Log.parse(...args))
|
|
123
140
|
}
|
|
124
141
|
|
|
125
|
-
module.exports = { use, toggle, reset, error, warn, info, debug, trace }
|
|
142
|
+
module.exports = { use, toggle, reset, error, warn, info, debug, trace, setStackTraceLimitFunction }
|
|
@@ -298,6 +298,7 @@ class TextMapPropagator {
|
|
|
298
298
|
|
|
299
299
|
_extractSpanContext (carrier) {
|
|
300
300
|
let context = null
|
|
301
|
+
let style = ''
|
|
301
302
|
for (const extractor of this._config.tracePropagationStyle.extract) {
|
|
302
303
|
let extractedContext = null
|
|
303
304
|
switch (extractor) {
|
|
@@ -331,9 +332,9 @@ class TextMapPropagator {
|
|
|
331
332
|
|
|
332
333
|
if (context === null) {
|
|
333
334
|
context = extractedContext
|
|
335
|
+
style = extractor
|
|
334
336
|
if (this._config.tracePropagationExtractFirst) {
|
|
335
|
-
|
|
336
|
-
return context
|
|
337
|
+
break
|
|
337
338
|
}
|
|
338
339
|
} else {
|
|
339
340
|
// If extractor is tracecontext, add tracecontext specific information to the context
|
|
@@ -342,7 +343,7 @@ class TextMapPropagator {
|
|
|
342
343
|
this._extractTraceparentContext(carrier), context, carrier)
|
|
343
344
|
}
|
|
344
345
|
if (extractedContext._traceId && extractedContext._spanId &&
|
|
345
|
-
|
|
346
|
+
extractedContext.toTraceId(true) !== context.toTraceId(true)) {
|
|
346
347
|
const link = {
|
|
347
348
|
context: extractedContext,
|
|
348
349
|
attributes: { reason: 'terminated_context', context_headers: extractor }
|
|
@@ -354,6 +355,19 @@ class TextMapPropagator {
|
|
|
354
355
|
|
|
355
356
|
this._extractBaggageItems(carrier, context)
|
|
356
357
|
|
|
358
|
+
if (this._config.tracePropagationBehaviorExtract === 'ignore') {
|
|
359
|
+
context._links = []
|
|
360
|
+
} else if (this._config.tracePropagationBehaviorExtract === 'restart') {
|
|
361
|
+
context._links = []
|
|
362
|
+
context._links.push({
|
|
363
|
+
context,
|
|
364
|
+
attributes:
|
|
365
|
+
{
|
|
366
|
+
reason: 'propagation_behavior_extract', context_headers: style
|
|
367
|
+
}
|
|
368
|
+
})
|
|
369
|
+
}
|
|
370
|
+
|
|
357
371
|
return context || this._extractSqsdContext(carrier)
|
|
358
372
|
}
|
|
359
373
|
|
|
@@ -319,6 +319,12 @@ class DatadogSpan {
|
|
|
319
319
|
let spanContext
|
|
320
320
|
let startTime
|
|
321
321
|
|
|
322
|
+
let baggage = {}
|
|
323
|
+
if (parent && parent._isRemote && this._parentTracer?._config?.tracePropagationBehaviorExtract !== 'continue') {
|
|
324
|
+
baggage = parent._baggageItems
|
|
325
|
+
parent = null
|
|
326
|
+
}
|
|
327
|
+
|
|
322
328
|
if (fields.context) {
|
|
323
329
|
spanContext = fields.context
|
|
324
330
|
if (!spanContext._trace.startTime) {
|
|
@@ -352,6 +358,10 @@ class DatadogSpan {
|
|
|
352
358
|
.padStart(8, '0')
|
|
353
359
|
.padEnd(16, '0')
|
|
354
360
|
}
|
|
361
|
+
|
|
362
|
+
if (this._parentTracer?._config?.tracePropagationBehaviorExtract === 'restart') {
|
|
363
|
+
spanContext._baggageItems = baggage
|
|
364
|
+
}
|
|
355
365
|
}
|
|
356
366
|
|
|
357
367
|
spanContext._trace.ticks = spanContext._trace.ticks || now()
|
|
@@ -120,6 +120,12 @@ const TEST_LEVEL_EVENT_TYPES = [
|
|
|
120
120
|
'test_module_end',
|
|
121
121
|
'test_session_end'
|
|
122
122
|
]
|
|
123
|
+
const TEST_RETRY_REASON_TYPES = {
|
|
124
|
+
efd: 'early_flake_detection',
|
|
125
|
+
atr: 'auto_test_retry',
|
|
126
|
+
atf: 'attempt_to_fix',
|
|
127
|
+
ext: 'external'
|
|
128
|
+
}
|
|
123
129
|
|
|
124
130
|
const DD_TEST_IS_USER_PROVIDED_SERVICE = '_dd.test.is_user_provided_service'
|
|
125
131
|
|
|
@@ -227,6 +233,7 @@ module.exports = {
|
|
|
227
233
|
DD_CAPABILITIES_TEST_MANAGEMENT_DISABLE,
|
|
228
234
|
DD_CAPABILITIES_TEST_MANAGEMENT_ATTEMPT_TO_FIX,
|
|
229
235
|
TEST_LEVEL_EVENT_TYPES,
|
|
236
|
+
TEST_RETRY_REASON_TYPES,
|
|
230
237
|
getNumFromKnownTests,
|
|
231
238
|
getFileAndLineNumberFromError,
|
|
232
239
|
DI_ERROR_DEBUG_INFO_CAPTURED,
|
|
@@ -16,8 +16,6 @@ const perf = require('perf_hooks').performance
|
|
|
16
16
|
const telemetryMetrics = require('../../telemetry/metrics')
|
|
17
17
|
const profilersNamespace = telemetryMetrics.manager.namespace('profilers')
|
|
18
18
|
|
|
19
|
-
const containerId = docker.id()
|
|
20
|
-
|
|
21
19
|
const statusCodeCounters = []
|
|
22
20
|
const requestCounter = profilersNamespace.count('profile_api.requests', [])
|
|
23
21
|
const sizeDistribution = profilersNamespace.distribution('profile_api.bytes', [])
|
|
@@ -155,9 +153,7 @@ class AgentExporter extends EventSerializer {
|
|
|
155
153
|
timeout: this._backoffTime * Math.pow(2, attempt)
|
|
156
154
|
}
|
|
157
155
|
|
|
158
|
-
|
|
159
|
-
options.headers['Datadog-Container-ID'] = containerId
|
|
160
|
-
}
|
|
156
|
+
docker.inject(options.headers)
|
|
161
157
|
|
|
162
158
|
if (this._url.protocol === 'unix:') {
|
|
163
159
|
options.socketPath = this._url.pathname
|
|
@@ -70,7 +70,9 @@ function ensureChannelsActivated () {
|
|
|
70
70
|
class NativeWallProfiler {
|
|
71
71
|
constructor (options = {}) {
|
|
72
72
|
this.type = 'wall'
|
|
73
|
-
|
|
73
|
+
// Currently there's a crash sometimes on worker threads trying to collect async IDs so for the
|
|
74
|
+
// time being we'll constrain it to only the main thread.
|
|
75
|
+
this._asyncIdEnabled = !!options.asyncIdEnabled && require('worker_threads').isMainThread
|
|
74
76
|
this._codeHotspotsEnabled = !!options.codeHotspotsEnabled
|
|
75
77
|
this._cpuProfilingEnabled = !!options.cpuProfilingEnabled
|
|
76
78
|
this._endpointCollectionEnabled = !!options.endpointCollectionEnabled
|
|
@@ -70,7 +70,8 @@ class Tracer extends NoopProxy {
|
|
|
70
70
|
this._modules = {
|
|
71
71
|
appsec: new LazyModule(() => require('./appsec')),
|
|
72
72
|
iast: new LazyModule(() => require('./appsec/iast')),
|
|
73
|
-
llmobs: new LazyModule(() => require('./llmobs'))
|
|
73
|
+
llmobs: new LazyModule(() => require('./llmobs')),
|
|
74
|
+
rewriter: new LazyModule(() => require('./appsec/iast/taint-tracking/rewriter'))
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
|
|
@@ -178,6 +179,8 @@ class Tracer extends NoopProxy {
|
|
|
178
179
|
|
|
179
180
|
this._enableOrDisableTracing(config)
|
|
180
181
|
|
|
182
|
+
this._modules.rewriter.enable(config)
|
|
183
|
+
|
|
181
184
|
if (config.tracing) {
|
|
182
185
|
if (config.isManualApiEnabled) {
|
|
183
186
|
const TestApiManualPlugin = require('./ci-visibility/test-api-manual/test-api-manual-plugin')
|
|
@@ -247,6 +250,7 @@ class Tracer extends NoopProxy {
|
|
|
247
250
|
if (config.iast.enabled) {
|
|
248
251
|
this._modules.iast.enable(config, this._tracer)
|
|
249
252
|
}
|
|
253
|
+
// This needs to be after the IAST module is enabled
|
|
250
254
|
} else if (this._tracingInitialized) {
|
|
251
255
|
this._modules.appsec.disable()
|
|
252
256
|
this._modules.iast.disable()
|