dd-trace 5.73.0 → 5.75.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 +2 -0
- package/index.d.ts +39 -7
- package/loader-hook.mjs +52 -1
- package/package.json +8 -16
- package/packages/datadog-core/src/utils/src/set.js +5 -1
- package/packages/datadog-esbuild/index.js +105 -36
- package/packages/datadog-esbuild/src/utils.js +198 -0
- package/packages/datadog-instrumentations/src/cookie-parser.js +0 -2
- package/packages/datadog-instrumentations/src/cucumber.js +2 -2
- package/packages/datadog-instrumentations/src/express.js +82 -0
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +238 -0
- package/packages/datadog-instrumentations/src/jest.js +2 -1
- package/packages/datadog-instrumentations/src/mariadb.js +9 -7
- package/packages/datadog-instrumentations/src/playwright.js +226 -93
- package/packages/datadog-instrumentations/src/router.js +63 -6
- package/packages/datadog-instrumentations/src/vitest.js +44 -12
- package/packages/datadog-instrumentations/src/ws.js +3 -3
- package/packages/datadog-plugin-aws-sdk/src/base.js +0 -1
- package/packages/datadog-plugin-express/src/code_origin.js +2 -0
- package/packages/datadog-plugin-playwright/src/index.js +74 -31
- package/packages/datadog-plugin-ws/src/close.js +1 -1
- package/packages/datadog-shimmer/src/shimmer.js +2 -0
- package/packages/dd-trace/src/aiguard/sdk.js +25 -3
- package/packages/dd-trace/src/aiguard/tags.js +4 -1
- package/packages/dd-trace/src/config-helper.js +4 -1
- package/packages/dd-trace/src/config.js +599 -592
- package/packages/dd-trace/src/config_defaults.js +14 -12
- package/packages/dd-trace/src/plugins/util/ci.js +3 -2
- package/packages/dd-trace/src/plugins/util/stacktrace.js +16 -1
- package/packages/dd-trace/src/proxy.js +1 -1
- package/packages/dd-trace/src/supported-configurations.json +1 -0
- package/packages/dd-trace/src/telemetry/endpoints.js +27 -1
- package/packages/dd-trace/src/telemetry/index.js +16 -13
- package/packages/dd-trace/src/telemetry/logs/log-collector.js +5 -3
- package/register.js +1 -11
- package/scripts/preinstall.js +3 -1
- package/version.js +2 -1
|
@@ -259,7 +259,7 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
259
259
|
testStartCh.runStores(ctx, () => {})
|
|
260
260
|
const promises = {}
|
|
261
261
|
try {
|
|
262
|
-
this.eventBroadcaster.on('envelope',
|
|
262
|
+
this.eventBroadcaster.on('envelope', async (testCase) => {
|
|
263
263
|
// Only supported from >=8.0.0
|
|
264
264
|
if (testCase?.testCaseFinished) {
|
|
265
265
|
const { testCaseFinished: { willBeRetried } } = testCase
|
|
@@ -289,7 +289,7 @@ function wrapRun (pl, isLatestVersion, version) {
|
|
|
289
289
|
testStartCh.runStores(newCtx, () => {})
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
|
-
})
|
|
292
|
+
})
|
|
293
293
|
let promise
|
|
294
294
|
|
|
295
295
|
testFnCh.runStores(ctx, () => {
|
|
@@ -3,8 +3,18 @@
|
|
|
3
3
|
const { createWrapRouterMethod } = require('./router')
|
|
4
4
|
const shimmer = require('../../datadog-shimmer')
|
|
5
5
|
const { addHook, channel, tracingChannel } = require('./helpers/instrument')
|
|
6
|
+
const {
|
|
7
|
+
setRouterMountPath,
|
|
8
|
+
markAppMounted,
|
|
9
|
+
normalizeRoutePaths,
|
|
10
|
+
wrapRouteMethodsAndPublish,
|
|
11
|
+
extractMountPaths,
|
|
12
|
+
hasRouterCycle,
|
|
13
|
+
collectRoutesFromRouter
|
|
14
|
+
} = require('./helpers/router-helper')
|
|
6
15
|
|
|
7
16
|
const handleChannel = channel('apm:express:request:handle')
|
|
17
|
+
const routeAddedChannel = channel('apm:express:route:added')
|
|
8
18
|
|
|
9
19
|
function wrapHandle (handle) {
|
|
10
20
|
return function handleWithTrace (req, res) {
|
|
@@ -56,8 +66,80 @@ function wrapResponseRender (render) {
|
|
|
56
66
|
}
|
|
57
67
|
}
|
|
58
68
|
|
|
69
|
+
function wrapAppAll (all) {
|
|
70
|
+
return function wrappedAll (path, ...otherArgs) {
|
|
71
|
+
if (!routeAddedChannel.hasSubscribers) return all.call(this, path, ...otherArgs)
|
|
72
|
+
|
|
73
|
+
const paths = normalizeRoutePaths(path)
|
|
74
|
+
|
|
75
|
+
for (const p of paths) {
|
|
76
|
+
routeAddedChannel.publish({ method: '*', path: p })
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return all.call(this, path, ...otherArgs)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Wrap app.route() to instrument Route object
|
|
84
|
+
function wrapAppRoute (route) {
|
|
85
|
+
return function wrappedRoute (path, ...otherArgs) {
|
|
86
|
+
const routeObj = route.call(this, path, ...otherArgs)
|
|
87
|
+
|
|
88
|
+
if (!routeAddedChannel.hasSubscribers) return routeObj
|
|
89
|
+
|
|
90
|
+
const paths = normalizeRoutePaths(path)
|
|
91
|
+
|
|
92
|
+
if (!paths.length) return routeObj
|
|
93
|
+
|
|
94
|
+
wrapRouteMethodsAndPublish(routeObj, paths, ({ method, path }) => {
|
|
95
|
+
routeAddedChannel.publish({ method, path })
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
return routeObj
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function wrapAppUse (use) {
|
|
103
|
+
return function wrappedUse (...args) {
|
|
104
|
+
if (!args.length) return use.call(this)
|
|
105
|
+
|
|
106
|
+
// Get mount argument and use it to register each router against the exact paths Express will use.
|
|
107
|
+
const { mountPaths, startIdx } = extractMountPaths(args[0])
|
|
108
|
+
const pathsToRegister = mountPaths.length ? mountPaths : ['/']
|
|
109
|
+
|
|
110
|
+
for (let i = startIdx; i < args.length; i++) {
|
|
111
|
+
const router = args[i]
|
|
112
|
+
|
|
113
|
+
if (!router || typeof router !== 'function') continue
|
|
114
|
+
|
|
115
|
+
markAppMounted(router)
|
|
116
|
+
|
|
117
|
+
// Avoid enumerating routes for routers that contain cycles.
|
|
118
|
+
// Express will refuse those at runtime, but collecting them here could loop forever.
|
|
119
|
+
let skipCollection = false
|
|
120
|
+
if (routeAddedChannel.hasSubscribers) {
|
|
121
|
+
skipCollection = hasRouterCycle(router)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
for (const mountPath of pathsToRegister) {
|
|
125
|
+
const normalizedMountPath = mountPath || '/'
|
|
126
|
+
setRouterMountPath(router, normalizedMountPath)
|
|
127
|
+
|
|
128
|
+
if (!skipCollection && routeAddedChannel.hasSubscribers) {
|
|
129
|
+
collectRoutesFromRouter(router, normalizedMountPath)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return use.apply(this, args)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
59
138
|
addHook({ name: 'express', versions: ['>=4'], file: ['lib/express.js'] }, express => {
|
|
60
139
|
shimmer.wrap(express.application, 'handle', wrapHandle)
|
|
140
|
+
shimmer.wrap(express.application, 'all', wrapAppAll)
|
|
141
|
+
shimmer.wrap(express.application, 'route', wrapAppRoute)
|
|
142
|
+
shimmer.wrap(express.application, 'use', wrapAppUse)
|
|
61
143
|
|
|
62
144
|
shimmer.wrap(express.response, 'json', wrapResponseJson)
|
|
63
145
|
shimmer.wrap(express.response, 'jsonp', wrapResponseJson)
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { channel } = require('./instrument')
|
|
4
|
+
const shimmer = require('../../../datadog-shimmer')
|
|
5
|
+
|
|
6
|
+
const routerMountPaths = new WeakMap() // to track mount paths for router instances
|
|
7
|
+
const layerMatchers = new WeakMap() // to store layer matchers
|
|
8
|
+
const appMountedRouters = new WeakSet() // to track routers mounted via app.use()
|
|
9
|
+
|
|
10
|
+
const METHODS = [...require('http').METHODS.map(v => v.toLowerCase()), 'all']
|
|
11
|
+
|
|
12
|
+
const routeAddedChannel = channel('apm:express:route:added')
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Joins two URL path segments into a single path
|
|
16
|
+
*
|
|
17
|
+
* @param {string} base - The base path
|
|
18
|
+
* @param {string} path - The path to append
|
|
19
|
+
* @returns {string|null} The joined path or null if the combination would create an invalid route in Express
|
|
20
|
+
*/
|
|
21
|
+
function joinPath (base, path) {
|
|
22
|
+
if (!base || base === '/') return path || '/'
|
|
23
|
+
if (!path || path === '/') return base
|
|
24
|
+
|
|
25
|
+
// Express does not normalize paths without leading slashes.
|
|
26
|
+
// If either path doesn't start with '/', we should skip this combination.
|
|
27
|
+
// Allow only empty string for path
|
|
28
|
+
if (path !== '' && !path.startsWith('/')) return null
|
|
29
|
+
if (!base.startsWith('/')) return null
|
|
30
|
+
|
|
31
|
+
// Handle duplicate slashes when base ends with / and path starts with /
|
|
32
|
+
if (base.endsWith('/') && path.startsWith('/')) {
|
|
33
|
+
return base + path.slice(1)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return base + path
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Normalize route definitions coming from Express into a string representation
|
|
40
|
+
function normalizeRoutePath (path) {
|
|
41
|
+
if (path == null) return null
|
|
42
|
+
if (typeof path === 'string') return path
|
|
43
|
+
if (path instanceof RegExp) return path.toString()
|
|
44
|
+
|
|
45
|
+
return String(path)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Recursively publish every route reachable from the router.
|
|
49
|
+
function collectRoutesFromRouter (router, prefix) {
|
|
50
|
+
if (!router?.stack?.length) return
|
|
51
|
+
|
|
52
|
+
for (const layer of router.stack) {
|
|
53
|
+
if (layer.route) {
|
|
54
|
+
// This layer has a direct route
|
|
55
|
+
const route = layer.route
|
|
56
|
+
|
|
57
|
+
const fullPaths = getRouteFullPaths(route, prefix)
|
|
58
|
+
|
|
59
|
+
for (const fullPath of fullPaths) {
|
|
60
|
+
for (const [method, enabled] of Object.entries(route.methods || {})) {
|
|
61
|
+
if (!enabled) continue
|
|
62
|
+
routeAddedChannel.publish({
|
|
63
|
+
method: normalizeMethodName(method),
|
|
64
|
+
path: fullPath
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} else if (layer.handle?.stack?.length) {
|
|
69
|
+
// This layer contains a nested router
|
|
70
|
+
// Extract mount path from layer
|
|
71
|
+
const mountPath = typeof layer.path === 'string'
|
|
72
|
+
? layer.path
|
|
73
|
+
: getLayerMatchers(layer)?.[0]?.path || ''
|
|
74
|
+
|
|
75
|
+
const nestedPrefix = joinPath(prefix, mountPath)
|
|
76
|
+
if (nestedPrefix === null) continue
|
|
77
|
+
|
|
78
|
+
// Set the mount path for the nested router
|
|
79
|
+
setRouterMountPath(layer.handle, nestedPrefix)
|
|
80
|
+
markAppMounted(layer.handle)
|
|
81
|
+
// Recursively collect from nested routers
|
|
82
|
+
collectRoutesFromRouter(layer.handle, nestedPrefix)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Flatten any route definition into an array of normalized path strings.
|
|
88
|
+
function normalizeRoutePaths (path) {
|
|
89
|
+
if (path == null) return []
|
|
90
|
+
|
|
91
|
+
if (Array.isArray(path) === false) {
|
|
92
|
+
const normalized = normalizeRoutePath(path)
|
|
93
|
+
return [normalized]
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const paths = path.flat(Infinity)
|
|
97
|
+
const result = []
|
|
98
|
+
for (const _path of paths) {
|
|
99
|
+
const normalized = normalizeRoutePath(_path)
|
|
100
|
+
if (normalized !== null) {
|
|
101
|
+
result.push(normalized)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return result
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function setRouterMountPath (router, mountPath) {
|
|
109
|
+
if (!router || typeof mountPath !== 'string') return
|
|
110
|
+
const existing = routerMountPaths.get(router)
|
|
111
|
+
if (existing) {
|
|
112
|
+
existing.add(mountPath)
|
|
113
|
+
} else {
|
|
114
|
+
routerMountPaths.set(router, new Set([mountPath]))
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function getRouterMountPaths (router) {
|
|
119
|
+
const paths = routerMountPaths.get(router)
|
|
120
|
+
if (!paths) return []
|
|
121
|
+
return [...paths]
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function setLayerMatchers (layer, matchers) {
|
|
125
|
+
layerMatchers.set(layer, matchers)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function getLayerMatchers (layer) {
|
|
129
|
+
return layerMatchers.get(layer)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function normalizeMethodName (method) {
|
|
133
|
+
return method === '_all' || method === 'all' ? '*' : method
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getRouteFullPaths (route, prefix) {
|
|
137
|
+
if (!route) return []
|
|
138
|
+
|
|
139
|
+
const routePaths = normalizeRoutePaths(route.path)
|
|
140
|
+
const pathsToPublish = routePaths.length ? routePaths : ['']
|
|
141
|
+
|
|
142
|
+
return pathsToPublish
|
|
143
|
+
.map(routePath => joinPath(prefix, routePath))
|
|
144
|
+
.filter(path => path !== null) // Filter out invalid path combinations
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function markAppMounted (router) {
|
|
148
|
+
if (router) appMountedRouters.add(router)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function isAppMounted (router) {
|
|
152
|
+
return appMountedRouters.has(router)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Normalise the optional mount argument provided to app.use()/router.use().
|
|
157
|
+
* Express accepts strings, regex, arrays (possibly nested), or
|
|
158
|
+
* no mount path at all; this helper returns the flattened set of paths along
|
|
159
|
+
* with the index where actual middleware arguments start.
|
|
160
|
+
*/
|
|
161
|
+
function extractMountPaths (path) {
|
|
162
|
+
const hasMount = typeof path === 'string' || path instanceof RegExp || Array.isArray(path)
|
|
163
|
+
|
|
164
|
+
if (!hasMount) {
|
|
165
|
+
return { mountPaths: ['/'], startIdx: 0 }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const paths = normalizeRoutePaths(path)
|
|
169
|
+
return {
|
|
170
|
+
mountPaths: paths.length ? paths : ['/'],
|
|
171
|
+
startIdx: 1
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Detect cycle router graphs.
|
|
177
|
+
*/
|
|
178
|
+
function hasRouterCycle (router, stack = new Set()) {
|
|
179
|
+
if (!router?.stack?.length) return false
|
|
180
|
+
if (stack.has(router)) return true
|
|
181
|
+
|
|
182
|
+
stack.add(router)
|
|
183
|
+
|
|
184
|
+
for (const layer of router.stack) {
|
|
185
|
+
if (!layer?.route && layer?.handle?.stack?.length) {
|
|
186
|
+
const hasCycle = hasRouterCycle(layer.handle, stack)
|
|
187
|
+
if (hasCycle) {
|
|
188
|
+
return true
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
stack.delete(router)
|
|
194
|
+
return false
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function wrapRouteMethodsAndPublish (route, paths, publish) {
|
|
198
|
+
if (!route || !paths.length) return
|
|
199
|
+
|
|
200
|
+
const filteredPaths = paths.filter(Boolean)
|
|
201
|
+
if (!filteredPaths.length) return
|
|
202
|
+
|
|
203
|
+
const uniquePaths = new Set(filteredPaths)
|
|
204
|
+
|
|
205
|
+
METHODS.forEach(method => {
|
|
206
|
+
if (typeof route[method] !== 'function') return
|
|
207
|
+
|
|
208
|
+
shimmer.wrap(route, method, (originalMethod) => function wrappedRouteMethod (...args) {
|
|
209
|
+
const normalizedMethod = normalizeMethodName(method)
|
|
210
|
+
|
|
211
|
+
for (const path of uniquePaths) {
|
|
212
|
+
publish({
|
|
213
|
+
method: normalizedMethod,
|
|
214
|
+
path
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return originalMethod.apply(this, args)
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
module.exports = {
|
|
224
|
+
setRouterMountPath,
|
|
225
|
+
getRouterMountPaths,
|
|
226
|
+
joinPath,
|
|
227
|
+
setLayerMatchers,
|
|
228
|
+
getLayerMatchers,
|
|
229
|
+
markAppMounted,
|
|
230
|
+
isAppMounted,
|
|
231
|
+
normalizeRoutePath,
|
|
232
|
+
normalizeRoutePaths,
|
|
233
|
+
getRouteFullPaths,
|
|
234
|
+
wrapRouteMethodsAndPublish,
|
|
235
|
+
extractMountPaths,
|
|
236
|
+
hasRouterCycle,
|
|
237
|
+
collectRoutesFromRouter
|
|
238
|
+
}
|
|
@@ -1335,7 +1335,7 @@ addHook({
|
|
|
1335
1335
|
}
|
|
1336
1336
|
const returnedValue = requireModuleOrMock.apply(this, arguments)
|
|
1337
1337
|
if (process.exitCode === 1) {
|
|
1338
|
-
if (this.loggedReferenceErrors
|
|
1338
|
+
if (this.loggedReferenceErrors?.size > 0) {
|
|
1339
1339
|
const errorMessage = [...this.loggedReferenceErrors][0]
|
|
1340
1340
|
testSuiteErrorCh.publish({
|
|
1341
1341
|
errorMessage,
|
|
@@ -1427,6 +1427,7 @@ function enqueueWrapper (enqueue) {
|
|
|
1427
1427
|
if (worker && !wrappedWorkers.has(worker)) {
|
|
1428
1428
|
shimmer.wrap(worker._child, 'send', sendWrapper)
|
|
1429
1429
|
shimmer.wrap(worker, '_onMessage', onMessageWrapper)
|
|
1430
|
+
worker._child.removeAllListeners('message')
|
|
1430
1431
|
worker._child.on('message', worker._onMessage.bind(worker))
|
|
1431
1432
|
wrappedWorkers.add(worker)
|
|
1432
1433
|
}
|
|
@@ -82,12 +82,7 @@ function createWrapQueryCallback (options) {
|
|
|
82
82
|
|
|
83
83
|
const cb = arguments[arguments.length - 1]
|
|
84
84
|
const ctx = { sql, conf: options }
|
|
85
|
-
|
|
86
|
-
if (typeof cb !== 'function') {
|
|
87
|
-
arguments.length += 1
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
arguments[arguments.length - 1] = shimmer.wrapFunction(cb, cb => function (err) {
|
|
85
|
+
const wrapper = (cb) => function (err) {
|
|
91
86
|
if (err) {
|
|
92
87
|
ctx.error = err
|
|
93
88
|
errorCh.publish(ctx)
|
|
@@ -96,7 +91,14 @@ function createWrapQueryCallback (options) {
|
|
|
96
91
|
return typeof cb === 'function'
|
|
97
92
|
? finishCh.runStores(ctx, cb, this, ...arguments)
|
|
98
93
|
: finishCh.publish(ctx)
|
|
99
|
-
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (typeof cb === 'function') {
|
|
97
|
+
arguments[arguments.length - 1] = shimmer.wrapFunction(cb, wrapper)
|
|
98
|
+
} else {
|
|
99
|
+
arguments.length += 1
|
|
100
|
+
arguments[arguments.length - 1] = wrapper()
|
|
101
|
+
}
|
|
100
102
|
|
|
101
103
|
return startCh.runStores(ctx, query, this, ...arguments)
|
|
102
104
|
}
|