dd-trace 4.38.1 → 4.40.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 +0 -3
- package/README.md +8 -18
- package/ci/init.js +7 -0
- package/ext/exporters.d.ts +1 -0
- package/ext/exporters.js +2 -1
- package/ext/tags.d.ts +1 -0
- package/ext/tags.js +1 -0
- package/index.d.ts +18 -3
- package/initialize.mjs +52 -0
- package/package.json +9 -12
- package/packages/datadog-instrumentations/src/amqplib.js +5 -2
- package/packages/datadog-instrumentations/src/apollo-server-core.js +0 -1
- package/packages/datadog-instrumentations/src/apollo-server.js +0 -1
- package/packages/datadog-instrumentations/src/body-parser.js +0 -1
- package/packages/datadog-instrumentations/src/check_require_cache.js +67 -5
- package/packages/datadog-instrumentations/src/cookie-parser.js +0 -1
- package/packages/datadog-instrumentations/src/express.js +0 -1
- package/packages/datadog-instrumentations/src/graphql.js +0 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +5 -2
- package/packages/datadog-instrumentations/src/http/server.js +0 -1
- package/packages/datadog-instrumentations/src/jest.js +6 -3
- package/packages/datadog-instrumentations/src/mocha/common.js +48 -0
- package/packages/datadog-instrumentations/src/mocha/main.js +487 -0
- package/packages/datadog-instrumentations/src/mocha/utils.js +306 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +51 -0
- package/packages/datadog-instrumentations/src/mocha.js +4 -673
- package/packages/datadog-instrumentations/src/openai.js +188 -17
- package/packages/datadog-instrumentations/src/playwright.js +4 -3
- package/packages/datadog-instrumentations/src/router.js +1 -1
- package/packages/datadog-instrumentations/src/selenium.js +13 -6
- package/packages/datadog-plugin-graphql/src/resolve.js +4 -0
- package/packages/datadog-plugin-mocha/src/index.js +82 -8
- package/packages/datadog-plugin-next/src/index.js +1 -2
- package/packages/datadog-plugin-openai/src/index.js +219 -73
- package/packages/dd-trace/src/appsec/addresses.js +4 -2
- package/packages/dd-trace/src/appsec/blocking.js +19 -25
- package/packages/dd-trace/src/appsec/channels.js +2 -1
- package/packages/dd-trace/src/appsec/graphql.js +10 -3
- package/packages/dd-trace/src/appsec/index.js +11 -4
- package/packages/dd-trace/src/appsec/rasp.js +35 -0
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
- package/packages/dd-trace/src/appsec/remote_config/index.js +1 -0
- package/packages/dd-trace/src/appsec/rule_manager.js +15 -25
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -5
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +3 -1
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -1
- package/packages/dd-trace/src/config.js +97 -22
- package/packages/dd-trace/src/constants.js +2 -0
- package/packages/dd-trace/src/encode/0.4.js +47 -8
- package/packages/dd-trace/src/exporter.js +1 -0
- package/packages/dd-trace/src/flare/file.js +44 -0
- package/packages/dd-trace/src/flare/index.js +98 -0
- package/packages/dd-trace/src/log/channels.js +54 -29
- package/packages/dd-trace/src/log/writer.js +7 -49
- package/packages/dd-trace/src/opentelemetry/span.js +8 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +57 -12
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
- package/packages/dd-trace/src/plugins/util/test.js +6 -0
- package/packages/dd-trace/src/priority_sampler.js +8 -4
- package/packages/dd-trace/src/profiler.js +2 -1
- package/packages/dd-trace/src/profiling/config.js +1 -0
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/{ssi-telemetry.js → ssi-heuristics.js} +64 -36
- package/packages/dd-trace/src/profiling/ssi-telemetry-mock-profiler.js +4 -9
- package/packages/dd-trace/src/proxy.js +49 -15
- package/packages/dd-trace/src/ritm.js +13 -1
- package/packages/dd-trace/src/sampling_rule.js +2 -1
- package/packages/dd-trace/src/startup-log.js +19 -15
- package/packages/dd-trace/src/telemetry/index.js +6 -2
- package/packages/dd-trace/src/tracer.js +3 -0
- package/packages/dd-trace/src/plugins/util/ip_blocklist.js +0 -51
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
getTestSuitePath,
|
|
5
|
+
removeEfdStringFromTestName,
|
|
6
|
+
addEfdStringToTestName
|
|
7
|
+
} = require('../../../dd-trace/src/plugins/util/test')
|
|
8
|
+
const { channel, AsyncResource } = require('../helpers/instrument')
|
|
9
|
+
const shimmer = require('../../../datadog-shimmer')
|
|
10
|
+
|
|
11
|
+
// test channels
|
|
12
|
+
const testStartCh = channel('ci:mocha:test:start')
|
|
13
|
+
const testFinishCh = channel('ci:mocha:test:finish')
|
|
14
|
+
const errorCh = channel('ci:mocha:test:error')
|
|
15
|
+
const skipCh = channel('ci:mocha:test:skip')
|
|
16
|
+
|
|
17
|
+
// suite channels
|
|
18
|
+
const testSuiteErrorCh = channel('ci:mocha:test-suite:error')
|
|
19
|
+
|
|
20
|
+
const testToAr = new WeakMap()
|
|
21
|
+
const originalFns = new WeakMap()
|
|
22
|
+
const testToStartLine = new WeakMap()
|
|
23
|
+
const testFileToSuiteAr = new Map()
|
|
24
|
+
const wrappedFunctions = new WeakSet()
|
|
25
|
+
|
|
26
|
+
function isNewTest (test, knownTests) {
|
|
27
|
+
const testSuite = getTestSuitePath(test.file, process.cwd())
|
|
28
|
+
const testName = removeEfdStringFromTestName(test.fullTitle())
|
|
29
|
+
const testsForSuite = knownTests.mocha?.[testSuite] || []
|
|
30
|
+
return !testsForSuite.includes(testName)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function retryTest (test, earlyFlakeDetectionNumRetries) {
|
|
34
|
+
const originalTestName = test.title
|
|
35
|
+
const suite = test.parent
|
|
36
|
+
for (let retryIndex = 0; retryIndex < earlyFlakeDetectionNumRetries; retryIndex++) {
|
|
37
|
+
const clonedTest = test.clone()
|
|
38
|
+
clonedTest.title = addEfdStringToTestName(originalTestName, retryIndex + 1)
|
|
39
|
+
suite.addTest(clonedTest)
|
|
40
|
+
clonedTest._ddIsNew = true
|
|
41
|
+
clonedTest._ddIsEfdRetry = true
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getSuitesByTestFile (root) {
|
|
46
|
+
const suitesByTestFile = {}
|
|
47
|
+
function getSuites (suite) {
|
|
48
|
+
if (suite.file) {
|
|
49
|
+
if (suitesByTestFile[suite.file]) {
|
|
50
|
+
suitesByTestFile[suite.file].push(suite)
|
|
51
|
+
} else {
|
|
52
|
+
suitesByTestFile[suite.file] = [suite]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
suite.suites.forEach(suite => {
|
|
56
|
+
getSuites(suite)
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
getSuites(root)
|
|
60
|
+
|
|
61
|
+
const numSuitesByTestFile = Object.keys(suitesByTestFile).reduce((acc, testFile) => {
|
|
62
|
+
acc[testFile] = suitesByTestFile[testFile].length
|
|
63
|
+
return acc
|
|
64
|
+
}, {})
|
|
65
|
+
|
|
66
|
+
return { suitesByTestFile, numSuitesByTestFile }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isMochaRetry (test) {
|
|
70
|
+
return test._currentRetry !== undefined && test._currentRetry !== 0
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getTestFullName (test) {
|
|
74
|
+
return `mocha.${getTestSuitePath(test.file, process.cwd())}.${removeEfdStringFromTestName(test.fullTitle())}`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getTestStatus (test) {
|
|
78
|
+
if (test.isPending()) {
|
|
79
|
+
return 'skip'
|
|
80
|
+
}
|
|
81
|
+
if (test.isFailed() || test.timedOut) {
|
|
82
|
+
return 'fail'
|
|
83
|
+
}
|
|
84
|
+
return 'pass'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function getTestAsyncResource (test) {
|
|
88
|
+
if (!test.fn) {
|
|
89
|
+
return testToAr.get(test)
|
|
90
|
+
}
|
|
91
|
+
if (!wrappedFunctions.has(test.fn)) {
|
|
92
|
+
return testToAr.get(test.fn)
|
|
93
|
+
}
|
|
94
|
+
const originalFn = originalFns.get(test.fn)
|
|
95
|
+
return testToAr.get(originalFn)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function runnableWrapper (RunnablePackage) {
|
|
99
|
+
shimmer.wrap(RunnablePackage.prototype, 'run', run => function () {
|
|
100
|
+
if (!testStartCh.hasSubscribers) {
|
|
101
|
+
return run.apply(this, arguments)
|
|
102
|
+
}
|
|
103
|
+
const isBeforeEach = this.parent._beforeEach.includes(this)
|
|
104
|
+
const isAfterEach = this.parent._afterEach.includes(this)
|
|
105
|
+
|
|
106
|
+
const isTestHook = isBeforeEach || isAfterEach
|
|
107
|
+
|
|
108
|
+
// we restore the original user defined function
|
|
109
|
+
if (wrappedFunctions.has(this.fn)) {
|
|
110
|
+
const originalFn = originalFns.get(this.fn)
|
|
111
|
+
this.fn = originalFn
|
|
112
|
+
wrappedFunctions.delete(this.fn)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (isTestHook || this.type === 'test') {
|
|
116
|
+
const test = isTestHook ? this.ctx.currentTest : this
|
|
117
|
+
const asyncResource = getTestAsyncResource(test)
|
|
118
|
+
|
|
119
|
+
if (asyncResource) {
|
|
120
|
+
// we bind the test fn to the correct async resource
|
|
121
|
+
const newFn = asyncResource.bind(this.fn)
|
|
122
|
+
|
|
123
|
+
// we store the original function, not to lose it
|
|
124
|
+
originalFns.set(newFn, this.fn)
|
|
125
|
+
this.fn = newFn
|
|
126
|
+
|
|
127
|
+
wrappedFunctions.add(this.fn)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return run.apply(this, arguments)
|
|
132
|
+
})
|
|
133
|
+
return RunnablePackage
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getOnTestHandler (isMain, newTests) {
|
|
137
|
+
return function (test) {
|
|
138
|
+
if (isMochaRetry(test)) {
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
const testStartLine = testToStartLine.get(test)
|
|
142
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
143
|
+
testToAr.set(test.fn, asyncResource)
|
|
144
|
+
|
|
145
|
+
const {
|
|
146
|
+
file: testSuiteAbsolutePath,
|
|
147
|
+
title,
|
|
148
|
+
_ddIsNew: isNew,
|
|
149
|
+
_ddIsEfdRetry: isEfdRetry
|
|
150
|
+
} = test
|
|
151
|
+
|
|
152
|
+
const testInfo = {
|
|
153
|
+
testName: test.fullTitle(),
|
|
154
|
+
testSuiteAbsolutePath,
|
|
155
|
+
title,
|
|
156
|
+
testStartLine
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (isMain) {
|
|
160
|
+
testInfo.isNew = isNew
|
|
161
|
+
testInfo.isEfdRetry = isEfdRetry
|
|
162
|
+
// We want to store the result of the new tests
|
|
163
|
+
if (isNew) {
|
|
164
|
+
const testFullName = getTestFullName(test)
|
|
165
|
+
if (newTests[testFullName]) {
|
|
166
|
+
newTests[testFullName].push(test)
|
|
167
|
+
} else {
|
|
168
|
+
newTests[testFullName] = [test]
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
testInfo.isParallel = true
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
asyncResource.runInAsyncScope(() => {
|
|
176
|
+
testStartCh.publish(testInfo)
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function getOnTestEndHandler () {
|
|
182
|
+
return function (test) {
|
|
183
|
+
const asyncResource = getTestAsyncResource(test)
|
|
184
|
+
const status = getTestStatus(test)
|
|
185
|
+
|
|
186
|
+
// if there are afterEach to be run, we don't finish the test yet
|
|
187
|
+
if (asyncResource && !test.parent._afterEach.length) {
|
|
188
|
+
asyncResource.runInAsyncScope(() => {
|
|
189
|
+
testFinishCh.publish(status)
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function getOnHookEndHandler () {
|
|
196
|
+
return function (hook) {
|
|
197
|
+
const test = hook.ctx.currentTest
|
|
198
|
+
if (test && hook.parent._afterEach.includes(hook)) { // only if it's an afterEach
|
|
199
|
+
const isLastAfterEach = hook.parent._afterEach.indexOf(hook) === hook.parent._afterEach.length - 1
|
|
200
|
+
if (isLastAfterEach) {
|
|
201
|
+
const status = getTestStatus(test)
|
|
202
|
+
const asyncResource = getTestAsyncResource(test)
|
|
203
|
+
asyncResource.runInAsyncScope(() => {
|
|
204
|
+
testFinishCh.publish(status)
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function getOnFailHandler (isMain) {
|
|
212
|
+
return function (testOrHook, err) {
|
|
213
|
+
const testFile = testOrHook.file
|
|
214
|
+
let test = testOrHook
|
|
215
|
+
const isHook = testOrHook.type === 'hook'
|
|
216
|
+
if (isHook && testOrHook.ctx) {
|
|
217
|
+
test = testOrHook.ctx.currentTest
|
|
218
|
+
}
|
|
219
|
+
let testAsyncResource
|
|
220
|
+
if (test) {
|
|
221
|
+
testAsyncResource = getTestAsyncResource(test)
|
|
222
|
+
}
|
|
223
|
+
if (testAsyncResource) {
|
|
224
|
+
testAsyncResource.runInAsyncScope(() => {
|
|
225
|
+
if (isHook) {
|
|
226
|
+
err.message = `${testOrHook.fullTitle()}: ${err.message}`
|
|
227
|
+
errorCh.publish(err)
|
|
228
|
+
// if it's a hook and it has failed, 'test end' will not be called
|
|
229
|
+
testFinishCh.publish('fail')
|
|
230
|
+
} else {
|
|
231
|
+
errorCh.publish(err)
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (isMain) {
|
|
237
|
+
const testSuiteAsyncResource = testFileToSuiteAr.get(testFile)
|
|
238
|
+
|
|
239
|
+
if (testSuiteAsyncResource) {
|
|
240
|
+
// we propagate the error to the suite
|
|
241
|
+
const testSuiteError = new Error(
|
|
242
|
+
`"${testOrHook.parent.fullTitle()}" failed with message "${err.message}"`
|
|
243
|
+
)
|
|
244
|
+
testSuiteError.stack = err.stack
|
|
245
|
+
testSuiteAsyncResource.runInAsyncScope(() => {
|
|
246
|
+
testSuiteErrorCh.publish(testSuiteError)
|
|
247
|
+
})
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function getOnPendingHandler () {
|
|
254
|
+
return function (test) {
|
|
255
|
+
const testStartLine = testToStartLine.get(test)
|
|
256
|
+
const {
|
|
257
|
+
file: testSuiteAbsolutePath,
|
|
258
|
+
title
|
|
259
|
+
} = test
|
|
260
|
+
|
|
261
|
+
const testInfo = {
|
|
262
|
+
testName: test.fullTitle(),
|
|
263
|
+
testSuiteAbsolutePath,
|
|
264
|
+
title,
|
|
265
|
+
testStartLine
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const asyncResource = getTestAsyncResource(test)
|
|
269
|
+
if (asyncResource) {
|
|
270
|
+
asyncResource.runInAsyncScope(() => {
|
|
271
|
+
skipCh.publish(testInfo)
|
|
272
|
+
})
|
|
273
|
+
} else {
|
|
274
|
+
// if there is no async resource, the test has been skipped through `test.skip`
|
|
275
|
+
// or the parent suite is skipped
|
|
276
|
+
const skippedTestAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
277
|
+
if (test.fn) {
|
|
278
|
+
testToAr.set(test.fn, skippedTestAsyncResource)
|
|
279
|
+
} else {
|
|
280
|
+
testToAr.set(test, skippedTestAsyncResource)
|
|
281
|
+
}
|
|
282
|
+
skippedTestAsyncResource.runInAsyncScope(() => {
|
|
283
|
+
skipCh.publish(testInfo)
|
|
284
|
+
})
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
module.exports = {
|
|
289
|
+
isNewTest,
|
|
290
|
+
retryTest,
|
|
291
|
+
getSuitesByTestFile,
|
|
292
|
+
isMochaRetry,
|
|
293
|
+
getTestFullName,
|
|
294
|
+
getTestStatus,
|
|
295
|
+
runnableWrapper,
|
|
296
|
+
testToAr,
|
|
297
|
+
originalFns,
|
|
298
|
+
getTestAsyncResource,
|
|
299
|
+
testToStartLine,
|
|
300
|
+
getOnTestHandler,
|
|
301
|
+
getOnTestEndHandler,
|
|
302
|
+
getOnHookEndHandler,
|
|
303
|
+
getOnFailHandler,
|
|
304
|
+
getOnPendingHandler,
|
|
305
|
+
testFileToSuiteAr
|
|
306
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { addHook, channel } = require('../helpers/instrument')
|
|
4
|
+
const shimmer = require('../../../datadog-shimmer')
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
runnableWrapper,
|
|
8
|
+
getOnTestHandler,
|
|
9
|
+
getOnTestEndHandler,
|
|
10
|
+
getOnHookEndHandler,
|
|
11
|
+
getOnFailHandler,
|
|
12
|
+
getOnPendingHandler
|
|
13
|
+
} = require('./utils')
|
|
14
|
+
require('./common')
|
|
15
|
+
|
|
16
|
+
const workerFinishCh = channel('ci:mocha:worker:finish')
|
|
17
|
+
|
|
18
|
+
// Runner is also hooked in mocha/main.js, but in here we only generate test events.
|
|
19
|
+
addHook({
|
|
20
|
+
name: 'mocha',
|
|
21
|
+
versions: ['>=5.2.0'],
|
|
22
|
+
file: 'lib/runner.js'
|
|
23
|
+
}, function (Runner) {
|
|
24
|
+
shimmer.wrap(Runner.prototype, 'run', run => function () {
|
|
25
|
+
// We flush when the worker ends with its test file (a mocha instance in a worker runs a single test file)
|
|
26
|
+
this.on('end', () => {
|
|
27
|
+
workerFinishCh.publish()
|
|
28
|
+
})
|
|
29
|
+
this.on('test', getOnTestHandler(false))
|
|
30
|
+
|
|
31
|
+
this.on('test end', getOnTestEndHandler())
|
|
32
|
+
|
|
33
|
+
// If the hook passes, 'hook end' will be emitted. Otherwise, 'fail' will be emitted
|
|
34
|
+
this.on('hook end', getOnHookEndHandler())
|
|
35
|
+
|
|
36
|
+
this.on('fail', getOnFailHandler(false))
|
|
37
|
+
|
|
38
|
+
this.on('pending', getOnPendingHandler())
|
|
39
|
+
|
|
40
|
+
return run.apply(this, arguments)
|
|
41
|
+
})
|
|
42
|
+
return Runner
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// Used both in serial and parallel mode, and by both the main process and the workers
|
|
46
|
+
// Used to set the correct async resource to the test.
|
|
47
|
+
addHook({
|
|
48
|
+
name: 'mocha',
|
|
49
|
+
versions: ['>=5.2.0'],
|
|
50
|
+
file: 'lib/runnable.js'
|
|
51
|
+
}, runnableWrapper)
|