dd-trace 5.31.0 → 5.33.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 +17 -14
- package/index.d.ts +11 -1
- package/package.json +6 -5
- package/packages/datadog-instrumentations/src/aws-sdk.js +4 -1
- package/packages/datadog-instrumentations/src/cucumber.js +31 -14
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
- package/packages/datadog-instrumentations/src/jest.js +105 -56
- package/packages/datadog-instrumentations/src/mocha/main.js +9 -4
- package/packages/datadog-instrumentations/src/mocha/utils.js +27 -9
- package/packages/datadog-instrumentations/src/mocha/worker.js +4 -2
- package/packages/datadog-instrumentations/src/node-serialize.js +22 -0
- package/packages/datadog-instrumentations/src/openai.js +2 -0
- package/packages/datadog-instrumentations/src/playwright.js +8 -3
- package/packages/datadog-instrumentations/src/vitest.js +134 -62
- package/packages/datadog-instrumentations/src/vm.js +49 -0
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +16 -0
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +63 -0
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +287 -0
- package/packages/datadog-plugin-aws-sdk/src/services/index.js +1 -0
- package/packages/datadog-plugin-cucumber/src/index.js +31 -31
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -8
- package/packages/datadog-plugin-cypress/src/support.js +6 -2
- package/packages/datadog-plugin-fetch/src/index.js +3 -3
- package/packages/datadog-plugin-http/src/client.js +5 -33
- package/packages/datadog-plugin-jest/src/index.js +37 -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 +19 -35
- package/packages/datadog-plugin-playwright/src/index.js +3 -1
- package/packages/datadog-plugin-vitest/src/index.js +33 -35
- 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/cookie-analyzer.js +3 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/untrusted-deserialization-analyzer.js +16 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +41 -24
- package/packages/dd-trace/src/appsec/iast/iast-context.js +12 -0
- package/packages/dd-trace/src/appsec/iast/path-line.js +19 -23
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +9 -8
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +75 -24
- package/packages/dd-trace/src/appsec/rasp/utils.js +10 -5
- package/packages/dd-trace/src/appsec/stack_trace.js +38 -28
- 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/ci-visibility-exporter.js +5 -4
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +18 -3
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +5 -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 +43 -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 +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/defaults.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +30 -13
- package/packages/dd-trace/src/debugger/devtools_client/send.js +4 -8
- 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 +12 -10
- 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/bedrockruntime.js +59 -0
- 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/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/opentelemetry/context_manager.js +43 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +58 -27
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +0 -2
- package/packages/dd-trace/src/plugins/util/test.js +44 -12
- package/packages/dd-trace/src/priority_sampler.js +4 -1
- 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
|
@@ -59,6 +59,11 @@ class HttpClientPlugin extends ClientPlugin {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
if (this.shouldInjectTraceHeaders(options, uri)) {
|
|
62
|
+
// Clone the headers object in case an upstream lib has a reference to the original headers
|
|
63
|
+
// Implemented due to aws-sdk issue where request signing is broken if we mutate the headers
|
|
64
|
+
// Explained further in:
|
|
65
|
+
// https://github.com/open-telemetry/opentelemetry-js-contrib/issues/1609#issuecomment-1826167348
|
|
66
|
+
options.headers = Object.assign({}, options.headers)
|
|
62
67
|
this.tracer.inject(span, HTTP_HEADERS, options.headers)
|
|
63
68
|
}
|
|
64
69
|
|
|
@@ -72,10 +77,6 @@ class HttpClientPlugin extends ClientPlugin {
|
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
shouldInjectTraceHeaders (options, uri) {
|
|
75
|
-
if (hasAmazonSignature(options) && !this.config.enablePropagationWithAmazonHeaders) {
|
|
76
|
-
return false
|
|
77
|
-
}
|
|
78
|
-
|
|
79
80
|
if (!this.config.propagationFilter(uri)) {
|
|
80
81
|
return false
|
|
81
82
|
}
|
|
@@ -212,31 +213,6 @@ function getHooks (config) {
|
|
|
212
213
|
return { request }
|
|
213
214
|
}
|
|
214
215
|
|
|
215
|
-
function hasAmazonSignature (options) {
|
|
216
|
-
if (!options) {
|
|
217
|
-
return false
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (options.headers) {
|
|
221
|
-
const headers = Object.keys(options.headers)
|
|
222
|
-
.reduce((prev, next) => Object.assign(prev, {
|
|
223
|
-
[next.toLowerCase()]: options.headers[next]
|
|
224
|
-
}), {})
|
|
225
|
-
|
|
226
|
-
if (headers['x-amz-signature']) {
|
|
227
|
-
return true
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if ([].concat(headers.authorization).some(startsWith('AWS4-HMAC-SHA256'))) {
|
|
231
|
-
return true
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const search = options.search || options.path
|
|
236
|
-
|
|
237
|
-
return search && search.toLowerCase().indexOf('x-amz-signature=') !== -1
|
|
238
|
-
}
|
|
239
|
-
|
|
240
216
|
function extractSessionDetails (options) {
|
|
241
217
|
if (typeof options === 'string') {
|
|
242
218
|
return new URL(options).host
|
|
@@ -248,8 +224,4 @@ function extractSessionDetails (options) {
|
|
|
248
224
|
return { host, port }
|
|
249
225
|
}
|
|
250
226
|
|
|
251
|
-
function startsWith (searchString) {
|
|
252
|
-
return value => String(value).startsWith(searchString)
|
|
253
|
-
}
|
|
254
|
-
|
|
255
227
|
module.exports = HttpClientPlugin
|
|
@@ -23,11 +23,8 @@ const {
|
|
|
23
23
|
JEST_DISPLAY_NAME,
|
|
24
24
|
TEST_IS_RUM_ACTIVE,
|
|
25
25
|
TEST_BROWSER_DRIVER,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
DI_DEBUG_ERROR_FILE,
|
|
29
|
-
DI_DEBUG_ERROR_LINE,
|
|
30
|
-
TEST_NAME
|
|
26
|
+
getFormattedError,
|
|
27
|
+
TEST_RETRY_REASON
|
|
31
28
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
32
29
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
33
30
|
const id = require('../../dd-trace/src/id')
|
|
@@ -44,11 +41,20 @@ const {
|
|
|
44
41
|
} = require('../../dd-trace/src/ci-visibility/telemetry')
|
|
45
42
|
|
|
46
43
|
const isJestWorker = !!process.env.JEST_WORKER_ID
|
|
47
|
-
const debuggerParameterPerTest = new Map()
|
|
48
44
|
|
|
49
45
|
// https://github.com/facebook/jest/blob/d6ad15b0f88a05816c2fe034dd6900d28315d570/packages/jest-worker/src/types.ts#L38
|
|
50
46
|
const CHILD_MESSAGE_END = 2
|
|
51
47
|
|
|
48
|
+
function withTimeout (promise, timeoutMs) {
|
|
49
|
+
return new Promise(resolve => {
|
|
50
|
+
// Set a timeout to resolve after 1s
|
|
51
|
+
setTimeout(resolve, timeoutMs)
|
|
52
|
+
|
|
53
|
+
// Also resolve if the original promise resolves
|
|
54
|
+
promise.then(resolve)
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
52
58
|
class JestPlugin extends CiPlugin {
|
|
53
59
|
static get id () {
|
|
54
60
|
return 'jest'
|
|
@@ -162,6 +168,7 @@ class JestPlugin extends CiPlugin {
|
|
|
162
168
|
config._ddIsFlakyTestRetriesEnabled = this.libraryConfig?.isFlakyTestRetriesEnabled ?? false
|
|
163
169
|
config._ddFlakyTestRetriesCount = this.libraryConfig?.flakyTestRetriesCount
|
|
164
170
|
config._ddIsDiEnabled = this.libraryConfig?.isDiEnabled ?? false
|
|
171
|
+
config._ddIsKnownTestsEnabled = this.libraryConfig?.isKnownTestsEnabled ?? false
|
|
165
172
|
})
|
|
166
173
|
})
|
|
167
174
|
|
|
@@ -260,6 +267,12 @@ class JestPlugin extends CiPlugin {
|
|
|
260
267
|
})
|
|
261
268
|
})
|
|
262
269
|
|
|
270
|
+
this.addSub('ci:jest:worker-report:logs', (logsPayloads) => {
|
|
271
|
+
JSON.parse(logsPayloads).forEach(({ testConfiguration, logMessage }) => {
|
|
272
|
+
this.tracer._exporter.exportDiLogs(testConfiguration, logMessage)
|
|
273
|
+
})
|
|
274
|
+
})
|
|
275
|
+
|
|
263
276
|
this.addSub('ci:jest:test-suite:finish', ({ status, errorMessage, error }) => {
|
|
264
277
|
this.testSuiteSpan.setTag(TEST_STATUS, status)
|
|
265
278
|
if (error) {
|
|
@@ -308,32 +321,10 @@ class JestPlugin extends CiPlugin {
|
|
|
308
321
|
const span = this.startTestSpan(test)
|
|
309
322
|
|
|
310
323
|
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
|
-
}
|
|
324
|
+
this.activeTestSpan = span
|
|
334
325
|
})
|
|
335
326
|
|
|
336
|
-
this.addSub('ci:jest:test:finish', ({ status, testStartLine }) => {
|
|
327
|
+
this.addSub('ci:jest:test:finish', ({ status, testStartLine, promises, shouldRemoveProbe }) => {
|
|
337
328
|
const span = storage.getStore().span
|
|
338
329
|
span.setTag(TEST_STATUS, status)
|
|
339
330
|
if (testStartLine) {
|
|
@@ -354,20 +345,28 @@ class JestPlugin extends CiPlugin {
|
|
|
354
345
|
|
|
355
346
|
span.finish()
|
|
356
347
|
finishAllTraceSpans(span)
|
|
348
|
+
this.activeTestSpan = null
|
|
349
|
+
if (shouldRemoveProbe && this.runningTestProbeId) {
|
|
350
|
+
promises.isProbeRemoved = withTimeout(this.removeDiProbe(this.runningTestProbeId), 2000)
|
|
351
|
+
this.runningTestProbeId = null
|
|
352
|
+
}
|
|
357
353
|
})
|
|
358
354
|
|
|
359
|
-
this.addSub('ci:jest:test:err', ({ error,
|
|
355
|
+
this.addSub('ci:jest:test:err', ({ error, shouldSetProbe, promises }) => {
|
|
360
356
|
if (error) {
|
|
361
357
|
const store = storage.getStore()
|
|
362
358
|
if (store && store.span) {
|
|
363
359
|
const span = store.span
|
|
364
360
|
span.setTag(TEST_STATUS, 'fail')
|
|
365
|
-
span.setTag('error', error)
|
|
366
|
-
if (
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
361
|
+
span.setTag('error', getFormattedError(error, this.repositoryRoot))
|
|
362
|
+
if (shouldSetProbe) {
|
|
363
|
+
const probeInformation = this.addDiProbe(error)
|
|
364
|
+
if (probeInformation) {
|
|
365
|
+
const { probeId, setProbePromise, stackIndex } = probeInformation
|
|
366
|
+
this.runningTestProbeId = probeId
|
|
367
|
+
this.testErrorStackIndex = stackIndex
|
|
368
|
+
promises.isProbeReady = withTimeout(setProbePromise, 2000)
|
|
369
|
+
}
|
|
371
370
|
}
|
|
372
371
|
}
|
|
373
372
|
}
|
|
@@ -413,6 +412,7 @@ class JestPlugin extends CiPlugin {
|
|
|
413
412
|
extraTags[TEST_IS_NEW] = 'true'
|
|
414
413
|
if (isEfdRetry) {
|
|
415
414
|
extraTags[TEST_IS_RETRY] = 'true'
|
|
415
|
+
extraTags[TEST_RETRY_REASON] = 'efd'
|
|
416
416
|
}
|
|
417
417
|
}
|
|
418
418
|
|
|
@@ -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
|
|
@@ -31,11 +31,7 @@ const {
|
|
|
31
31
|
MOCHA_IS_PARALLEL,
|
|
32
32
|
TEST_IS_RUM_ACTIVE,
|
|
33
33
|
TEST_BROWSER_DRIVER,
|
|
34
|
-
|
|
35
|
-
DI_ERROR_DEBUG_INFO_CAPTURED,
|
|
36
|
-
DI_DEBUG_ERROR_SNAPSHOT_ID,
|
|
37
|
-
DI_DEBUG_ERROR_FILE,
|
|
38
|
-
DI_DEBUG_ERROR_LINE
|
|
34
|
+
TEST_RETRY_REASON
|
|
39
35
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
40
36
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
41
37
|
const {
|
|
@@ -52,8 +48,6 @@ const {
|
|
|
52
48
|
const id = require('../../dd-trace/src/id')
|
|
53
49
|
const log = require('../../dd-trace/src/log')
|
|
54
50
|
|
|
55
|
-
const debuggerParameterPerTest = new Map()
|
|
56
|
-
|
|
57
51
|
function getTestSuiteLevelVisibilityTags (testSuiteSpan) {
|
|
58
52
|
const testSuiteSpanContext = testSuiteSpan.context()
|
|
59
53
|
const suiteTags = {
|
|
@@ -192,36 +186,15 @@ class MochaPlugin extends CiPlugin {
|
|
|
192
186
|
const store = storage.getStore()
|
|
193
187
|
const span = this.startTestSpan(testInfo)
|
|
194
188
|
|
|
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
189
|
this.enter(span, store)
|
|
190
|
+
this.activeTestSpan = span
|
|
218
191
|
})
|
|
219
192
|
|
|
220
193
|
this.addSub('ci:mocha:worker:finish', () => {
|
|
221
194
|
this.tracer._exporter.flush()
|
|
222
195
|
})
|
|
223
196
|
|
|
224
|
-
this.addSub('ci:mocha:test:finish', ({ status, hasBeenRetried }) => {
|
|
197
|
+
this.addSub('ci:mocha:test:finish', ({ status, hasBeenRetried, isLastRetry }) => {
|
|
225
198
|
const store = storage.getStore()
|
|
226
199
|
const span = store?.span
|
|
227
200
|
|
|
@@ -245,6 +218,11 @@ class MochaPlugin extends CiPlugin {
|
|
|
245
218
|
|
|
246
219
|
span.finish()
|
|
247
220
|
finishAllTraceSpans(span)
|
|
221
|
+
this.activeTestSpan = null
|
|
222
|
+
if (this.di && this.libraryConfig?.isDiEnabled && this.runningTestProbeId && isLastRetry) {
|
|
223
|
+
this.removeDiProbe(this.runningTestProbeId)
|
|
224
|
+
this.runningTestProbeId = null
|
|
225
|
+
}
|
|
248
226
|
}
|
|
249
227
|
})
|
|
250
228
|
|
|
@@ -271,7 +249,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
271
249
|
}
|
|
272
250
|
})
|
|
273
251
|
|
|
274
|
-
this.addSub('ci:mocha:test:retry', ({ isFirstAttempt, willBeRetried, err }) => {
|
|
252
|
+
this.addSub('ci:mocha:test:retry', ({ isFirstAttempt, willBeRetried, err, test }) => {
|
|
275
253
|
const store = storage.getStore()
|
|
276
254
|
const span = store?.span
|
|
277
255
|
if (span) {
|
|
@@ -294,10 +272,15 @@ class MochaPlugin extends CiPlugin {
|
|
|
294
272
|
browserDriver: spanTags[TEST_BROWSER_DRIVER]
|
|
295
273
|
}
|
|
296
274
|
)
|
|
297
|
-
if (willBeRetried && this.di && this.libraryConfig?.isDiEnabled) {
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
275
|
+
if (isFirstAttempt && willBeRetried && this.di && this.libraryConfig?.isDiEnabled) {
|
|
276
|
+
const probeInformation = this.addDiProbe(err)
|
|
277
|
+
if (probeInformation) {
|
|
278
|
+
const { probeId, stackIndex } = probeInformation
|
|
279
|
+
this.runningTestProbeId = probeId
|
|
280
|
+
this.testErrorStackIndex = stackIndex
|
|
281
|
+
test._ddShouldWaitForHitProbe = true
|
|
282
|
+
// TODO: we're not waiting for setProbePromise to be resolved, so there might be race conditions
|
|
283
|
+
}
|
|
301
284
|
}
|
|
302
285
|
|
|
303
286
|
span.finish()
|
|
@@ -439,6 +422,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
439
422
|
extraTags[TEST_IS_NEW] = 'true'
|
|
440
423
|
if (isEfdRetry) {
|
|
441
424
|
extraTags[TEST_IS_RETRY] = 'true'
|
|
425
|
+
extraTags[TEST_RETRY_REASON] = 'efd'
|
|
442
426
|
}
|
|
443
427
|
}
|
|
444
428
|
|
|
@@ -15,7 +15,8 @@ const {
|
|
|
15
15
|
TEST_IS_NEW,
|
|
16
16
|
TEST_IS_RETRY,
|
|
17
17
|
TEST_EARLY_FLAKE_ENABLED,
|
|
18
|
-
TELEMETRY_TEST_SESSION
|
|
18
|
+
TELEMETRY_TEST_SESSION,
|
|
19
|
+
TEST_RETRY_REASON
|
|
19
20
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
20
21
|
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
21
22
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
@@ -144,6 +145,7 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
144
145
|
span.setTag(TEST_IS_NEW, 'true')
|
|
145
146
|
if (isEfdRetry) {
|
|
146
147
|
span.setTag(TEST_IS_RETRY, 'true')
|
|
148
|
+
span.setTag(TEST_RETRY_REASON, 'efd')
|
|
147
149
|
}
|
|
148
150
|
}
|
|
149
151
|
if (isRetry) {
|
|
@@ -18,11 +18,7 @@ const {
|
|
|
18
18
|
TEST_IS_NEW,
|
|
19
19
|
TEST_EARLY_FLAKE_ENABLED,
|
|
20
20
|
TEST_EARLY_FLAKE_ABORT_REASON,
|
|
21
|
-
|
|
22
|
-
DI_ERROR_DEBUG_INFO_CAPTURED,
|
|
23
|
-
DI_DEBUG_ERROR_SNAPSHOT_ID,
|
|
24
|
-
DI_DEBUG_ERROR_FILE,
|
|
25
|
-
DI_DEBUG_ERROR_LINE
|
|
21
|
+
TEST_RETRY_REASON
|
|
26
22
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
27
23
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
28
24
|
const {
|
|
@@ -36,8 +32,6 @@ const {
|
|
|
36
32
|
// This is because there's some loss of resolution.
|
|
37
33
|
const MILLISECONDS_TO_SUBTRACT_FROM_FAILED_TEST_DURATION = 5
|
|
38
34
|
|
|
39
|
-
const debuggerParameterPerTest = new Map()
|
|
40
|
-
|
|
41
35
|
class VitestPlugin extends CiPlugin {
|
|
42
36
|
static get id () {
|
|
43
37
|
return 'vitest'
|
|
@@ -67,7 +61,14 @@ class VitestPlugin extends CiPlugin {
|
|
|
67
61
|
onDone(isFaulty)
|
|
68
62
|
})
|
|
69
63
|
|
|
70
|
-
this.addSub('ci:vitest:test:start', ({
|
|
64
|
+
this.addSub('ci:vitest:test:start', ({
|
|
65
|
+
testName,
|
|
66
|
+
testSuiteAbsolutePath,
|
|
67
|
+
isRetry,
|
|
68
|
+
isNew,
|
|
69
|
+
mightHitProbe,
|
|
70
|
+
isRetryReasonEfd
|
|
71
|
+
}) => {
|
|
71
72
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
72
73
|
const store = storage.getStore()
|
|
73
74
|
|
|
@@ -80,6 +81,9 @@ class VitestPlugin extends CiPlugin {
|
|
|
80
81
|
if (isNew) {
|
|
81
82
|
extraTags[TEST_IS_NEW] = 'true'
|
|
82
83
|
}
|
|
84
|
+
if (isRetryReasonEfd) {
|
|
85
|
+
extraTags[TEST_RETRY_REASON] = 'efd'
|
|
86
|
+
}
|
|
83
87
|
|
|
84
88
|
const span = this.startTestSpan(
|
|
85
89
|
testName,
|
|
@@ -88,27 +92,13 @@ class VitestPlugin extends CiPlugin {
|
|
|
88
92
|
extraTags
|
|
89
93
|
)
|
|
90
94
|
|
|
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
|
|
95
|
+
this.enter(span, store)
|
|
102
96
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
span.setTag(DI_DEBUG_ERROR_FILE, file)
|
|
108
|
-
span.setTag(DI_DEBUG_ERROR_LINE, line)
|
|
97
|
+
// TODO: there might be multiple tests for which mightHitProbe is true, so activeTestSpan
|
|
98
|
+
// might be wrongly overwritten.
|
|
99
|
+
if (mightHitProbe) {
|
|
100
|
+
this.activeTestSpan = span
|
|
109
101
|
}
|
|
110
|
-
|
|
111
|
-
this.enter(span, store)
|
|
112
102
|
})
|
|
113
103
|
|
|
114
104
|
this.addSub('ci:vitest:test:finish-time', ({ status, task }) => {
|
|
@@ -137,15 +127,19 @@ class VitestPlugin extends CiPlugin {
|
|
|
137
127
|
}
|
|
138
128
|
})
|
|
139
129
|
|
|
140
|
-
this.addSub('ci:vitest:test:error', ({ duration, error,
|
|
130
|
+
this.addSub('ci:vitest:test:error', ({ duration, error, shouldSetProbe, promises }) => {
|
|
141
131
|
const store = storage.getStore()
|
|
142
132
|
const span = store?.span
|
|
143
133
|
|
|
144
134
|
if (span) {
|
|
145
|
-
if (
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
135
|
+
if (shouldSetProbe && this.di) {
|
|
136
|
+
const probeInformation = this.addDiProbe(error)
|
|
137
|
+
if (probeInformation) {
|
|
138
|
+
const { probeId, stackIndex, setProbePromise } = probeInformation
|
|
139
|
+
this.runningTestProbeId = probeId
|
|
140
|
+
this.testErrorStackIndex = stackIndex
|
|
141
|
+
promises.setProbePromise = setProbePromise
|
|
142
|
+
}
|
|
149
143
|
}
|
|
150
144
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test', {
|
|
151
145
|
hasCodeowners: !!span.context()._tags[TEST_CODE_OWNERS]
|
|
@@ -158,13 +152,13 @@ class VitestPlugin extends CiPlugin {
|
|
|
158
152
|
if (duration) {
|
|
159
153
|
span.finish(span._startTime + duration - MILLISECONDS_TO_SUBTRACT_FROM_FAILED_TEST_DURATION) // milliseconds
|
|
160
154
|
} else {
|
|
161
|
-
span.finish() // retries
|
|
155
|
+
span.finish() // `duration` is empty for retries, so we'll use clock time
|
|
162
156
|
}
|
|
163
157
|
finishAllTraceSpans(span)
|
|
164
158
|
}
|
|
165
159
|
})
|
|
166
160
|
|
|
167
|
-
this.addSub('ci:vitest:test:skip', ({ testName, testSuiteAbsolutePath }) => {
|
|
161
|
+
this.addSub('ci:vitest:test:skip', ({ testName, testSuiteAbsolutePath, isNew }) => {
|
|
168
162
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
169
163
|
const testSpan = this.startTestSpan(
|
|
170
164
|
testName,
|
|
@@ -173,7 +167,8 @@ class VitestPlugin extends CiPlugin {
|
|
|
173
167
|
{
|
|
174
168
|
[TEST_SOURCE_FILE]: testSuite,
|
|
175
169
|
[TEST_SOURCE_START]: 1, // we can't get the proper start line in vitest
|
|
176
|
-
[TEST_STATUS]: 'skip'
|
|
170
|
+
[TEST_STATUS]: 'skip',
|
|
171
|
+
...(isNew ? { [TEST_IS_NEW]: 'true' } : {})
|
|
177
172
|
}
|
|
178
173
|
)
|
|
179
174
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test', {
|
|
@@ -242,6 +237,9 @@ class VitestPlugin extends CiPlugin {
|
|
|
242
237
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
|
|
243
238
|
// TODO: too frequent flush - find for method in worker to decrease frequency
|
|
244
239
|
this.tracer._exporter.flush(onFinish)
|
|
240
|
+
if (this.runningTestProbeId) {
|
|
241
|
+
this.removeDiProbe(this.runningTestProbeId)
|
|
242
|
+
}
|
|
245
243
|
})
|
|
246
244
|
|
|
247
245
|
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 () {
|