dd-trace 5.85.0 → 5.86.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/index.d.ts +20 -1
- package/package.json +1 -1
- package/packages/datadog-core/src/storage.js +30 -12
- package/packages/datadog-instrumentations/src/jest.js +34 -9
- package/packages/datadog-instrumentations/src/mocha/main.js +9 -0
- package/packages/datadog-instrumentations/src/prisma.js +225 -30
- package/packages/datadog-instrumentations/src/ws.js +22 -0
- package/packages/datadog-plugin-cucumber/src/index.js +4 -10
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +5 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -4
- package/packages/datadog-plugin-http/src/server.js +23 -8
- package/packages/datadog-plugin-jest/src/index.js +29 -10
- package/packages/datadog-plugin-jest/src/util.js +7 -1
- package/packages/datadog-plugin-mocha/src/index.js +5 -17
- package/packages/datadog-plugin-playwright/src/index.js +3 -0
- package/packages/datadog-plugin-prisma/src/datadog-tracing-helper.js +37 -14
- package/packages/datadog-plugin-prisma/src/index.js +8 -5
- package/packages/datadog-plugin-router/src/index.js +28 -19
- package/packages/datadog-plugin-vitest/src/index.js +6 -10
- package/packages/datadog-plugin-ws/src/server.js +8 -0
- package/packages/dd-trace/src/appsec/iast/path-line.js +1 -0
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +19 -0
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +15 -0
- package/packages/dd-trace/src/ci-visibility/telemetry.js +36 -0
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +44 -1
- package/packages/dd-trace/src/exporters/common/request.js +35 -35
- package/packages/dd-trace/src/id.js +1 -1
- package/packages/dd-trace/src/lambda/context.js +27 -0
- package/packages/dd-trace/src/lambda/handler.js +5 -18
- package/packages/dd-trace/src/log/writer.js +1 -5
- package/packages/dd-trace/src/plugins/ci_plugin.js +63 -1
- package/packages/dd-trace/src/plugins/util/git.js +27 -30
- package/packages/dd-trace/src/plugins/util/test.js +3 -1
- package/packages/dd-trace/src/plugins/util/web.js +1 -0
- package/packages/dd-trace/src/profiling/config.js +6 -14
- package/packages/dd-trace/src/profiling/exporters/agent.js +23 -24
- package/packages/dd-trace/src/profiling/profiler.js +2 -0
- package/packages/dd-trace/src/startup-log.js +1 -0
|
@@ -13,12 +13,11 @@ class HttpServerPlugin extends ServerPlugin {
|
|
|
13
13
|
|
|
14
14
|
constructor (...args) {
|
|
15
15
|
super(...args)
|
|
16
|
-
this._parentStore = undefined
|
|
17
16
|
this.addTraceSub('exit', message => this.exit(message))
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
start ({ req, res, abortController }) {
|
|
21
|
-
|
|
20
|
+
let store = storage('legacy').getStore()
|
|
22
21
|
const span = web.startSpan(
|
|
23
22
|
this.tracer,
|
|
24
23
|
{
|
|
@@ -32,11 +31,21 @@ class HttpServerPlugin extends ServerPlugin {
|
|
|
32
31
|
span.setTag(COMPONENT, this.constructor.id)
|
|
33
32
|
span._integrationName = this.constructor.id
|
|
34
33
|
|
|
35
|
-
this._parentStore = store
|
|
36
|
-
this.enter(span, { ...store, req, res })
|
|
37
|
-
|
|
38
34
|
const context = web.getContext(req)
|
|
39
35
|
|
|
36
|
+
if (context) {
|
|
37
|
+
context.parentStore = store
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Only AppSec needs the request scope to be active for any async work that
|
|
41
|
+
// may be scheduled after the synchronous `request` event returns (e.g.
|
|
42
|
+
// Fastify).
|
|
43
|
+
if (incomingHttpRequestStart.hasSubscribers) {
|
|
44
|
+
store = { ...store, req, res }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
this.enter(span, store)
|
|
48
|
+
|
|
40
49
|
if (!context.instrumented) {
|
|
41
50
|
context.res.writeHead = web.wrapWriteHead(context)
|
|
42
51
|
context.instrumented = true
|
|
@@ -64,9 +73,15 @@ class HttpServerPlugin extends ServerPlugin {
|
|
|
64
73
|
}
|
|
65
74
|
|
|
66
75
|
exit ({ req }) {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
76
|
+
const context = web.getContext(req)
|
|
77
|
+
const parentStore = context?.parentStore
|
|
78
|
+
|
|
79
|
+
const span = parentStore?.span
|
|
80
|
+
this.enter(span, parentStore)
|
|
81
|
+
|
|
82
|
+
if (context) {
|
|
83
|
+
context.parentStore = undefined
|
|
84
|
+
}
|
|
70
85
|
}
|
|
71
86
|
|
|
72
87
|
configure (config) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
|
|
4
4
|
const { storage } = require('../../datadog-core')
|
|
5
5
|
const { getEnvironmentVariable, getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
|
|
6
|
+
const { appClosing: appClosingTelemetry } = require('../../dd-trace/src/telemetry')
|
|
6
7
|
|
|
7
8
|
const {
|
|
8
9
|
TEST_STATUS,
|
|
@@ -25,8 +26,6 @@ const {
|
|
|
25
26
|
TEST_EARLY_FLAKE_ENABLED,
|
|
26
27
|
TEST_EARLY_FLAKE_ABORT_REASON,
|
|
27
28
|
JEST_DISPLAY_NAME,
|
|
28
|
-
TEST_IS_RUM_ACTIVE,
|
|
29
|
-
TEST_BROWSER_DRIVER,
|
|
30
29
|
getFormattedError,
|
|
31
30
|
TEST_RETRY_REASON,
|
|
32
31
|
TEST_MANAGEMENT_ENABLED,
|
|
@@ -157,7 +156,9 @@ class JestPlugin extends CiPlugin {
|
|
|
157
156
|
this.testModuleSpan.finish()
|
|
158
157
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
|
|
159
158
|
this.testSessionSpan.finish()
|
|
160
|
-
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session'
|
|
159
|
+
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session', {
|
|
160
|
+
hasFailedTestReplay: this.libraryConfig?.isDiEnabled || undefined,
|
|
161
|
+
})
|
|
161
162
|
finishAllTraceSpans(this.testSessionSpan)
|
|
162
163
|
|
|
163
164
|
this.telemetry.count(TELEMETRY_TEST_SESSION, {
|
|
@@ -165,6 +166,7 @@ class JestPlugin extends CiPlugin {
|
|
|
165
166
|
autoInjected: !!getValueFromEnvSources('DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER'),
|
|
166
167
|
})
|
|
167
168
|
|
|
169
|
+
appClosingTelemetry()
|
|
168
170
|
this.tracer._exporter.flush(() => {
|
|
169
171
|
if (onDone) {
|
|
170
172
|
onDone()
|
|
@@ -277,6 +279,23 @@ class JestPlugin extends CiPlugin {
|
|
|
277
279
|
}
|
|
278
280
|
})
|
|
279
281
|
|
|
282
|
+
this.addSub('ci:jest:worker-report:telemetry', data => {
|
|
283
|
+
const telemetryEvents = JSON.parse(data)
|
|
284
|
+
for (const event of telemetryEvents) {
|
|
285
|
+
if (event.type === 'ciVisEvent') {
|
|
286
|
+
this.telemetry.ciVisEvent(event.name, event.testLevel, {
|
|
287
|
+
...event.tags,
|
|
288
|
+
testFramework: event.testFramework,
|
|
289
|
+
isUnsupportedCIProvider: event.isUnsupportedCIProvider,
|
|
290
|
+
})
|
|
291
|
+
} else if (event.type === 'count') {
|
|
292
|
+
this.telemetry.count(event.name, event.tags, event.value)
|
|
293
|
+
} else if (event.type === 'distribution') {
|
|
294
|
+
this.telemetry.distribution(event.name, event.tags, event.measure)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
})
|
|
298
|
+
|
|
280
299
|
this.addSub('ci:jest:test-suite:finish', ({ status, errorMessage, error, testSuiteAbsolutePath }) => {
|
|
281
300
|
const testSuiteSpan = this.testSuiteSpanPerTestSuiteAbsolutePath.get(testSuiteAbsolutePath)
|
|
282
301
|
if (!testSuiteSpan) {
|
|
@@ -391,16 +410,10 @@ class JestPlugin extends CiPlugin {
|
|
|
391
410
|
span.setTag(TEST_RETRY_REASON, TEST_RETRY_REASON_TYPES.atr)
|
|
392
411
|
}
|
|
393
412
|
|
|
394
|
-
const spanTags = span.context()._tags
|
|
395
413
|
this.telemetry.ciVisEvent(
|
|
396
414
|
TELEMETRY_EVENT_FINISHED,
|
|
397
415
|
'test',
|
|
398
|
-
|
|
399
|
-
hasCodeOwners: !!spanTags[TEST_CODE_OWNERS],
|
|
400
|
-
isNew: spanTags[TEST_IS_NEW] === 'true',
|
|
401
|
-
isRum: spanTags[TEST_IS_RUM_ACTIVE] === 'true',
|
|
402
|
-
browserDriver: spanTags[TEST_BROWSER_DRIVER],
|
|
403
|
-
}
|
|
416
|
+
this.getTestTelemetryTags(span)
|
|
404
417
|
)
|
|
405
418
|
|
|
406
419
|
span.finish()
|
|
@@ -433,6 +446,12 @@ class JestPlugin extends CiPlugin {
|
|
|
433
446
|
span.setTag(TEST_MANAGEMENT_IS_DISABLED, 'true')
|
|
434
447
|
}
|
|
435
448
|
|
|
449
|
+
this.telemetry.ciVisEvent(
|
|
450
|
+
TELEMETRY_EVENT_FINISHED,
|
|
451
|
+
'test',
|
|
452
|
+
this.getTestTelemetryTags(span)
|
|
453
|
+
)
|
|
454
|
+
|
|
436
455
|
span.finish()
|
|
437
456
|
})
|
|
438
457
|
|
|
@@ -166,4 +166,10 @@ function getJestSuitesToRun (skippableSuites, originalTests, rootDir) {
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
module.exports = {
|
|
169
|
+
module.exports = {
|
|
170
|
+
SEED_SUFFIX_RE,
|
|
171
|
+
getFormattedJestTestParameters,
|
|
172
|
+
getJestTestName,
|
|
173
|
+
getJestSuitesToRun,
|
|
174
|
+
isMarkedAsUnskippable,
|
|
175
|
+
}
|
|
@@ -23,8 +23,6 @@ const {
|
|
|
23
23
|
TEST_EARLY_FLAKE_ENABLED,
|
|
24
24
|
TEST_EARLY_FLAKE_ABORT_REASON,
|
|
25
25
|
MOCHA_IS_PARALLEL,
|
|
26
|
-
TEST_IS_RUM_ACTIVE,
|
|
27
|
-
TEST_BROWSER_DRIVER,
|
|
28
26
|
TEST_RETRY_REASON,
|
|
29
27
|
TEST_MANAGEMENT_ENABLED,
|
|
30
28
|
TEST_MANAGEMENT_IS_QUARANTINED,
|
|
@@ -236,16 +234,10 @@ class MochaPlugin extends CiPlugin {
|
|
|
236
234
|
span.setTag(TEST_RETRY_REASON, TEST_RETRY_REASON_TYPES.atf)
|
|
237
235
|
}
|
|
238
236
|
|
|
239
|
-
const spanTags = span.context()._tags
|
|
240
237
|
this.telemetry.ciVisEvent(
|
|
241
238
|
TELEMETRY_EVENT_FINISHED,
|
|
242
239
|
'test',
|
|
243
|
-
|
|
244
|
-
hasCodeOwners: !!spanTags[TEST_CODE_OWNERS],
|
|
245
|
-
isNew: spanTags[TEST_IS_NEW] === 'true',
|
|
246
|
-
isRum: spanTags[TEST_IS_RUM_ACTIVE] === 'true',
|
|
247
|
-
browserDriver: spanTags[TEST_BROWSER_DRIVER],
|
|
248
|
-
}
|
|
240
|
+
this.getTestTelemetryTags(span)
|
|
249
241
|
)
|
|
250
242
|
|
|
251
243
|
span.finish()
|
|
@@ -310,16 +302,10 @@ class MochaPlugin extends CiPlugin {
|
|
|
310
302
|
span.setTag('error', err)
|
|
311
303
|
}
|
|
312
304
|
|
|
313
|
-
const spanTags = span.context()._tags
|
|
314
305
|
this.telemetry.ciVisEvent(
|
|
315
306
|
TELEMETRY_EVENT_FINISHED,
|
|
316
307
|
'test',
|
|
317
|
-
|
|
318
|
-
hasCodeOwners: !!spanTags[TEST_CODE_OWNERS],
|
|
319
|
-
isNew: spanTags[TEST_IS_NEW] === 'true',
|
|
320
|
-
isRum: spanTags[TEST_IS_RUM_ACTIVE] === 'true',
|
|
321
|
-
browserDriver: spanTags[TEST_BROWSER_DRIVER],
|
|
322
|
-
}
|
|
308
|
+
this.getTestTelemetryTags(span)
|
|
323
309
|
)
|
|
324
310
|
if (isFirstAttempt && willBeRetried && this.di && this.libraryConfig?.isDiEnabled) {
|
|
325
311
|
const probeInformation = this.addDiProbe(err)
|
|
@@ -402,7 +388,9 @@ class MochaPlugin extends CiPlugin {
|
|
|
402
388
|
this.testModuleSpan.finish()
|
|
403
389
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
|
|
404
390
|
this.testSessionSpan.finish()
|
|
405
|
-
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session'
|
|
391
|
+
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session', {
|
|
392
|
+
hasFailedTestReplay: this.libraryConfig?.isDiEnabled || undefined,
|
|
393
|
+
})
|
|
406
394
|
finishAllTraceSpans(this.testSessionSpan)
|
|
407
395
|
this.telemetry.count(TELEMETRY_TEST_SESSION, {
|
|
408
396
|
provider: this.ciProviderName,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const tracingChannel = require('dc-polyfill')
|
|
3
|
+
const { tracingChannel } = /** @type {import('node:diagnostics_channel')} */ (require('dc-polyfill'))
|
|
4
4
|
const clientCH = tracingChannel('apm:prisma')
|
|
5
5
|
const { storage } = require('../../datadog-core')
|
|
6
6
|
|
|
@@ -10,21 +10,12 @@ const allowedClientSpanOperations = new Set([
|
|
|
10
10
|
'transaction',
|
|
11
11
|
])
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* @typedef {object} DbConfig
|
|
15
|
-
* @property {string} [user]
|
|
16
|
-
* @property {string} [password]
|
|
17
|
-
* @property {string} [host]
|
|
18
|
-
* @property {string} [port]
|
|
19
|
-
* @property {string} [database]
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
13
|
class DatadogTracingHelper {
|
|
23
14
|
#prismaClient
|
|
24
15
|
#dbConfig
|
|
25
16
|
|
|
26
17
|
/**
|
|
27
|
-
* @param {DbConfig|undefined} dbConfig
|
|
18
|
+
* @param {import('../../datadog-instrumentations/src/prisma').DbConfig|undefined} dbConfig
|
|
28
19
|
* @param {import('./index')} prismaClient
|
|
29
20
|
*/
|
|
30
21
|
constructor (dbConfig, prismaClient) {
|
|
@@ -36,7 +27,12 @@ class DatadogTracingHelper {
|
|
|
36
27
|
return true
|
|
37
28
|
}
|
|
38
29
|
|
|
39
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Needs a sampled tracecontext to generate engine spans
|
|
32
|
+
*
|
|
33
|
+
* @param {object} [context]
|
|
34
|
+
* @returns {string}
|
|
35
|
+
*/
|
|
40
36
|
getTraceParent (context) {
|
|
41
37
|
const store = storage('legacy').getStore()
|
|
42
38
|
const span = store?.span
|
|
@@ -57,12 +53,31 @@ class DatadogTracingHelper {
|
|
|
57
53
|
return '00-00000000000000000000000000000000-0000000000000000-01'
|
|
58
54
|
}
|
|
59
55
|
|
|
56
|
+
/**
|
|
57
|
+
* @param {object[]} spans
|
|
58
|
+
*/
|
|
60
59
|
dispatchEngineSpans (spans) {
|
|
60
|
+
if (!spans?.length) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
const childrenByParent = new Map()
|
|
61
64
|
for (const span of spans) {
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
const parentId = span.parentId
|
|
66
|
+
const children = childrenByParent.get(parentId)
|
|
67
|
+
if (children) {
|
|
68
|
+
children.push(span)
|
|
69
|
+
} else {
|
|
70
|
+
childrenByParent.set(parentId, [span])
|
|
64
71
|
}
|
|
65
72
|
}
|
|
73
|
+
|
|
74
|
+
const roots = childrenByParent.get(null)
|
|
75
|
+
if (!roots) {
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
for (const span of roots) {
|
|
79
|
+
this.#prismaClient.startEngineSpan({ engineSpan: span, childrenByParent, dbConfig: this.#dbConfig })
|
|
80
|
+
}
|
|
66
81
|
}
|
|
67
82
|
|
|
68
83
|
getActiveContext () {
|
|
@@ -70,7 +85,15 @@ class DatadogTracingHelper {
|
|
|
70
85
|
return store?.span?._spanContext
|
|
71
86
|
}
|
|
72
87
|
|
|
88
|
+
/**
|
|
89
|
+
* @param {object} options
|
|
90
|
+
* @param {Function} callback
|
|
91
|
+
* @returns {unknown}
|
|
92
|
+
*/
|
|
73
93
|
runInChildSpan (options, callback) {
|
|
94
|
+
if (!clientCH.start?.hasSubscribers) {
|
|
95
|
+
return callback.apply(this, arguments)
|
|
96
|
+
}
|
|
74
97
|
if (typeof options === 'string') {
|
|
75
98
|
options = {
|
|
76
99
|
name: options,
|
|
@@ -32,13 +32,15 @@ class PrismaPlugin extends DatabasePlugin {
|
|
|
32
32
|
super(...args)
|
|
33
33
|
|
|
34
34
|
// Subscribe to helper initialization to inject callbacks
|
|
35
|
-
this.addSub('apm:prisma:helper:init', (
|
|
35
|
+
this.addSub('apm:prisma:helper:init', (ctx) => {
|
|
36
|
+
const prismaHelperCtx =
|
|
37
|
+
/** @type {import('../../datadog-instrumentations/src/prisma').PrismaHelperCtx} */ (ctx)
|
|
36
38
|
prismaHelperCtx.helper = new DatadogTracingHelper(prismaHelperCtx.dbConfig, this)
|
|
37
39
|
})
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
startEngineSpan (ctx) {
|
|
41
|
-
const { engineSpan,
|
|
43
|
+
const { engineSpan, childrenByParent, childOf, dbConfig } = ctx
|
|
42
44
|
const service = this.serviceName({ pluginConfig: this.config, system: this.system })
|
|
43
45
|
const spanName = engineSpan.name.slice(14) // remove 'prisma:engine:' prefix
|
|
44
46
|
const options = {
|
|
@@ -71,9 +73,10 @@ class PrismaPlugin extends DatabasePlugin {
|
|
|
71
73
|
|
|
72
74
|
const activeSpan = this.startSpan(this.operationName({ operation: 'engine' }), options)
|
|
73
75
|
activeSpan._startTime = hrTimeToUnixTimeMs(engineSpan.startTime)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
const children = childrenByParent.get(engineSpan.id)
|
|
77
|
+
if (children) {
|
|
78
|
+
for (const span of children) {
|
|
79
|
+
const startCtx = { engineSpan: span, childrenByParent, childOf: activeSpan, dbConfig }
|
|
77
80
|
this.startEngineSpan(startCtx)
|
|
78
81
|
}
|
|
79
82
|
}
|
|
@@ -9,26 +9,31 @@ const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
|
9
9
|
class RouterPlugin extends WebPlugin {
|
|
10
10
|
static id = 'router'
|
|
11
11
|
|
|
12
|
+
#storeStacks = new WeakMap()
|
|
13
|
+
#contexts = new WeakMap()
|
|
14
|
+
|
|
12
15
|
constructor (...args) {
|
|
13
16
|
super(...args)
|
|
14
17
|
|
|
15
|
-
this._storeStack = []
|
|
16
|
-
this._contexts = new WeakMap()
|
|
17
|
-
|
|
18
18
|
this.addSub(`apm:${this.constructor.id}:middleware:enter`, ({ req, name, route }) => {
|
|
19
|
-
const childOf = this
|
|
19
|
+
const childOf = this.#getActive(req) || this.#getStoreSpan()
|
|
20
20
|
|
|
21
21
|
if (!childOf) return
|
|
22
22
|
|
|
23
|
-
const span = this
|
|
24
|
-
const context = this
|
|
23
|
+
const span = this.#getMiddlewareSpan(name, childOf)
|
|
24
|
+
const context = this.#createContext(req, route, childOf)
|
|
25
25
|
|
|
26
26
|
if (childOf !== span) {
|
|
27
27
|
context.middleware.push(span)
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
const store = storage('legacy').getStore()
|
|
31
|
-
this.
|
|
31
|
+
let storeStack = this.#storeStacks.get(req)
|
|
32
|
+
if (!storeStack) {
|
|
33
|
+
storeStack = []
|
|
34
|
+
this.#storeStacks.set(req, storeStack)
|
|
35
|
+
}
|
|
36
|
+
storeStack.push(store)
|
|
32
37
|
this.enter(span, store)
|
|
33
38
|
|
|
34
39
|
web.patch(req)
|
|
@@ -36,7 +41,7 @@ class RouterPlugin extends WebPlugin {
|
|
|
36
41
|
})
|
|
37
42
|
|
|
38
43
|
this.addSub(`apm:${this.constructor.id}:middleware:next`, ({ req }) => {
|
|
39
|
-
const context = this.
|
|
44
|
+
const context = this.#contexts.get(req)
|
|
40
45
|
|
|
41
46
|
if (!context) return
|
|
42
47
|
|
|
@@ -44,7 +49,7 @@ class RouterPlugin extends WebPlugin {
|
|
|
44
49
|
})
|
|
45
50
|
|
|
46
51
|
this.addSub(`apm:${this.constructor.id}:middleware:finish`, ({ req }) => {
|
|
47
|
-
const context = this.
|
|
52
|
+
const context = this.#contexts.get(req)
|
|
48
53
|
|
|
49
54
|
if (!context || context.middleware.length === 0) return
|
|
50
55
|
|
|
@@ -52,7 +57,11 @@ class RouterPlugin extends WebPlugin {
|
|
|
52
57
|
})
|
|
53
58
|
|
|
54
59
|
this.addSub(`apm:${this.constructor.id}:middleware:exit`, ({ req }) => {
|
|
55
|
-
const
|
|
60
|
+
const storeStack = this.#storeStacks.get(req)
|
|
61
|
+
const savedStore = storeStack && storeStack.pop()
|
|
62
|
+
if (storeStack && storeStack.length === 0) {
|
|
63
|
+
this.#storeStacks.delete(req)
|
|
64
|
+
}
|
|
56
65
|
const span = savedStore && savedStore.span
|
|
57
66
|
this.enter(span, savedStore)
|
|
58
67
|
})
|
|
@@ -62,7 +71,7 @@ class RouterPlugin extends WebPlugin {
|
|
|
62
71
|
|
|
63
72
|
if (!this.config.middleware) return
|
|
64
73
|
|
|
65
|
-
const span = this
|
|
74
|
+
const span = this.#getActive(req)
|
|
66
75
|
|
|
67
76
|
if (!span) return
|
|
68
77
|
|
|
@@ -70,7 +79,7 @@ class RouterPlugin extends WebPlugin {
|
|
|
70
79
|
})
|
|
71
80
|
|
|
72
81
|
this.addSub('apm:http:server:request:finish', ({ req }) => {
|
|
73
|
-
const context = this.
|
|
82
|
+
const context = this.#contexts.get(req)
|
|
74
83
|
|
|
75
84
|
if (!context) return
|
|
76
85
|
|
|
@@ -82,8 +91,8 @@ class RouterPlugin extends WebPlugin {
|
|
|
82
91
|
})
|
|
83
92
|
}
|
|
84
93
|
|
|
85
|
-
|
|
86
|
-
const context = this.
|
|
94
|
+
#getActive (req) {
|
|
95
|
+
const context = this.#contexts.get(req)
|
|
87
96
|
|
|
88
97
|
if (!context) return
|
|
89
98
|
if (context.middleware.length === 0) return context.span
|
|
@@ -91,13 +100,13 @@ class RouterPlugin extends WebPlugin {
|
|
|
91
100
|
return context.middleware.at(-1)
|
|
92
101
|
}
|
|
93
102
|
|
|
94
|
-
|
|
103
|
+
#getStoreSpan () {
|
|
95
104
|
const store = storage('legacy').getStore()
|
|
96
105
|
|
|
97
106
|
return store && store.span
|
|
98
107
|
}
|
|
99
108
|
|
|
100
|
-
|
|
109
|
+
#getMiddlewareSpan (name, childOf) {
|
|
101
110
|
if (this.config.middleware === false) {
|
|
102
111
|
return childOf
|
|
103
112
|
}
|
|
@@ -116,8 +125,8 @@ class RouterPlugin extends WebPlugin {
|
|
|
116
125
|
return span
|
|
117
126
|
}
|
|
118
127
|
|
|
119
|
-
|
|
120
|
-
let context = this.
|
|
128
|
+
#createContext (req, route, span) {
|
|
129
|
+
let context = this.#contexts.get(req)
|
|
121
130
|
|
|
122
131
|
if (!route || route === '/' || route === '*') {
|
|
123
132
|
route = ''
|
|
@@ -140,7 +149,7 @@ class RouterPlugin extends WebPlugin {
|
|
|
140
149
|
middleware: [],
|
|
141
150
|
}
|
|
142
151
|
|
|
143
|
-
this.
|
|
152
|
+
this.#contexts.set(req, context)
|
|
144
153
|
}
|
|
145
154
|
|
|
146
155
|
return context
|
|
@@ -206,9 +206,7 @@ class VitestPlugin extends CiPlugin {
|
|
|
206
206
|
|
|
207
207
|
this.addSub('ci:vitest:test:pass', ({ span, task }) => {
|
|
208
208
|
if (span) {
|
|
209
|
-
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test',
|
|
210
|
-
hasCodeowners: !!span.context()._tags[TEST_CODE_OWNERS],
|
|
211
|
-
})
|
|
209
|
+
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test', this.getTestTelemetryTags(span))
|
|
212
210
|
span.setTag(TEST_STATUS, 'pass')
|
|
213
211
|
span.finish(this.taskToFinishTime.get(task))
|
|
214
212
|
finishAllTraceSpans(span)
|
|
@@ -236,9 +234,7 @@ class VitestPlugin extends CiPlugin {
|
|
|
236
234
|
promises.setProbePromise = setProbePromise
|
|
237
235
|
}
|
|
238
236
|
}
|
|
239
|
-
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test',
|
|
240
|
-
hasCodeowners: !!span.context()._tags[TEST_CODE_OWNERS],
|
|
241
|
-
})
|
|
237
|
+
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test', this.getTestTelemetryTags(span))
|
|
242
238
|
span.setTag(TEST_STATUS, 'fail')
|
|
243
239
|
|
|
244
240
|
if (error) {
|
|
@@ -272,9 +268,7 @@ class VitestPlugin extends CiPlugin {
|
|
|
272
268
|
...(isNew ? { [TEST_IS_NEW]: 'true' } : {}),
|
|
273
269
|
}
|
|
274
270
|
)
|
|
275
|
-
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test',
|
|
276
|
-
hasCodeowners: !!testSpan.context()._tags[TEST_CODE_OWNERS],
|
|
277
|
-
})
|
|
271
|
+
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test', this.getTestTelemetryTags(testSpan))
|
|
278
272
|
testSpan.finish()
|
|
279
273
|
})
|
|
280
274
|
|
|
@@ -401,7 +395,9 @@ class VitestPlugin extends CiPlugin {
|
|
|
401
395
|
this.testModuleSpan.finish()
|
|
402
396
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
|
|
403
397
|
this.testSessionSpan.finish()
|
|
404
|
-
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session'
|
|
398
|
+
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session', {
|
|
399
|
+
hasFailedTestReplay: this.libraryConfig?.isDiEnabled || undefined,
|
|
400
|
+
})
|
|
405
401
|
finishAllTraceSpans(this.testSessionSpan)
|
|
406
402
|
this.telemetry.count(TELEMETRY_TEST_SESSION, {
|
|
407
403
|
provider: this.ciProviderName,
|
|
@@ -17,6 +17,14 @@ class WSServerPlugin extends TracingPlugin {
|
|
|
17
17
|
static get type () { return 'websocket' }
|
|
18
18
|
static get kind () { return 'request' }
|
|
19
19
|
|
|
20
|
+
constructor (...args) {
|
|
21
|
+
super(...args)
|
|
22
|
+
|
|
23
|
+
// Bind the setSocket channel so internal ws event handlers (data, close)
|
|
24
|
+
// don't capture their async context.
|
|
25
|
+
this.addBind('tracing:ws:server:connect:setSocket', () => {})
|
|
26
|
+
}
|
|
27
|
+
|
|
20
28
|
bindStart (ctx) {
|
|
21
29
|
const req = ctx.req
|
|
22
30
|
|
|
@@ -95,7 +95,7 @@ function getKnownTests ({
|
|
|
95
95
|
|
|
96
96
|
const numTests = getNumFromKnownTests(knownTests)
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
distributionMetric(TELEMETRY_KNOWN_TESTS_RESPONSE_TESTS, {}, numTests)
|
|
99
99
|
distributionMetric(TELEMETRY_KNOWN_TESTS_RESPONSE_BYTES, {}, res.length)
|
|
100
100
|
|
|
101
101
|
log.debug('Number of received known tests:', numTests)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const {
|
|
4
4
|
JEST_WORKER_COVERAGE_PAYLOAD_CODE,
|
|
5
5
|
JEST_WORKER_TRACE_PAYLOAD_CODE,
|
|
6
|
+
JEST_WORKER_TELEMETRY_PAYLOAD_CODE,
|
|
6
7
|
CUCUMBER_WORKER_TRACE_PAYLOAD_CODE,
|
|
7
8
|
MOCHA_WORKER_TRACE_PAYLOAD_CODE,
|
|
8
9
|
JEST_WORKER_LOGS_PAYLOAD_CODE,
|
|
@@ -56,6 +57,13 @@ function getInterprocessLogsCode () {
|
|
|
56
57
|
return null
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
function getInterprocessTelemetryCode () {
|
|
61
|
+
if (getEnvironmentVariable('JEST_WORKER_ID')) {
|
|
62
|
+
return JEST_WORKER_TELEMETRY_PAYLOAD_CODE
|
|
63
|
+
}
|
|
64
|
+
return null
|
|
65
|
+
}
|
|
66
|
+
|
|
59
67
|
/**
|
|
60
68
|
* Lightweight exporter whose writers only do simple JSON serialization
|
|
61
69
|
* of trace, coverage and logs payloads, which they send to the test framework's main process.
|
|
@@ -66,10 +74,18 @@ class TestWorkerCiVisibilityExporter {
|
|
|
66
74
|
const interprocessTraceCode = getInterprocessTraceCode()
|
|
67
75
|
const interprocessCoverageCode = getInterprocessCoverageCode()
|
|
68
76
|
const interprocessLogsCode = getInterprocessLogsCode()
|
|
77
|
+
const interprocessTelemetryCode = getInterprocessTelemetryCode()
|
|
69
78
|
|
|
70
79
|
this._writer = new Writer(interprocessTraceCode)
|
|
71
80
|
this._coverageWriter = new Writer(interprocessCoverageCode)
|
|
72
81
|
this._logsWriter = new Writer(interprocessLogsCode)
|
|
82
|
+
// TODO: add support for test workers other than Jest
|
|
83
|
+
if (interprocessTelemetryCode) {
|
|
84
|
+
this._telemetryWriter = new Writer(interprocessTelemetryCode)
|
|
85
|
+
this.exportTelemetry = function (telemetryEvent) {
|
|
86
|
+
this._telemetryWriter.append(telemetryEvent)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
73
89
|
}
|
|
74
90
|
|
|
75
91
|
export (payload) {
|
|
@@ -89,6 +105,9 @@ class TestWorkerCiVisibilityExporter {
|
|
|
89
105
|
this._writer.flush(onDone)
|
|
90
106
|
this._coverageWriter.flush()
|
|
91
107
|
this._logsWriter.flush()
|
|
108
|
+
if (this._telemetryWriter) {
|
|
109
|
+
this._telemetryWriter.flush()
|
|
110
|
+
}
|
|
92
111
|
}
|
|
93
112
|
}
|
|
94
113
|
|
|
@@ -7,6 +7,14 @@ const FormData = require('../../exporters/common/form-data')
|
|
|
7
7
|
const request = require('../../exporters/common/request')
|
|
8
8
|
const log = require('../../log')
|
|
9
9
|
const { getValueFromEnvSources } = require('../../config/helper')
|
|
10
|
+
const {
|
|
11
|
+
incrementCountMetric,
|
|
12
|
+
distributionMetric,
|
|
13
|
+
TELEMETRY_COVERAGE_UPLOAD,
|
|
14
|
+
TELEMETRY_COVERAGE_UPLOAD_MS,
|
|
15
|
+
TELEMETRY_COVERAGE_UPLOAD_ERRORS,
|
|
16
|
+
TELEMETRY_COVERAGE_UPLOAD_BYTES,
|
|
17
|
+
} = require('../telemetry')
|
|
10
18
|
|
|
11
19
|
const UPLOAD_TIMEOUT_MS = 30_000
|
|
12
20
|
|
|
@@ -79,8 +87,15 @@ function uploadCoverageReport (
|
|
|
79
87
|
|
|
80
88
|
log.debug('Uploading coverage report %s to %s%s', filePath, url, options.path)
|
|
81
89
|
|
|
90
|
+
incrementCountMetric(TELEMETRY_COVERAGE_UPLOAD)
|
|
91
|
+
distributionMetric(TELEMETRY_COVERAGE_UPLOAD_BYTES, {}, compressedCoverage.length)
|
|
92
|
+
|
|
93
|
+
const startTime = Date.now()
|
|
94
|
+
|
|
82
95
|
request(form, options, (err, res, statusCode) => {
|
|
96
|
+
distributionMetric(TELEMETRY_COVERAGE_UPLOAD_MS, {}, Date.now() - startTime)
|
|
83
97
|
if (err) {
|
|
98
|
+
incrementCountMetric(TELEMETRY_COVERAGE_UPLOAD_ERRORS, { statusCode })
|
|
84
99
|
log.error('Error uploading coverage report: %s', err.message)
|
|
85
100
|
return callback(err)
|
|
86
101
|
}
|