dd-trace 4.41.0 → 4.43.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 -2
- package/ext/exporters.d.ts +1 -1
- package/index.d.ts +105 -37
- package/init.js +40 -1
- package/initialize.mjs +8 -5
- package/package.json +29 -29
- package/packages/datadog-core/src/storage/index.js +1 -10
- package/packages/datadog-esbuild/index.js +5 -1
- package/packages/datadog-instrumentations/src/aws-sdk.js +2 -1
- package/packages/datadog-instrumentations/src/child_process.js +2 -2
- package/packages/datadog-instrumentations/src/cucumber.js +76 -34
- package/packages/datadog-instrumentations/src/fs.js +1 -1
- package/packages/datadog-instrumentations/src/hapi.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/hook.js +8 -3
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +4 -3
- package/packages/datadog-instrumentations/src/helpers/register.js +56 -5
- package/packages/datadog-instrumentations/src/http/client.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +17 -2
- package/packages/datadog-instrumentations/src/kafkajs.js +1 -1
- package/packages/datadog-instrumentations/src/ldapjs.js +2 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +12 -1
- package/packages/datadog-instrumentations/src/mocha/utils.js +58 -14
- package/packages/datadog-instrumentations/src/mocha/worker.js +1 -0
- package/packages/datadog-instrumentations/src/mquery.js +2 -2
- package/packages/datadog-instrumentations/src/next.js +1 -1
- package/packages/datadog-instrumentations/src/pg.js +2 -2
- package/packages/datadog-instrumentations/src/playwright.js +47 -33
- package/packages/datadog-instrumentations/src/restify.js +1 -1
- package/packages/datadog-instrumentations/src/vitest.js +349 -0
- package/packages/datadog-plugin-aws-sdk/src/base.js +8 -1
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +6 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +23 -5
- package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
- package/packages/datadog-plugin-child_process/src/index.js +1 -1
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -4
- package/packages/datadog-plugin-cucumber/src/index.js +24 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +79 -42
- package/packages/datadog-plugin-cypress/src/plugin.js +4 -3
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +7 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +25 -4
- package/packages/datadog-plugin-mongodb-core/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/index.js +57 -35
- package/packages/datadog-plugin-openai/src/token-estimator.js +20 -0
- package/packages/datadog-plugin-playwright/src/index.js +4 -1
- package/packages/datadog-plugin-sharedb/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +167 -0
- package/packages/dd-trace/src/analytics_sampler.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/path-line.js +2 -19
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +3 -1
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -0
- package/packages/dd-trace/src/appsec/index.js +4 -4
- package/packages/dd-trace/src/appsec/passport.js +1 -1
- package/packages/dd-trace/src/appsec/rasp.js +32 -5
- package/packages/dd-trace/src/appsec/recommended.json +208 -3
- package/packages/dd-trace/src/appsec/reporter.js +60 -20
- package/packages/dd-trace/src/appsec/sdk/track_event.js +3 -0
- package/packages/dd-trace/src/appsec/stack_trace.js +90 -0
- package/packages/dd-trace/src/appsec/standalone.js +130 -0
- package/packages/dd-trace/src/appsec/telemetry.js +33 -1
- package/packages/dd-trace/src/appsec/waf/index.js +2 -2
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +3 -3
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
- package/packages/dd-trace/src/config.js +136 -63
- package/packages/dd-trace/src/constants.js +3 -1
- package/packages/dd-trace/src/datastreams/processor.js +3 -2
- package/packages/dd-trace/src/exporters/agent/index.js +2 -2
- package/packages/dd-trace/src/format.js +1 -0
- package/packages/dd-trace/src/opentelemetry/span.js +1 -1
- package/packages/dd-trace/src/opentelemetry/tracer.js +6 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +12 -0
- package/packages/dd-trace/src/opentracing/span.js +4 -1
- package/packages/dd-trace/src/opentracing/tracer.js +2 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +7 -0
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/test.js +5 -1
- package/packages/dd-trace/src/priority_sampler.js +2 -5
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/proxy.js +3 -1
- package/packages/dd-trace/src/rate_limiter.js +2 -2
- package/packages/dd-trace/src/span_stats.js +4 -3
- package/packages/dd-trace/src/telemetry/init-telemetry.js +75 -0
- package/packages/dd-trace/src/tracer.js +2 -2
- package/packages/dd-trace/src/util.js +6 -1
- package/packages/datadog-core/src/storage/async_hooks.js +0 -49
|
@@ -2,7 +2,7 @@ const semver = require('semver')
|
|
|
2
2
|
|
|
3
3
|
const { addHook, channel, AsyncResource } = require('./helpers/instrument')
|
|
4
4
|
const shimmer = require('../../datadog-shimmer')
|
|
5
|
-
const { parseAnnotations, getTestSuitePath } = require('../../dd-trace/src/plugins/util/test')
|
|
5
|
+
const { parseAnnotations, getTestSuitePath, NUM_FAILED_TEST_RETRIES } = require('../../dd-trace/src/plugins/util/test')
|
|
6
6
|
const log = require('../../dd-trace/src/log')
|
|
7
7
|
|
|
8
8
|
const testStartCh = channel('ci:playwright:test:start')
|
|
@@ -21,6 +21,7 @@ const testToAr = new WeakMap()
|
|
|
21
21
|
const testSuiteToAr = new Map()
|
|
22
22
|
const testSuiteToTestStatuses = new Map()
|
|
23
23
|
const testSuiteToErrors = new Map()
|
|
24
|
+
const testSessionAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
24
25
|
|
|
25
26
|
let applyRepeatEachIndex = null
|
|
26
27
|
|
|
@@ -35,6 +36,7 @@ const STATUS_TO_TEST_STATUS = {
|
|
|
35
36
|
|
|
36
37
|
let remainingTestsByFile = {}
|
|
37
38
|
let isEarlyFlakeDetectionEnabled = false
|
|
39
|
+
let isFlakyTestRetriesEnabled = false
|
|
38
40
|
let earlyFlakeDetectionNumRetries = 0
|
|
39
41
|
let knownTests = {}
|
|
40
42
|
let rootDir = ''
|
|
@@ -196,6 +198,31 @@ function getTestSuiteError (testSuiteAbsolutePath) {
|
|
|
196
198
|
return new Error(`${errors.length} errors in this test suite:\n${errors.map(e => e.message).join('\n------\n')}`)
|
|
197
199
|
}
|
|
198
200
|
|
|
201
|
+
function getTestByTestId (dispatcher, testId) {
|
|
202
|
+
if (dispatcher._testById) {
|
|
203
|
+
return dispatcher._testById.get(testId)?.test
|
|
204
|
+
}
|
|
205
|
+
const allTests = dispatcher._allTests || dispatcher._ddAllTests
|
|
206
|
+
if (allTests) {
|
|
207
|
+
return allTests.find(({ id }) => id === testId)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function getChannelPromise (channelToPublishTo) {
|
|
212
|
+
return new Promise(resolve => {
|
|
213
|
+
testSessionAsyncResource.runInAsyncScope(() => {
|
|
214
|
+
channelToPublishTo.publish({ onDone: resolve })
|
|
215
|
+
})
|
|
216
|
+
})
|
|
217
|
+
}
|
|
218
|
+
// eslint-disable-next-line
|
|
219
|
+
// Inspired by https://github.com/microsoft/playwright/blob/2b77ed4d7aafa85a600caa0b0d101b72c8437eeb/packages/playwright/src/reporters/base.ts#L293
|
|
220
|
+
// We can't use test.outcome() directly because it's set on follow up handlers:
|
|
221
|
+
// our `testEndHandler` is called before the outcome is set.
|
|
222
|
+
function testWillRetry (test, testStatus) {
|
|
223
|
+
return testStatus === 'fail' && test.results.length <= test.retries
|
|
224
|
+
}
|
|
225
|
+
|
|
199
226
|
function testBeginHandler (test, browserName) {
|
|
200
227
|
const {
|
|
201
228
|
_requireFile: testSuiteAbsolutePath,
|
|
@@ -249,7 +276,8 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout) {
|
|
|
249
276
|
testAsyncResource.runInAsyncScope(() => {
|
|
250
277
|
testFinishCh.publish({
|
|
251
278
|
testStatus,
|
|
252
|
-
steps: testResult
|
|
279
|
+
steps: testResult?.steps || [],
|
|
280
|
+
isRetry: testResult?.retry > 0,
|
|
253
281
|
error,
|
|
254
282
|
extraTags: annotationTags,
|
|
255
283
|
isNew: test._ddIsNew,
|
|
@@ -267,8 +295,10 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout) {
|
|
|
267
295
|
addErrorToTestSuite(testSuiteAbsolutePath, error)
|
|
268
296
|
}
|
|
269
297
|
|
|
270
|
-
|
|
271
|
-
|
|
298
|
+
if (!testWillRetry(test, testStatus)) {
|
|
299
|
+
remainingTestsByFile[testSuiteAbsolutePath] = remainingTestsByFile[testSuiteAbsolutePath]
|
|
300
|
+
.filter(currentTest => currentTest !== test)
|
|
301
|
+
}
|
|
272
302
|
|
|
273
303
|
// Last test, we finish the suite
|
|
274
304
|
if (!remainingTestsByFile[testSuiteAbsolutePath].length) {
|
|
@@ -335,16 +365,6 @@ function dispatcherHook (dispatcherExport) {
|
|
|
335
365
|
return dispatcherExport
|
|
336
366
|
}
|
|
337
367
|
|
|
338
|
-
function getTestByTestId (dispatcher, testId) {
|
|
339
|
-
if (dispatcher._testById) {
|
|
340
|
-
return dispatcher._testById.get(testId)?.test
|
|
341
|
-
}
|
|
342
|
-
const allTests = dispatcher._allTests || dispatcher._ddAllTests
|
|
343
|
-
if (allTests) {
|
|
344
|
-
return allTests.find(({ id }) => id === testId)
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
368
|
function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
349
369
|
shimmer.wrap(dispatcherExport.Dispatcher.prototype, 'run', runWrapper)
|
|
350
370
|
shimmer.wrap(dispatcherExport.Dispatcher.prototype, '_createWorker', createWorker => function () {
|
|
@@ -373,8 +393,6 @@ function runnerHook (runnerExport, playwrightVersion) {
|
|
|
373
393
|
shimmer.wrap(runnerExport.Runner.prototype, 'runAllTests', runAllTests => async function () {
|
|
374
394
|
let onDone
|
|
375
395
|
|
|
376
|
-
const testSessionAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
377
|
-
|
|
378
396
|
rootDir = getRootDir(this)
|
|
379
397
|
|
|
380
398
|
const processArgv = process.argv.slice(2).join(' ')
|
|
@@ -383,46 +401,42 @@ function runnerHook (runnerExport, playwrightVersion) {
|
|
|
383
401
|
testSessionStartCh.publish({ command, frameworkVersion: playwrightVersion, rootDir })
|
|
384
402
|
})
|
|
385
403
|
|
|
386
|
-
const configurationPromise = new Promise((resolve) => {
|
|
387
|
-
onDone = resolve
|
|
388
|
-
})
|
|
389
|
-
|
|
390
|
-
testSessionAsyncResource.runInAsyncScope(() => {
|
|
391
|
-
libraryConfigurationCh.publish({ onDone })
|
|
392
|
-
})
|
|
393
|
-
|
|
394
404
|
try {
|
|
395
|
-
const { err, libraryConfig } = await
|
|
405
|
+
const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh)
|
|
396
406
|
if (!err) {
|
|
397
407
|
isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
|
|
398
408
|
earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
|
|
409
|
+
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
|
|
399
410
|
}
|
|
400
411
|
} catch (e) {
|
|
412
|
+
isEarlyFlakeDetectionEnabled = false
|
|
401
413
|
log.error(e)
|
|
402
414
|
}
|
|
403
415
|
|
|
404
416
|
if (isEarlyFlakeDetectionEnabled && semver.gte(playwrightVersion, MINIMUM_SUPPORTED_VERSION_EFD)) {
|
|
405
|
-
const knownTestsPromise = new Promise((resolve) => {
|
|
406
|
-
onDone = resolve
|
|
407
|
-
})
|
|
408
|
-
testSessionAsyncResource.runInAsyncScope(() => {
|
|
409
|
-
knownTestsCh.publish({ onDone })
|
|
410
|
-
})
|
|
411
|
-
|
|
412
417
|
try {
|
|
413
|
-
const { err, knownTests: receivedKnownTests } = await
|
|
418
|
+
const { err, knownTests: receivedKnownTests } = await getChannelPromise(knownTestsCh)
|
|
414
419
|
if (!err) {
|
|
415
420
|
knownTests = receivedKnownTests
|
|
416
421
|
} else {
|
|
417
422
|
isEarlyFlakeDetectionEnabled = false
|
|
418
423
|
}
|
|
419
424
|
} catch (err) {
|
|
425
|
+
isEarlyFlakeDetectionEnabled = false
|
|
420
426
|
log.error(err)
|
|
421
427
|
}
|
|
422
428
|
}
|
|
423
429
|
|
|
424
430
|
const projects = getProjectsFromRunner(this)
|
|
425
431
|
|
|
432
|
+
if (isFlakyTestRetriesEnabled) {
|
|
433
|
+
projects.forEach(project => {
|
|
434
|
+
if (project.retries === 0) { // Only if it hasn't been set by the user
|
|
435
|
+
project.retries = NUM_FAILED_TEST_RETRIES
|
|
436
|
+
}
|
|
437
|
+
})
|
|
438
|
+
}
|
|
439
|
+
|
|
426
440
|
const runAllTestsReturn = await runAllTests.apply(this, arguments)
|
|
427
441
|
|
|
428
442
|
Object.values(remainingTestsByFile).forEach(tests => {
|
|
@@ -51,7 +51,7 @@ function wrapFn (fn) {
|
|
|
51
51
|
|
|
52
52
|
try {
|
|
53
53
|
const result = fn.apply(this, arguments)
|
|
54
|
-
if (result && typeof result === 'object' && typeof result.then === 'function') {
|
|
54
|
+
if (result !== null && typeof result === 'object' && typeof result.then === 'function') {
|
|
55
55
|
return result.then(function () {
|
|
56
56
|
nextChannel.publish({ req })
|
|
57
57
|
finishChannel.publish({ req })
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
const { addHook, channel, AsyncResource } = require('./helpers/instrument')
|
|
2
|
+
const shimmer = require('../../datadog-shimmer')
|
|
3
|
+
const { NUM_FAILED_TEST_RETRIES } = require('../../dd-trace/src/plugins/util/test')
|
|
4
|
+
|
|
5
|
+
// test hooks
|
|
6
|
+
const testStartCh = channel('ci:vitest:test:start')
|
|
7
|
+
const testFinishTimeCh = channel('ci:vitest:test:finish-time')
|
|
8
|
+
const testPassCh = channel('ci:vitest:test:pass')
|
|
9
|
+
const testErrorCh = channel('ci:vitest:test:error')
|
|
10
|
+
const testSkipCh = channel('ci:vitest:test:skip')
|
|
11
|
+
|
|
12
|
+
// test suite hooks
|
|
13
|
+
const testSuiteStartCh = channel('ci:vitest:test-suite:start')
|
|
14
|
+
const testSuiteFinishCh = channel('ci:vitest:test-suite:finish')
|
|
15
|
+
const testSuiteErrorCh = channel('ci:vitest:test-suite:error')
|
|
16
|
+
|
|
17
|
+
// test session hooks
|
|
18
|
+
const testSessionStartCh = channel('ci:vitest:session:start')
|
|
19
|
+
const testSessionFinishCh = channel('ci:vitest:session:finish')
|
|
20
|
+
const libraryConfigurationCh = channel('ci:vitest:library-configuration')
|
|
21
|
+
|
|
22
|
+
const taskToAsync = new WeakMap()
|
|
23
|
+
|
|
24
|
+
const sessionAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
25
|
+
|
|
26
|
+
function isReporterPackage (vitestPackage) {
|
|
27
|
+
return vitestPackage.B?.name === 'BaseSequencer'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// from 2.0.0
|
|
31
|
+
function isReporterPackageNew (vitestPackage) {
|
|
32
|
+
return vitestPackage.e?.name === 'BaseSequencer'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getChannelPromise (channelToPublishTo) {
|
|
36
|
+
return new Promise(resolve => {
|
|
37
|
+
sessionAsyncResource.runInAsyncScope(() => {
|
|
38
|
+
channelToPublishTo.publish({ onDone: resolve })
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getSessionStatus (state) {
|
|
44
|
+
if (state.getCountOfFailedTests() > 0) {
|
|
45
|
+
return 'fail'
|
|
46
|
+
}
|
|
47
|
+
if (state.pathsSet.size === 0) {
|
|
48
|
+
return 'skip'
|
|
49
|
+
}
|
|
50
|
+
return 'pass'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// eslint-disable-next-line
|
|
54
|
+
// From https://github.com/vitest-dev/vitest/blob/51c04e2f44d91322b334f8ccbcdb368facc3f8ec/packages/runner/src/run.ts#L243-L250
|
|
55
|
+
function getVitestTestStatus (test, retryCount) {
|
|
56
|
+
if (test.result.state !== 'fail') {
|
|
57
|
+
if (!test.repeats) {
|
|
58
|
+
return 'pass'
|
|
59
|
+
} else if (test.repeats && (test.retry ?? 0) === retryCount) {
|
|
60
|
+
return 'pass'
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return 'fail'
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getTypeTasks (fileTasks, type = 'test') {
|
|
67
|
+
const typeTasks = []
|
|
68
|
+
|
|
69
|
+
function getTasks (tasks) {
|
|
70
|
+
for (const task of tasks) {
|
|
71
|
+
if (task.type === type) {
|
|
72
|
+
typeTasks.push(task)
|
|
73
|
+
} else if (task.tasks) {
|
|
74
|
+
getTasks(task.tasks)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
getTasks(fileTasks)
|
|
80
|
+
|
|
81
|
+
return typeTasks
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function getTestName (task) {
|
|
85
|
+
let testName = task.name
|
|
86
|
+
let currentTask = task.suite
|
|
87
|
+
|
|
88
|
+
while (currentTask) {
|
|
89
|
+
if (currentTask.name) {
|
|
90
|
+
testName = `${currentTask.name} ${testName}`
|
|
91
|
+
}
|
|
92
|
+
currentTask = currentTask.suite
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return testName
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function getSortWrapper (sort) {
|
|
99
|
+
return async function () {
|
|
100
|
+
if (!testSessionFinishCh.hasSubscribers) {
|
|
101
|
+
return sort.apply(this, arguments)
|
|
102
|
+
}
|
|
103
|
+
// There isn't any other async function that we seem to be able to hook into
|
|
104
|
+
// So we will use the sort from BaseSequencer. This means that a custom sequencer
|
|
105
|
+
// will not work. This will be a known limitation.
|
|
106
|
+
let isFlakyTestRetriesEnabled = false
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh)
|
|
110
|
+
if (!err) {
|
|
111
|
+
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
|
|
112
|
+
}
|
|
113
|
+
} catch (e) {
|
|
114
|
+
isFlakyTestRetriesEnabled = false
|
|
115
|
+
}
|
|
116
|
+
if (isFlakyTestRetriesEnabled && !this.ctx.config.retry) {
|
|
117
|
+
this.ctx.config.retry = NUM_FAILED_TEST_RETRIES
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
shimmer.wrap(this.ctx, 'exit', exit => async function () {
|
|
121
|
+
let onFinish
|
|
122
|
+
|
|
123
|
+
const flushPromise = new Promise(resolve => {
|
|
124
|
+
onFinish = resolve
|
|
125
|
+
})
|
|
126
|
+
const failedSuites = this.state.getFailedFilepaths()
|
|
127
|
+
let error
|
|
128
|
+
if (failedSuites.length) {
|
|
129
|
+
error = new Error(`Test suites failed: ${failedSuites.length}.`)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
sessionAsyncResource.runInAsyncScope(() => {
|
|
133
|
+
testSessionFinishCh.publish({
|
|
134
|
+
status: getSessionStatus(this.state),
|
|
135
|
+
onFinish,
|
|
136
|
+
error
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
await flushPromise
|
|
141
|
+
|
|
142
|
+
return exit.apply(this, arguments)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
return sort.apply(this, arguments)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
addHook({
|
|
150
|
+
name: 'vitest',
|
|
151
|
+
versions: ['>=1.6.0'],
|
|
152
|
+
file: 'dist/runners.js'
|
|
153
|
+
}, (vitestPackage) => {
|
|
154
|
+
const { VitestTestRunner } = vitestPackage
|
|
155
|
+
// test start (only tests that are not marked as skip or todo)
|
|
156
|
+
shimmer.wrap(VitestTestRunner.prototype, 'onBeforeTryTask', onBeforeTryTask => async function (task, retryInfo) {
|
|
157
|
+
if (!testStartCh.hasSubscribers) {
|
|
158
|
+
return onBeforeTryTask.apply(this, arguments)
|
|
159
|
+
}
|
|
160
|
+
const { retry: numAttempt } = retryInfo
|
|
161
|
+
// We finish the previous test here because we know it has failed already
|
|
162
|
+
if (numAttempt > 0) {
|
|
163
|
+
const asyncResource = taskToAsync.get(task)
|
|
164
|
+
const testError = task.result?.errors?.[0]
|
|
165
|
+
if (asyncResource) {
|
|
166
|
+
asyncResource.runInAsyncScope(() => {
|
|
167
|
+
testErrorCh.publish({ error: testError })
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
173
|
+
taskToAsync.set(task, asyncResource)
|
|
174
|
+
|
|
175
|
+
asyncResource.runInAsyncScope(() => {
|
|
176
|
+
testStartCh.publish({
|
|
177
|
+
testName: getTestName(task),
|
|
178
|
+
testSuiteAbsolutePath: task.file.filepath,
|
|
179
|
+
isRetry: numAttempt > 0
|
|
180
|
+
})
|
|
181
|
+
})
|
|
182
|
+
return onBeforeTryTask.apply(this, arguments)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
// test finish (only passed tests)
|
|
186
|
+
shimmer.wrap(VitestTestRunner.prototype, 'onAfterTryTask', onAfterTryTask =>
|
|
187
|
+
async function (task, { retry: retryCount }) {
|
|
188
|
+
if (!testFinishTimeCh.hasSubscribers) {
|
|
189
|
+
return onAfterTryTask.apply(this, arguments)
|
|
190
|
+
}
|
|
191
|
+
const result = await onAfterTryTask.apply(this, arguments)
|
|
192
|
+
|
|
193
|
+
const status = getVitestTestStatus(task, retryCount)
|
|
194
|
+
const asyncResource = taskToAsync.get(task)
|
|
195
|
+
|
|
196
|
+
if (asyncResource) {
|
|
197
|
+
// We don't finish here because the test might fail in a later hook
|
|
198
|
+
asyncResource.runInAsyncScope(() => {
|
|
199
|
+
testFinishTimeCh.publish({ status, task })
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return result
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
return vitestPackage
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
addHook({
|
|
210
|
+
name: 'vitest',
|
|
211
|
+
versions: ['>=2.0.0'],
|
|
212
|
+
filePattern: 'dist/vendor/index.*'
|
|
213
|
+
}, (vitestPackage) => {
|
|
214
|
+
// there are multiple index* files so we have to check the exported values
|
|
215
|
+
if (isReporterPackageNew(vitestPackage)) {
|
|
216
|
+
shimmer.wrap(vitestPackage.e.prototype, 'sort', getSortWrapper)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return vitestPackage
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
addHook({
|
|
223
|
+
name: 'vitest',
|
|
224
|
+
versions: ['>=1.6.0'],
|
|
225
|
+
filePattern: 'dist/vendor/index.*'
|
|
226
|
+
}, (vitestPackage) => {
|
|
227
|
+
// there are multiple index* files so we have to check the exported values
|
|
228
|
+
if (isReporterPackage(vitestPackage)) {
|
|
229
|
+
shimmer.wrap(vitestPackage.B.prototype, 'sort', getSortWrapper)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return vitestPackage
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
// Can't specify file because compiled vitest includes hashes in their files
|
|
236
|
+
addHook({
|
|
237
|
+
name: 'vitest',
|
|
238
|
+
versions: ['>=1.6.0'],
|
|
239
|
+
filePattern: 'dist/vendor/cac.*'
|
|
240
|
+
}, (vitestPackage, frameworkVersion) => {
|
|
241
|
+
shimmer.wrap(vitestPackage, 'c', oldCreateCli => function () {
|
|
242
|
+
if (!testSessionStartCh.hasSubscribers) {
|
|
243
|
+
return oldCreateCli.apply(this, arguments)
|
|
244
|
+
}
|
|
245
|
+
sessionAsyncResource.runInAsyncScope(() => {
|
|
246
|
+
const processArgv = process.argv.slice(2).join(' ')
|
|
247
|
+
testSessionStartCh.publish({ command: `vitest ${processArgv}`, frameworkVersion })
|
|
248
|
+
})
|
|
249
|
+
return oldCreateCli.apply(this, arguments)
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
return vitestPackage
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
// test suite start and finish
|
|
256
|
+
// only relevant for workers
|
|
257
|
+
addHook({
|
|
258
|
+
name: '@vitest/runner',
|
|
259
|
+
versions: ['>=1.6.0'],
|
|
260
|
+
file: 'dist/index.js'
|
|
261
|
+
}, vitestPackage => {
|
|
262
|
+
shimmer.wrap(vitestPackage, 'startTests', startTests => async function (testPath) {
|
|
263
|
+
let testSuiteError = null
|
|
264
|
+
if (!testSuiteStartCh.hasSubscribers) {
|
|
265
|
+
return startTests.apply(this, arguments)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const testSuiteAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
269
|
+
testSuiteAsyncResource.runInAsyncScope(() => {
|
|
270
|
+
testSuiteStartCh.publish(testPath[0])
|
|
271
|
+
})
|
|
272
|
+
const startTestsResponse = await startTests.apply(this, arguments)
|
|
273
|
+
|
|
274
|
+
let onFinish = null
|
|
275
|
+
const onFinishPromise = new Promise(resolve => {
|
|
276
|
+
onFinish = resolve
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
const testTasks = getTypeTasks(startTestsResponse[0].tasks)
|
|
280
|
+
|
|
281
|
+
// Only one test task per test, even if there are retries
|
|
282
|
+
testTasks.forEach(task => {
|
|
283
|
+
const testAsyncResource = taskToAsync.get(task)
|
|
284
|
+
const { result } = task
|
|
285
|
+
|
|
286
|
+
if (result) {
|
|
287
|
+
const { state, duration, errors } = result
|
|
288
|
+
if (state === 'skip') { // programmatic skip
|
|
289
|
+
testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.file.filepath })
|
|
290
|
+
} else if (state === 'pass') {
|
|
291
|
+
if (testAsyncResource) {
|
|
292
|
+
testAsyncResource.runInAsyncScope(() => {
|
|
293
|
+
testPassCh.publish({ task })
|
|
294
|
+
})
|
|
295
|
+
}
|
|
296
|
+
} else if (state === 'fail') {
|
|
297
|
+
// If it's failing, we have no accurate finish time, so we have to use `duration`
|
|
298
|
+
let testError
|
|
299
|
+
|
|
300
|
+
if (errors?.length) {
|
|
301
|
+
testError = errors[0]
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (testAsyncResource) {
|
|
305
|
+
const isRetry = task.result?.retryCount > 0
|
|
306
|
+
// `duration` is the duration of all the retries, so it can't be used if there are retries
|
|
307
|
+
testAsyncResource.runInAsyncScope(() => {
|
|
308
|
+
testErrorCh.publish({ duration: !isRetry ? duration : undefined, error: testError })
|
|
309
|
+
})
|
|
310
|
+
}
|
|
311
|
+
if (errors?.length) {
|
|
312
|
+
testSuiteError = testError // we store the error to bubble it up to the suite
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
} else { // test.skip or test.todo
|
|
316
|
+
testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.file.filepath })
|
|
317
|
+
}
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
const testSuiteResult = startTestsResponse[0].result
|
|
321
|
+
|
|
322
|
+
if (testSuiteResult.errors?.length) { // Errors from root level hooks
|
|
323
|
+
testSuiteError = testSuiteResult.errors[0]
|
|
324
|
+
} else if (testSuiteResult.state === 'fail') { // Errors from `describe` level hooks
|
|
325
|
+
const suiteTasks = getTypeTasks(startTestsResponse[0].tasks, 'suite')
|
|
326
|
+
const failedSuites = suiteTasks.filter(task => task.result?.state === 'fail')
|
|
327
|
+
if (failedSuites.length && failedSuites[0].result?.errors?.length) {
|
|
328
|
+
testSuiteError = failedSuites[0].result.errors[0]
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (testSuiteError) {
|
|
333
|
+
testSuiteAsyncResource.runInAsyncScope(() => {
|
|
334
|
+
testSuiteErrorCh.publish({ error: testSuiteError })
|
|
335
|
+
})
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
testSuiteAsyncResource.runInAsyncScope(() => {
|
|
339
|
+
testSuiteFinishCh.publish({ status: testSuiteResult.state, onFinish })
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
// TODO: fix too frequent flushes
|
|
343
|
+
await onFinishPromise
|
|
344
|
+
|
|
345
|
+
return startTestsResponse
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
return vitestPackage
|
|
349
|
+
})
|
|
@@ -64,11 +64,17 @@ class BaseAwsSdkPlugin extends ClientPlugin {
|
|
|
64
64
|
span.setTag('region', region)
|
|
65
65
|
})
|
|
66
66
|
|
|
67
|
-
this.addSub(`apm:aws:request:complete:${this.serviceIdentifier}`, ({ response }) => {
|
|
67
|
+
this.addSub(`apm:aws:request:complete:${this.serviceIdentifier}`, ({ response, cbExists = false }) => {
|
|
68
68
|
const store = storage.getStore()
|
|
69
69
|
if (!store) return
|
|
70
70
|
const { span } = store
|
|
71
71
|
if (!span) return
|
|
72
|
+
// try to extract DSM context from response if no callback exists as extraction normally happens in CB
|
|
73
|
+
if (!cbExists && this.serviceIdentifier === 'sqs') {
|
|
74
|
+
const params = response.request.params
|
|
75
|
+
const operation = response.request.operation
|
|
76
|
+
this.responseExtractDSMContext(operation, params, response.data ?? response, span)
|
|
77
|
+
}
|
|
72
78
|
this.addResponseTags(span, response)
|
|
73
79
|
this.finish(span, response, response.error)
|
|
74
80
|
})
|
|
@@ -159,6 +165,7 @@ function normalizeConfig (config, serviceIdentifier) {
|
|
|
159
165
|
|
|
160
166
|
return Object.assign({}, config, specificConfig, {
|
|
161
167
|
splitByAwsService: config.splitByAwsService !== false,
|
|
168
|
+
batchPropagationEnabled: config.batchPropagationEnabled !== false,
|
|
162
169
|
hooks
|
|
163
170
|
})
|
|
164
171
|
}
|
|
@@ -21,7 +21,7 @@ class DynamoDb extends BaseAwsSdkPlugin {
|
|
|
21
21
|
// batch operations have different format, collect table name for batch
|
|
22
22
|
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#batchGetItem-property`
|
|
23
23
|
// dynamoDB batch TableName
|
|
24
|
-
if (params.RequestItems) {
|
|
24
|
+
if (params.RequestItems !== null) {
|
|
25
25
|
if (typeof params.RequestItems === 'object') {
|
|
26
26
|
if (Object.keys(params.RequestItems).length === 1) {
|
|
27
27
|
const tableName = Object.keys(params.RequestItems)[0]
|
|
@@ -52,7 +52,7 @@ class Kinesis extends BaseAwsSdkPlugin {
|
|
|
52
52
|
|
|
53
53
|
// extract DSM context after as we might not have a parent-child but may have a DSM context
|
|
54
54
|
this.responseExtractDSMContext(
|
|
55
|
-
request.operation, response, span || null, streamName
|
|
55
|
+
request.operation, request.params, response, span || null, { streamName }
|
|
56
56
|
)
|
|
57
57
|
}
|
|
58
58
|
})
|
|
@@ -100,7 +100,8 @@ class Kinesis extends BaseAwsSdkPlugin {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
responseExtractDSMContext (operation, response, span,
|
|
103
|
+
responseExtractDSMContext (operation, params, response, span, kwargs = {}) {
|
|
104
|
+
const { streamName } = kwargs
|
|
104
105
|
if (!this.config.dsmEnabled) return
|
|
105
106
|
if (operation !== 'getRecords') return
|
|
106
107
|
if (!response || !response.Records || !response.Records[0]) return
|
|
@@ -151,7 +152,12 @@ class Kinesis extends BaseAwsSdkPlugin {
|
|
|
151
152
|
case 'putRecords':
|
|
152
153
|
stream = params.StreamArn ? params.StreamArn : (params.StreamName ? params.StreamName : '')
|
|
153
154
|
for (let i = 0; i < params.Records.length; i++) {
|
|
154
|
-
this.injectToMessage(
|
|
155
|
+
this.injectToMessage(
|
|
156
|
+
span,
|
|
157
|
+
params.Records[i],
|
|
158
|
+
stream,
|
|
159
|
+
i === 0 || (this.config.kinesis && this.config.kinesis.batchPropagationEnabled)
|
|
160
|
+
)
|
|
155
161
|
}
|
|
156
162
|
}
|
|
157
163
|
}
|
|
@@ -59,7 +59,12 @@ class Sns extends BaseAwsSdkPlugin {
|
|
|
59
59
|
break
|
|
60
60
|
case 'publishBatch':
|
|
61
61
|
for (let i = 0; i < params.PublishBatchRequestEntries.length; i++) {
|
|
62
|
-
this.injectToMessage(
|
|
62
|
+
this.injectToMessage(
|
|
63
|
+
span,
|
|
64
|
+
params.PublishBatchRequestEntries[i],
|
|
65
|
+
params.TopicArn,
|
|
66
|
+
i === 0 || (this.config.sns && this.config.sns.batchPropagationEnabled)
|
|
67
|
+
)
|
|
63
68
|
}
|
|
64
69
|
break
|
|
65
70
|
}
|
|
@@ -23,7 +23,7 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
23
23
|
const plugin = this
|
|
24
24
|
const contextExtraction = this.responseExtract(request.params, request.operation, response)
|
|
25
25
|
let span
|
|
26
|
-
let parsedMessageAttributes
|
|
26
|
+
let parsedMessageAttributes = null
|
|
27
27
|
if (contextExtraction && contextExtraction.datadogContext) {
|
|
28
28
|
obj.needsFinish = true
|
|
29
29
|
const options = {
|
|
@@ -39,8 +39,9 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
39
39
|
this.enter(span, store)
|
|
40
40
|
}
|
|
41
41
|
// extract DSM context after as we might not have a parent-child but may have a DSM context
|
|
42
|
+
|
|
42
43
|
this.responseExtractDSMContext(
|
|
43
|
-
request.operation, request.params, response, span || null, parsedMessageAttributes
|
|
44
|
+
request.operation, request.params, response, span || null, { parsedMessageAttributes }
|
|
44
45
|
)
|
|
45
46
|
})
|
|
46
47
|
|
|
@@ -165,7 +166,8 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
168
|
|
|
168
|
-
responseExtractDSMContext (operation, params, response, span,
|
|
169
|
+
responseExtractDSMContext (operation, params, response, span, kwargs = {}) {
|
|
170
|
+
let { parsedAttributes } = kwargs
|
|
169
171
|
if (!this.config.dsmEnabled) return
|
|
170
172
|
if (operation !== 'receiveMessage') return
|
|
171
173
|
if (!response || !response.Messages || !response.Messages[0]) return
|
|
@@ -188,7 +190,7 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
188
190
|
// SQS to SQS
|
|
189
191
|
}
|
|
190
192
|
}
|
|
191
|
-
if (message.MessageAttributes && message.MessageAttributes._datadog) {
|
|
193
|
+
if (!parsedAttributes && message.MessageAttributes && message.MessageAttributes._datadog) {
|
|
192
194
|
parsedAttributes = this.parseDatadogAttributes(message.MessageAttributes._datadog)
|
|
193
195
|
}
|
|
194
196
|
}
|
|
@@ -216,7 +218,23 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
216
218
|
break
|
|
217
219
|
case 'sendMessageBatch':
|
|
218
220
|
for (let i = 0; i < params.Entries.length; i++) {
|
|
219
|
-
this.injectToMessage(
|
|
221
|
+
this.injectToMessage(
|
|
222
|
+
span,
|
|
223
|
+
params.Entries[i],
|
|
224
|
+
params.QueueUrl,
|
|
225
|
+
i === 0 || (this.config.sqs && this.config.sqs.batchPropagationEnabled)
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
break
|
|
229
|
+
case 'receiveMessage':
|
|
230
|
+
if (!params.MessageAttributeNames) {
|
|
231
|
+
params.MessageAttributeNames = ['_datadog']
|
|
232
|
+
} else if (
|
|
233
|
+
!params.MessageAttributeNames.includes('_datadog') &&
|
|
234
|
+
!params.MessageAttributeNames.includes('.*') &&
|
|
235
|
+
!params.MessageAttributeNames.includes('All')
|
|
236
|
+
) {
|
|
237
|
+
params.MessageAttributeNames.push('_datadog')
|
|
220
238
|
}
|
|
221
239
|
break
|
|
222
240
|
}
|
|
@@ -47,7 +47,7 @@ class Stepfunctions extends BaseAwsSdkPlugin {
|
|
|
47
47
|
|
|
48
48
|
try {
|
|
49
49
|
const inputObj = JSON.parse(input)
|
|
50
|
-
if (inputObj && typeof inputObj === 'object') {
|
|
50
|
+
if (inputObj !== null && typeof inputObj === 'object') {
|
|
51
51
|
// We've parsed the input JSON string
|
|
52
52
|
inputObj._datadog = {}
|
|
53
53
|
this.tracer.inject(span, 'text_map', inputObj._datadog)
|
|
@@ -54,7 +54,7 @@ class ChildProcessPlugin extends TracingPlugin {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
this.startSpan('command_execution', {
|
|
57
|
-
service: this.config.service,
|
|
57
|
+
service: this.config.service || this._tracerConfig.service,
|
|
58
58
|
resource: (shell === true) ? 'sh' : cmdFields[0],
|
|
59
59
|
type: 'system',
|
|
60
60
|
meta
|