dd-trace 5.76.0 → 5.78.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 -1
- package/package.json +14 -14
- package/packages/datadog-instrumentations/src/express.js +18 -7
- package/packages/datadog-instrumentations/src/jest.js +16 -19
- package/packages/datadog-instrumentations/src/router.js +9 -8
- package/packages/datadog-plugin-cucumber/src/index.js +2 -1
- package/packages/datadog-plugin-grpc/src/util.js +5 -1
- package/packages/datadog-plugin-http/src/client.js +5 -1
- package/packages/datadog-plugin-http2/src/client.js +5 -1
- package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
- package/packages/dd-trace/src/appsec/channels.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +7 -0
- package/packages/dd-trace/src/appsec/iast/path-line.js +3 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -3
- package/packages/dd-trace/src/appsec/rasp/lfi.js +23 -9
- package/packages/dd-trace/src/appsec/stack_trace.js +6 -6
- package/packages/dd-trace/src/config.js +10 -1
- package/packages/dd-trace/src/debugger/devtools_client/index.js +8 -1
- package/packages/dd-trace/src/debugger/devtools_client/log.js +15 -4
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +5 -1
- package/packages/dd-trace/src/exporters/common/docker.js +1 -1
- package/packages/dd-trace/src/exporters/span-stats/writer.js +4 -5
- package/packages/dd-trace/src/format.js +4 -5
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +9 -6
- package/packages/dd-trace/src/llmobs/plugins/anthropic.js +3 -6
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +3 -2
- package/packages/dd-trace/src/llmobs/span_processor.js +2 -2
- package/packages/dd-trace/src/llmobs/util.js +1 -1
- package/packages/dd-trace/src/openfeature/writers/exposures.js +2 -2
- package/packages/dd-trace/src/opentelemetry/span.js +3 -2
- package/packages/dd-trace/src/plugins/util/serverless.js +1 -2
- package/packages/dd-trace/src/plugins/util/test.js +10 -1
- package/packages/dd-trace/src/plugins/util/web.js +5 -1
- package/packages/dd-trace/src/proxy.js +2 -2
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +2 -2
- package/packages/dd-trace/src/span_processor.js +4 -1
- package/packages/dd-trace/src/startup-log.js +24 -38
package/LICENSE-3rdparty.csv
CHANGED
|
@@ -11,7 +11,7 @@ require,@opentelemetry/api,Apache license 2.0,Copyright OpenTelemetry Authors
|
|
|
11
11
|
require,@opentelemetry/api-logs,Apache license 2.0,Copyright OpenTelemetry Authors
|
|
12
12
|
require,@opentelemetry/core,Apache license 2.0,Copyright OpenTelemetry Authors
|
|
13
13
|
require,@opentelemetry/resources,Apache license 2.0,Copyright OpenTelemetry Authors
|
|
14
|
-
require,@isaacs/ttlcache,
|
|
14
|
+
require,@isaacs/ttlcache,Blue Oak,Copyright Isaac Z. Schlueter and Contributors
|
|
15
15
|
require,crypto-randomuuid,MIT,Copyright 2021 Node.js Foundation and contributors
|
|
16
16
|
require,dc-polyfill,MIT,Copyright 2023 Datadog Inc.
|
|
17
17
|
require,escape-string-regexp,MIT,Copyright Sindre Sorhus
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.78.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -125,15 +125,15 @@
|
|
|
125
125
|
"@datadog/native-appsec": "10.3.0",
|
|
126
126
|
"@datadog/native-iast-taint-tracking": "4.0.0",
|
|
127
127
|
"@datadog/native-metrics": "3.1.1",
|
|
128
|
-
"@datadog/openfeature-node-server": "0.1.0-preview.
|
|
128
|
+
"@datadog/openfeature-node-server": "0.1.0-preview.15",
|
|
129
129
|
"@datadog/pprof": "5.12.0",
|
|
130
130
|
"@datadog/sketches-js": "2.1.1",
|
|
131
|
-
"@datadog/wasm-js-rewriter": "
|
|
132
|
-
"@isaacs/ttlcache": "^
|
|
131
|
+
"@datadog/wasm-js-rewriter": "5.0.1",
|
|
132
|
+
"@isaacs/ttlcache": "^2.0.1",
|
|
133
133
|
"@opentelemetry/api": ">=1.0.0 <1.10.0",
|
|
134
134
|
"@opentelemetry/api-logs": "<1.0.0",
|
|
135
135
|
"@opentelemetry/core": ">=1.14.0 <1.31.0",
|
|
136
|
-
"@opentelemetry/resources": ">=1.0.0 <1.
|
|
136
|
+
"@opentelemetry/resources": ">=1.0.0 <1.31.0",
|
|
137
137
|
"crypto-randomuuid": "^1.0.0",
|
|
138
138
|
"dc-polyfill": "^0.1.10",
|
|
139
139
|
"escape-string-regexp": "^5.0.0",
|
|
@@ -160,13 +160,13 @@
|
|
|
160
160
|
"ttl-set": "^1.0.0"
|
|
161
161
|
},
|
|
162
162
|
"devDependencies": {
|
|
163
|
-
"@babel/helpers": "^7.
|
|
163
|
+
"@babel/helpers": "^7.28.4",
|
|
164
164
|
"@eslint/eslintrc": "^3.3.1",
|
|
165
|
-
"@eslint/js": "^9.
|
|
165
|
+
"@eslint/js": "^9.39.0",
|
|
166
166
|
"@msgpack/msgpack": "^3.1.2",
|
|
167
167
|
"@openfeature/core": "^1.9.0",
|
|
168
168
|
"@openfeature/server-sdk": "^1.20.0",
|
|
169
|
-
"@stylistic/eslint-plugin": "^5.
|
|
169
|
+
"@stylistic/eslint-plugin": "^5.5.0",
|
|
170
170
|
"@types/chai": "^4.3.16",
|
|
171
171
|
"@types/mocha": "^10.0.10",
|
|
172
172
|
"@types/node": "^18.19.106",
|
|
@@ -175,15 +175,15 @@
|
|
|
175
175
|
"axios": "^1.12.2",
|
|
176
176
|
"benchmark": "^2.1.4",
|
|
177
177
|
"body-parser": "^2.2.0",
|
|
178
|
-
"bun": "1.3.
|
|
178
|
+
"bun": "1.3.2",
|
|
179
179
|
"chai": "^4.5.0",
|
|
180
|
-
"eslint": "^9.
|
|
181
|
-
"eslint-plugin-cypress": "^5.
|
|
180
|
+
"eslint": "^9.39.0",
|
|
181
|
+
"eslint-plugin-cypress": "^5.2.0",
|
|
182
182
|
"eslint-plugin-import": "^2.32.0",
|
|
183
|
-
"eslint-plugin-mocha": "^11.
|
|
184
|
-
"eslint-plugin-n": "^17.
|
|
183
|
+
"eslint-plugin-mocha": "^11.2.0",
|
|
184
|
+
"eslint-plugin-n": "^17.23.1",
|
|
185
185
|
"eslint-plugin-promise": "^7.2.1",
|
|
186
|
-
"eslint-plugin-unicorn": "^
|
|
186
|
+
"eslint-plugin-unicorn": "^62.0.0",
|
|
187
187
|
"express": "^5.1.0",
|
|
188
188
|
"glob": "^10.4.5",
|
|
189
189
|
"globals": "^16.3.0",
|
|
@@ -53,12 +53,21 @@ function wrapResponseRender (render) {
|
|
|
53
53
|
return render.apply(this, arguments)
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
const abortController = new AbortController()
|
|
56
57
|
return responseRenderChannel.traceSync(
|
|
57
|
-
|
|
58
|
+
function () {
|
|
59
|
+
if (abortController.signal.aborted) {
|
|
60
|
+
const error = abortController.signal.reason || new Error('Aborted')
|
|
61
|
+
throw error
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return render.apply(this, arguments)
|
|
65
|
+
},
|
|
58
66
|
{
|
|
59
67
|
req: this.req,
|
|
60
68
|
view,
|
|
61
|
-
options
|
|
69
|
+
options,
|
|
70
|
+
abortController
|
|
62
71
|
},
|
|
63
72
|
this,
|
|
64
73
|
...arguments
|
|
@@ -67,26 +76,28 @@ function wrapResponseRender (render) {
|
|
|
67
76
|
}
|
|
68
77
|
|
|
69
78
|
function wrapAppAll (all) {
|
|
70
|
-
return function wrappedAll (
|
|
71
|
-
if (!routeAddedChannel.hasSubscribers) return all.
|
|
79
|
+
return function wrappedAll (...args) {
|
|
80
|
+
if (!routeAddedChannel.hasSubscribers) return all.apply(this, args)
|
|
72
81
|
|
|
82
|
+
const path = args[0]
|
|
73
83
|
const paths = normalizeRoutePaths(path)
|
|
74
84
|
|
|
75
85
|
for (const p of paths) {
|
|
76
86
|
routeAddedChannel.publish({ method: '*', path: p })
|
|
77
87
|
}
|
|
78
88
|
|
|
79
|
-
return all.
|
|
89
|
+
return all.apply(this, args)
|
|
80
90
|
}
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
// Wrap app.route() to instrument Route object
|
|
84
94
|
function wrapAppRoute (route) {
|
|
85
|
-
return function wrappedRoute (
|
|
86
|
-
const routeObj = route.
|
|
95
|
+
return function wrappedRoute (...args) {
|
|
96
|
+
const routeObj = route.apply(this, args)
|
|
87
97
|
|
|
88
98
|
if (!routeAddedChannel.hasSubscribers) return routeObj
|
|
89
99
|
|
|
100
|
+
const path = args[0]
|
|
90
101
|
const paths = normalizeRoutePaths(path)
|
|
91
102
|
|
|
92
103
|
if (!paths.length) return routeObj
|
|
@@ -140,7 +140,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
140
140
|
this.hasSnapshotTests = undefined
|
|
141
141
|
this.testSuiteAbsolutePath = context.testPath
|
|
142
142
|
|
|
143
|
-
this.displayName = config.projectConfig?.displayName?.name
|
|
143
|
+
this.displayName = config.projectConfig?.displayName?.name || config.displayName
|
|
144
144
|
this.testEnvironmentOptions = getTestEnvironmentOptions(config)
|
|
145
145
|
|
|
146
146
|
const repositoryRoot = this.testEnvironmentOptions._ddRepositoryRoot
|
|
@@ -411,23 +411,19 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
411
411
|
} else {
|
|
412
412
|
originalHookFns.set(hook, hookFn)
|
|
413
413
|
}
|
|
414
|
-
|
|
415
|
-
// eslint-disable-next-line unicorn/consistent-function-scoping
|
|
416
|
-
const wrapperHook = function () {
|
|
414
|
+
const newHookFn = shimmer.wrapFunction(hookFn, hookFn => function () {
|
|
417
415
|
return testFnCh.runStores(ctx, () => hookFn.apply(this, arguments))
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
Object.defineProperty(wrapperHook, 'length', { value: hookFn.length })
|
|
421
|
-
hook.fn = wrapperHook
|
|
416
|
+
})
|
|
417
|
+
hook.fn = newHookFn
|
|
422
418
|
}
|
|
423
419
|
const originalFn = event.test.fn
|
|
424
420
|
originalTestFns.set(event.test, originalFn)
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
event.test.fn =
|
|
421
|
+
|
|
422
|
+
const newFn = shimmer.wrapFunction(event.test.fn, testFn => function () {
|
|
423
|
+
return testFnCh.runStores(ctx, () => testFn.apply(this, arguments))
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
event.test.fn = newFn
|
|
431
427
|
})
|
|
432
428
|
}
|
|
433
429
|
|
|
@@ -1293,7 +1289,7 @@ addHook({
|
|
|
1293
1289
|
|
|
1294
1290
|
shimmer.wrap(Runtime.prototype, '_createJestObjectFor', _createJestObjectFor => function (from) {
|
|
1295
1291
|
const result = _createJestObjectFor.apply(this, arguments)
|
|
1296
|
-
const suiteFilePath = this._testPath
|
|
1292
|
+
const suiteFilePath = this._testPath || from
|
|
1297
1293
|
|
|
1298
1294
|
shimmer.wrap(result, 'mock', mock => function (moduleName) {
|
|
1299
1295
|
// If the library is mocked with `jest.mock`, we don't want to bypass jest's own require engine
|
|
@@ -1349,9 +1345,6 @@ addHook({
|
|
|
1349
1345
|
}
|
|
1350
1346
|
}
|
|
1351
1347
|
return returnedValue
|
|
1352
|
-
} catch (error) {
|
|
1353
|
-
testSuiteErrorCh.publish({ error, testSuiteAbsolutePath: this._testPath })
|
|
1354
|
-
throw error
|
|
1355
1348
|
} finally {
|
|
1356
1349
|
// Restore original prepareStackTrace
|
|
1357
1350
|
Error.prepareStackTrace = originalPrepareStackTrace
|
|
@@ -1450,7 +1443,11 @@ addHook({
|
|
|
1450
1443
|
}, (childProcessWorker) => {
|
|
1451
1444
|
const ChildProcessWorker = childProcessWorker.default
|
|
1452
1445
|
shimmer.wrap(ChildProcessWorker.prototype, 'send', sendWrapper)
|
|
1453
|
-
|
|
1446
|
+
if (ChildProcessWorker.prototype._onMessage) {
|
|
1447
|
+
shimmer.wrap(ChildProcessWorker.prototype, '_onMessage', onMessageWrapper)
|
|
1448
|
+
} else if (ChildProcessWorker.prototype.onMessage) {
|
|
1449
|
+
shimmer.wrap(ChildProcessWorker.prototype, 'onMessage', onMessageWrapper)
|
|
1450
|
+
}
|
|
1454
1451
|
return childProcessWorker
|
|
1455
1452
|
})
|
|
1456
1453
|
|
|
@@ -146,33 +146,34 @@ function createWrapRouterMethod (name) {
|
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
function wrapMethod (original) {
|
|
149
|
-
return shimmer.wrapFunction(original, original => function methodWithTrace (
|
|
149
|
+
return shimmer.wrapFunction(original, original => function methodWithTrace (...args) {
|
|
150
150
|
let offset = 0
|
|
151
151
|
if (this.stack) {
|
|
152
152
|
offset = Array.isArray(this.stack) ? this.stack.length : 1
|
|
153
153
|
}
|
|
154
|
-
const router = original.
|
|
154
|
+
const router = original.apply(this, args)
|
|
155
155
|
|
|
156
156
|
if (typeof this.stack === 'function') {
|
|
157
157
|
this.stack = [{ handle: this.stack }]
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
if (routeAddedChannel.hasSubscribers) {
|
|
161
|
-
routeAddedChannel.publish({ topOfStackFunc: methodWithTrace, layer: this.stack
|
|
161
|
+
routeAddedChannel.publish({ topOfStackFunc: methodWithTrace, layer: this.stack?.at(-1) })
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
const fn = args[0]
|
|
165
|
+
|
|
164
166
|
// Publish only if this router was mounted by app.use() (prevents early '/sub/...')
|
|
165
167
|
if (routeAddedChannel.hasSubscribers && isAppMounted(this) && this.stack?.length > offset) {
|
|
166
168
|
// Handle nested router mounting for 'use' method
|
|
167
|
-
if (original.name === 'use' &&
|
|
169
|
+
if (original.name === 'use' && args.length >= 2) {
|
|
168
170
|
const { mountPaths, startIdx } = extractMountPaths(fn)
|
|
169
171
|
|
|
170
172
|
if (mountPaths.length) {
|
|
171
173
|
const parentPaths = getRouterMountPaths(this)
|
|
172
|
-
const callArgs = [fn, ...otherArgs]
|
|
173
174
|
|
|
174
|
-
for (let i = startIdx; i <
|
|
175
|
-
const nestedRouter =
|
|
175
|
+
for (let i = startIdx; i < args.length; i++) {
|
|
176
|
+
const nestedRouter = args[i]
|
|
176
177
|
|
|
177
178
|
if (!nestedRouter || typeof nestedRouter !== 'function') continue
|
|
178
179
|
|
|
@@ -206,7 +207,7 @@ function createWrapRouterMethod (name) {
|
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
209
|
|
|
209
|
-
if (this.stack
|
|
210
|
+
if (this.stack?.length > offset) {
|
|
210
211
|
wrapStack(this.stack.slice(offset), extractMatchers(fn))
|
|
211
212
|
}
|
|
212
213
|
|
|
@@ -51,7 +51,8 @@ const {
|
|
|
51
51
|
} = require('../../dd-trace/src/ci-visibility/telemetry')
|
|
52
52
|
|
|
53
53
|
const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
|
|
54
|
-
const BREAKPOINT_SET_GRACE_PERIOD_MS =
|
|
54
|
+
const BREAKPOINT_SET_GRACE_PERIOD_MS = 400
|
|
55
|
+
|
|
55
56
|
const isCucumberWorker = !!getEnvironmentVariable('CUCUMBER_WORKER_ID')
|
|
56
57
|
|
|
57
58
|
class CucumberPlugin extends CiPlugin {
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
const pick = require('../../datadog-core/src/utils/src/pick')
|
|
4
4
|
const log = require('../../dd-trace/src/log')
|
|
5
5
|
|
|
6
|
+
function getEmptyObject () {
|
|
7
|
+
return {}
|
|
8
|
+
}
|
|
9
|
+
|
|
6
10
|
module.exports = {
|
|
7
11
|
getMethodMetadata (path, kind) {
|
|
8
12
|
const tags = {
|
|
@@ -57,6 +61,6 @@ module.exports = {
|
|
|
57
61
|
log.error('Expected \'%s\' to be an array or function.', filter)
|
|
58
62
|
}
|
|
59
63
|
|
|
60
|
-
return
|
|
64
|
+
return getEmptyObject
|
|
61
65
|
}
|
|
62
66
|
}
|
|
@@ -183,13 +183,17 @@ function normalizeClientConfig (config) {
|
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
function is400ErrorCode (code) {
|
|
187
|
+
return code < 400 || code >= 500
|
|
188
|
+
}
|
|
189
|
+
|
|
186
190
|
function getStatusValidator (config) {
|
|
187
191
|
if (typeof config.validateStatus === 'function') {
|
|
188
192
|
return config.validateStatus
|
|
189
193
|
} else if (config.hasOwnProperty('validateStatus')) {
|
|
190
194
|
log.error('Expected `validateStatus` to be a function.')
|
|
191
195
|
}
|
|
192
|
-
return
|
|
196
|
+
return is400ErrorCode
|
|
193
197
|
}
|
|
194
198
|
|
|
195
199
|
function getFilter (config) {
|
|
@@ -162,13 +162,17 @@ function hasAmazonSignature (headers, path) {
|
|
|
162
162
|
return false
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
function is400ErrorCode (code) {
|
|
166
|
+
return code < 400 || code >= 500
|
|
167
|
+
}
|
|
168
|
+
|
|
165
169
|
function getStatusValidator (config) {
|
|
166
170
|
if (typeof config.validateStatus === 'function') {
|
|
167
171
|
return config.validateStatus
|
|
168
172
|
} else if (config.hasOwnProperty('validateStatus')) {
|
|
169
173
|
log.error('Expected `validateStatus` to be a function.')
|
|
170
174
|
}
|
|
171
|
-
return
|
|
175
|
+
return is400ErrorCode
|
|
172
176
|
}
|
|
173
177
|
|
|
174
178
|
function normalizeConfig (config) {
|
|
@@ -12,6 +12,7 @@ module.exports = {
|
|
|
12
12
|
cookieParser: dc.channel('datadog:cookie-parser:read:finish'),
|
|
13
13
|
expressMiddlewareError: dc.channel('apm:express:middleware:error'),
|
|
14
14
|
expressProcessParams: dc.channel('datadog:express:process_params:start'),
|
|
15
|
+
expressResponseRenderStart: dc.channel('tracing:datadog:express:response:render:start'),
|
|
15
16
|
expressSession: dc.channel('datadog:express-session:middleware:finish'),
|
|
16
17
|
fastifyBodyParser: dc.channel('datadog:fastify:body-parser:finish'),
|
|
17
18
|
fastifyCookieParser: dc.channel('datadog:fastify-cookie:read:finish'),
|
|
@@ -68,6 +68,13 @@ class PathTraversalAnalyzer extends InjectionAnalyzer {
|
|
|
68
68
|
}
|
|
69
69
|
this.analyze(pathArguments)
|
|
70
70
|
})
|
|
71
|
+
|
|
72
|
+
this.addSub('tracing:datadog:express:response:render:start', (ctx) => {
|
|
73
|
+
const store = storage('legacy').getStore()
|
|
74
|
+
if (!store) return
|
|
75
|
+
|
|
76
|
+
this.analyze([ctx.view])
|
|
77
|
+
})
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
_isExcluded (location) {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const process = require('process')
|
|
5
|
+
const { fileURLToPath } = require('url')
|
|
5
6
|
const { ddBasePath } = require('../../util')
|
|
6
7
|
const { getOriginalPathAndLineFromSourceMap } = require('./taint-tracking/rewriter')
|
|
7
8
|
const pathLine = {
|
|
@@ -31,11 +32,11 @@ function getNonDDCallSiteFrames (callSiteFrames, externallyExcludedPaths) {
|
|
|
31
32
|
const result = []
|
|
32
33
|
|
|
33
34
|
for (const callsite of callSiteFrames) {
|
|
34
|
-
let filepath = callsite.file
|
|
35
|
+
let filepath = callsite.file?.startsWith('file://') ? fileURLToPath(callsite.file) : callsite.file
|
|
35
36
|
|
|
36
37
|
if (globalThis.__DD_ESBUILD_IAST_WITH_SM) {
|
|
37
38
|
const callsiteLocation = {
|
|
38
|
-
path:
|
|
39
|
+
path: filepath,
|
|
39
40
|
line: callsite.line,
|
|
40
41
|
column: callsite.column
|
|
41
42
|
}
|
|
@@ -8,8 +8,8 @@ const STRINGIFY_SENSITIVE_KEY = STRINGIFY_RANGE_KEY + 'SENSITIVE'
|
|
|
8
8
|
const STRINGIFY_SENSITIVE_NOT_STRING_KEY = STRINGIFY_SENSITIVE_KEY + 'NOTSTRING'
|
|
9
9
|
|
|
10
10
|
// eslint-disable-next-line @stylistic/max-len
|
|
11
|
-
const KEYS_REGEX_WITH_SENSITIVE_RANGES = new RegExp(`(?:"(${STRINGIFY_RANGE_KEY}_
|
|
12
|
-
const KEYS_REGEX_WITHOUT_SENSITIVE_RANGES = new RegExp(`"(${STRINGIFY_RANGE_KEY}_
|
|
11
|
+
const KEYS_REGEX_WITH_SENSITIVE_RANGES = new RegExp(String.raw`(?:"(${STRINGIFY_RANGE_KEY}_\d+_))|(?:"(${STRINGIFY_SENSITIVE_KEY}_\d+_(\d+)_))|("${STRINGIFY_SENSITIVE_NOT_STRING_KEY}_\d+_([\s0-9.a-zA-Z]*)")`, 'gm')
|
|
12
|
+
const KEYS_REGEX_WITHOUT_SENSITIVE_RANGES = new RegExp(String.raw`"(${STRINGIFY_RANGE_KEY}_\d+_)`, 'gm')
|
|
13
13
|
|
|
14
14
|
const sensitiveValueRegex = new RegExp(DEFAULT_IAST_REDACTION_VALUE_PATTERN, 'gmi')
|
|
15
15
|
|
|
@@ -84,9 +84,10 @@ function sendVulnerabilities (vulnerabilities, span) {
|
|
|
84
84
|
const jsonToSend = vulnerabilitiesFormatter.toJson(validatedVulnerabilities)
|
|
85
85
|
|
|
86
86
|
if (jsonToSend.vulnerabilities.length > 0) {
|
|
87
|
-
const tags = {
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
const tags = {
|
|
88
|
+
// TODO: Store this outside of the span and set the tag in the exporter.
|
|
89
|
+
[IAST_JSON_TAG_KEY]: JSON.stringify(jsonToSend)
|
|
90
|
+
}
|
|
90
91
|
span.addTags(tags)
|
|
91
92
|
}
|
|
92
93
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { isAbsolute } = require('path')
|
|
4
|
+
|
|
5
|
+
const { fsOperationStart, incomingHttpRequestStart, expressResponseRenderStart } = require('../channels')
|
|
4
6
|
const { storage } = require('../../../../datadog-core')
|
|
5
7
|
const { enable: enableFsPlugin, disable: disableFsPlugin, RASP_MODULE } = require('./fs-plugin')
|
|
6
8
|
const { FS_OPERATION_PATH } = require('../addresses')
|
|
7
9
|
const waf = require('../waf')
|
|
8
10
|
const { RULE_TYPES, handleResult } = require('./utils')
|
|
9
|
-
const { isAbsolute } = require('path')
|
|
10
11
|
|
|
11
12
|
let config
|
|
12
13
|
let enabled
|
|
@@ -25,6 +26,7 @@ function enable (_config) {
|
|
|
25
26
|
function disable () {
|
|
26
27
|
if (fsOperationStart.hasSubscribers) fsOperationStart.unsubscribe(analyzeLfi)
|
|
27
28
|
if (incomingHttpRequestStart.hasSubscribers) incomingHttpRequestStart.unsubscribe(onFirstReceivedRequest)
|
|
29
|
+
if (expressResponseRenderStart.hasSubscribers) expressResponseRenderStart.unsubscribe(analyzeLfiInResponseRender)
|
|
28
30
|
|
|
29
31
|
disableFsPlugin(RASP_MODULE)
|
|
30
32
|
|
|
@@ -42,10 +44,18 @@ function onFirstReceivedRequest () {
|
|
|
42
44
|
|
|
43
45
|
if (!analyzeSubscribed) {
|
|
44
46
|
fsOperationStart.subscribe(analyzeLfi)
|
|
47
|
+
expressResponseRenderStart.subscribe(analyzeLfiInResponseRender)
|
|
45
48
|
analyzeSubscribed = true
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
51
|
|
|
52
|
+
function analyzeLfiInResponseRender (ctx) {
|
|
53
|
+
const store = storage('legacy').getStore()
|
|
54
|
+
if (!store) return
|
|
55
|
+
|
|
56
|
+
analyzeLfiPath(ctx.view, ctx.req, store.res, ctx.abortController)
|
|
57
|
+
}
|
|
58
|
+
|
|
49
59
|
function analyzeLfi (ctx) {
|
|
50
60
|
const store = storage('legacy').getStore()
|
|
51
61
|
if (!store) return
|
|
@@ -54,15 +64,19 @@ function analyzeLfi (ctx) {
|
|
|
54
64
|
if (!req || !fs) return
|
|
55
65
|
|
|
56
66
|
getPaths(ctx, fs).forEach(path => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
analyzeLfiPath(path, req, res, ctx.abortController)
|
|
68
|
+
})
|
|
69
|
+
}
|
|
60
70
|
|
|
61
|
-
|
|
71
|
+
function analyzeLfiPath (path, req, res, abortController) {
|
|
72
|
+
const ephemeral = {
|
|
73
|
+
[FS_OPERATION_PATH]: path
|
|
74
|
+
}
|
|
62
75
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
})
|
|
76
|
+
const raspRule = { type: RULE_TYPES.LFI }
|
|
77
|
+
|
|
78
|
+
const result = waf.run({ ephemeral }, req, raspRule)
|
|
79
|
+
handleResult(result, req, res, abortController, config, raspRule)
|
|
66
80
|
}
|
|
67
81
|
|
|
68
82
|
function getPaths (ctx, fs) {
|
|
@@ -42,9 +42,9 @@ function filterOutFramesFromLibrary (callSiteList) {
|
|
|
42
42
|
if (globalThis.__DD_ESBUILD_IAST_WITH_SM) {
|
|
43
43
|
// bundled with SourceMap, get original file and line to discriminate if comes from dd-trace or not
|
|
44
44
|
const callSiteLocation = {
|
|
45
|
-
path: callSite.getFileName(),
|
|
46
|
-
line: callSite.getLineNumber(),
|
|
47
|
-
column: callSite.getColumnNumber()
|
|
45
|
+
path: callSite.getTranslatedFileName?.() ?? callSite.getFileName(),
|
|
46
|
+
line: callSite.getTranslatedLineNumber?.() ?? callSite.getLineNumber(),
|
|
47
|
+
column: callSite.getTranslatedColumnNumber?.() ?? callSite.getColumnNumber()
|
|
48
48
|
}
|
|
49
49
|
const { path } = getOriginalPathAndLineFromSourceMap(callSiteLocation)
|
|
50
50
|
return !path?.startsWith(ddBasePath)
|
|
@@ -68,9 +68,9 @@ function getCallsiteFrames (maxDepth = 32, constructorOpt = getCallsiteFrames, c
|
|
|
68
68
|
const callSite = filteredFrames[index]
|
|
69
69
|
indexedFrames.push({
|
|
70
70
|
id: index,
|
|
71
|
-
file: callSite.getFileName(),
|
|
72
|
-
line: callSite.getLineNumber(),
|
|
73
|
-
column: callSite.getColumnNumber(),
|
|
71
|
+
file: callSite.getTranslatedFileName?.() ?? callSite.getFileName(),
|
|
72
|
+
line: callSite.getTranslatedLineNumber?.() ?? callSite.getLineNumber(),
|
|
73
|
+
column: callSite.getTranslatedColumnNumber?.() ?? callSite.getColumnNumber(),
|
|
74
74
|
function: callSite.getFunctionName(),
|
|
75
75
|
class_name: callSite.getTypeName(),
|
|
76
76
|
isNative: callSite.isNative()
|
|
@@ -4,6 +4,7 @@ const fs = require('fs')
|
|
|
4
4
|
const os = require('os')
|
|
5
5
|
const uuid = require('crypto-randomuuid') // we need to keep the old uuid dep because of cypress
|
|
6
6
|
const { URL } = require('url')
|
|
7
|
+
|
|
7
8
|
const log = require('./log')
|
|
8
9
|
const tagger = require('./tagger')
|
|
9
10
|
const set = require('../../datadog-core/src/utils/src/set')
|
|
@@ -1507,4 +1508,12 @@ function getAgentUrl (url, options) {
|
|
|
1507
1508
|
}
|
|
1508
1509
|
}
|
|
1509
1510
|
|
|
1510
|
-
|
|
1511
|
+
let configInstance = null
|
|
1512
|
+
function getConfig (options) {
|
|
1513
|
+
if (!configInstance) {
|
|
1514
|
+
configInstance = new Config(options)
|
|
1515
|
+
}
|
|
1516
|
+
return configInstance
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
module.exports = getConfig
|
|
@@ -58,6 +58,11 @@ if (SUPPORT_ARRAY_BUFFER_RESIZE) {
|
|
|
58
58
|
session.on('Debugger.paused', async ({ params }) => {
|
|
59
59
|
const start = process.hrtime.bigint()
|
|
60
60
|
|
|
61
|
+
if (params.reason !== 'other') {
|
|
62
|
+
// This error should not be caught, and should exit the worker thread, effectively stopping the debugging session
|
|
63
|
+
throw new Error(`Unexpected Debugger.paused reason: ${params.reason}`)
|
|
64
|
+
}
|
|
65
|
+
|
|
61
66
|
let maxReferenceDepth, maxCollectionSize, maxFieldCount, maxLength
|
|
62
67
|
let sampled = false
|
|
63
68
|
let numberOfProbesWithSnapshots = 0
|
|
@@ -168,7 +173,9 @@ session.on('Debugger.paused', async ({ params }) => {
|
|
|
168
173
|
await session.post('Debugger.resume')
|
|
169
174
|
const diff = process.hrtime.bigint() - start // TODO: Recored as telemetry (DEBUG-2858)
|
|
170
175
|
|
|
171
|
-
|
|
176
|
+
// This doesn't measure the overhead of the CDP protocol. The actual pause time is slightly larger.
|
|
177
|
+
// On my machine I'm seeing around 1.7ms of overhead.
|
|
178
|
+
log.debug(() => `[debugger:devtools_client] Finished processing breakpoints - main thread paused for: ~${
|
|
172
179
|
Number(diff) / 1_000_000
|
|
173
180
|
} ms`)
|
|
174
181
|
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
/** @typedef {'error'|'warn'|'info'|'debug'} Level */
|
|
4
|
+
/** @typedef {(...args: unknown[]) => void} LogFn */
|
|
5
|
+
/** @typedef {Record<Level, LogFn>} Logger */
|
|
6
|
+
|
|
3
7
|
const { workerData } = require('node:worker_threads')
|
|
4
8
|
|
|
5
9
|
// For testing purposes, we allow `workerData` to be undefined and fallback to a default config
|
|
6
|
-
const { config: { debug, logLevel }, logPort } = workerData ?? {
|
|
10
|
+
const { config: { debug = false, logLevel } = {}, logPort } = workerData ?? {}
|
|
7
11
|
|
|
12
|
+
/** @type {Level[]} */
|
|
8
13
|
const LEVELS = ['error', 'warn', 'info', 'debug']
|
|
9
14
|
const on = (level, ...args) => {
|
|
10
15
|
if (typeof args[0] === 'function') {
|
|
@@ -14,6 +19,12 @@ const on = (level, ...args) => {
|
|
|
14
19
|
}
|
|
15
20
|
const off = () => {}
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
22
|
+
const threshold = LEVELS.indexOf(logLevel)
|
|
23
|
+
|
|
24
|
+
/** @type {Logger} */
|
|
25
|
+
module.exports = Object.fromEntries(
|
|
26
|
+
LEVELS.map(level => [
|
|
27
|
+
level,
|
|
28
|
+
debug && threshold >= LEVELS.indexOf(level) ? on.bind(null, level) : off
|
|
29
|
+
])
|
|
30
|
+
)
|
|
@@ -13,6 +13,10 @@ module.exports = {
|
|
|
13
13
|
getLocalStateForCallFrame
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
function returnError () {
|
|
17
|
+
return new Error('Error getting local state')
|
|
18
|
+
}
|
|
19
|
+
|
|
16
20
|
async function getLocalStateForCallFrame (
|
|
17
21
|
callFrame,
|
|
18
22
|
{
|
|
@@ -37,7 +41,7 @@ async function getLocalStateForCallFrame (
|
|
|
37
41
|
// TODO: We might be able to get part of the scope chain.
|
|
38
42
|
// Consider if we could set errors just for the part of the scope chain that throws during collection.
|
|
39
43
|
log.error('[debugger:devtools_client] Error getting local state for call frame', err)
|
|
40
|
-
return
|
|
44
|
+
return returnError
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
// Delay calling `processRawState` so the caller gets a chance to resume the main thread before processing `rawState`
|
|
@@ -12,7 +12,7 @@ const uuidSource =
|
|
|
12
12
|
const containerSource = '[0-9a-f]{64}'
|
|
13
13
|
const taskSource = String.raw`[0-9a-f]{32}-\d+`
|
|
14
14
|
const lineReg = /^(\d+):([^:]*):(.+)$/m
|
|
15
|
-
const entityReg = new RegExp(`.*(${uuidSource}|${containerSource}|${taskSource})(
|
|
15
|
+
const entityReg = new RegExp(String.raw`.*(${uuidSource}|${containerSource}|${taskSource})(?:\.scope)?$`, 'm')
|
|
16
16
|
|
|
17
17
|
let inode = 0
|
|
18
18
|
let cgroup = ''
|
|
@@ -36,13 +36,12 @@ function makeRequest (data, url, cb) {
|
|
|
36
36
|
'Datadog-Meta-Lang': 'javascript',
|
|
37
37
|
'Datadog-Meta-Tracer-Version': pkg.version,
|
|
38
38
|
'Content-Type': 'application/msgpack'
|
|
39
|
-
}
|
|
39
|
+
},
|
|
40
|
+
protocol: url.protocol,
|
|
41
|
+
hostname: url.hostname,
|
|
42
|
+
port: url.port
|
|
40
43
|
}
|
|
41
44
|
|
|
42
|
-
options.protocol = url.protocol
|
|
43
|
-
options.hostname = url.hostname
|
|
44
|
-
options.port = url.port
|
|
45
|
-
|
|
46
45
|
log.debug('Request to the intake: %j', options)
|
|
47
46
|
|
|
48
47
|
request(data, options, (err, res) => {
|
|
@@ -32,13 +32,13 @@ const map = {
|
|
|
32
32
|
'resource.name': 'resource'
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
function format (span) {
|
|
35
|
+
function format (span, isChunkRoot) {
|
|
36
36
|
const formatted = formatSpan(span)
|
|
37
37
|
|
|
38
38
|
extractSpanLinks(formatted, span)
|
|
39
39
|
extractSpanEvents(formatted, span)
|
|
40
40
|
extractRootTags(formatted, span)
|
|
41
|
-
extractChunkTags(formatted, span)
|
|
41
|
+
extractChunkTags(formatted, span, isChunkRoot)
|
|
42
42
|
extractTags(formatted, span)
|
|
43
43
|
|
|
44
44
|
return formatted
|
|
@@ -192,11 +192,10 @@ function extractRootTags (formattedSpan, span) {
|
|
|
192
192
|
addTag({}, formattedSpan.metrics, TOP_LEVEL_KEY, 1)
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
function extractChunkTags (formattedSpan, span) {
|
|
195
|
+
function extractChunkTags (formattedSpan, span, isChunkRoot) {
|
|
196
196
|
const context = span.context()
|
|
197
|
-
const isLocalRoot = span === context._trace.started[0]
|
|
198
197
|
|
|
199
|
-
if (!
|
|
198
|
+
if (!isChunkRoot) return
|
|
200
199
|
|
|
201
200
|
for (const [key, value] of Object.entries(context._trace.tags)) {
|
|
202
201
|
addTag(formattedSpan.meta, formattedSpan.metrics, key, value)
|
|
@@ -82,7 +82,9 @@ class VercelAILLMObsPlugin extends BaseLLMObsPlugin {
|
|
|
82
82
|
* @param {string} toolDescription
|
|
83
83
|
* @returns {string | undefined}
|
|
84
84
|
*/
|
|
85
|
-
findToolName (toolDescription) {
|
|
85
|
+
findToolName (toolName, toolDescription) {
|
|
86
|
+
if (Number.isNaN(Number.parseInt(toolName))) return toolName
|
|
87
|
+
|
|
86
88
|
for (const availableTool of this.#availableTools) {
|
|
87
89
|
const description = availableTool.description
|
|
88
90
|
if (description === toolDescription && availableTool.id) {
|
|
@@ -260,16 +262,17 @@ class VercelAILLMObsPlugin extends BaseLLMObsPlugin {
|
|
|
260
262
|
|
|
261
263
|
const formattedToolCalls = []
|
|
262
264
|
for (const toolCall of outputMessageToolCalls) {
|
|
263
|
-
const
|
|
265
|
+
const toolArgs = toolCall.args ?? toolCall.input
|
|
266
|
+
const toolCallArgs = typeof toolArgs === 'string' ? getJsonStringValue(toolArgs, {}) : toolArgs
|
|
264
267
|
const toolDescription = toolsForModel?.find(tool => toolCall.toolName === tool.name)?.description
|
|
265
|
-
const name = this.findToolName(toolDescription)
|
|
268
|
+
const name = this.findToolName(toolCall.toolName, toolDescription)
|
|
266
269
|
this.#toolCallIdsToName[toolCall.toolCallId] = name
|
|
267
270
|
|
|
268
271
|
formattedToolCalls.push({
|
|
269
272
|
arguments: toolCallArgs,
|
|
270
273
|
name,
|
|
271
274
|
toolId: toolCall.toolCallId,
|
|
272
|
-
type: 'function'
|
|
275
|
+
type: toolCall.toolCallType ?? 'function'
|
|
273
276
|
})
|
|
274
277
|
}
|
|
275
278
|
|
|
@@ -317,10 +320,10 @@ class VercelAILLMObsPlugin extends BaseLLMObsPlugin {
|
|
|
317
320
|
finalContent += part.text ?? part.data
|
|
318
321
|
} else if (type === 'tool-call') {
|
|
319
322
|
const toolDescription = toolsForModel?.find(tool => part.toolName === tool.name)?.description
|
|
320
|
-
const name = this.findToolName(toolDescription)
|
|
323
|
+
const name = this.findToolName(part.toolName, toolDescription)
|
|
321
324
|
|
|
322
325
|
toolCalls.push({
|
|
323
|
-
arguments: part.args,
|
|
326
|
+
arguments: part.args ?? part.input,
|
|
324
327
|
name,
|
|
325
328
|
toolId: part.toolCallId,
|
|
326
329
|
type: 'function'
|
|
@@ -242,12 +242,9 @@ class AnthropicLLMObsPlugin extends LLMObsPlugin {
|
|
|
242
242
|
const cacheWriteTokens = usage.cache_creation_input_tokens
|
|
243
243
|
const cacheReadTokens = usage.cache_read_input_tokens
|
|
244
244
|
|
|
245
|
-
const metrics = {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
(inputTokens ?? 0) +
|
|
249
|
-
(cacheWriteTokens ?? 0) +
|
|
250
|
-
(cacheReadTokens ?? 0)
|
|
245
|
+
const metrics = {
|
|
246
|
+
inputTokens: (inputTokens ?? 0) + (cacheWriteTokens ?? 0) + (cacheReadTokens ?? 0)
|
|
247
|
+
}
|
|
251
248
|
|
|
252
249
|
if (outputTokens) metrics.outputTokens = outputTokens
|
|
253
250
|
const totalTokens = metrics.inputTokens + (outputTokens ?? 0)
|
|
@@ -37,8 +37,9 @@ class LangChainLLMObsHandler {
|
|
|
37
37
|
return message
|
|
38
38
|
}
|
|
39
39
|
try {
|
|
40
|
-
const messageContent = {
|
|
41
|
-
|
|
40
|
+
const messageContent = {
|
|
41
|
+
content: message.content || ''
|
|
42
|
+
}
|
|
42
43
|
|
|
43
44
|
const role = this.getRole(message)
|
|
44
45
|
if (role) messageContent.role = role
|
|
@@ -203,8 +203,8 @@ class LLMObsSpanProcessor {
|
|
|
203
203
|
// This function can be reused for other fields if needed
|
|
204
204
|
// Messages, Documents, and Metrics are safeguarded in `llmobs/tagger.js`
|
|
205
205
|
#addObject (obj, carrier) {
|
|
206
|
-
|
|
207
|
-
seenObjects
|
|
206
|
+
// Capture root object by default
|
|
207
|
+
const seenObjects = new WeakSet([obj])
|
|
208
208
|
|
|
209
209
|
const isCircular = value => {
|
|
210
210
|
if (typeof value !== 'object') return false
|
|
@@ -6,7 +6,7 @@ function encodeUnicode (str = '') {
|
|
|
6
6
|
let result = ''
|
|
7
7
|
for (let i = 0; i < str.length; i++) {
|
|
8
8
|
const code = str.charCodeAt(i)
|
|
9
|
-
result += code > 127 ?
|
|
9
|
+
result += code > 127 ? String.raw`\u${code.toString(16).padStart(4, '0')}` : str[i]
|
|
10
10
|
}
|
|
11
11
|
return result
|
|
12
12
|
}
|
|
@@ -27,7 +27,7 @@ const {
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* @typedef {Object} ExposureContext
|
|
30
|
-
* @property {string}
|
|
30
|
+
* @property {string} service - Service name
|
|
31
31
|
* @property {string} [version] - Service version
|
|
32
32
|
* @property {string} [env] - Service environment
|
|
33
33
|
*/
|
|
@@ -127,7 +127,7 @@ class ExposuresWriter extends BaseFFEWriter {
|
|
|
127
127
|
*/
|
|
128
128
|
_buildContext () {
|
|
129
129
|
const context = {
|
|
130
|
-
|
|
130
|
+
service: this._config.service || 'unknown'
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
// Only include version and env if they are defined
|
|
@@ -10,7 +10,7 @@ const { timeInputToHrTime } = require('@opentelemetry/core')
|
|
|
10
10
|
const tracer = require('../../')
|
|
11
11
|
const DatadogSpan = require('../opentracing/span')
|
|
12
12
|
const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK, IGNORE_OTEL_ERROR } = require('../constants')
|
|
13
|
-
const { SERVICE_NAME, RESOURCE_NAME } = require('../../../../ext/tags')
|
|
13
|
+
const { SERVICE_NAME, RESOURCE_NAME, SPAN_KIND } = require('../../../../ext/tags')
|
|
14
14
|
const kinds = require('../../../../ext/kinds')
|
|
15
15
|
|
|
16
16
|
const SpanContext = require('./span_context')
|
|
@@ -146,7 +146,8 @@ class Span {
|
|
|
146
146
|
integrationName: parentTracer?._isOtelLibrary ? 'otel.library' : 'otel',
|
|
147
147
|
tags: {
|
|
148
148
|
[SERVICE_NAME]: _tracer._service,
|
|
149
|
-
[RESOURCE_NAME]: spanName
|
|
149
|
+
[RESOURCE_NAME]: spanName,
|
|
150
|
+
[SPAN_KIND]: spanKindNames[kind]
|
|
150
151
|
},
|
|
151
152
|
links
|
|
152
153
|
}, _tracer._debug)
|
|
@@ -627,20 +627,29 @@ function getCodeOwnersFileEntries (rootDir) {
|
|
|
627
627
|
return entries.reverse()
|
|
628
628
|
}
|
|
629
629
|
|
|
630
|
+
const codeOwnersPerFileName = new Map()
|
|
631
|
+
|
|
630
632
|
function getCodeOwnersForFilename (filename, entries) {
|
|
631
633
|
if (!entries) {
|
|
632
634
|
return null
|
|
633
635
|
}
|
|
636
|
+
if (codeOwnersPerFileName.has(filename)) {
|
|
637
|
+
return codeOwnersPerFileName.get(filename)
|
|
638
|
+
}
|
|
634
639
|
for (const entry of entries) {
|
|
635
640
|
try {
|
|
636
641
|
const isResponsible = ignore().add(entry.pattern).ignores(filename)
|
|
637
642
|
if (isResponsible) {
|
|
638
|
-
|
|
643
|
+
const codeOwners = JSON.stringify(entry.owners)
|
|
644
|
+
codeOwnersPerFileName.set(filename, codeOwners)
|
|
645
|
+
return codeOwners
|
|
639
646
|
}
|
|
640
647
|
} catch {
|
|
648
|
+
codeOwnersPerFileName.set(filename, null)
|
|
641
649
|
return null
|
|
642
650
|
}
|
|
643
651
|
}
|
|
652
|
+
codeOwnersPerFileName.set(filename, null)
|
|
644
653
|
return null
|
|
645
654
|
}
|
|
646
655
|
|
|
@@ -577,13 +577,17 @@ function getHeadersToRecord (config) {
|
|
|
577
577
|
return []
|
|
578
578
|
}
|
|
579
579
|
|
|
580
|
+
function isNot500ErrorCode (code) {
|
|
581
|
+
return code < 500
|
|
582
|
+
}
|
|
583
|
+
|
|
580
584
|
function getStatusValidator (config) {
|
|
581
585
|
if (typeof config.validateStatus === 'function') {
|
|
582
586
|
return config.validateStatus
|
|
583
587
|
} else if (config.hasOwnProperty('validateStatus')) {
|
|
584
588
|
log.error('Expected `validateStatus` to be a function.')
|
|
585
589
|
}
|
|
586
|
-
return
|
|
590
|
+
return isNot500ErrorCode
|
|
587
591
|
}
|
|
588
592
|
|
|
589
593
|
const noop = () => {}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const NoopProxy = require('./noop/proxy')
|
|
3
3
|
const DatadogTracer = require('./tracer')
|
|
4
|
-
const
|
|
4
|
+
const getConfig = require('./config')
|
|
5
5
|
const runtimeMetrics = require('./runtime_metrics')
|
|
6
6
|
const log = require('./log')
|
|
7
7
|
const { setStartupLogPluginManager } = require('./startup-log')
|
|
@@ -98,7 +98,7 @@ class Tracer extends NoopProxy {
|
|
|
98
98
|
this._initialized = true
|
|
99
99
|
|
|
100
100
|
try {
|
|
101
|
-
const config =
|
|
101
|
+
const config = getConfig(options) // TODO: support dynamic code config
|
|
102
102
|
|
|
103
103
|
if (config.crashtracking.enabled) {
|
|
104
104
|
require('./crashtracking').start(config)
|
|
@@ -22,7 +22,7 @@ let nativeMetrics = null
|
|
|
22
22
|
let gcObserver = null
|
|
23
23
|
let interval = null
|
|
24
24
|
let client = null
|
|
25
|
-
let lastTime =
|
|
25
|
+
let lastTime = 0
|
|
26
26
|
let lastCpuUsage = null
|
|
27
27
|
let eventLoopDelayObserver = null
|
|
28
28
|
|
|
@@ -103,7 +103,6 @@ module.exports = {
|
|
|
103
103
|
interval = null
|
|
104
104
|
|
|
105
105
|
client = null
|
|
106
|
-
lastTime = 0n
|
|
107
106
|
lastCpuUsage = null
|
|
108
107
|
|
|
109
108
|
gcObserver?.disconnect()
|
|
@@ -339,6 +338,7 @@ function startGCObserver () {
|
|
|
339
338
|
|
|
340
339
|
gcObserver = new PerformanceObserver(list => {
|
|
341
340
|
for (const entry of list.getEntries()) {
|
|
341
|
+
// @ts-expect-error - entry.detail?.kind and entry.kind are not typed
|
|
342
342
|
const type = gcType(entry.detail?.kind || entry.kind)
|
|
343
343
|
const duration = entry.duration * 1_000_000
|
|
344
344
|
|
|
@@ -47,11 +47,14 @@ class SpanProcessor {
|
|
|
47
47
|
this._spanSampler.sample(spanContext)
|
|
48
48
|
this._gitMetadataTagger.tagGitMetadata(spanContext)
|
|
49
49
|
|
|
50
|
+
let isChunkRoot = true
|
|
51
|
+
|
|
50
52
|
for (const span of started) {
|
|
51
53
|
if (span._duration === undefined) {
|
|
52
54
|
active.push(span)
|
|
53
55
|
} else {
|
|
54
|
-
const formattedSpan = format(span)
|
|
56
|
+
const formattedSpan = format(span, isChunkRoot)
|
|
57
|
+
isChunkRoot = false
|
|
55
58
|
this._stats?.onSpanFinished(formattedSpan)
|
|
56
59
|
formatted.push(formattedSpan)
|
|
57
60
|
|
|
@@ -10,18 +10,10 @@ const tracerVersion = require('../../../package.json').version
|
|
|
10
10
|
const errors = {}
|
|
11
11
|
let config
|
|
12
12
|
let pluginManager
|
|
13
|
+
/** @type {import('./sampling_rule')[]} */
|
|
13
14
|
let samplingRules = []
|
|
14
15
|
let alreadyRan = false
|
|
15
16
|
|
|
16
|
-
/**
|
|
17
|
-
* @returns {Record<string, unknown>}
|
|
18
|
-
*/
|
|
19
|
-
function getIntegrationsAndAnalytics () {
|
|
20
|
-
return {
|
|
21
|
-
integrations_loaded: Object.keys(pluginManager._pluginsByName)
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
17
|
/**
|
|
26
18
|
* @param {{ agentError: { code: string, message: string } }} [options]
|
|
27
19
|
*/
|
|
@@ -70,36 +62,30 @@ function tracerInfo () {
|
|
|
70
62
|
return JSON.stringify(this, (_key_, value) => {
|
|
71
63
|
return typeof value === 'bigint' || typeof value === 'symbol' ? String(value) : value
|
|
72
64
|
})
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
65
|
+
},
|
|
66
|
+
date: new Date().toISOString(),
|
|
67
|
+
os_name: os.type(),
|
|
68
|
+
os_version: os.release(),
|
|
69
|
+
architecture: os.arch(),
|
|
70
|
+
version: tracerVersion,
|
|
71
|
+
lang: 'nodejs',
|
|
72
|
+
lang_version: process.versions.node,
|
|
73
|
+
env: config.env,
|
|
74
|
+
enabled: config.enabled,
|
|
75
|
+
service: config.service,
|
|
76
|
+
agent_url: url,
|
|
77
|
+
debug: !!config.debug,
|
|
78
|
+
sample_rate: config.sampler.sampleRate,
|
|
79
|
+
sampling_rules: samplingRules,
|
|
80
|
+
tags: config.tags,
|
|
81
|
+
...(config.tags && config.tags.version && { dd_version: config.tags.version }),
|
|
82
|
+
log_injection_enabled: !!config.logInjection,
|
|
83
|
+
runtime_metrics_enabled: !!config.runtimeMetrics,
|
|
84
|
+
profiling_enabled: config.profiling?.enabled === 'true' || config.profiling?.enabled === 'auto',
|
|
85
|
+
integrations_loaded: Object.keys(pluginManager._pluginsByName),
|
|
86
|
+
appsec_enabled: !!config.appsec.enabled,
|
|
93
87
|
}
|
|
94
88
|
|
|
95
|
-
out.log_injection_enabled = !!config.logInjection
|
|
96
|
-
out.runtime_metrics_enabled = !!config.runtimeMetrics
|
|
97
|
-
const profilingEnabled = config.profiling?.enabled
|
|
98
|
-
out.profiling_enabled = profilingEnabled === 'true' || profilingEnabled === 'auto'
|
|
99
|
-
Object.assign(out, getIntegrationsAndAnalytics())
|
|
100
|
-
|
|
101
|
-
out.appsec_enabled = !!config.appsec.enabled
|
|
102
|
-
|
|
103
89
|
return out
|
|
104
90
|
}
|
|
105
91
|
|
|
@@ -118,7 +104,7 @@ function setStartupLogPluginManager (thePluginManager) {
|
|
|
118
104
|
}
|
|
119
105
|
|
|
120
106
|
/**
|
|
121
|
-
* @param {import('./sampling_rule')} theRules
|
|
107
|
+
* @param {import('./sampling_rule')[]} theRules
|
|
122
108
|
*/
|
|
123
109
|
function setSamplingRules (theRules) {
|
|
124
110
|
samplingRules = theRules
|