dd-trace 5.2.0 → 5.4.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 -0
- package/README.md +1 -32
- package/ci/init.js +1 -4
- package/index.d.ts +21 -0
- package/package.json +7 -6
- package/packages/datadog-instrumentations/src/amqplib.js +1 -1
- package/packages/datadog-instrumentations/src/child_process.js +150 -0
- package/packages/datadog-instrumentations/src/cucumber.js +12 -12
- package/packages/datadog-instrumentations/src/express.js +20 -0
- package/packages/datadog-instrumentations/src/grpc/client.js +56 -36
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -2
- package/packages/datadog-instrumentations/src/jest.js +149 -11
- package/packages/datadog-instrumentations/src/mocha.js +142 -16
- package/packages/datadog-instrumentations/src/mongoose.js +23 -10
- package/packages/datadog-instrumentations/src/next.js +17 -3
- package/packages/datadog-instrumentations/src/playwright.js +41 -9
- package/packages/datadog-plugin-amqplib/src/consumer.js +10 -1
- package/packages/datadog-plugin-amqplib/src/producer.js +14 -1
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +107 -1
- package/packages/datadog-plugin-child_process/src/index.js +91 -0
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +125 -0
- package/packages/datadog-plugin-cucumber/src/index.js +16 -11
- package/packages/datadog-plugin-cypress/src/plugin.js +52 -23
- package/packages/datadog-plugin-grpc/src/client.js +16 -2
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +43 -6
- package/packages/datadog-plugin-kafkajs/src/consumer.js +16 -0
- package/packages/datadog-plugin-mocha/src/index.js +47 -17
- package/packages/datadog-plugin-playwright/src/index.js +19 -5
- package/packages/datadog-plugin-rhea/src/consumer.js +11 -1
- package/packages/datadog-plugin-rhea/src/producer.js +11 -0
- package/packages/dd-trace/src/appsec/addresses.js +2 -0
- package/packages/dd-trace/src/appsec/api_security_sampler.js +16 -3
- package/packages/dd-trace/src/appsec/channels.js +2 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +7 -28
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +10 -6
- package/packages/dd-trace/src/appsec/iast/context/context-plugin.js +90 -0
- package/packages/dd-trace/src/appsec/iast/context/kafka-ctx-plugin.js +14 -0
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +12 -1
- package/packages/dd-trace/src/appsec/iast/index.js +4 -4
- package/packages/dd-trace/src/appsec/iast/overhead-controller.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +1 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +10 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +53 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +10 -46
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +13 -9
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +47 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +3 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +29 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +17 -2
- 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 +2 -2
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +83 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +25 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +2 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +83 -41
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +30 -8
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +7 -1
- package/packages/dd-trace/src/ci-visibility/{intelligent-test-runner/get-itr-configuration.js → requests/get-library-configuration.js} +18 -6
- package/packages/dd-trace/src/config.js +22 -9
- package/packages/dd-trace/src/datastreams/processor.js +6 -0
- package/packages/dd-trace/src/datastreams/writer.js +2 -5
- package/packages/dd-trace/src/dogstatsd.js +3 -5
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +5 -3
- package/packages/dd-trace/src/exporters/common/request.js +21 -3
- package/packages/dd-trace/src/format.js +25 -1
- package/packages/dd-trace/src/noop/span.js +1 -0
- package/packages/dd-trace/src/opentelemetry/span.js +9 -2
- package/packages/dd-trace/src/opentracing/span.js +38 -0
- package/packages/dd-trace/src/opentracing/span_context.js +12 -6
- package/packages/dd-trace/src/opentracing/tracer.js +2 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +25 -9
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/git.js +6 -0
- package/packages/dd-trace/src/plugins/util/test.js +53 -8
- package/packages/dd-trace/src/profiling/config.js +22 -22
- package/packages/dd-trace/src/proxy.js +31 -23
- package/packages/dd-trace/src/span_processor.js +5 -1
- package/packages/dd-trace/src/telemetry/index.js +6 -0
- package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
- package/packages/dd-trace/src/telemetry/send-data.js +0 -3
- package/packages/datadog-instrumentations/src/child-process.js +0 -29
- package/packages/dd-trace/src/plugins/util/exec.js +0 -34
|
@@ -24,7 +24,8 @@ const {
|
|
|
24
24
|
TEST_SKIPPED_BY_ITR,
|
|
25
25
|
TEST_ITR_UNSKIPPABLE,
|
|
26
26
|
TEST_ITR_FORCED_RUN,
|
|
27
|
-
ITR_CORRELATION_ID
|
|
27
|
+
ITR_CORRELATION_ID,
|
|
28
|
+
TEST_SOURCE_FILE
|
|
28
29
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
29
30
|
const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
|
|
30
31
|
const log = require('../../dd-trace/src/log')
|
|
@@ -45,7 +46,8 @@ const {
|
|
|
45
46
|
GIT_REPOSITORY_URL,
|
|
46
47
|
GIT_COMMIT_SHA,
|
|
47
48
|
GIT_BRANCH,
|
|
48
|
-
CI_PROVIDER_NAME
|
|
49
|
+
CI_PROVIDER_NAME,
|
|
50
|
+
CI_WORKSPACE_PATH
|
|
49
51
|
} = require('../../dd-trace/src/plugins/util/tags')
|
|
50
52
|
const {
|
|
51
53
|
OS_VERSION,
|
|
@@ -113,20 +115,21 @@ function getSuiteStatus (suiteStats) {
|
|
|
113
115
|
if (suiteStats.failures !== undefined && suiteStats.failures > 0) {
|
|
114
116
|
return 'fail'
|
|
115
117
|
}
|
|
116
|
-
if (suiteStats.tests !== undefined &&
|
|
118
|
+
if (suiteStats.tests !== undefined &&
|
|
119
|
+
(suiteStats.tests === suiteStats.pending || suiteStats.tests === suiteStats.skipped)) {
|
|
117
120
|
return 'skip'
|
|
118
121
|
}
|
|
119
122
|
return 'pass'
|
|
120
123
|
}
|
|
121
124
|
|
|
122
|
-
function
|
|
125
|
+
function getLibraryConfiguration (tracer, testConfiguration) {
|
|
123
126
|
return new Promise(resolve => {
|
|
124
|
-
if (!tracer._tracer._exporter
|
|
127
|
+
if (!tracer._tracer._exporter?.getLibraryConfiguration) {
|
|
125
128
|
return resolve({ err: new Error('CI Visibility was not initialized correctly') })
|
|
126
129
|
}
|
|
127
130
|
|
|
128
|
-
tracer._tracer._exporter.
|
|
129
|
-
resolve({ err,
|
|
131
|
+
tracer._tracer._exporter.getLibraryConfiguration(testConfiguration, (err, libraryConfig) => {
|
|
132
|
+
resolve({ err, libraryConfig })
|
|
130
133
|
})
|
|
131
134
|
})
|
|
132
135
|
}
|
|
@@ -136,7 +139,7 @@ function getSkippableTests (isSuitesSkippingEnabled, tracer, testConfiguration)
|
|
|
136
139
|
return Promise.resolve({ skippableTests: [] })
|
|
137
140
|
}
|
|
138
141
|
return new Promise(resolve => {
|
|
139
|
-
if (!tracer._tracer._exporter
|
|
142
|
+
if (!tracer._tracer._exporter?.getLibraryConfiguration) {
|
|
140
143
|
return resolve({ err: new Error('CI Visibility was not initialized correctly') })
|
|
141
144
|
}
|
|
142
145
|
tracer._tracer._exporter.getSkippableSuites(testConfiguration, (err, skippableTests, correlationId) => {
|
|
@@ -186,7 +189,8 @@ module.exports = (on, config) => {
|
|
|
186
189
|
[RUNTIME_NAME]: runtimeName,
|
|
187
190
|
[RUNTIME_VERSION]: runtimeVersion,
|
|
188
191
|
[GIT_BRANCH]: branch,
|
|
189
|
-
[CI_PROVIDER_NAME]: ciProviderName
|
|
192
|
+
[CI_PROVIDER_NAME]: ciProviderName,
|
|
193
|
+
[CI_WORKSPACE_PATH]: repositoryRoot
|
|
190
194
|
} = testEnvironmentMetadata
|
|
191
195
|
|
|
192
196
|
const isUnsupportedCIProvider = !ciProviderName
|
|
@@ -205,7 +209,7 @@ module.exports = (on, config) => {
|
|
|
205
209
|
testLevel: 'test'
|
|
206
210
|
}
|
|
207
211
|
|
|
208
|
-
const codeOwnersEntries = getCodeOwnersFileEntries()
|
|
212
|
+
const codeOwnersEntries = getCodeOwnersFileEntries(repositoryRoot)
|
|
209
213
|
|
|
210
214
|
let activeSpan = null
|
|
211
215
|
let testSessionSpan = null
|
|
@@ -243,6 +247,10 @@ module.exports = (on, config) => {
|
|
|
243
247
|
if (testSessionSpan && testModuleSpan) {
|
|
244
248
|
testSuiteTags[TEST_SESSION_ID] = testSessionSpan.context().toTraceId()
|
|
245
249
|
testSuiteTags[TEST_MODULE_ID] = testModuleSpan.context().toSpanId()
|
|
250
|
+
// If testSuiteSpan couldn't be created, we'll use the testModuleSpan as the parent
|
|
251
|
+
if (!testSuiteSpan) {
|
|
252
|
+
testSuiteTags[TEST_SUITE_ID] = testModuleSpan.context().toSpanId()
|
|
253
|
+
}
|
|
246
254
|
}
|
|
247
255
|
|
|
248
256
|
const {
|
|
@@ -283,13 +291,26 @@ module.exports = (on, config) => {
|
|
|
283
291
|
})
|
|
284
292
|
}
|
|
285
293
|
|
|
294
|
+
function getTestSuiteSpan (suite) {
|
|
295
|
+
const testSuiteSpanMetadata = getTestSuiteCommonTags(command, frameworkVersion, suite, TEST_FRAMEWORK_NAME)
|
|
296
|
+
ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
|
|
297
|
+
return tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_suite`, {
|
|
298
|
+
childOf: testModuleSpan,
|
|
299
|
+
tags: {
|
|
300
|
+
[COMPONENT]: TEST_FRAMEWORK_NAME,
|
|
301
|
+
...testEnvironmentMetadata,
|
|
302
|
+
...testSuiteSpanMetadata
|
|
303
|
+
}
|
|
304
|
+
})
|
|
305
|
+
}
|
|
306
|
+
|
|
286
307
|
on('before:run', (details) => {
|
|
287
|
-
return
|
|
308
|
+
return getLibraryConfiguration(tracer, testConfiguration).then(({ err, libraryConfig }) => {
|
|
288
309
|
if (err) {
|
|
289
310
|
log.error(err)
|
|
290
311
|
} else {
|
|
291
|
-
isSuitesSkippingEnabled =
|
|
292
|
-
isCodeCoverageEnabled =
|
|
312
|
+
isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled
|
|
313
|
+
isCodeCoverageEnabled = libraryConfig.isCodeCoverageEnabled
|
|
293
314
|
}
|
|
294
315
|
|
|
295
316
|
return getSkippableTests(isSuitesSkippingEnabled, tracer, testConfiguration)
|
|
@@ -346,6 +367,13 @@ module.exports = (on, config) => {
|
|
|
346
367
|
const cypressTests = tests || []
|
|
347
368
|
const finishedTests = finishedTestsByFile[spec.relative] || []
|
|
348
369
|
|
|
370
|
+
if (!testSuiteSpan) {
|
|
371
|
+
// dd:testSuiteStart hasn't been triggered for whatever reason
|
|
372
|
+
// We will create the test suite span on the spot if that's the case
|
|
373
|
+
log.warn('There was an error creating the test suite event.')
|
|
374
|
+
testSuiteSpan = getTestSuiteSpan(spec.relative)
|
|
375
|
+
}
|
|
376
|
+
|
|
349
377
|
// Get tests that didn't go through `dd:afterEach`
|
|
350
378
|
// and create a skipped test span for each of them
|
|
351
379
|
cypressTests.filter(({ title }) => {
|
|
@@ -359,6 +387,11 @@ module.exports = (on, config) => {
|
|
|
359
387
|
cypressTestName === test.name && spec.relative === test.suite
|
|
360
388
|
)
|
|
361
389
|
const skippedTestSpan = getTestSpan(cypressTestName, spec.relative)
|
|
390
|
+
if (spec.absolute && repositoryRoot) {
|
|
391
|
+
skippedTestSpan.setTag(TEST_SOURCE_FILE, getTestSuitePath(spec.absolute, repositoryRoot))
|
|
392
|
+
} else {
|
|
393
|
+
skippedTestSpan.setTag(TEST_SOURCE_FILE, spec.relative)
|
|
394
|
+
}
|
|
362
395
|
skippedTestSpan.setTag(TEST_STATUS, 'skip')
|
|
363
396
|
if (isSkippedByItr) {
|
|
364
397
|
skippedTestSpan.setTag(TEST_SKIPPED_BY_ITR, 'true')
|
|
@@ -390,6 +423,11 @@ module.exports = (on, config) => {
|
|
|
390
423
|
if (itrCorrelationId) {
|
|
391
424
|
finishedTest.testSpan.setTag(ITR_CORRELATION_ID, itrCorrelationId)
|
|
392
425
|
}
|
|
426
|
+
if (spec.absolute && repositoryRoot) {
|
|
427
|
+
finishedTest.testSpan.setTag(TEST_SOURCE_FILE, getTestSuitePath(spec.absolute, repositoryRoot))
|
|
428
|
+
} else {
|
|
429
|
+
finishedTest.testSpan.setTag(TEST_SOURCE_FILE, spec.relative)
|
|
430
|
+
}
|
|
393
431
|
finishedTest.testSpan.finish(finishedTest.finishTime)
|
|
394
432
|
})
|
|
395
433
|
|
|
@@ -457,16 +495,7 @@ module.exports = (on, config) => {
|
|
|
457
495
|
if (testSuiteSpan) {
|
|
458
496
|
return null
|
|
459
497
|
}
|
|
460
|
-
|
|
461
|
-
testSuiteSpan = tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_suite`, {
|
|
462
|
-
childOf: testModuleSpan,
|
|
463
|
-
tags: {
|
|
464
|
-
[COMPONENT]: TEST_FRAMEWORK_NAME,
|
|
465
|
-
...testEnvironmentMetadata,
|
|
466
|
-
...testSuiteSpanMetadata
|
|
467
|
-
}
|
|
468
|
-
})
|
|
469
|
-
ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
|
|
498
|
+
testSuiteSpan = getTestSuiteSpan(suite)
|
|
470
499
|
return null
|
|
471
500
|
},
|
|
472
501
|
'dd:beforeEach': (test) => {
|
|
@@ -41,7 +41,6 @@ class GrpcClientPlugin extends ClientPlugin {
|
|
|
41
41
|
'grpc.status.code': 0
|
|
42
42
|
}
|
|
43
43
|
}, false)
|
|
44
|
-
|
|
45
44
|
// needed as precursor for peer.service
|
|
46
45
|
if (method.service && method.package) {
|
|
47
46
|
span.setTag('rpc.service', method.package + '.' + method.service)
|
|
@@ -68,7 +67,7 @@ class GrpcClientPlugin extends ClientPlugin {
|
|
|
68
67
|
this.addError(error, span)
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
finish ({ span, result }) {
|
|
70
|
+
finish ({ span, result, peer }) {
|
|
72
71
|
if (!span) return
|
|
73
72
|
|
|
74
73
|
const { code, metadata } = result || {}
|
|
@@ -80,6 +79,21 @@ class GrpcClientPlugin extends ClientPlugin {
|
|
|
80
79
|
addMetadataTags(span, metadata, metadataFilter, 'response')
|
|
81
80
|
}
|
|
82
81
|
|
|
82
|
+
if (peer) {
|
|
83
|
+
// The only scheme we want to support here is ipv[46]:port, although
|
|
84
|
+
// more are supported by the library
|
|
85
|
+
// https://github.com/grpc/grpc/blob/v1.60.0/doc/naming.md
|
|
86
|
+
const parts = peer.split(':')
|
|
87
|
+
if (parts[parts.length - 1].match(/^\d+/)) {
|
|
88
|
+
const port = parts[parts.length - 1]
|
|
89
|
+
const ip = parts.slice(0, -1).join(':')
|
|
90
|
+
span.setTag('network.destination.ip', ip)
|
|
91
|
+
span.setTag('network.destination.port', port)
|
|
92
|
+
} else {
|
|
93
|
+
span.setTag('network.destination.ip', peer)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
83
97
|
this.tagPeerService(span)
|
|
84
98
|
span.finish()
|
|
85
99
|
}
|
|
@@ -122,7 +122,7 @@ class HttpClientPlugin extends ClientPlugin {
|
|
|
122
122
|
// conditions for no error:
|
|
123
123
|
// 1. not using a custom agent instance with custom timeout specified
|
|
124
124
|
// 2. no invocation of `req.setTimeout`
|
|
125
|
-
if (!args.options.agent?.options
|
|
125
|
+
if (!args.options.agent?.options?.timeout && !customRequestTimeout) return
|
|
126
126
|
|
|
127
127
|
span.setTag('error', 1)
|
|
128
128
|
}
|
|
@@ -14,7 +14,11 @@ const {
|
|
|
14
14
|
TEST_ITR_UNSKIPPABLE,
|
|
15
15
|
TEST_ITR_FORCED_RUN,
|
|
16
16
|
TEST_CODE_OWNERS,
|
|
17
|
-
ITR_CORRELATION_ID
|
|
17
|
+
ITR_CORRELATION_ID,
|
|
18
|
+
TEST_SOURCE_FILE,
|
|
19
|
+
TEST_IS_NEW,
|
|
20
|
+
TEST_EARLY_FLAKE_IS_RETRY,
|
|
21
|
+
TEST_EARLY_FLAKE_IS_ENABLED
|
|
18
22
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
19
23
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
20
24
|
const id = require('../../dd-trace/src/id')
|
|
@@ -81,7 +85,9 @@ class JestPlugin extends CiPlugin {
|
|
|
81
85
|
numSkippedSuites,
|
|
82
86
|
hasUnskippableSuites,
|
|
83
87
|
hasForcedToRunSuites,
|
|
84
|
-
error
|
|
88
|
+
error,
|
|
89
|
+
isEarlyFlakeDetectionEnabled,
|
|
90
|
+
onDone
|
|
85
91
|
}) => {
|
|
86
92
|
this.testSessionSpan.setTag(TEST_STATUS, status)
|
|
87
93
|
this.testModuleSpan.setTag(TEST_STATUS, status)
|
|
@@ -106,23 +112,35 @@ class JestPlugin extends CiPlugin {
|
|
|
106
112
|
}
|
|
107
113
|
)
|
|
108
114
|
|
|
115
|
+
if (isEarlyFlakeDetectionEnabled) {
|
|
116
|
+
this.testSessionSpan.setTag(TEST_EARLY_FLAKE_IS_ENABLED, 'true')
|
|
117
|
+
}
|
|
118
|
+
|
|
109
119
|
this.testModuleSpan.finish()
|
|
110
120
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
|
|
111
121
|
this.testSessionSpan.finish()
|
|
112
122
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
|
|
113
123
|
finishAllTraceSpans(this.testSessionSpan)
|
|
114
|
-
|
|
124
|
+
|
|
125
|
+
this.tracer._exporter.flush(() => {
|
|
126
|
+
if (onDone) {
|
|
127
|
+
onDone()
|
|
128
|
+
}
|
|
129
|
+
})
|
|
115
130
|
})
|
|
116
131
|
|
|
117
132
|
// Test suites can be run in a different process from jest's main one.
|
|
118
133
|
// This subscriber changes the configuration objects from jest to inject the trace id
|
|
119
|
-
// of the test session to the processes that run the test suites.
|
|
134
|
+
// of the test session to the processes that run the test suites, and other data.
|
|
120
135
|
this.addSub('ci:jest:session:configuration', configs => {
|
|
121
136
|
configs.forEach(config => {
|
|
122
137
|
config._ddTestSessionId = this.testSessionSpan.context().toTraceId()
|
|
123
138
|
config._ddTestModuleId = this.testModuleSpan.context().toSpanId()
|
|
124
139
|
config._ddTestCommand = this.testSessionSpan.context()._tags[TEST_COMMAND]
|
|
125
140
|
config._ddItrCorrelationId = this.itrCorrelationId
|
|
141
|
+
config._ddIsEarlyFlakeDetectionEnabled = !!this.libraryConfig?.isEarlyFlakeDetectionEnabled
|
|
142
|
+
config._ddEarlyFlakeDetectionNumRetries = this.libraryConfig?.earlyFlakeDetectionNumRetries ?? 0
|
|
143
|
+
config._ddRepositoryRoot = this.repositoryRoot
|
|
126
144
|
})
|
|
127
145
|
})
|
|
128
146
|
|
|
@@ -223,7 +241,7 @@ class JestPlugin extends CiPlugin {
|
|
|
223
241
|
})
|
|
224
242
|
|
|
225
243
|
/**
|
|
226
|
-
* This can't use `this.
|
|
244
|
+
* This can't use `this.libraryConfig` like `ci:mocha:test-suite:code-coverage`
|
|
227
245
|
* because this subscription happens in a different process from the one
|
|
228
246
|
* fetching the ITR config.
|
|
229
247
|
*/
|
|
@@ -286,7 +304,17 @@ class JestPlugin extends CiPlugin {
|
|
|
286
304
|
}
|
|
287
305
|
|
|
288
306
|
startTestSpan (test) {
|
|
289
|
-
const {
|
|
307
|
+
const {
|
|
308
|
+
suite,
|
|
309
|
+
name,
|
|
310
|
+
runner,
|
|
311
|
+
testParameters,
|
|
312
|
+
frameworkVersion,
|
|
313
|
+
testStartLine,
|
|
314
|
+
testSourceFile,
|
|
315
|
+
isNew,
|
|
316
|
+
isEfdRetry
|
|
317
|
+
} = test
|
|
290
318
|
|
|
291
319
|
const extraTags = {
|
|
292
320
|
[JEST_TEST_RUNNER]: runner,
|
|
@@ -296,6 +324,15 @@ class JestPlugin extends CiPlugin {
|
|
|
296
324
|
if (testStartLine) {
|
|
297
325
|
extraTags[TEST_SOURCE_START] = testStartLine
|
|
298
326
|
}
|
|
327
|
+
// If for whatever we don't have the source file, we'll fall back to the suite name
|
|
328
|
+
extraTags[TEST_SOURCE_FILE] = testSourceFile || suite
|
|
329
|
+
|
|
330
|
+
if (isNew) {
|
|
331
|
+
extraTags[TEST_IS_NEW] = 'true'
|
|
332
|
+
if (isEfdRetry) {
|
|
333
|
+
extraTags[TEST_EARLY_FLAKE_IS_RETRY] = 'true'
|
|
334
|
+
}
|
|
335
|
+
}
|
|
299
336
|
|
|
300
337
|
return super.startTestSpan(name, suite, this.testSuiteSpan, extraTags)
|
|
301
338
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const dc = require('dc-polyfill')
|
|
3
4
|
const { getMessageSize, CONTEXT_PROPAGATION_KEY } = require('../../dd-trace/src/datastreams/processor')
|
|
4
5
|
const ConsumerPlugin = require('../../dd-trace/src/plugins/consumer')
|
|
5
6
|
|
|
7
|
+
const afterStartCh = dc.channel('dd-trace:kafkajs:consumer:afterStart')
|
|
8
|
+
const beforeFinishCh = dc.channel('dd-trace:kafkajs:consumer:beforeFinish')
|
|
9
|
+
|
|
6
10
|
class KafkajsConsumerPlugin extends ConsumerPlugin {
|
|
7
11
|
static get id () { return 'kafkajs' }
|
|
8
12
|
static get operation () { return 'consume' }
|
|
@@ -79,6 +83,18 @@ class KafkajsConsumerPlugin extends ConsumerPlugin {
|
|
|
79
83
|
this.tracer
|
|
80
84
|
.setCheckpoint(['direction:in', `group:${groupId}`, `topic:${topic}`, 'type:kafka'], span, payloadSize)
|
|
81
85
|
}
|
|
86
|
+
|
|
87
|
+
if (afterStartCh.hasSubscribers) {
|
|
88
|
+
afterStartCh.publish({ topic, partition, message, groupId })
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
finish () {
|
|
93
|
+
if (beforeFinishCh.hasSubscribers) {
|
|
94
|
+
beforeFinishCh.publish()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
super.finish()
|
|
82
98
|
}
|
|
83
99
|
}
|
|
84
100
|
|
|
@@ -15,7 +15,12 @@ const {
|
|
|
15
15
|
TEST_ITR_UNSKIPPABLE,
|
|
16
16
|
TEST_ITR_FORCED_RUN,
|
|
17
17
|
TEST_CODE_OWNERS,
|
|
18
|
-
ITR_CORRELATION_ID
|
|
18
|
+
ITR_CORRELATION_ID,
|
|
19
|
+
TEST_SOURCE_FILE,
|
|
20
|
+
removeEfdStringFromTestName,
|
|
21
|
+
TEST_IS_NEW,
|
|
22
|
+
TEST_EARLY_FLAKE_IS_RETRY,
|
|
23
|
+
TEST_EARLY_FLAKE_IS_ENABLED
|
|
19
24
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
20
25
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
21
26
|
const {
|
|
@@ -38,11 +43,11 @@ class MochaPlugin extends CiPlugin {
|
|
|
38
43
|
super(...args)
|
|
39
44
|
|
|
40
45
|
this._testSuites = new Map()
|
|
41
|
-
this.
|
|
46
|
+
this._testTitleToParams = {}
|
|
42
47
|
this.sourceRoot = process.cwd()
|
|
43
48
|
|
|
44
49
|
this.addSub('ci:mocha:test-suite:code-coverage', ({ coverageFiles, suiteFile }) => {
|
|
45
|
-
if (!this.
|
|
50
|
+
if (!this.libraryConfig?.isCodeCoverageEnabled) {
|
|
46
51
|
return
|
|
47
52
|
}
|
|
48
53
|
const testSuiteSpan = this._testSuites.get(suiteFile)
|
|
@@ -98,7 +103,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
98
103
|
}
|
|
99
104
|
})
|
|
100
105
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
|
|
101
|
-
if (this.
|
|
106
|
+
if (this.libraryConfig?.isCodeCoverageEnabled) {
|
|
102
107
|
this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_STARTED, 'suite', { library: 'istanbul' })
|
|
103
108
|
}
|
|
104
109
|
if (itrCorrelationId) {
|
|
@@ -130,9 +135,9 @@ class MochaPlugin extends CiPlugin {
|
|
|
130
135
|
}
|
|
131
136
|
})
|
|
132
137
|
|
|
133
|
-
this.addSub('ci:mocha:test:start', (
|
|
138
|
+
this.addSub('ci:mocha:test:start', (testInfo) => {
|
|
134
139
|
const store = storage.getStore()
|
|
135
|
-
const span = this.startTestSpan(
|
|
140
|
+
const span = this.startTestSpan(testInfo)
|
|
136
141
|
|
|
137
142
|
this.enter(span, store)
|
|
138
143
|
})
|
|
@@ -155,12 +160,12 @@ class MochaPlugin extends CiPlugin {
|
|
|
155
160
|
}
|
|
156
161
|
})
|
|
157
162
|
|
|
158
|
-
this.addSub('ci:mocha:test:skip', (
|
|
163
|
+
this.addSub('ci:mocha:test:skip', (testInfo) => {
|
|
159
164
|
const store = storage.getStore()
|
|
160
165
|
// skipped through it.skip, so the span is not created yet
|
|
161
166
|
// for this test
|
|
162
167
|
if (!store) {
|
|
163
|
-
const testSpan = this.startTestSpan(
|
|
168
|
+
const testSpan = this.startTestSpan(testInfo)
|
|
164
169
|
this.enter(testSpan, store)
|
|
165
170
|
}
|
|
166
171
|
})
|
|
@@ -178,8 +183,8 @@ class MochaPlugin extends CiPlugin {
|
|
|
178
183
|
}
|
|
179
184
|
})
|
|
180
185
|
|
|
181
|
-
this.addSub('ci:mocha:test:parameterize', ({
|
|
182
|
-
this.
|
|
186
|
+
this.addSub('ci:mocha:test:parameterize', ({ title, params }) => {
|
|
187
|
+
this._testTitleToParams[title] = params
|
|
183
188
|
})
|
|
184
189
|
|
|
185
190
|
this.addSub('ci:mocha:session:finish', ({
|
|
@@ -189,10 +194,11 @@ class MochaPlugin extends CiPlugin {
|
|
|
189
194
|
numSkippedSuites,
|
|
190
195
|
hasForcedToRunSuites,
|
|
191
196
|
hasUnskippableSuites,
|
|
192
|
-
error
|
|
197
|
+
error,
|
|
198
|
+
isEarlyFlakeDetectionEnabled
|
|
193
199
|
}) => {
|
|
194
200
|
if (this.testSessionSpan) {
|
|
195
|
-
const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.
|
|
201
|
+
const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.libraryConfig || {}
|
|
196
202
|
this.testSessionSpan.setTag(TEST_STATUS, status)
|
|
197
203
|
this.testModuleSpan.setTag(TEST_STATUS, status)
|
|
198
204
|
|
|
@@ -216,23 +222,34 @@ class MochaPlugin extends CiPlugin {
|
|
|
216
222
|
}
|
|
217
223
|
)
|
|
218
224
|
|
|
225
|
+
if (isEarlyFlakeDetectionEnabled) {
|
|
226
|
+
this.testSessionSpan.setTag(TEST_EARLY_FLAKE_IS_ENABLED, 'true')
|
|
227
|
+
}
|
|
228
|
+
|
|
219
229
|
this.testModuleSpan.finish()
|
|
220
230
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
|
|
221
231
|
this.testSessionSpan.finish()
|
|
222
232
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session')
|
|
223
233
|
finishAllTraceSpans(this.testSessionSpan)
|
|
224
234
|
}
|
|
225
|
-
this.
|
|
235
|
+
this.libraryConfig = null
|
|
226
236
|
this.tracer._exporter.flush()
|
|
227
237
|
})
|
|
228
238
|
}
|
|
229
239
|
|
|
230
|
-
startTestSpan (
|
|
231
|
-
const
|
|
232
|
-
|
|
240
|
+
startTestSpan (testInfo) {
|
|
241
|
+
const {
|
|
242
|
+
testSuiteAbsolutePath,
|
|
243
|
+
title,
|
|
244
|
+
isNew,
|
|
245
|
+
isEfdRetry,
|
|
246
|
+
testStartLine
|
|
247
|
+
} = testInfo
|
|
248
|
+
|
|
249
|
+
const testName = removeEfdStringFromTestName(testInfo.testName)
|
|
233
250
|
|
|
234
251
|
const extraTags = {}
|
|
235
|
-
const testParametersString = getTestParametersString(this.
|
|
252
|
+
const testParametersString = getTestParametersString(this._testTitleToParams, title)
|
|
236
253
|
if (testParametersString) {
|
|
237
254
|
extraTags[TEST_PARAMETERS] = testParametersString
|
|
238
255
|
}
|
|
@@ -244,6 +261,19 @@ class MochaPlugin extends CiPlugin {
|
|
|
244
261
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.sourceRoot)
|
|
245
262
|
const testSuiteSpan = this._testSuites.get(testSuiteAbsolutePath)
|
|
246
263
|
|
|
264
|
+
if (this.repositoryRoot !== this.sourceRoot && !!this.repositoryRoot) {
|
|
265
|
+
extraTags[TEST_SOURCE_FILE] = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
266
|
+
} else {
|
|
267
|
+
extraTags[TEST_SOURCE_FILE] = testSuite
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (isNew) {
|
|
271
|
+
extraTags[TEST_IS_NEW] = 'true'
|
|
272
|
+
if (isEfdRetry) {
|
|
273
|
+
extraTags[TEST_EARLY_FLAKE_IS_RETRY] = 'true'
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
247
277
|
return super.startTestSpan(testName, testSuite, testSuiteSpan, extraTags)
|
|
248
278
|
}
|
|
249
279
|
}
|
|
@@ -9,7 +9,9 @@ const {
|
|
|
9
9
|
getTestSuitePath,
|
|
10
10
|
getTestSuiteCommonTags,
|
|
11
11
|
TEST_SOURCE_START,
|
|
12
|
-
TEST_CODE_OWNERS
|
|
12
|
+
TEST_CODE_OWNERS,
|
|
13
|
+
TEST_SOURCE_FILE,
|
|
14
|
+
TEST_CONFIGURATION_BROWSER_NAME
|
|
13
15
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
14
16
|
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
15
17
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
@@ -76,10 +78,11 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
76
78
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
|
|
77
79
|
})
|
|
78
80
|
|
|
79
|
-
this.addSub('ci:playwright:test:start', ({ testName, testSuiteAbsolutePath, testSourceLine }) => {
|
|
81
|
+
this.addSub('ci:playwright:test:start', ({ testName, testSuiteAbsolutePath, testSourceLine, browserName }) => {
|
|
80
82
|
const store = storage.getStore()
|
|
81
83
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.rootDir)
|
|
82
|
-
const
|
|
84
|
+
const testSourceFile = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
85
|
+
const span = this.startTestSpan(testName, testSuite, testSourceFile, testSourceLine, browserName)
|
|
83
86
|
|
|
84
87
|
this.enter(span, store)
|
|
85
88
|
})
|
|
@@ -126,9 +129,20 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
126
129
|
})
|
|
127
130
|
}
|
|
128
131
|
|
|
129
|
-
startTestSpan (testName, testSuite, testSourceLine) {
|
|
132
|
+
startTestSpan (testName, testSuite, testSourceFile, testSourceLine, browserName) {
|
|
130
133
|
const testSuiteSpan = this._testSuites.get(testSuite)
|
|
131
|
-
|
|
134
|
+
|
|
135
|
+
const extraTags = {
|
|
136
|
+
[TEST_SOURCE_START]: testSourceLine
|
|
137
|
+
}
|
|
138
|
+
if (testSourceFile) {
|
|
139
|
+
extraTags[TEST_SOURCE_FILE] = testSourceFile || testSuite
|
|
140
|
+
}
|
|
141
|
+
if (browserName) {
|
|
142
|
+
extraTags[TEST_CONFIGURATION_BROWSER_NAME] = browserName
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return super.startTestSpan(testName, testSuite, testSuiteSpan, extraTags)
|
|
132
146
|
}
|
|
133
147
|
}
|
|
134
148
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const ConsumerPlugin = require('../../dd-trace/src/plugins/consumer')
|
|
4
4
|
const { storage } = require('../../datadog-core')
|
|
5
|
+
const { getAmqpMessageSize, CONTEXT_PROPAGATION_KEY } = require('../../dd-trace/src/datastreams/processor')
|
|
5
6
|
|
|
6
7
|
class RheaConsumerPlugin extends ConsumerPlugin {
|
|
7
8
|
static get id () { return 'rhea' }
|
|
@@ -19,7 +20,7 @@ class RheaConsumerPlugin extends ConsumerPlugin {
|
|
|
19
20
|
const name = getResourceNameFromMessage(msgObj)
|
|
20
21
|
const childOf = extractTextMap(msgObj, this.tracer)
|
|
21
22
|
|
|
22
|
-
this.startSpan({
|
|
23
|
+
const span = this.startSpan({
|
|
23
24
|
childOf,
|
|
24
25
|
resource: name,
|
|
25
26
|
type: 'worker',
|
|
@@ -29,6 +30,15 @@ class RheaConsumerPlugin extends ConsumerPlugin {
|
|
|
29
30
|
'amqp.link.role': 'receiver'
|
|
30
31
|
}
|
|
31
32
|
})
|
|
33
|
+
|
|
34
|
+
if (this.config.dsmEnabled && msgObj.message) {
|
|
35
|
+
const payloadSize = getAmqpMessageSize(
|
|
36
|
+
{ headers: msgObj.message.delivery_annotations, content: msgObj.message.body }
|
|
37
|
+
)
|
|
38
|
+
this.tracer.decodeDataStreamsContext(msgObj.message.delivery_annotations[CONTEXT_PROPAGATION_KEY])
|
|
39
|
+
this.tracer
|
|
40
|
+
.setCheckpoint(['direction:in', `topic:${name}`, 'type:rabbitmq'], span, payloadSize)
|
|
41
|
+
}
|
|
32
42
|
}
|
|
33
43
|
}
|
|
34
44
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
|
|
4
4
|
const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
|
|
5
|
+
const { encodePathwayContext } = require('../../dd-trace/src/datastreams/pathway')
|
|
6
|
+
const { getAmqpMessageSize, CONTEXT_PROPAGATION_KEY } = require('../../dd-trace/src/datastreams/processor')
|
|
5
7
|
|
|
6
8
|
class RheaProducerPlugin extends ProducerPlugin {
|
|
7
9
|
static get id () { return 'rhea' }
|
|
@@ -36,6 +38,15 @@ function addDeliveryAnnotations (msg, tracer, span) {
|
|
|
36
38
|
msg.delivery_annotations = msg.delivery_annotations || {}
|
|
37
39
|
|
|
38
40
|
tracer.inject(span, 'text_map', msg.delivery_annotations)
|
|
41
|
+
|
|
42
|
+
if (tracer._config.dsmEnabled) {
|
|
43
|
+
const targetName = span.context()._tags['amqp.link.target.address']
|
|
44
|
+
const payloadSize = getAmqpMessageSize({ content: msg.body, headers: msg.delivery_annotations })
|
|
45
|
+
const dataStreamsContext = tracer
|
|
46
|
+
.setCheckpoint(['direction:out', `exchange:${targetName}`, 'type:rabbitmq'], span, payloadSize)
|
|
47
|
+
const pathwayCtx = encodePathwayContext(dataStreamsContext)
|
|
48
|
+
msg.delivery_annotations[CONTEXT_PROPAGATION_KEY] = pathwayCtx
|
|
49
|
+
}
|
|
39
50
|
}
|
|
40
51
|
}
|
|
41
52
|
|
|
@@ -15,6 +15,8 @@ module.exports = {
|
|
|
15
15
|
HTTP_INCOMING_GRAPHQL_RESOLVERS: 'graphql.server.all_resolvers',
|
|
16
16
|
HTTP_INCOMING_GRAPHQL_RESOLVER: 'graphql.server.resolver',
|
|
17
17
|
|
|
18
|
+
HTTP_OUTGOING_BODY: 'server.response.body',
|
|
19
|
+
|
|
18
20
|
HTTP_CLIENT_IP: 'http.client_ip',
|
|
19
21
|
|
|
20
22
|
USER_ID: 'usr.id',
|
|
@@ -5,6 +5,8 @@ const log = require('../log')
|
|
|
5
5
|
let enabled
|
|
6
6
|
let requestSampling
|
|
7
7
|
|
|
8
|
+
const sampledRequests = new WeakSet()
|
|
9
|
+
|
|
8
10
|
function configure ({ apiSecurity }) {
|
|
9
11
|
enabled = apiSecurity.enabled
|
|
10
12
|
setRequestSampling(apiSecurity.requestSampling)
|
|
@@ -32,17 +34,28 @@ function parseRequestSampling (requestSampling) {
|
|
|
32
34
|
return parsed
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
function sampleRequest () {
|
|
37
|
+
function sampleRequest (req) {
|
|
36
38
|
if (!enabled || !requestSampling) {
|
|
37
39
|
return false
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
const shouldSample = Math.random() <= requestSampling
|
|
43
|
+
|
|
44
|
+
if (shouldSample) {
|
|
45
|
+
sampledRequests.add(req)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return shouldSample
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isSampled (req) {
|
|
52
|
+
return sampledRequests.has(req)
|
|
41
53
|
}
|
|
42
54
|
|
|
43
55
|
module.exports = {
|
|
44
56
|
configure,
|
|
45
57
|
disable,
|
|
46
58
|
setRequestSampling,
|
|
47
|
-
sampleRequest
|
|
59
|
+
sampleRequest,
|
|
60
|
+
isSampled
|
|
48
61
|
}
|
|
@@ -16,5 +16,6 @@ module.exports = {
|
|
|
16
16
|
queryParser: dc.channel('datadog:query:read:finish'),
|
|
17
17
|
setCookieChannel: dc.channel('datadog:iast:set-cookie'),
|
|
18
18
|
nextBodyParsed: dc.channel('apm:next:body-parsed'),
|
|
19
|
-
nextQueryParsed: dc.channel('apm:next:query-parsed')
|
|
19
|
+
nextQueryParsed: dc.channel('apm:next:query-parsed'),
|
|
20
|
+
responseBody: dc.channel('datadog:express:response:json:start')
|
|
20
21
|
}
|
|
@@ -8,7 +8,7 @@ class CommandInjectionAnalyzer extends InjectionAnalyzer {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
onConfigure () {
|
|
11
|
-
this.addSub('datadog:child_process:execution:start', ({ command }) => this.analyze(command))
|
|
11
|
+
this.addSub('tracing:datadog:child_process:execution:start', ({ command }) => this.analyze(command))
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|