dd-trace 5.30.0 → 5.32.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 -0
- package/README.md +9 -7
- package/package.json +7 -6
- package/packages/datadog-core/src/storage.js +11 -2
- package/packages/datadog-instrumentations/src/aerospike.js +1 -1
- package/packages/datadog-instrumentations/src/aws-sdk.js +2 -1
- package/packages/datadog-instrumentations/src/cucumber.js +14 -5
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
- package/packages/datadog-instrumentations/src/jest.js +70 -36
- package/packages/datadog-instrumentations/src/mocha/utils.js +23 -7
- package/packages/datadog-instrumentations/src/node-serialize.js +22 -0
- package/packages/datadog-instrumentations/src/openai.js +2 -0
- package/packages/datadog-instrumentations/src/vitest.js +107 -59
- package/packages/datadog-instrumentations/src/vm.js +49 -0
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime.js +295 -0
- package/packages/datadog-plugin-aws-sdk/src/services/index.js +1 -0
- package/packages/datadog-plugin-cucumber/src/index.js +30 -32
- package/packages/datadog-plugin-jest/src/index.js +34 -37
- package/packages/datadog-plugin-langchain/src/index.js +12 -80
- package/packages/datadog-plugin-langchain/src/tracing.js +89 -0
- package/packages/datadog-plugin-mocha/src/index.js +18 -36
- package/packages/datadog-plugin-vitest/src/index.js +20 -34
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/code-injection-analyzer.js +2 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/untrusted-deserialization-analyzer.js +16 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +9 -8
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
- package/packages/dd-trace/src/appsec/remote_config/manager.js +11 -1
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +37 -0
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +65 -28
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +57 -17
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +18 -3
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +20 -3
- package/packages/dd-trace/src/config.js +39 -3
- package/packages/dd-trace/src/crashtracking/crashtracker.js +9 -0
- package/packages/dd-trace/src/crashtracking/noop.js +3 -0
- package/packages/dd-trace/src/datastreams/fnv.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/config.js +3 -1
- package/packages/dd-trace/src/debugger/devtools_client/defaults.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +32 -14
- package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +36 -0
- package/packages/dd-trace/src/debugger/devtools_client/send.js +29 -10
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +35 -1
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +112 -0
- package/packages/dd-trace/src/debugger/devtools_client/status.js +20 -11
- package/packages/dd-trace/src/debugger/index.js +2 -13
- package/packages/dd-trace/src/llmobs/plugins/base.js +40 -11
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chain.js +24 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +111 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/embedding.js +42 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +102 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/llm.js +32 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +131 -0
- package/packages/dd-trace/src/llmobs/plugins/openai.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +90 -26
- package/packages/dd-trace/src/llmobs/tagger.js +11 -3
- package/packages/dd-trace/src/llmobs/util.js +7 -1
- package/packages/dd-trace/src/llmobs/writers/spans/agentProxy.js +3 -3
- package/packages/dd-trace/src/log/index.js +8 -9
- package/packages/dd-trace/src/noop/proxy.js +2 -2
- package/packages/dd-trace/src/noop/span.js +1 -1
- package/packages/dd-trace/src/opentelemetry/context_manager.js +43 -3
- package/packages/dd-trace/src/opentracing/span.js +11 -1
- package/packages/dd-trace/src/opentracing/span_context.js +12 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +57 -27
- package/packages/dd-trace/src/plugins/util/test.js +42 -12
- package/packages/dd-trace/src/priority_sampler.js +7 -2
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +21 -0
- package/packages/dd-trace/src/profiling/profiler.js +11 -8
- package/packages/dd-trace/src/profiling/profilers/events.js +17 -1
- package/packages/dd-trace/src/proxy.js +6 -3
- package/packages/dd-trace/src/scope.js +1 -1
- package/packages/dd-trace/src/telemetry/index.js +2 -0
|
@@ -26,12 +26,7 @@ const {
|
|
|
26
26
|
TEST_MODULE,
|
|
27
27
|
TEST_MODULE_ID,
|
|
28
28
|
TEST_SUITE,
|
|
29
|
-
CUCUMBER_IS_PARALLEL
|
|
30
|
-
TEST_NAME,
|
|
31
|
-
DI_ERROR_DEBUG_INFO_CAPTURED,
|
|
32
|
-
DI_DEBUG_ERROR_SNAPSHOT_ID,
|
|
33
|
-
DI_DEBUG_ERROR_FILE,
|
|
34
|
-
DI_DEBUG_ERROR_LINE
|
|
29
|
+
CUCUMBER_IS_PARALLEL
|
|
35
30
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
36
31
|
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
37
32
|
const { COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants')
|
|
@@ -50,8 +45,8 @@ const {
|
|
|
50
45
|
} = require('../../dd-trace/src/ci-visibility/telemetry')
|
|
51
46
|
const id = require('../../dd-trace/src/id')
|
|
52
47
|
|
|
48
|
+
const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
|
|
53
49
|
const isCucumberWorker = !!process.env.CUCUMBER_WORKER_ID
|
|
54
|
-
const debuggerParameterPerTest = new Map()
|
|
55
50
|
|
|
56
51
|
function getTestSuiteTags (testSuiteSpan) {
|
|
57
52
|
const suiteTags = {
|
|
@@ -210,7 +205,13 @@ class CucumberPlugin extends CiPlugin {
|
|
|
210
205
|
this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_FINISHED, 'suite', { library: 'istanbul' })
|
|
211
206
|
})
|
|
212
207
|
|
|
213
|
-
this.addSub('ci:cucumber:test:start', ({
|
|
208
|
+
this.addSub('ci:cucumber:test:start', ({
|
|
209
|
+
testName,
|
|
210
|
+
testFileAbsolutePath,
|
|
211
|
+
testSourceLine,
|
|
212
|
+
isParallel,
|
|
213
|
+
promises
|
|
214
|
+
}) => {
|
|
214
215
|
const store = storage.getStore()
|
|
215
216
|
const testSuite = getTestSuitePath(testFileAbsolutePath, this.sourceRoot)
|
|
216
217
|
const testSourceFile = getTestSuitePath(testFileAbsolutePath, this.repositoryRoot)
|
|
@@ -227,38 +228,30 @@ class CucumberPlugin extends CiPlugin {
|
|
|
227
228
|
|
|
228
229
|
this.enter(testSpan, store)
|
|
229
230
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
this.retriedTestIds = {
|
|
237
|
-
spanId: spanContext.toSpanId(),
|
|
238
|
-
traceId: spanContext.toTraceId()
|
|
239
|
-
}
|
|
240
|
-
const { snapshotId, file, line } = debuggerParameters
|
|
241
|
-
|
|
242
|
-
// TODO: should these be added on test:end if and only if the probe is hit?
|
|
243
|
-
// Sync issues: `hitProbePromise` might be resolved after the test ends
|
|
244
|
-
testSpan.setTag(DI_ERROR_DEBUG_INFO_CAPTURED, 'true')
|
|
245
|
-
testSpan.setTag(DI_DEBUG_ERROR_SNAPSHOT_ID, snapshotId)
|
|
246
|
-
testSpan.setTag(DI_DEBUG_ERROR_FILE, file)
|
|
247
|
-
testSpan.setTag(DI_DEBUG_ERROR_LINE, line)
|
|
231
|
+
this.activeTestSpan = testSpan
|
|
232
|
+
// Time we give the breakpoint to be hit
|
|
233
|
+
if (promises && this.runningTestProbeId) {
|
|
234
|
+
promises.hitBreakpointPromise = new Promise((resolve) => {
|
|
235
|
+
setTimeout(resolve, BREAKPOINT_HIT_GRACE_PERIOD_MS)
|
|
236
|
+
})
|
|
248
237
|
}
|
|
249
238
|
})
|
|
250
239
|
|
|
251
|
-
this.addSub('ci:cucumber:test:retry', ({
|
|
240
|
+
this.addSub('ci:cucumber:test:retry', ({ isFirstAttempt, error }) => {
|
|
252
241
|
const store = storage.getStore()
|
|
253
242
|
const span = store.span
|
|
254
|
-
if (
|
|
243
|
+
if (!isFirstAttempt) {
|
|
255
244
|
span.setTag(TEST_IS_RETRY, 'true')
|
|
256
245
|
}
|
|
257
246
|
span.setTag('error', error)
|
|
258
|
-
if (this.di && error && this.libraryConfig?.isDiEnabled) {
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
247
|
+
if (isFirstAttempt && this.di && error && this.libraryConfig?.isDiEnabled) {
|
|
248
|
+
const probeInformation = this.addDiProbe(error)
|
|
249
|
+
if (probeInformation) {
|
|
250
|
+
const { probeId, stackIndex } = probeInformation
|
|
251
|
+
this.runningTestProbeId = probeId
|
|
252
|
+
this.testErrorStackIndex = stackIndex
|
|
253
|
+
// TODO: we're not waiting for setProbePromise to be resolved, so there might be race conditions
|
|
254
|
+
}
|
|
262
255
|
}
|
|
263
256
|
span.setTag(TEST_STATUS, 'fail')
|
|
264
257
|
span.finish()
|
|
@@ -363,6 +356,11 @@ class CucumberPlugin extends CiPlugin {
|
|
|
363
356
|
if (isCucumberWorker) {
|
|
364
357
|
this.tracer._exporter.flush()
|
|
365
358
|
}
|
|
359
|
+
this.activeTestSpan = null
|
|
360
|
+
if (this.runningTestProbeId) {
|
|
361
|
+
this.removeDiProbe(this.runningTestProbeId)
|
|
362
|
+
this.runningTestProbeId = null
|
|
363
|
+
}
|
|
366
364
|
}
|
|
367
365
|
})
|
|
368
366
|
|
|
@@ -23,11 +23,7 @@ const {
|
|
|
23
23
|
JEST_DISPLAY_NAME,
|
|
24
24
|
TEST_IS_RUM_ACTIVE,
|
|
25
25
|
TEST_BROWSER_DRIVER,
|
|
26
|
-
|
|
27
|
-
DI_DEBUG_ERROR_SNAPSHOT_ID,
|
|
28
|
-
DI_DEBUG_ERROR_FILE,
|
|
29
|
-
DI_DEBUG_ERROR_LINE,
|
|
30
|
-
TEST_NAME
|
|
26
|
+
getFormattedError
|
|
31
27
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
32
28
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
33
29
|
const id = require('../../dd-trace/src/id')
|
|
@@ -44,11 +40,20 @@ const {
|
|
|
44
40
|
} = require('../../dd-trace/src/ci-visibility/telemetry')
|
|
45
41
|
|
|
46
42
|
const isJestWorker = !!process.env.JEST_WORKER_ID
|
|
47
|
-
const debuggerParameterPerTest = new Map()
|
|
48
43
|
|
|
49
44
|
// https://github.com/facebook/jest/blob/d6ad15b0f88a05816c2fe034dd6900d28315d570/packages/jest-worker/src/types.ts#L38
|
|
50
45
|
const CHILD_MESSAGE_END = 2
|
|
51
46
|
|
|
47
|
+
function withTimeout (promise, timeoutMs) {
|
|
48
|
+
return new Promise(resolve => {
|
|
49
|
+
// Set a timeout to resolve after 1s
|
|
50
|
+
setTimeout(resolve, timeoutMs)
|
|
51
|
+
|
|
52
|
+
// Also resolve if the original promise resolves
|
|
53
|
+
promise.then(resolve)
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
52
57
|
class JestPlugin extends CiPlugin {
|
|
53
58
|
static get id () {
|
|
54
59
|
return 'jest'
|
|
@@ -260,6 +265,12 @@ class JestPlugin extends CiPlugin {
|
|
|
260
265
|
})
|
|
261
266
|
})
|
|
262
267
|
|
|
268
|
+
this.addSub('ci:jest:worker-report:logs', (logsPayloads) => {
|
|
269
|
+
JSON.parse(logsPayloads).forEach(({ testConfiguration, logMessage }) => {
|
|
270
|
+
this.tracer._exporter.exportDiLogs(testConfiguration, logMessage)
|
|
271
|
+
})
|
|
272
|
+
})
|
|
273
|
+
|
|
263
274
|
this.addSub('ci:jest:test-suite:finish', ({ status, errorMessage, error }) => {
|
|
264
275
|
this.testSuiteSpan.setTag(TEST_STATUS, status)
|
|
265
276
|
if (error) {
|
|
@@ -308,32 +319,10 @@ class JestPlugin extends CiPlugin {
|
|
|
308
319
|
const span = this.startTestSpan(test)
|
|
309
320
|
|
|
310
321
|
this.enter(span, store)
|
|
311
|
-
|
|
312
|
-
const { name: testName } = test
|
|
313
|
-
|
|
314
|
-
const debuggerParameters = debuggerParameterPerTest.get(testName)
|
|
315
|
-
|
|
316
|
-
// If we have a debugger probe, we need to add the snapshot id to the span
|
|
317
|
-
if (debuggerParameters) {
|
|
318
|
-
const spanContext = span.context()
|
|
319
|
-
|
|
320
|
-
// TODO: handle race conditions with this.retriedTestIds
|
|
321
|
-
this.retriedTestIds = {
|
|
322
|
-
spanId: spanContext.toSpanId(),
|
|
323
|
-
traceId: spanContext.toTraceId()
|
|
324
|
-
}
|
|
325
|
-
const { snapshotId, file, line } = debuggerParameters
|
|
326
|
-
|
|
327
|
-
// TODO: should these be added on test:end if and only if the probe is hit?
|
|
328
|
-
// Sync issues: `hitProbePromise` might be resolved after the test ends
|
|
329
|
-
span.setTag(DI_ERROR_DEBUG_INFO_CAPTURED, 'true')
|
|
330
|
-
span.setTag(DI_DEBUG_ERROR_SNAPSHOT_ID, snapshotId)
|
|
331
|
-
span.setTag(DI_DEBUG_ERROR_FILE, file)
|
|
332
|
-
span.setTag(DI_DEBUG_ERROR_LINE, line)
|
|
333
|
-
}
|
|
322
|
+
this.activeTestSpan = span
|
|
334
323
|
})
|
|
335
324
|
|
|
336
|
-
this.addSub('ci:jest:test:finish', ({ status, testStartLine }) => {
|
|
325
|
+
this.addSub('ci:jest:test:finish', ({ status, testStartLine, promises, shouldRemoveProbe }) => {
|
|
337
326
|
const span = storage.getStore().span
|
|
338
327
|
span.setTag(TEST_STATUS, status)
|
|
339
328
|
if (testStartLine) {
|
|
@@ -354,20 +343,28 @@ class JestPlugin extends CiPlugin {
|
|
|
354
343
|
|
|
355
344
|
span.finish()
|
|
356
345
|
finishAllTraceSpans(span)
|
|
346
|
+
this.activeTestSpan = null
|
|
347
|
+
if (shouldRemoveProbe && this.runningTestProbeId) {
|
|
348
|
+
promises.isProbeRemoved = withTimeout(this.removeDiProbe(this.runningTestProbeId), 2000)
|
|
349
|
+
this.runningTestProbeId = null
|
|
350
|
+
}
|
|
357
351
|
})
|
|
358
352
|
|
|
359
|
-
this.addSub('ci:jest:test:err', ({ error,
|
|
353
|
+
this.addSub('ci:jest:test:err', ({ error, shouldSetProbe, promises }) => {
|
|
360
354
|
if (error) {
|
|
361
355
|
const store = storage.getStore()
|
|
362
356
|
if (store && store.span) {
|
|
363
357
|
const span = store.span
|
|
364
358
|
span.setTag(TEST_STATUS, 'fail')
|
|
365
|
-
span.setTag('error', error)
|
|
366
|
-
if (
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
359
|
+
span.setTag('error', getFormattedError(error, this.repositoryRoot))
|
|
360
|
+
if (shouldSetProbe) {
|
|
361
|
+
const probeInformation = this.addDiProbe(error)
|
|
362
|
+
if (probeInformation) {
|
|
363
|
+
const { probeId, setProbePromise, stackIndex } = probeInformation
|
|
364
|
+
this.runningTestProbeId = probeId
|
|
365
|
+
this.testErrorStackIndex = stackIndex
|
|
366
|
+
promises.isProbeReady = withTimeout(setProbePromise, 2000)
|
|
367
|
+
}
|
|
371
368
|
}
|
|
372
369
|
}
|
|
373
370
|
}
|
|
@@ -1,89 +1,21 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
3
|
+
const LangChainTracingPlugin = require('./tracing')
|
|
4
|
+
const LangChainLLMObsPlugin = require('../../dd-trace/src/llmobs/plugins/langchain')
|
|
5
|
+
const CompositePlugin = require('../../dd-trace/src/plugins/composite')
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
const MODEL = 'langchain.request.model'
|
|
9
|
-
const PROVIDER = 'langchain.request.provider'
|
|
10
|
-
const TYPE = 'langchain.request.type'
|
|
11
|
-
|
|
12
|
-
const LangChainHandler = require('./handlers/default')
|
|
13
|
-
const LangChainChatModelHandler = require('./handlers/language_models/chat_model')
|
|
14
|
-
const LangChainLLMHandler = require('./handlers/language_models/llm')
|
|
15
|
-
const LangChainChainHandler = require('./handlers/chain')
|
|
16
|
-
const LangChainEmbeddingHandler = require('./handlers/embedding')
|
|
17
|
-
|
|
18
|
-
class LangChainPlugin extends TracingPlugin {
|
|
7
|
+
class LangChainPlugin extends CompositePlugin {
|
|
19
8
|
static get id () { return 'langchain' }
|
|
20
|
-
static get
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const langchainConfig = this._tracerConfig.langchain || {}
|
|
30
|
-
this.handlers = {
|
|
31
|
-
chain: new LangChainChainHandler(langchainConfig),
|
|
32
|
-
chat_model: new LangChainChatModelHandler(langchainConfig),
|
|
33
|
-
llm: new LangChainLLMHandler(langchainConfig),
|
|
34
|
-
embedding: new LangChainEmbeddingHandler(langchainConfig),
|
|
35
|
-
default: new LangChainHandler(langchainConfig)
|
|
9
|
+
static get plugins () {
|
|
10
|
+
return {
|
|
11
|
+
// ordering here is important - the llm observability plugin must come first
|
|
12
|
+
// so that we can add annotations associated with the span before it finishes.
|
|
13
|
+
// however, because the tracing plugin uses `bindStart` vs the llmobs' `start`,
|
|
14
|
+
// the span is guaranteed to be created in the tracing plugin before the llmobs one is called
|
|
15
|
+
llmobs: LangChainLLMObsPlugin,
|
|
16
|
+
tracing: LangChainTracingPlugin
|
|
36
17
|
}
|
|
37
18
|
}
|
|
38
|
-
|
|
39
|
-
bindStart (ctx) {
|
|
40
|
-
const { resource, type } = ctx
|
|
41
|
-
const handler = this.handlers[type]
|
|
42
|
-
|
|
43
|
-
const instance = ctx.instance
|
|
44
|
-
const apiKey = handler.extractApiKey(instance)
|
|
45
|
-
const provider = handler.extractProvider(instance)
|
|
46
|
-
const model = handler.extractModel(instance)
|
|
47
|
-
|
|
48
|
-
const tags = handler.getSpanStartTags(ctx, provider) || []
|
|
49
|
-
|
|
50
|
-
if (apiKey) tags[API_KEY] = apiKey
|
|
51
|
-
if (provider) tags[PROVIDER] = provider
|
|
52
|
-
if (model) tags[MODEL] = model
|
|
53
|
-
if (type) tags[TYPE] = type
|
|
54
|
-
|
|
55
|
-
const span = this.startSpan('langchain.request', {
|
|
56
|
-
service: this.config.service,
|
|
57
|
-
resource,
|
|
58
|
-
kind: 'client',
|
|
59
|
-
meta: {
|
|
60
|
-
[MEASURED]: 1,
|
|
61
|
-
...tags
|
|
62
|
-
}
|
|
63
|
-
}, false)
|
|
64
|
-
|
|
65
|
-
const store = storage.getStore() || {}
|
|
66
|
-
ctx.currentStore = { ...store, span }
|
|
67
|
-
|
|
68
|
-
return ctx.currentStore
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
asyncEnd (ctx) {
|
|
72
|
-
const span = ctx.currentStore.span
|
|
73
|
-
|
|
74
|
-
const { type } = ctx
|
|
75
|
-
|
|
76
|
-
const handler = this.handlers[type]
|
|
77
|
-
const tags = handler.getSpanEndTags(ctx) || {}
|
|
78
|
-
|
|
79
|
-
span.addTags(tags)
|
|
80
|
-
|
|
81
|
-
span.finish()
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
getHandler (type) {
|
|
85
|
-
return this.handlers[type] || this.handlers.default
|
|
86
|
-
}
|
|
87
19
|
}
|
|
88
20
|
|
|
89
21
|
module.exports = LangChainPlugin
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { MEASURED } = require('../../../ext/tags')
|
|
4
|
+
const { storage } = require('../../datadog-core')
|
|
5
|
+
const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
|
|
6
|
+
|
|
7
|
+
const API_KEY = 'langchain.request.api_key'
|
|
8
|
+
const MODEL = 'langchain.request.model'
|
|
9
|
+
const PROVIDER = 'langchain.request.provider'
|
|
10
|
+
const TYPE = 'langchain.request.type'
|
|
11
|
+
|
|
12
|
+
const LangChainHandler = require('./handlers/default')
|
|
13
|
+
const LangChainChatModelHandler = require('./handlers/language_models/chat_model')
|
|
14
|
+
const LangChainLLMHandler = require('./handlers/language_models/llm')
|
|
15
|
+
const LangChainChainHandler = require('./handlers/chain')
|
|
16
|
+
const LangChainEmbeddingHandler = require('./handlers/embedding')
|
|
17
|
+
|
|
18
|
+
class LangChainTracingPlugin extends TracingPlugin {
|
|
19
|
+
static get id () { return 'langchain' }
|
|
20
|
+
static get operation () { return 'invoke' }
|
|
21
|
+
static get system () { return 'langchain' }
|
|
22
|
+
static get prefix () {
|
|
23
|
+
return 'tracing:apm:langchain:invoke'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
constructor () {
|
|
27
|
+
super(...arguments)
|
|
28
|
+
|
|
29
|
+
const langchainConfig = this._tracerConfig.langchain || {}
|
|
30
|
+
this.handlers = {
|
|
31
|
+
chain: new LangChainChainHandler(langchainConfig),
|
|
32
|
+
chat_model: new LangChainChatModelHandler(langchainConfig),
|
|
33
|
+
llm: new LangChainLLMHandler(langchainConfig),
|
|
34
|
+
embedding: new LangChainEmbeddingHandler(langchainConfig),
|
|
35
|
+
default: new LangChainHandler(langchainConfig)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
bindStart (ctx) {
|
|
40
|
+
const { resource, type } = ctx
|
|
41
|
+
const handler = this.handlers[type]
|
|
42
|
+
|
|
43
|
+
const instance = ctx.instance
|
|
44
|
+
const apiKey = handler.extractApiKey(instance)
|
|
45
|
+
const provider = handler.extractProvider(instance)
|
|
46
|
+
const model = handler.extractModel(instance)
|
|
47
|
+
|
|
48
|
+
const tags = handler.getSpanStartTags(ctx, provider) || []
|
|
49
|
+
|
|
50
|
+
if (apiKey) tags[API_KEY] = apiKey
|
|
51
|
+
if (provider) tags[PROVIDER] = provider
|
|
52
|
+
if (model) tags[MODEL] = model
|
|
53
|
+
if (type) tags[TYPE] = type
|
|
54
|
+
|
|
55
|
+
const span = this.startSpan('langchain.request', {
|
|
56
|
+
service: this.config.service,
|
|
57
|
+
resource,
|
|
58
|
+
kind: 'client',
|
|
59
|
+
meta: {
|
|
60
|
+
[MEASURED]: 1,
|
|
61
|
+
...tags
|
|
62
|
+
}
|
|
63
|
+
}, false)
|
|
64
|
+
|
|
65
|
+
const store = storage.getStore() || {}
|
|
66
|
+
ctx.currentStore = { ...store, span }
|
|
67
|
+
|
|
68
|
+
return ctx.currentStore
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
asyncEnd (ctx) {
|
|
72
|
+
const span = ctx.currentStore.span
|
|
73
|
+
|
|
74
|
+
const { type } = ctx
|
|
75
|
+
|
|
76
|
+
const handler = this.handlers[type]
|
|
77
|
+
const tags = handler.getSpanEndTags(ctx) || {}
|
|
78
|
+
|
|
79
|
+
span.addTags(tags)
|
|
80
|
+
|
|
81
|
+
span.finish()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getHandler (type) {
|
|
85
|
+
return this.handlers[type] || this.handlers.default
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = LangChainTracingPlugin
|
|
@@ -30,12 +30,7 @@ const {
|
|
|
30
30
|
TEST_SUITE,
|
|
31
31
|
MOCHA_IS_PARALLEL,
|
|
32
32
|
TEST_IS_RUM_ACTIVE,
|
|
33
|
-
TEST_BROWSER_DRIVER
|
|
34
|
-
TEST_NAME,
|
|
35
|
-
DI_ERROR_DEBUG_INFO_CAPTURED,
|
|
36
|
-
DI_DEBUG_ERROR_SNAPSHOT_ID,
|
|
37
|
-
DI_DEBUG_ERROR_FILE,
|
|
38
|
-
DI_DEBUG_ERROR_LINE
|
|
33
|
+
TEST_BROWSER_DRIVER
|
|
39
34
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
40
35
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
41
36
|
const {
|
|
@@ -52,8 +47,6 @@ const {
|
|
|
52
47
|
const id = require('../../dd-trace/src/id')
|
|
53
48
|
const log = require('../../dd-trace/src/log')
|
|
54
49
|
|
|
55
|
-
const debuggerParameterPerTest = new Map()
|
|
56
|
-
|
|
57
50
|
function getTestSuiteLevelVisibilityTags (testSuiteSpan) {
|
|
58
51
|
const testSuiteSpanContext = testSuiteSpan.context()
|
|
59
52
|
const suiteTags = {
|
|
@@ -192,36 +185,15 @@ class MochaPlugin extends CiPlugin {
|
|
|
192
185
|
const store = storage.getStore()
|
|
193
186
|
const span = this.startTestSpan(testInfo)
|
|
194
187
|
|
|
195
|
-
const { testName } = testInfo
|
|
196
|
-
|
|
197
|
-
const debuggerParameters = debuggerParameterPerTest.get(testName)
|
|
198
|
-
|
|
199
|
-
if (debuggerParameters) {
|
|
200
|
-
const spanContext = span.context()
|
|
201
|
-
|
|
202
|
-
// TODO: handle race conditions with this.retriedTestIds
|
|
203
|
-
this.retriedTestIds = {
|
|
204
|
-
spanId: spanContext.toSpanId(),
|
|
205
|
-
traceId: spanContext.toTraceId()
|
|
206
|
-
}
|
|
207
|
-
const { snapshotId, file, line } = debuggerParameters
|
|
208
|
-
|
|
209
|
-
// TODO: should these be added on test:end if and only if the probe is hit?
|
|
210
|
-
// Sync issues: `hitProbePromise` might be resolved after the test ends
|
|
211
|
-
span.setTag(DI_ERROR_DEBUG_INFO_CAPTURED, 'true')
|
|
212
|
-
span.setTag(DI_DEBUG_ERROR_SNAPSHOT_ID, snapshotId)
|
|
213
|
-
span.setTag(DI_DEBUG_ERROR_FILE, file)
|
|
214
|
-
span.setTag(DI_DEBUG_ERROR_LINE, line)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
188
|
this.enter(span, store)
|
|
189
|
+
this.activeTestSpan = span
|
|
218
190
|
})
|
|
219
191
|
|
|
220
192
|
this.addSub('ci:mocha:worker:finish', () => {
|
|
221
193
|
this.tracer._exporter.flush()
|
|
222
194
|
})
|
|
223
195
|
|
|
224
|
-
this.addSub('ci:mocha:test:finish', ({ status, hasBeenRetried }) => {
|
|
196
|
+
this.addSub('ci:mocha:test:finish', ({ status, hasBeenRetried, isLastRetry }) => {
|
|
225
197
|
const store = storage.getStore()
|
|
226
198
|
const span = store?.span
|
|
227
199
|
|
|
@@ -245,6 +217,11 @@ class MochaPlugin extends CiPlugin {
|
|
|
245
217
|
|
|
246
218
|
span.finish()
|
|
247
219
|
finishAllTraceSpans(span)
|
|
220
|
+
this.activeTestSpan = null
|
|
221
|
+
if (this.di && this.libraryConfig?.isDiEnabled && this.runningTestProbeId && isLastRetry) {
|
|
222
|
+
this.removeDiProbe(this.runningTestProbeId)
|
|
223
|
+
this.runningTestProbeId = null
|
|
224
|
+
}
|
|
248
225
|
}
|
|
249
226
|
})
|
|
250
227
|
|
|
@@ -271,7 +248,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
271
248
|
}
|
|
272
249
|
})
|
|
273
250
|
|
|
274
|
-
this.addSub('ci:mocha:test:retry', ({ isFirstAttempt, willBeRetried, err }) => {
|
|
251
|
+
this.addSub('ci:mocha:test:retry', ({ isFirstAttempt, willBeRetried, err, test }) => {
|
|
275
252
|
const store = storage.getStore()
|
|
276
253
|
const span = store?.span
|
|
277
254
|
if (span) {
|
|
@@ -294,10 +271,15 @@ class MochaPlugin extends CiPlugin {
|
|
|
294
271
|
browserDriver: spanTags[TEST_BROWSER_DRIVER]
|
|
295
272
|
}
|
|
296
273
|
)
|
|
297
|
-
if (willBeRetried && this.di && this.libraryConfig?.isDiEnabled) {
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
274
|
+
if (isFirstAttempt && willBeRetried && this.di && this.libraryConfig?.isDiEnabled) {
|
|
275
|
+
const probeInformation = this.addDiProbe(err)
|
|
276
|
+
if (probeInformation) {
|
|
277
|
+
const { probeId, stackIndex } = probeInformation
|
|
278
|
+
this.runningTestProbeId = probeId
|
|
279
|
+
this.testErrorStackIndex = stackIndex
|
|
280
|
+
test._ddShouldWaitForHitProbe = true
|
|
281
|
+
// TODO: we're not waiting for setProbePromise to be resolved, so there might be race conditions
|
|
282
|
+
}
|
|
301
283
|
}
|
|
302
284
|
|
|
303
285
|
span.finish()
|
|
@@ -17,12 +17,7 @@ const {
|
|
|
17
17
|
TEST_SOURCE_START,
|
|
18
18
|
TEST_IS_NEW,
|
|
19
19
|
TEST_EARLY_FLAKE_ENABLED,
|
|
20
|
-
TEST_EARLY_FLAKE_ABORT_REASON
|
|
21
|
-
TEST_NAME,
|
|
22
|
-
DI_ERROR_DEBUG_INFO_CAPTURED,
|
|
23
|
-
DI_DEBUG_ERROR_SNAPSHOT_ID,
|
|
24
|
-
DI_DEBUG_ERROR_FILE,
|
|
25
|
-
DI_DEBUG_ERROR_LINE
|
|
20
|
+
TEST_EARLY_FLAKE_ABORT_REASON
|
|
26
21
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
27
22
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
28
23
|
const {
|
|
@@ -36,8 +31,6 @@ const {
|
|
|
36
31
|
// This is because there's some loss of resolution.
|
|
37
32
|
const MILLISECONDS_TO_SUBTRACT_FROM_FAILED_TEST_DURATION = 5
|
|
38
33
|
|
|
39
|
-
const debuggerParameterPerTest = new Map()
|
|
40
|
-
|
|
41
34
|
class VitestPlugin extends CiPlugin {
|
|
42
35
|
static get id () {
|
|
43
36
|
return 'vitest'
|
|
@@ -67,7 +60,7 @@ class VitestPlugin extends CiPlugin {
|
|
|
67
60
|
onDone(isFaulty)
|
|
68
61
|
})
|
|
69
62
|
|
|
70
|
-
this.addSub('ci:vitest:test:start', ({ testName, testSuiteAbsolutePath, isRetry, isNew }) => {
|
|
63
|
+
this.addSub('ci:vitest:test:start', ({ testName, testSuiteAbsolutePath, isRetry, isNew, mightHitProbe }) => {
|
|
71
64
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
72
65
|
const store = storage.getStore()
|
|
73
66
|
|
|
@@ -88,27 +81,13 @@ class VitestPlugin extends CiPlugin {
|
|
|
88
81
|
extraTags
|
|
89
82
|
)
|
|
90
83
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (debuggerParameters) {
|
|
94
|
-
const spanContext = span.context()
|
|
95
|
-
|
|
96
|
-
// TODO: handle race conditions with this.retriedTestIds
|
|
97
|
-
this.retriedTestIds = {
|
|
98
|
-
spanId: spanContext.toSpanId(),
|
|
99
|
-
traceId: spanContext.toTraceId()
|
|
100
|
-
}
|
|
101
|
-
const { snapshotId, file, line } = debuggerParameters
|
|
84
|
+
this.enter(span, store)
|
|
102
85
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
span.setTag(DI_DEBUG_ERROR_FILE, file)
|
|
108
|
-
span.setTag(DI_DEBUG_ERROR_LINE, line)
|
|
86
|
+
// TODO: there might be multiple tests for which mightHitProbe is true, so activeTestSpan
|
|
87
|
+
// might be wrongly overwritten.
|
|
88
|
+
if (mightHitProbe) {
|
|
89
|
+
this.activeTestSpan = span
|
|
109
90
|
}
|
|
110
|
-
|
|
111
|
-
this.enter(span, store)
|
|
112
91
|
})
|
|
113
92
|
|
|
114
93
|
this.addSub('ci:vitest:test:finish-time', ({ status, task }) => {
|
|
@@ -137,15 +116,19 @@ class VitestPlugin extends CiPlugin {
|
|
|
137
116
|
}
|
|
138
117
|
})
|
|
139
118
|
|
|
140
|
-
this.addSub('ci:vitest:test:error', ({ duration, error,
|
|
119
|
+
this.addSub('ci:vitest:test:error', ({ duration, error, shouldSetProbe, promises }) => {
|
|
141
120
|
const store = storage.getStore()
|
|
142
121
|
const span = store?.span
|
|
143
122
|
|
|
144
123
|
if (span) {
|
|
145
|
-
if (
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
124
|
+
if (shouldSetProbe && this.di) {
|
|
125
|
+
const probeInformation = this.addDiProbe(error)
|
|
126
|
+
if (probeInformation) {
|
|
127
|
+
const { probeId, stackIndex, setProbePromise } = probeInformation
|
|
128
|
+
this.runningTestProbeId = probeId
|
|
129
|
+
this.testErrorStackIndex = stackIndex
|
|
130
|
+
promises.setProbePromise = setProbePromise
|
|
131
|
+
}
|
|
149
132
|
}
|
|
150
133
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test', {
|
|
151
134
|
hasCodeowners: !!span.context()._tags[TEST_CODE_OWNERS]
|
|
@@ -158,7 +141,7 @@ class VitestPlugin extends CiPlugin {
|
|
|
158
141
|
if (duration) {
|
|
159
142
|
span.finish(span._startTime + duration - MILLISECONDS_TO_SUBTRACT_FROM_FAILED_TEST_DURATION) // milliseconds
|
|
160
143
|
} else {
|
|
161
|
-
span.finish() // retries
|
|
144
|
+
span.finish() // `duration` is empty for retries, so we'll use clock time
|
|
162
145
|
}
|
|
163
146
|
finishAllTraceSpans(span)
|
|
164
147
|
}
|
|
@@ -242,6 +225,9 @@ class VitestPlugin extends CiPlugin {
|
|
|
242
225
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
|
|
243
226
|
// TODO: too frequent flush - find for method in worker to decrease frequency
|
|
244
227
|
this.tracer._exporter.flush(onFinish)
|
|
228
|
+
if (this.runningTestProbeId) {
|
|
229
|
+
this.removeDiProbe(this.runningTestProbeId)
|
|
230
|
+
}
|
|
245
231
|
})
|
|
246
232
|
|
|
247
233
|
this.addSub('ci:vitest:test-suite:error', ({ error }) => {
|
|
@@ -17,6 +17,7 @@ module.exports = {
|
|
|
17
17
|
SSRF: require('./ssrf-analyzer'),
|
|
18
18
|
TEMPLATE_INJECTION_ANALYZER: require('./template-injection-analyzer'),
|
|
19
19
|
UNVALIDATED_REDIRECT_ANALYZER: require('./unvalidated-redirect-analyzer'),
|
|
20
|
+
UNTRUSTED_DESERIALIZATION_ANALYZER: require('./untrusted-deserialization-analyzer'),
|
|
20
21
|
WEAK_CIPHER_ANALYZER: require('./weak-cipher-analyzer'),
|
|
21
22
|
WEAK_HASH_ANALYZER: require('./weak-hash-analyzer'),
|
|
22
23
|
WEAK_RANDOMNESS_ANALYZER: require('./weak-randomness-analyzer'),
|
|
@@ -10,6 +10,8 @@ class CodeInjectionAnalyzer extends InjectionAnalyzer {
|
|
|
10
10
|
|
|
11
11
|
onConfigure () {
|
|
12
12
|
this.addSub('datadog:eval:call', ({ script }) => this.analyze(script))
|
|
13
|
+
this.addSub('datadog:vm:run-script:start', ({ code }) => this.analyze(code))
|
|
14
|
+
this.addSub('datadog:vm:source-text-module:start', ({ code }) => this.analyze(code))
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
_areRangesVulnerable () {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const InjectionAnalyzer = require('./injection-analyzer')
|
|
4
|
+
const { UNTRUSTED_DESERIALIZATION } = require('../vulnerabilities')
|
|
5
|
+
|
|
6
|
+
class UntrustedDeserializationAnalyzer extends InjectionAnalyzer {
|
|
7
|
+
constructor () {
|
|
8
|
+
super(UNTRUSTED_DESERIALIZATION)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
onConfigure () {
|
|
12
|
+
this.addSub('datadog:node-serialize:unserialize:start', ({ obj }) => this.analyze(obj))
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = new UntrustedDeserializationAnalyzer()
|