dd-trace 6.17.0 → 6.19.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/ci/vitest-no-worker-init-setup.mjs +22 -12
- package/index.d.ts +55 -11
- package/package.json +6 -6
- package/packages/datadog-instrumentations/src/anthropic.js +111 -9
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +5 -1
- package/packages/datadog-instrumentations/src/cucumber.js +50 -9
- package/packages/datadog-instrumentations/src/cypress-config.js +27 -15
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +6 -0
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +0 -6
- package/packages/datadog-instrumentations/src/helpers/register.js +20 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +41 -4
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +15 -13
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +51 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +13 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +28 -3
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +6 -0
- package/packages/datadog-instrumentations/src/jest/session-error.js +102 -0
- package/packages/datadog-instrumentations/src/jest.js +30 -13
- package/packages/datadog-instrumentations/src/mocha/main.js +7 -5
- package/packages/datadog-instrumentations/src/mocha/utils.js +8 -8
- package/packages/datadog-instrumentations/src/playwright-reporter.js +3 -4
- package/packages/datadog-instrumentations/src/playwright.js +73 -6
- package/packages/datadog-instrumentations/src/undici.js +7 -3
- package/packages/datadog-instrumentations/src/vitest-main.js +95 -21
- package/packages/datadog-instrumentations/src/vitest-worker.js +34 -3
- package/packages/datadog-instrumentations/src/webdriverio.js +28 -12
- package/packages/datadog-instrumentations/src/ws.js +41 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +83 -29
- package/packages/datadog-plugin-cucumber/src/index.js +22 -8
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +51 -14
- package/packages/datadog-plugin-cypress/src/index.js +3 -1
- package/packages/datadog-plugin-fetch/src/index.js +3 -0
- package/packages/datadog-plugin-jest/src/index.js +3 -4
- package/packages/datadog-plugin-mocha/src/index.js +3 -4
- package/packages/datadog-plugin-oracledb/src/connection-parser.js +3 -1
- package/packages/datadog-plugin-playwright/src/index.js +11 -6
- package/packages/datadog-plugin-undici/src/index.js +5 -0
- package/packages/datadog-plugin-vitest/src/index.js +3 -4
- package/packages/datadog-plugin-ws/src/producer.js +1 -1
- package/packages/datadog-shimmer/src/shimmer.js +14 -10
- package/packages/datadog-turbopack/index.js +4 -1
- package/packages/datadog-turbopack/src/loader.js +6 -2
- package/packages/dd-trace/src/agent/info.js +5 -4
- package/packages/dd-trace/src/aiguard/client.js +38 -19
- package/packages/dd-trace/src/aiguard/integrations/anthropic.js +29 -4
- package/packages/dd-trace/src/aiguard/integrations/index.js +1 -1
- package/packages/dd-trace/src/aiguard/integrations/openai.js +20 -56
- package/packages/dd-trace/src/aiguard/integrations/stream.js +60 -0
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +64 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +19 -19
- package/packages/dd-trace/src/config/index.js +7 -6
- package/packages/dd-trace/src/config/supported-configurations.json +16 -5
- package/packages/dd-trace/src/constants.js +2 -0
- package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -1
- package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +5 -3
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
- package/packages/dd-trace/src/debugger/index.js +1 -1
- package/packages/dd-trace/src/evp_proxy/constants.js +2 -0
- package/packages/dd-trace/src/evp_proxy/direct.js +17 -13
- package/packages/dd-trace/src/evp_proxy/discovery.js +14 -10
- package/packages/dd-trace/src/evp_proxy/path.js +21 -7
- package/packages/dd-trace/src/exporters/common/url.js +33 -2
- package/packages/dd-trace/src/llmobs/constants/tags.js +31 -8
- package/packages/dd-trace/src/llmobs/gen-ai-tags.js +127 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +18 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +33 -8
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +77 -41
- package/packages/dd-trace/src/llmobs/plugins/base.js +154 -15
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +162 -10
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +65 -16
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +14 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +34 -35
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +10 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +25 -2
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +8 -7
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +13 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/realtime.js +5 -0
- package/packages/dd-trace/src/llmobs/plugins/vertexai.js +7 -0
- package/packages/dd-trace/src/llmobs/prompts/manager.js +10 -2
- package/packages/dd-trace/src/llmobs/prompts/prompt.js +79 -12
- package/packages/dd-trace/src/llmobs/span_processor.js +10 -65
- package/packages/dd-trace/src/llmobs/tagger.js +11 -40
- package/packages/dd-trace/src/openfeature/configuration_source.js +4 -1
- package/packages/dd-trace/src/openfeature/flagging_provider.js +3 -3
- package/packages/dd-trace/src/openfeature/index.js +6 -2
- package/packages/dd-trace/src/openfeature/writers/base.js +90 -17
- package/packages/dd-trace/src/openfeature/writers/exposures.js +4 -0
- package/packages/dd-trace/src/openfeature/writers/util.js +104 -33
- package/packages/dd-trace/src/opentelemetry/trace/index.js +5 -0
- package/packages/dd-trace/src/opentracing/tracer.js +3 -3
- package/packages/dd-trace/src/plugin_manager.js +27 -8
- package/packages/dd-trace/src/plugins/ci_plugin.js +1 -1
- package/packages/dd-trace/src/plugins/util/test.js +13 -4
- package/packages/dd-trace/src/profiler.js +51 -5
- package/packages/dd-trace/src/profiling/profiler.js +24 -20
- package/packages/dd-trace/src/proxy.js +2 -2
- package/packages/dd-trace/src/ritm.js +12 -9
- package/packages/dd-trace/src/span_processor.js +6 -1
- package/packages/dd-trace/src/telemetry/send-data.js +2 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
- package/packages/dd-trace/src/tracer.js +5 -4
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/packages/datadog-instrumentations/src/azure-cosmos.js +0 -7
- package/packages/datadog-instrumentations/src/bullmq.js +0 -11
- package/packages/datadog-instrumentations/src/langchain.js +0 -7
- package/packages/datadog-instrumentations/src/langgraph.js +0 -7
- package/packages/datadog-instrumentations/src/mercurius.js +0 -11
|
@@ -426,15 +426,16 @@ function registerManualAfterScreenshotHandlers (on, handlers, datadogHandler) {
|
|
|
426
426
|
}
|
|
427
427
|
|
|
428
428
|
/**
|
|
429
|
-
* Registers one Cypress
|
|
429
|
+
* Registers one Cypress lifecycle handler that runs every collected handler
|
|
430
430
|
* in registration order. Cypress 10+ otherwise keeps only the last handler.
|
|
431
431
|
*
|
|
432
432
|
* @param {Function} on Cypress event registration function
|
|
433
|
-
* @param {
|
|
434
|
-
* @param {Function
|
|
433
|
+
* @param {'before:run'|'after:spec'} event lifecycle event
|
|
434
|
+
* @param {Function[]} handlers collected lifecycle handlers
|
|
435
|
+
* @param {Function} [datadogHandler] Datadog lifecycle handler
|
|
435
436
|
* @param {Function} [cleanup] removes generated support files after an error
|
|
436
437
|
*/
|
|
437
|
-
function
|
|
438
|
+
function registerLifecycleHandlers (on, event, handlers, datadogHandler, cleanup) {
|
|
438
439
|
const userHandlers = datadogHandler
|
|
439
440
|
? handlers.filter(handler => handler !== datadogHandler)
|
|
440
441
|
: handlers
|
|
@@ -442,27 +443,31 @@ function registerAfterSpecHandlers (on, handlers, datadogHandler, cleanup) {
|
|
|
442
443
|
if (userHandlers.length === 0) {
|
|
443
444
|
if (datadogHandler) {
|
|
444
445
|
if (cleanup) {
|
|
445
|
-
on(
|
|
446
|
+
on(event, (...args) => Promise.resolve().then(() => datadogHandler(...args)).catch((error) => {
|
|
446
447
|
cleanup()
|
|
447
448
|
throw error
|
|
448
449
|
}))
|
|
449
450
|
} else {
|
|
450
|
-
on(
|
|
451
|
+
on(event, datadogHandler)
|
|
451
452
|
}
|
|
452
453
|
}
|
|
453
454
|
return
|
|
454
455
|
}
|
|
455
456
|
|
|
456
|
-
on(
|
|
457
|
+
on(event, (...args) => {
|
|
457
458
|
const callHandler = datadogHandler
|
|
458
|
-
? handler => runUserHandler(() => handler(
|
|
459
|
-
: handler => handler(
|
|
459
|
+
? handler => runUserHandler(() => handler(...args))
|
|
460
|
+
: handler => handler(...args)
|
|
460
461
|
const chain = userHandlers.reduce(
|
|
461
462
|
(promise, handler) => promise.then(() => callHandler(handler)),
|
|
462
463
|
Promise.resolve()
|
|
463
464
|
)
|
|
464
465
|
if (!datadogHandler) return chain
|
|
465
|
-
const finalization = finalizeAfterUserHandlers(chain, (...
|
|
466
|
+
const finalization = finalizeAfterUserHandlers(chain, (...errors) => {
|
|
467
|
+
// A failed setup hook must abort the run before Datadog starts its session.
|
|
468
|
+
if (event === 'before:run' && errors.length > 0) return
|
|
469
|
+
return datadogHandler(...args, ...errors)
|
|
470
|
+
})
|
|
466
471
|
if (!cleanup) return finalization
|
|
467
472
|
return finalization.catch((error) => {
|
|
468
473
|
cleanup()
|
|
@@ -514,6 +519,9 @@ function registerDdTraceHooks (
|
|
|
514
519
|
if (generatedSupportFiles) registerGeneratedFilesForExitCleanup(generatedSupportFiles)
|
|
515
520
|
|
|
516
521
|
const cleanupWrapper = () => {
|
|
522
|
+
// Open mode reuses this support file across runs, including failed setup attempts.
|
|
523
|
+
// The registered process-exit cleanup owns its lifetime instead.
|
|
524
|
+
if (config.isInteractive) return
|
|
517
525
|
if (generatedSupportFiles) cleanupGeneratedFiles(generatedSupportFiles)
|
|
518
526
|
}
|
|
519
527
|
|
|
@@ -537,8 +545,13 @@ function registerDdTraceHooks (
|
|
|
537
545
|
})
|
|
538
546
|
}
|
|
539
547
|
|
|
548
|
+
const registerBeforeRun = handler => {
|
|
549
|
+
registerLifecycleHandlers(on, 'before:run', userBeforeRunHandlers, handler, cleanupWrapper)
|
|
550
|
+
}
|
|
551
|
+
|
|
540
552
|
const registerNoopHandlers = () => {
|
|
541
|
-
|
|
553
|
+
registerBeforeRun()
|
|
554
|
+
registerLifecycleHandlers(on, 'after:spec', userAfterSpecHandlers)
|
|
542
555
|
for (const h of userAfterScreenshotHandlers) on('after:screenshot', h)
|
|
543
556
|
registerAfterRunWithCleanup()
|
|
544
557
|
on('task', noopTask)
|
|
@@ -555,8 +568,8 @@ function registerDdTraceHooks (
|
|
|
555
568
|
manualPlugin.initialConfig.experimentalInteractiveRunEvents = config.experimentalInteractiveRunEvents
|
|
556
569
|
}
|
|
557
570
|
}
|
|
558
|
-
|
|
559
|
-
|
|
571
|
+
registerBeforeRun(manualPlugin.beforeRunHandler)
|
|
572
|
+
registerLifecycleHandlers(on, 'after:spec', userAfterSpecHandlers, manualPlugin.afterSpecHandler, cleanupWrapper)
|
|
560
573
|
registerManualAfterScreenshotHandlers(on, userAfterScreenshotHandlers, manualPlugin.afterScreenshotHandler)
|
|
561
574
|
registerAfterRunWithCleanup(manualPlugin.afterRunHandler)
|
|
562
575
|
on('task', manualPlugin.taskHandler)
|
|
@@ -572,8 +585,6 @@ function registerDdTraceHooks (
|
|
|
572
585
|
)
|
|
573
586
|
}
|
|
574
587
|
|
|
575
|
-
for (const handler of userBeforeRunHandlers) on('before:run', handler)
|
|
576
|
-
|
|
577
588
|
if (!setupNodeEventsCh.hasSubscribers) {
|
|
578
589
|
registerNoopHandlers()
|
|
579
590
|
return config
|
|
@@ -588,6 +599,7 @@ function registerDdTraceHooks (
|
|
|
588
599
|
userAfterSpecHandlers,
|
|
589
600
|
userAfterRunHandlers,
|
|
590
601
|
userAfterScreenshotHandlers,
|
|
602
|
+
registerBeforeRun,
|
|
591
603
|
cleanupWrapper,
|
|
592
604
|
registered: false,
|
|
593
605
|
configPromise: undefined,
|
|
@@ -13,6 +13,7 @@ const {
|
|
|
13
13
|
} = require('./instrumentation-utils')
|
|
14
14
|
const hooks = require('./hooks')
|
|
15
15
|
const instrumentations = require('./instrumentations')
|
|
16
|
+
const { isRewriteActivationEnabled } = require('./rewriter/targets')
|
|
16
17
|
const disabledInstrumentations = getDisabledInstrumentations()
|
|
17
18
|
|
|
18
19
|
// register.js has now set up ritm (require-in-the-middle). In bundled
|
|
@@ -94,6 +95,11 @@ dc.subscribe(CHANNEL, (message) => {
|
|
|
94
95
|
const name = payload.package
|
|
95
96
|
if (disabledInstrumentations.has(name)) return
|
|
96
97
|
|
|
98
|
+
if (payload.activate && isRewriteActivationEnabled(name)) {
|
|
99
|
+
loadChannel.publish({ name })
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
97
103
|
const isPrefixedWithNode = name.startsWith('node:')
|
|
98
104
|
const isNodeModule = isPrefixedWithNode || !hooks[name]
|
|
99
105
|
|
|
@@ -69,6 +69,7 @@ const earlyLoadFrameworks = new Map([
|
|
|
69
69
|
*/
|
|
70
70
|
module.exports.checkForRequiredModules = function () {
|
|
71
71
|
const packages = require('./hooks')
|
|
72
|
+
const { isRewriteTargetName } = require('./rewriter/targets')
|
|
72
73
|
const naughties = new Set()
|
|
73
74
|
const frameworksSeen = new Set()
|
|
74
75
|
let didWarn = false
|
|
@@ -98,7 +99,7 @@ module.exports.checkForRequiredModules = function () {
|
|
|
98
99
|
continue
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
if (naughties.has(pkg) || !(pkg in packages)) continue
|
|
102
|
+
if (naughties.has(pkg) || (!(pkg in packages) && !isRewriteTargetName(pkg))) continue
|
|
102
103
|
|
|
103
104
|
loadOrderWarnings.push(
|
|
104
105
|
() => `Warning: Package '${pkg}' was loaded before dd-trace! This may break instrumentation.`
|
|
@@ -25,14 +25,12 @@ module.exports = {
|
|
|
25
25
|
'@anthropic-ai/sdk': { esmFirst: true, fn: () => require('../anthropic') },
|
|
26
26
|
'@apollo/server': () => require('../apollo-server'),
|
|
27
27
|
'@apollo/gateway': () => require('../apollo'),
|
|
28
|
-
'@langchain/langgraph': { esmFirst: true, fn: () => require('../langgraph') },
|
|
29
28
|
'@modelcontextprotocol/sdk': { esmFirst: true, fn: () => require('../modelcontextprotocol-sdk') },
|
|
30
29
|
'@openai/agents': () => require('../openai-agents'),
|
|
31
30
|
'@openai/agents-openai': () => require('../openai-agents'),
|
|
32
31
|
'apollo-server-core': () => require('../apollo-server-core'),
|
|
33
32
|
'@aws-sdk/smithy-client': () => require('../aws-sdk'),
|
|
34
33
|
'@aws/durable-execution-sdk-js': () => require('../aws-durable-execution-sdk-js'),
|
|
35
|
-
'@azure/cosmos': { esmFirst: true, fn: () => require('../azure-cosmos') },
|
|
36
34
|
'@azure/event-hubs': () => require('../azure-event-hubs'),
|
|
37
35
|
'@azure/functions': () => require('../azure-functions'),
|
|
38
36
|
'durable-functions': () => require('../azure-durable-functions'),
|
|
@@ -54,7 +52,6 @@ module.exports = {
|
|
|
54
52
|
'@jest/test-sequencer': () => require('../jest'),
|
|
55
53
|
'@jest/transform': () => require('../jest'),
|
|
56
54
|
'@koa/router': () => require('../koa'),
|
|
57
|
-
'@langchain/core': { esmFirst: true, fn: () => require('../langchain') },
|
|
58
55
|
'@node-redis/client': () => require('../redis'),
|
|
59
56
|
'@opensearch-project/opensearch': () => require('../opensearch'),
|
|
60
57
|
'@opentelemetry/sdk-trace': () => require('../otel-sdk-trace'),
|
|
@@ -78,7 +75,6 @@ module.exports = {
|
|
|
78
75
|
bluebird: () => require('../bluebird'),
|
|
79
76
|
'body-parser': () => require('../body-parser'),
|
|
80
77
|
'browser-bunyan': () => require('../browser-bunyan'),
|
|
81
|
-
bullmq: () => require('../bullmq'),
|
|
82
78
|
bunyan: () => require('../bunyan'),
|
|
83
79
|
'cassandra-driver': () => require('../cassandra-driver'),
|
|
84
80
|
connect: () => require('../connect'),
|
|
@@ -114,14 +110,12 @@ module.exports = {
|
|
|
114
110
|
'koa-router': () => require('../koa'),
|
|
115
111
|
'@confluentinc/kafka-javascript': () => require('../confluentinc-kafka-javascript'),
|
|
116
112
|
kafkajs: () => require('../kafkajs'),
|
|
117
|
-
langchain: () => require('../langchain'),
|
|
118
113
|
ldapjs: () => require('../ldapjs'),
|
|
119
114
|
'light-my-request': () => require('../light-my-request'),
|
|
120
115
|
'limitd-client': () => require('../limitd-client'),
|
|
121
116
|
lodash: () => require('../lodash'),
|
|
122
117
|
mariadb: { esmFirst: true, fn: () => require('../mariadb') },
|
|
123
118
|
memcached: () => require('../memcached'),
|
|
124
|
-
mercurius: () => require('../mercurius'),
|
|
125
119
|
'microgateway-core': () => require('../microgateway-core'),
|
|
126
120
|
mocha: () => require('../mocha'),
|
|
127
121
|
'mocha-each': () => require('../mocha'),
|
|
@@ -25,6 +25,7 @@ const pathSepExpr = new RegExp(`\\${path.sep}`, 'g')
|
|
|
25
25
|
const disabledInstrumentations = getDisabledInstrumentations()
|
|
26
26
|
|
|
27
27
|
const loadChannel = channel('dd-trace:instrumentation:load')
|
|
28
|
+
const orchestrionLoadChannel = channel('dd-trace:instrumentation:load:orchestrion')
|
|
28
29
|
|
|
29
30
|
// Globals
|
|
30
31
|
if (!disabledInstrumentations.has('fetch')) {
|
|
@@ -55,6 +56,25 @@ const instrumentedNodeModules = new Map()
|
|
|
55
56
|
const instrumentedIntegrationsSuccess = new Map()
|
|
56
57
|
/** @type {Set<string>} */
|
|
57
58
|
const alreadyLoggedIncompatibleIntegrations = new Set()
|
|
59
|
+
/** @type {Set<string>} */
|
|
60
|
+
const compatibleOrchestrionTargets = new Set()
|
|
61
|
+
|
|
62
|
+
orchestrionLoadChannel.subscribe(({ moduleName, version, result }) => {
|
|
63
|
+
const nameVersion = `${moduleName}@${version}`
|
|
64
|
+
|
|
65
|
+
if (result === 'unsupported') {
|
|
66
|
+
if (!compatibleOrchestrionTargets.has(nameVersion) && !instrumentedIntegrationsSuccess.has(nameVersion)) {
|
|
67
|
+
instrumentedIntegrationsSuccess.set(nameVersion, false)
|
|
68
|
+
}
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (result === 'matched' || result === 'rewritten') {
|
|
73
|
+
compatibleOrchestrionTargets.add(nameVersion)
|
|
74
|
+
instrumentedIntegrationsSuccess.set(nameVersion, true)
|
|
75
|
+
if (result === 'rewritten') loadChannel.publish({ name: moduleName })
|
|
76
|
+
}
|
|
77
|
+
})
|
|
58
78
|
|
|
59
79
|
for (const name of names) {
|
|
60
80
|
if (disabledInstrumentations.has(name)) continue
|
|
@@ -19,7 +19,7 @@ const { getRewriteTarget } = require('./targets')
|
|
|
19
19
|
* { code: string, map?: string|object }} transform
|
|
20
20
|
*
|
|
21
21
|
* @typedef {(content: string|Buffer|ArrayBuffer|Uint8Array, filename: string, format?: string,
|
|
22
|
-
* target?: { moduleName: string, filePath: string }, sourceMap?: string|object) =>
|
|
22
|
+
* target?: { moduleName: string, filePath: string, activate?: boolean }, sourceMap?: string|object) =>
|
|
23
23
|
* { code: string|Buffer|ArrayBuffer|Uint8Array, map?: string|object }} BundlerRewriter
|
|
24
24
|
*/
|
|
25
25
|
|
|
@@ -62,7 +62,7 @@ function normalizeFilename (filename) {
|
|
|
62
62
|
* @param {string|Buffer|ArrayBuffer|Uint8Array} content
|
|
63
63
|
* @param {string} filename
|
|
64
64
|
* @param {string} [format]
|
|
65
|
-
* @param {{ moduleName: string, filePath: string }} [target]
|
|
65
|
+
* @param {{ moduleName: string, filePath: string, activate?: boolean }} [target]
|
|
66
66
|
* @returns {string|Buffer|ArrayBuffer|Uint8Array}
|
|
67
67
|
*/
|
|
68
68
|
function rewrite (content, filename, format, target) {
|
|
@@ -74,7 +74,7 @@ function rewrite (content, filename, format, target) {
|
|
|
74
74
|
if (!target) return content
|
|
75
75
|
|
|
76
76
|
const moduleType = format === 'module' ? 'esm' : 'cjs'
|
|
77
|
-
const { moduleName, filePath } = target
|
|
77
|
+
const { moduleName, filePath, activate } = target
|
|
78
78
|
if (disabled.has(moduleName)) return content
|
|
79
79
|
|
|
80
80
|
const version = getVersion(filename, filePath)
|
|
@@ -82,10 +82,18 @@ function rewrite (content, filename, format, target) {
|
|
|
82
82
|
|
|
83
83
|
const transformer = getMatcher(moduleType).getTransformer(moduleName, version, filePath)
|
|
84
84
|
|
|
85
|
-
if (!transformer) return content
|
|
85
|
+
if (!transformer && !activate) return content
|
|
86
86
|
|
|
87
87
|
const source = getSourceText(content)
|
|
88
88
|
|
|
89
|
+
if (!transformer) {
|
|
90
|
+
return appendOrchestrionLoad(
|
|
91
|
+
source,
|
|
92
|
+
{ moduleName, version, result: 'unsupported' },
|
|
93
|
+
moduleType
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
89
97
|
// TODO: pass existing sourcemap as input for remapping
|
|
90
98
|
let { code, map } = transformer.transform(source, moduleType)
|
|
91
99
|
|
|
@@ -98,6 +106,11 @@ function rewrite (content, filename, format, target) {
|
|
|
98
106
|
map = shiftSourceMapLine(map)
|
|
99
107
|
}
|
|
100
108
|
|
|
109
|
+
if (activate) {
|
|
110
|
+
const result = code === source ? 'matched' : 'rewritten'
|
|
111
|
+
code = appendOrchestrionLoad(code, { moduleName, version, result }, moduleType)
|
|
112
|
+
}
|
|
113
|
+
|
|
101
114
|
if (!map) return code
|
|
102
115
|
|
|
103
116
|
const inlineMap = Buffer.from(typeof map === 'string' ? map : JSON.stringify(map)).toString('base64')
|
|
@@ -236,6 +249,30 @@ function getSourceText (source) {
|
|
|
236
249
|
return Buffer.from(source).toString('utf8')
|
|
237
250
|
}
|
|
238
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Publish target-level compatibility on the application thread when a pure target is evaluated.
|
|
254
|
+
*
|
|
255
|
+
* @param {string} source
|
|
256
|
+
* @param {{ moduleName: string, version: string,
|
|
257
|
+
* result: 'unsupported'|'matched'|'rewritten' }} payload
|
|
258
|
+
* @param {'cjs'|'esm'} moduleType
|
|
259
|
+
*/
|
|
260
|
+
function appendOrchestrionLoad (source, payload, moduleType) {
|
|
261
|
+
const dcModule = JSON.stringify(getDcPolyfillSpecifier(moduleType) ?? 'diagnostics_channel')
|
|
262
|
+
const message = JSON.stringify(payload)
|
|
263
|
+
|
|
264
|
+
if (moduleType === 'esm') {
|
|
265
|
+
let binding = 'ddTraceOrchestrionDc'
|
|
266
|
+
let suffix = 0
|
|
267
|
+
while (source.includes(binding)) binding = `ddTraceOrchestrionDc${++suffix}`
|
|
268
|
+
|
|
269
|
+
return `${source};import ${binding} from ${dcModule};` +
|
|
270
|
+
`${binding}.channel('dd-trace:instrumentation:load:orchestrion').publish(${message});`
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return `${source};require(${dcModule}).channel('dd-trace:instrumentation:load:orchestrion').publish(${message});`
|
|
274
|
+
}
|
|
275
|
+
|
|
239
276
|
/**
|
|
240
277
|
* Account for a shebang restored ahead of the generated program. The transformer already maps original positions
|
|
241
278
|
* past the input shebang, so only the generated side needs an additional empty line.
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
//
|
|
3
|
+
// Activated rewrites publish their module name to the plugin manager after evaluation.
|
|
4
|
+
// Only pure Orchestrion integrations that need this activation set activate: true.
|
|
5
|
+
// Other entries use hooks or another activation path, or do not need activation from a rewrite.
|
|
4
6
|
const registry = [
|
|
5
7
|
{ instrumentations: require('./instrumentations/ai') },
|
|
6
|
-
{
|
|
8
|
+
{ activate: true, instrumentations: require('./instrumentations/azure-cosmos') },
|
|
7
9
|
{ instrumentations: require('./instrumentations/azure-durable-functions') },
|
|
8
|
-
{
|
|
10
|
+
{ activate: true, instrumentations: require('./instrumentations/bullmq') },
|
|
9
11
|
{ instrumentations: require('./instrumentations/claude-agent-sdk') },
|
|
10
12
|
{ instrumentations: require('./instrumentations/graphql') },
|
|
11
13
|
{ instrumentations: require('./instrumentations/graphql-jit') },
|
|
12
|
-
{
|
|
13
|
-
{
|
|
14
|
-
{
|
|
14
|
+
{ activate: true, instrumentations: require('./instrumentations/langchain') },
|
|
15
|
+
{ activate: true, instrumentations: require('./instrumentations/langgraph') },
|
|
16
|
+
{ activate: true, instrumentations: require('./instrumentations/mercurius') },
|
|
15
17
|
{ instrumentations: require('./instrumentations/modelcontextprotocol-sdk') },
|
|
16
18
|
{ instrumentations: require('./instrumentations/openai-agents') },
|
|
17
19
|
{ instrumentations: require('./instrumentations/playwright') },
|
|
@@ -21,10 +23,10 @@ const registry = [
|
|
|
21
23
|
{ instrumentations: require('./instrumentations/supabase') },
|
|
22
24
|
]
|
|
23
25
|
|
|
24
|
-
const
|
|
25
|
-
for (const {
|
|
26
|
-
if (!
|
|
27
|
-
for (const { module } of instrumentations)
|
|
26
|
+
const activatedModules = new Set()
|
|
27
|
+
for (const { activate, instrumentations } of registry) {
|
|
28
|
+
if (!activate) continue
|
|
29
|
+
for (const { module } of instrumentations) activatedModules.add(module.name)
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
const instrumentations = registry.flatMap(entry => entry.instrumentations)
|
|
@@ -32,8 +34,8 @@ const instrumentations = registry.flatMap(entry => entry.instrumentations)
|
|
|
32
34
|
/**
|
|
33
35
|
* @param {string} moduleName
|
|
34
36
|
*/
|
|
35
|
-
function
|
|
36
|
-
return
|
|
37
|
+
function isRewriteActivationEnabled (moduleName) {
|
|
38
|
+
return activatedModules.has(moduleName)
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
module.exports = {
|
|
41
|
+
module.exports = { isRewriteActivationEnabled, instrumentations, registry }
|
package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js
CHANGED
|
@@ -3,6 +3,57 @@
|
|
|
3
3
|
// Playwright keeps several hook targets in private local classes/functions.
|
|
4
4
|
// Keep these rewrites limited to bundled internals that addHook cannot wrap.
|
|
5
5
|
module.exports = [
|
|
6
|
+
...[
|
|
7
|
+
['>=1.38.0 <1.60.0', 'lib/common/config.js'],
|
|
8
|
+
['>=1.60.0', 'lib/common/index.js'],
|
|
9
|
+
].map(([versionRange, filePath]) => ({
|
|
10
|
+
module: { name: 'playwright', versionRange, filePath },
|
|
11
|
+
functionQuery: { className: 'FullProjectInternal', methodName: 'constructor', kind: 'Sync' },
|
|
12
|
+
channelName: 'FullProjectInternal',
|
|
13
|
+
})),
|
|
14
|
+
{
|
|
15
|
+
module: {
|
|
16
|
+
name: '@playwright/test',
|
|
17
|
+
versionRange: '>=1.18.0 <1.38.0',
|
|
18
|
+
filePath: 'lib/reporters/multiplexer.js',
|
|
19
|
+
},
|
|
20
|
+
functionQuery: {
|
|
21
|
+
className: 'Multiplexer',
|
|
22
|
+
methodName: 'onError',
|
|
23
|
+
kind: 'Sync',
|
|
24
|
+
},
|
|
25
|
+
channelName: 'Multiplexer_onError',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
module: {
|
|
29
|
+
name: 'playwright',
|
|
30
|
+
versionRange: '>=1.44.0 <1.60.0',
|
|
31
|
+
filePath: 'lib/runner/runner.js',
|
|
32
|
+
},
|
|
33
|
+
astQuery: 'FunctionDeclaration[id.name="writeLastRunInfo"] ' +
|
|
34
|
+
'CallExpression[callee.property.name="map"] > ArrowFunctionExpression',
|
|
35
|
+
channelName: 'lastRunTestId',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
module: {
|
|
39
|
+
name: 'playwright',
|
|
40
|
+
versionRange: '>=1.44.0 <1.60.0',
|
|
41
|
+
filePath: 'lib/runner/lastRun.js',
|
|
42
|
+
},
|
|
43
|
+
astQuery: 'ClassDeclaration[id.name="LastRunReporter"] ' +
|
|
44
|
+
'CallExpression[callee.property.name="map"] > ArrowFunctionExpression',
|
|
45
|
+
channelName: 'lastRunTestId',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
module: {
|
|
49
|
+
name: 'playwright',
|
|
50
|
+
versionRange: '>=1.60.0',
|
|
51
|
+
filePath: 'lib/runner/index.js',
|
|
52
|
+
},
|
|
53
|
+
astQuery: 'VariableDeclarator[id.name="LastRunReporter"] ' +
|
|
54
|
+
'CallExpression[callee.property.name="map"] > ArrowFunctionExpression',
|
|
55
|
+
channelName: 'lastRunTestId',
|
|
56
|
+
},
|
|
6
57
|
{
|
|
7
58
|
module: {
|
|
8
59
|
name: 'playwright',
|
package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
module.exports = [
|
|
4
|
+
{
|
|
5
|
+
module: {
|
|
6
|
+
name: '@wdio/config',
|
|
7
|
+
versionRange: '>=9.0.0',
|
|
8
|
+
filePath: 'build/node/index.js',
|
|
9
|
+
},
|
|
10
|
+
functionQuery: {
|
|
11
|
+
className: 'ConfigParser',
|
|
12
|
+
methodName: 'shard',
|
|
13
|
+
kind: 'Sync',
|
|
14
|
+
},
|
|
15
|
+
channelName: 'ConfigParser_shard',
|
|
16
|
+
},
|
|
4
17
|
{
|
|
5
18
|
module: {
|
|
6
19
|
name: '@wdio/cli',
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
//
|
|
3
|
+
// targets.json is generated by scripts/generate-rewriter-targets.js from ./instrumentation-registry.
|
|
4
4
|
const targets = require('./targets.json')
|
|
5
|
+
const rewriteTargetNames = new Set(Object.values(targets))
|
|
5
6
|
|
|
6
7
|
const NODE_MODULES = '/node_modules/'
|
|
7
8
|
|
|
@@ -9,6 +10,7 @@ const NODE_MODULES = '/node_modules/'
|
|
|
9
10
|
* @typedef {object} RewriteTarget
|
|
10
11
|
* @property {string} moduleName
|
|
11
12
|
* @property {string} filePath
|
|
13
|
+
* @property {boolean} [activate]
|
|
12
14
|
*/
|
|
13
15
|
|
|
14
16
|
/**
|
|
@@ -20,13 +22,36 @@ function getRewriteTarget (filename) {
|
|
|
20
22
|
if (nodeModulesIndex === -1) return
|
|
21
23
|
|
|
22
24
|
const modulePath = filename.slice(nodeModulesIndex + NODE_MODULES.length)
|
|
25
|
+
if (!Object.hasOwn(targets, modulePath)) return
|
|
23
26
|
const moduleName = targets[modulePath]
|
|
24
27
|
if (typeof moduleName !== 'string') return
|
|
28
|
+
const activate = isRewriteActivationEnabled(moduleName)
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
const rewriteTarget = {
|
|
27
31
|
moduleName,
|
|
28
32
|
filePath: modulePath.slice(moduleName.length + 1),
|
|
29
33
|
}
|
|
34
|
+
if (activate) rewriteTarget.activate = true
|
|
35
|
+
return rewriteTarget
|
|
30
36
|
}
|
|
31
37
|
|
|
32
|
-
|
|
38
|
+
function getRewriteTargetNames () {
|
|
39
|
+
return rewriteTargetNames.values()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {string} name
|
|
44
|
+
*/
|
|
45
|
+
function isRewriteTargetName (name) {
|
|
46
|
+
return rewriteTargetNames.has(name)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {string} moduleName
|
|
51
|
+
*/
|
|
52
|
+
function isRewriteActivationEnabled (moduleName) {
|
|
53
|
+
// Keep target-name-only consumers from loading every instrumentation descriptor.
|
|
54
|
+
return require('./instrumentation-registry').isRewriteActivationEnabled(moduleName)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { getRewriteTarget, getRewriteTargetNames, isRewriteActivationEnabled, isRewriteTargetName }
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"@modelcontextprotocol/sdk/dist/esm/server/mcp.js": "@modelcontextprotocol/sdk",
|
|
31
31
|
"@openai/agents-openai/dist/openaiChatCompletionsModel.js": "@openai/agents-openai",
|
|
32
32
|
"@openai/agents-openai/dist/openaiChatCompletionsModel.mjs": "@openai/agents-openai",
|
|
33
|
+
"@playwright/test/lib/reporters/multiplexer.js": "@playwright/test",
|
|
33
34
|
"@supabase/auth-js/dist/main/GoTrueClient.js": "@supabase/auth-js",
|
|
34
35
|
"@supabase/auth-js/dist/module/GoTrueClient.js": "@supabase/auth-js",
|
|
35
36
|
"@supabase/functions-js/dist/main/FunctionsClient.js": "@supabase/functions-js",
|
|
@@ -41,6 +42,7 @@
|
|
|
41
42
|
"@supabase/storage-js/dist/index.cjs": "@supabase/storage-js",
|
|
42
43
|
"@supabase/storage-js/dist/index.mjs": "@supabase/storage-js",
|
|
43
44
|
"@wdio/cli/build/index.js": "@wdio/cli",
|
|
45
|
+
"@wdio/config/build/node/index.js": "@wdio/config",
|
|
44
46
|
"@wdio/jasmine-framework/build/index.js": "@wdio/jasmine-framework",
|
|
45
47
|
"@wdio/local-runner/build/index.js": "@wdio/local-runner",
|
|
46
48
|
"@wdio/runner/build/index.js": "@wdio/runner",
|
|
@@ -75,8 +77,12 @@
|
|
|
75
77
|
"jasmine-core/lib/jasmine-core/jasmine.js": "jasmine-core",
|
|
76
78
|
"mercurius/index.js": "mercurius",
|
|
77
79
|
"playwright-core/lib/coreBundle.js": "playwright-core",
|
|
80
|
+
"playwright/lib/common/config.js": "playwright",
|
|
81
|
+
"playwright/lib/common/index.js": "playwright",
|
|
78
82
|
"playwright/lib/index.js": "playwright",
|
|
79
83
|
"playwright/lib/runner/index.js": "playwright",
|
|
84
|
+
"playwright/lib/runner/lastRun.js": "playwright",
|
|
85
|
+
"playwright/lib/runner/runner.js": "playwright",
|
|
80
86
|
"postgres/cjs/src/connection.js": "postgres",
|
|
81
87
|
"postgres/cjs/src/index.js": "postgres",
|
|
82
88
|
"postgres/cjs/src/query.js": "postgres",
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { stripVTControlCharacters } = require('node:util')
|
|
4
|
+
|
|
5
|
+
const { MAX_META_VALUE_LENGTH_TEST_OPTIMIZATION: MAX_LENGTH } =
|
|
6
|
+
require('../../../dd-trace/src/encode/tags-processors')
|
|
7
|
+
const log = require('../../../dd-trace/src/log')
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {{ name?: unknown, type?: unknown, message?: unknown, stack?: unknown }} SuiteError
|
|
11
|
+
* @typedef {object} JestResults
|
|
12
|
+
* @property {number} [numFailedTestSuites]
|
|
13
|
+
* @property {number} [numFailedTests]
|
|
14
|
+
* @property {{ testExecError?: SuiteError, failureMessage?: unknown, numFailingTests?: number }[]} [testResults]
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Includes complete, deduplicated suite errors only when all details fit.
|
|
19
|
+
*
|
|
20
|
+
* @param {JestResults} results
|
|
21
|
+
* @returns {Error}
|
|
22
|
+
*/
|
|
23
|
+
function getSessionError (results = {}) {
|
|
24
|
+
const { numFailedTestSuites = 0, numFailedTests = 0 } = results
|
|
25
|
+
const summary = `Failed test suites: ${numFailedTestSuites}. Failed tests: ${numFailedTests}`
|
|
26
|
+
const error = new Error(summary)
|
|
27
|
+
const groups = new Map()
|
|
28
|
+
let size = 0
|
|
29
|
+
try {
|
|
30
|
+
const { testResults } = results
|
|
31
|
+
if (!testResults) return error
|
|
32
|
+
for (const result of testResults) {
|
|
33
|
+
let name, message, stack
|
|
34
|
+
try {
|
|
35
|
+
const { testExecError } = result
|
|
36
|
+
if (testExecError && !result.numFailingTests) {
|
|
37
|
+
name = readField(testExecError.name)
|
|
38
|
+
if (name === '') name = readField(testExecError.type)
|
|
39
|
+
message = readField(testExecError.message)
|
|
40
|
+
stack = readField(testExecError.stack)
|
|
41
|
+
} else {
|
|
42
|
+
// Jest's report includes assertion failures, hook errors and any suite execution error.
|
|
43
|
+
name = 'Error'
|
|
44
|
+
message = readField(result.failureMessage)
|
|
45
|
+
stack = message
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
log.debug('Skipping unreadable Jest suite error in the test session summary')
|
|
49
|
+
continue
|
|
50
|
+
}
|
|
51
|
+
if (name === undefined || message === undefined || stack === undefined) return omitDetails(error)
|
|
52
|
+
if (!message && !stack) continue
|
|
53
|
+
name ||= 'Error'
|
|
54
|
+
const key = JSON.stringify([name, message, stack])
|
|
55
|
+
const group = groups.get(key)
|
|
56
|
+
if (group) {
|
|
57
|
+
group.count++
|
|
58
|
+
} else {
|
|
59
|
+
// Retained text must fit across error.message and error.stack; also bound grouping memory.
|
|
60
|
+
size += name.length + message.length + stack.length
|
|
61
|
+
if (size > 2 * MAX_LENGTH) return omitDetails(error)
|
|
62
|
+
groups.set(key, { name, message, stack, count: 1 })
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} catch {
|
|
66
|
+
log.debug('Unable to read Jest results for the test session summary')
|
|
67
|
+
return error
|
|
68
|
+
}
|
|
69
|
+
if (!groups.size) return error
|
|
70
|
+
|
|
71
|
+
const errors = [...groups.keys()].sort().map(key => groups.get(key))
|
|
72
|
+
let message = summary
|
|
73
|
+
let stack = ''
|
|
74
|
+
for (const { name, message: detail, stack: originalStack, count } of errors) {
|
|
75
|
+
message += `\n\n${name}: ${detail} (${count} suite${count === 1 ? '' : 's'})`
|
|
76
|
+
const prefix = errors.length === 1 ? '' : `Affected suites: ${count}\n`
|
|
77
|
+
stack += (stack ? '\n\n' : '') + prefix + (originalStack || `${name}: ${detail}`)
|
|
78
|
+
if (message.length > MAX_LENGTH || stack.length > MAX_LENGTH) return omitDetails(error)
|
|
79
|
+
}
|
|
80
|
+
error.name = errors.length === 1 ? errors[0].name : 'Error'
|
|
81
|
+
error.message = message
|
|
82
|
+
error.stack = stack
|
|
83
|
+
return error
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Returns undefined for oversized input before scanning or copying it.
|
|
88
|
+
* @param {unknown} value
|
|
89
|
+
*/
|
|
90
|
+
function readField (value) {
|
|
91
|
+
if (typeof value !== 'string') return ''
|
|
92
|
+
if (value.length > MAX_LENGTH) return
|
|
93
|
+
return stripVTControlCharacters(value)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** @param {Error} error */
|
|
97
|
+
function omitDetails (error) {
|
|
98
|
+
error.message += '\n\nError details exceed the size limit. See test and suite events for full details.'
|
|
99
|
+
return error
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = { getSessionError }
|