dd-trace 5.99.1 → 5.101.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-3rdparty.csv +0 -1
- package/index.d.ts +14 -0
- package/package.json +8 -8
- package/packages/datadog-instrumentations/src/cucumber.js +69 -5
- package/packages/datadog-instrumentations/src/cypress.js +5 -3
- package/packages/datadog-instrumentations/src/express.js +3 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/hono.js +15 -4
- package/packages/datadog-instrumentations/src/http/client.js +20 -3
- package/packages/datadog-instrumentations/src/jest.js +146 -90
- package/packages/datadog-instrumentations/src/mocha/common.js +4 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +43 -26
- package/packages/datadog-instrumentations/src/mocha/utils.js +114 -96
- package/packages/datadog-instrumentations/src/mocha/worker.js +7 -4
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +11 -6
- package/packages/datadog-instrumentations/src/path-to-regexp.js +44 -0
- package/packages/datadog-instrumentations/src/playwright.js +108 -18
- package/packages/datadog-instrumentations/src/router.js +53 -33
- package/packages/datadog-instrumentations/src/vitest.js +76 -30
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -1
- package/packages/datadog-plugin-bullmq/src/consumer.js +5 -4
- package/packages/datadog-plugin-bullmq/src/producer.js +37 -29
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +49 -9
- package/packages/datadog-plugin-cypress/src/plugin.js +5 -14
- package/packages/datadog-plugin-cypress/src/support.js +22 -21
- package/packages/datadog-plugin-grpc/src/client.js +1 -1
- package/packages/datadog-plugin-grpc/src/server.js +1 -1
- package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -9
- package/packages/datadog-plugin-kafkajs/src/producer.js +2 -8
- package/packages/datadog-plugin-mongodb-core/src/index.js +2 -3
- package/packages/datadog-plugin-playwright/src/index.js +6 -0
- package/packages/datadog-plugin-router/src/index.js +13 -0
- package/packages/dd-trace/index.js +4 -3
- package/packages/dd-trace/src/aiguard/sdk.js +2 -2
- package/packages/dd-trace/src/appsec/reporter.js +4 -1
- package/packages/dd-trace/src/baggage.js +10 -0
- package/packages/dd-trace/src/ci-visibility/lage.js +2 -1
- package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -33
- package/packages/dd-trace/src/config/config-types.d.ts +0 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +17 -41
- package/packages/dd-trace/src/config/index.js +7 -60
- package/packages/dd-trace/src/config/normalize-service.js +31 -0
- package/packages/dd-trace/src/config/supported-configurations.json +15 -32
- package/packages/dd-trace/src/datastreams/checkpointer.js +4 -10
- package/packages/dd-trace/src/datastreams/encoding.js +39 -28
- package/packages/dd-trace/src/datastreams/pathway.js +29 -26
- package/packages/dd-trace/src/datastreams/processor.js +17 -15
- package/packages/dd-trace/src/datastreams/size.js +6 -2
- package/packages/dd-trace/src/debugger/config.js +6 -3
- package/packages/dd-trace/src/debugger/devtools_client/index.js +2 -5
- package/packages/dd-trace/src/debugger/devtools_client/send.js +2 -1
- package/packages/dd-trace/src/dogstatsd.js +10 -7
- package/packages/dd-trace/src/encode/0.4.js +3 -3
- package/packages/dd-trace/src/encode/0.5.js +2 -2
- package/packages/dd-trace/src/encode/agentless-json.js +2 -2
- package/packages/dd-trace/src/encode/tags-processors.js +2 -27
- package/packages/dd-trace/src/exporters/common/request.js +22 -11
- package/packages/dd-trace/src/exporters/common/retry.js +104 -0
- package/packages/dd-trace/src/git_metadata.js +66 -0
- package/packages/dd-trace/src/git_metadata_tagger.js +13 -5
- package/packages/dd-trace/src/heap_snapshots.js +4 -4
- package/packages/dd-trace/src/id.js +15 -26
- package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +27 -16
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +3 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +30 -13
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +20 -50
- package/packages/dd-trace/src/llmobs/sdk.js +5 -1
- package/packages/dd-trace/src/llmobs/span_processor.js +28 -2
- package/packages/dd-trace/src/llmobs/tagger.js +42 -0
- package/packages/dd-trace/src/llmobs/telemetry.js +29 -0
- package/packages/dd-trace/src/llmobs/util.js +80 -5
- package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +2 -2
- package/packages/dd-trace/src/opentelemetry/active-span-proxy.js +42 -0
- package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +106 -0
- package/packages/dd-trace/src/opentelemetry/context_manager.js +22 -10
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +308 -0
- package/packages/dd-trace/src/opentelemetry/span.js +42 -108
- package/packages/dd-trace/src/opentelemetry/tracer.js +11 -36
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +95 -36
- package/packages/dd-trace/src/opentracing/propagation/tracestate.js +98 -32
- package/packages/dd-trace/src/opentracing/span.js +58 -49
- package/packages/dd-trace/src/opentracing/span_context.js +1 -0
- package/packages/dd-trace/src/plugins/util/ci.js +119 -32
- package/packages/dd-trace/src/plugins/util/test.js +293 -27
- package/packages/dd-trace/src/priority_sampler.js +6 -4
- package/packages/dd-trace/src/profiling/config.js +5 -4
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +2 -2
- package/packages/dd-trace/src/propagation-hash/index.js +1 -1
- package/packages/dd-trace/src/proxy.js +3 -3
- package/packages/dd-trace/src/remote_config/index.js +5 -3
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +1 -1
- package/packages/dd-trace/src/span_format.js +52 -5
- package/packages/dd-trace/src/span_processor.js +1 -5
- package/packages/dd-trace/src/spanleak.js +0 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +7 -5
- package/packages/dd-trace/src/tracer_metadata.js +1 -1
- package/packages/dd-trace/src/util.js +17 -0
- package/vendor/dist/path-to-regexp/LICENSE +0 -21
- package/vendor/dist/path-to-regexp/index.js +0 -1
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
const { performance } = require('perf_hooks')
|
|
5
5
|
const dateNow = Date.now
|
|
6
6
|
|
|
7
|
+
const satisfies = require('../../../vendor/dist/semifies')
|
|
7
8
|
const {
|
|
8
9
|
TEST_STATUS,
|
|
9
10
|
TEST_IS_RUM_ACTIVE,
|
|
@@ -55,7 +56,9 @@ const {
|
|
|
55
56
|
TEST_IS_MODIFIED,
|
|
56
57
|
TEST_HAS_DYNAMIC_NAME,
|
|
57
58
|
DYNAMIC_NAME_RE,
|
|
58
|
-
|
|
59
|
+
recordAttemptToFixExecution,
|
|
60
|
+
logAttemptToFixTestExecution,
|
|
61
|
+
logTestOptimizationSummary,
|
|
59
62
|
getPullRequestBaseBranch,
|
|
60
63
|
TEST_FINAL_STATUS,
|
|
61
64
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
@@ -106,6 +109,7 @@ const {
|
|
|
106
109
|
} = require('./source-map-utils')
|
|
107
110
|
|
|
108
111
|
const TEST_FRAMEWORK_NAME = 'cypress'
|
|
112
|
+
let hasWarnedDeprecatedCypressVersion = false
|
|
109
113
|
|
|
110
114
|
const CYPRESS_STATUS_TO_TEST_STATUS = {
|
|
111
115
|
passed: 'pass',
|
|
@@ -134,6 +138,20 @@ function getCypressVersion (details) {
|
|
|
134
138
|
return ''
|
|
135
139
|
}
|
|
136
140
|
|
|
141
|
+
function warnDeprecatedCypressVersion (version) {
|
|
142
|
+
if (DD_MAJOR >= 6 || hasWarnedDeprecatedCypressVersion || !version || !satisfies(version, '<12.0.0')) {
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
hasWarnedDeprecatedCypressVersion = true
|
|
147
|
+
// console.warn does not seem to work reliably in Cypress, so use console.log instead.
|
|
148
|
+
// eslint-disable-next-line no-console
|
|
149
|
+
console.log(
|
|
150
|
+
'WARNING: dd-trace support for Cypress<12.0.0 is deprecated' +
|
|
151
|
+
' and will not be supported in dd-trace v6. Please upgrade Cypress to >=12.0.0.'
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
137
155
|
function getRootDir (details) {
|
|
138
156
|
if (details?.config) {
|
|
139
157
|
return details.config.projectRoot || details.config.repoRoot || process.cwd()
|
|
@@ -278,9 +296,8 @@ function getFinalStatus ({
|
|
|
278
296
|
isQuarantined,
|
|
279
297
|
isDisabled,
|
|
280
298
|
}) {
|
|
281
|
-
// If the test is quarantined or disabled,
|
|
282
|
-
|
|
283
|
-
if (isQuarantined || isDisabled || status === 'skip') {
|
|
299
|
+
// If the test is quarantined or disabled, its final status is skip unless attempt-to-fix takes precedence.
|
|
300
|
+
if (status === 'skip' || (retryKind !== FINAL_STATUS_RETRY_KIND.atf && (isQuarantined || isDisabled))) {
|
|
284
301
|
return 'skip'
|
|
285
302
|
}
|
|
286
303
|
|
|
@@ -322,6 +339,8 @@ class CypressPlugin {
|
|
|
322
339
|
isImpactedTestsEnabled = false
|
|
323
340
|
modifiedFiles = []
|
|
324
341
|
newTestsWithDynamicNames = new Set()
|
|
342
|
+
attemptToFixExecutions = new Map()
|
|
343
|
+
loggedAttemptToFixTests = new Set()
|
|
325
344
|
|
|
326
345
|
constructor () {
|
|
327
346
|
const {
|
|
@@ -394,6 +413,8 @@ class CypressPlugin {
|
|
|
394
413
|
this.testManagementTests = undefined
|
|
395
414
|
this.isImpactedTestsEnabled = false
|
|
396
415
|
this.modifiedFiles = []
|
|
416
|
+
this.attemptToFixExecutions = new Map()
|
|
417
|
+
this.loggedAttemptToFixTests = new Set()
|
|
397
418
|
this.activeTestSpan = null
|
|
398
419
|
this.testSuiteSpan = null
|
|
399
420
|
this.testModuleSpan = null
|
|
@@ -429,6 +450,7 @@ class CypressPlugin {
|
|
|
429
450
|
this._isInit = true
|
|
430
451
|
this.tracer = tracer
|
|
431
452
|
this.cypressConfig = cypressConfig
|
|
453
|
+
warnDeprecatedCypressVersion(cypressConfig.version)
|
|
432
454
|
|
|
433
455
|
this.isTestIsolationEnabled = getIsTestIsolationEnabled(cypressConfig)
|
|
434
456
|
|
|
@@ -800,7 +822,10 @@ class CypressPlugin {
|
|
|
800
822
|
this.testSessionSpan.setTag(TEST_MANAGEMENT_ENABLED, 'true')
|
|
801
823
|
}
|
|
802
824
|
|
|
803
|
-
|
|
825
|
+
logTestOptimizationSummary({
|
|
826
|
+
attemptToFixExecutions: this.attemptToFixExecutions,
|
|
827
|
+
newTestsWithDynamicNames: this.newTestsWithDynamicNames,
|
|
828
|
+
})
|
|
804
829
|
|
|
805
830
|
this.testModuleSpan.finish()
|
|
806
831
|
this.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
|
|
@@ -939,10 +964,10 @@ class CypressPlugin {
|
|
|
939
964
|
if (cypressTest.displayError) {
|
|
940
965
|
latestError = new Error(cypressTest.displayError)
|
|
941
966
|
}
|
|
942
|
-
// Update test status - but NOT for quarantined tests where we intentionally
|
|
967
|
+
// Update test status - but NOT for non-ATF quarantined tests where we intentionally
|
|
943
968
|
// report 'fail' to Datadog even though Cypress sees it as 'pass'
|
|
944
969
|
const isQuarantinedTest = finishedTest.testSpan?.context()?._tags?.[TEST_MANAGEMENT_IS_QUARANTINED] === 'true'
|
|
945
|
-
if (cypressTestStatus !== finishedTest.testStatus && !isQuarantinedTest) {
|
|
970
|
+
if (cypressTestStatus !== finishedTest.testStatus && (!isQuarantinedTest || finishedTest.isAttemptToFix)) {
|
|
946
971
|
finishedTest.testSpan.setTag(TEST_STATUS, cypressTestStatus)
|
|
947
972
|
finishedTest.testSpan.setTag('error', latestError)
|
|
948
973
|
}
|
|
@@ -1050,6 +1075,10 @@ class CypressPlugin {
|
|
|
1050
1075
|
return { shouldSkip: true }
|
|
1051
1076
|
}
|
|
1052
1077
|
|
|
1078
|
+
if (isAttemptToFix) {
|
|
1079
|
+
logAttemptToFixTestExecution(testSuite, testName, this.loggedAttemptToFixTests)
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1053
1082
|
// For disabled tests (not attemptToFix), skip them
|
|
1054
1083
|
if (!isAttemptToFix && isDisabled) {
|
|
1055
1084
|
return { shouldSkip: true }
|
|
@@ -1090,6 +1119,7 @@ class CypressPlugin {
|
|
|
1090
1119
|
isAttemptToFix,
|
|
1091
1120
|
isModified,
|
|
1092
1121
|
isQuarantined: isQuarantinedFromSupport,
|
|
1122
|
+
isDisabled: isDisabledFromSupport,
|
|
1093
1123
|
} = test
|
|
1094
1124
|
if (coverage && this.isCodeCoverageEnabled && this.tracer._tracer._exporter?.exportCoverage) {
|
|
1095
1125
|
const coverageFiles = getCoveredFilenamesFromCoverage(coverage)
|
|
@@ -1119,6 +1149,7 @@ class CypressPlugin {
|
|
|
1119
1149
|
this.testStatuses[testName] = [testStatus]
|
|
1120
1150
|
}
|
|
1121
1151
|
const testStatuses = this.testStatuses[testName]
|
|
1152
|
+
const activeSpanTags = this.activeTestSpan.context()._tags
|
|
1122
1153
|
|
|
1123
1154
|
if (error) {
|
|
1124
1155
|
this.activeTestSpan.setTag('error', error)
|
|
@@ -1194,6 +1225,13 @@ class CypressPlugin {
|
|
|
1194
1225
|
this.activeTestSpan.setTag(TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED, 'true')
|
|
1195
1226
|
}
|
|
1196
1227
|
}
|
|
1228
|
+
recordAttemptToFixExecution(this.attemptToFixExecutions, {
|
|
1229
|
+
testSuite,
|
|
1230
|
+
testName,
|
|
1231
|
+
status: testStatus,
|
|
1232
|
+
isDisabled: activeSpanTags[TEST_MANAGEMENT_IS_DISABLED] === 'true',
|
|
1233
|
+
isQuarantined: activeSpanTags[TEST_MANAGEMENT_IS_QUARANTINED] === 'true',
|
|
1234
|
+
})
|
|
1197
1235
|
}
|
|
1198
1236
|
// ATR: set TEST_HAS_FAILED_ALL_RETRIES when all auto test retries were exhausted and every attempt failed
|
|
1199
1237
|
if (this.isFlakyTestRetriesEnabled && !isAttemptToFix && !isEfdRetry &&
|
|
@@ -1207,6 +1245,9 @@ class CypressPlugin {
|
|
|
1207
1245
|
if (isQuarantinedFromSupport) {
|
|
1208
1246
|
this.activeTestSpan.setTag(TEST_MANAGEMENT_IS_QUARANTINED, 'true')
|
|
1209
1247
|
}
|
|
1248
|
+
if (isDisabledFromSupport) {
|
|
1249
|
+
this.activeTestSpan.setTag(TEST_MANAGEMENT_IS_DISABLED, 'true')
|
|
1250
|
+
}
|
|
1210
1251
|
|
|
1211
1252
|
const finishedTest = {
|
|
1212
1253
|
testName,
|
|
@@ -1222,13 +1263,12 @@ class CypressPlugin {
|
|
|
1222
1263
|
this.finishedTestsByFile[testSuite] = [finishedTest]
|
|
1223
1264
|
}
|
|
1224
1265
|
// test spans are finished at after:spec
|
|
1225
|
-
const activeSpanTags = this.activeTestSpan.context()._tags
|
|
1226
1266
|
this.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test', {
|
|
1227
1267
|
hasCodeOwners: !!activeSpanTags[TEST_CODE_OWNERS],
|
|
1228
1268
|
isNew,
|
|
1229
1269
|
isRum: isRUMActive,
|
|
1230
1270
|
browserDriver: 'cypress',
|
|
1231
|
-
isQuarantined:
|
|
1271
|
+
isQuarantined: activeSpanTags[TEST_MANAGEMENT_IS_QUARANTINED] === 'true',
|
|
1232
1272
|
isModified,
|
|
1233
1273
|
isDisabled: activeSpanTags[TEST_MANAGEMENT_IS_DISABLED] === 'true',
|
|
1234
1274
|
})
|
|
@@ -23,22 +23,13 @@ const noopTask = {
|
|
|
23
23
|
module.exports = function CypressPlugin (on, config) {
|
|
24
24
|
const tracer = require('../../dd-trace')
|
|
25
25
|
|
|
26
|
-
if (satisfies(config.version, '<
|
|
27
|
-
if (DD_MAJOR >= 6) {
|
|
28
|
-
// eslint-disable-next-line no-console
|
|
29
|
-
console.error(
|
|
30
|
-
'ERROR: dd-trace v6 has deleted support for Cypress<10.2.0.'
|
|
31
|
-
)
|
|
32
|
-
on('task', noopTask)
|
|
33
|
-
return config
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// console.warn does not seem to work in cypress, so using console.log instead
|
|
26
|
+
if (DD_MAJOR >= 6 && satisfies(config.version, '<12.0.0')) {
|
|
37
27
|
// eslint-disable-next-line no-console
|
|
38
|
-
console.
|
|
39
|
-
'
|
|
40
|
-
' and will not be supported in future versions of dd-trace.'
|
|
28
|
+
console.error(
|
|
29
|
+
'ERROR: dd-trace v6 has deleted support for Cypress<12.0.0.'
|
|
41
30
|
)
|
|
31
|
+
on('task', noopTask)
|
|
32
|
+
return config
|
|
42
33
|
}
|
|
43
34
|
|
|
44
35
|
// The tracer was not init correctly for whatever reason (such as invalid DD_SITE)
|
|
@@ -15,8 +15,8 @@ let isModifiedTest = false
|
|
|
15
15
|
let isTestIsolationEnabled = false
|
|
16
16
|
// Array of test names that have been retried and the reason
|
|
17
17
|
const retryReasonsByTestName = new Map()
|
|
18
|
-
// Track
|
|
19
|
-
const
|
|
18
|
+
// Track test errors suppressed by test management so we can still report them to Datadog.
|
|
19
|
+
const suppressedTestFailures = new Map()
|
|
20
20
|
|
|
21
21
|
// Track the most recently loaded window in the AUT. Updated via the 'window:load'
|
|
22
22
|
// event so we always get the real app window (after cy.visit()), not the
|
|
@@ -61,13 +61,12 @@ Cypress.on('fail', (err, runnable) => {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
const testName = runnable.fullTitle()
|
|
64
|
-
const { isQuarantined, isDisabled } = getTestProperties(testName)
|
|
64
|
+
const { isAttemptToFix, isQuarantined, isDisabled } = getTestProperties(testName)
|
|
65
65
|
|
|
66
66
|
// Suppress failures for quarantined or disabled tests so they don't affect the exit code.
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
quarantinedTestErrors.set(testName, err)
|
|
67
|
+
// Attempt-to-fix ignores quarantine/disabled suppression and keeps the normal framework result.
|
|
68
|
+
if (!isAttemptToFix && (isQuarantined || isDisabled)) {
|
|
69
|
+
suppressedTestFailures.set(testName, { error: err, isQuarantined, isDisabled })
|
|
71
70
|
return
|
|
72
71
|
}
|
|
73
72
|
|
|
@@ -245,13 +244,14 @@ afterEach(function () {
|
|
|
245
244
|
const currentTest = Cypress.mocha.getRunner().suite.ctx.currentTest
|
|
246
245
|
const testName = currentTest.fullTitle()
|
|
247
246
|
|
|
248
|
-
// Check if this was a
|
|
249
|
-
const
|
|
250
|
-
const
|
|
247
|
+
// Check if this was a test management test that we suppressed the failure for.
|
|
248
|
+
const suppressedTestFailure = suppressedTestFailures.get(testName)
|
|
249
|
+
const suppressedError = suppressedTestFailure && suppressedTestFailure.error
|
|
250
|
+
const isTestManagementTestThatFailed = !!suppressedError
|
|
251
251
|
|
|
252
|
-
// For
|
|
253
|
-
const errorToReport =
|
|
254
|
-
? { message:
|
|
252
|
+
// For suppressed test management tests, convert Error to a serializable format for cy.task.
|
|
253
|
+
const errorToReport = isTestManagementTestThatFailed
|
|
254
|
+
? { message: suppressedError.message, stack: suppressedError.stack }
|
|
255
255
|
: currentTest.err
|
|
256
256
|
|
|
257
257
|
const testInfo = {
|
|
@@ -259,16 +259,17 @@ afterEach(function () {
|
|
|
259
259
|
testItTitle: currentTest.title,
|
|
260
260
|
testSuite: Cypress.mocha.getRootSuite().file,
|
|
261
261
|
testSuiteAbsolutePath: Cypress.spec && Cypress.spec.absolute,
|
|
262
|
-
//
|
|
263
|
-
state:
|
|
264
|
-
//
|
|
262
|
+
// Report the actual failed state to Datadog, not the pass state Cypress sees after suppression.
|
|
263
|
+
state: isTestManagementTestThatFailed ? 'failed' : currentTest.state,
|
|
264
|
+
// Include the actual error that was suppressed.
|
|
265
265
|
error: errorToReport,
|
|
266
266
|
isNew: currentTest._ddIsNew,
|
|
267
267
|
isEfdRetry: currentTest._ddIsEfdRetry,
|
|
268
268
|
isAttemptToFix: currentTest._ddIsAttemptToFix,
|
|
269
269
|
isModified: currentTest._ddIsModified,
|
|
270
|
-
// Mark
|
|
271
|
-
isQuarantined:
|
|
270
|
+
// Mark suppressed tests so the plugin can tag them with the correct test management reason.
|
|
271
|
+
isQuarantined: isTestManagementTestThatFailed && suppressedTestFailure.isQuarantined,
|
|
272
|
+
isDisabled: isTestManagementTestThatFailed && suppressedTestFailure.isDisabled,
|
|
272
273
|
}
|
|
273
274
|
try {
|
|
274
275
|
const invocationDetails = Cypress.mocha.getRunner().currentRunnable.invocationDetails
|
|
@@ -292,9 +293,9 @@ afterEach(function () {
|
|
|
292
293
|
// ignore error and continue
|
|
293
294
|
}
|
|
294
295
|
|
|
295
|
-
// Clean up the
|
|
296
|
-
if (
|
|
297
|
-
|
|
296
|
+
// Clean up the suppressed error tracking.
|
|
297
|
+
if (isTestManagementTestThatFailed) {
|
|
298
|
+
suppressedTestFailures.delete(testName)
|
|
298
299
|
}
|
|
299
300
|
|
|
300
301
|
cy.task('dd:afterEach', { test: testInfo, coverage })
|
|
@@ -64,7 +64,7 @@ class GrpcClientPlugin extends ClientPlugin {
|
|
|
64
64
|
|
|
65
65
|
error ({ span = this.activeSpan, error }) {
|
|
66
66
|
this.addCode(span, error.code)
|
|
67
|
-
if (error.code && !this._tracerConfig.
|
|
67
|
+
if (error.code && !this._tracerConfig.DD_GRPC_CLIENT_ERROR_STATUSES.includes(error.code)) {
|
|
68
68
|
return
|
|
69
69
|
}
|
|
70
70
|
this.addError(error, span)
|
|
@@ -70,7 +70,7 @@ class GrpcServerPlugin extends ServerPlugin {
|
|
|
70
70
|
if (!span) return
|
|
71
71
|
|
|
72
72
|
this.addCode(span, error.code)
|
|
73
|
-
if (error.code && !this._tracerConfig.
|
|
73
|
+
if (error.code && !this._tracerConfig.DD_GRPC_SERVER_ERROR_STATUSES.includes(error.code)) {
|
|
74
74
|
return
|
|
75
75
|
}
|
|
76
76
|
this.addError(error)
|
|
@@ -56,15 +56,8 @@ class KafkajsConsumerPlugin extends ConsumerPlugin {
|
|
|
56
56
|
|
|
57
57
|
commit (commitList) {
|
|
58
58
|
if (!this.config.dsmEnabled) return
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
'type',
|
|
62
|
-
'partition',
|
|
63
|
-
'offset',
|
|
64
|
-
'topic',
|
|
65
|
-
]
|
|
66
|
-
for (const commit of commitList.map(this.transformCommit)) {
|
|
67
|
-
if (keys.some(key => !commit.hasOwnProperty(key))) continue
|
|
59
|
+
for (const rawCommit of commitList) {
|
|
60
|
+
const commit = this.transformCommit(rawCommit)
|
|
68
61
|
this.tracer.setOffset(commit)
|
|
69
62
|
}
|
|
70
63
|
}
|
|
@@ -64,14 +64,8 @@ class KafkajsProducerPlugin extends ProducerPlugin {
|
|
|
64
64
|
|
|
65
65
|
if (!this.config.dsmEnabled) return
|
|
66
66
|
if (!commitList || !Array.isArray(commitList)) return
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
'partition',
|
|
70
|
-
'offset',
|
|
71
|
-
'topic',
|
|
72
|
-
]
|
|
73
|
-
for (const commit of commitList.map(r => this.transformProduceResponse(r, clusterId))) {
|
|
74
|
-
if (keys.some(key => !commit.hasOwnProperty(key))) continue
|
|
67
|
+
for (const rawCommit of commitList) {
|
|
68
|
+
const commit = this.transformProduceResponse(rawCommit, clusterId)
|
|
75
69
|
this.tracer.setOffset(commit)
|
|
76
70
|
}
|
|
77
71
|
}
|
|
@@ -160,10 +160,9 @@ function limitDepth (input) {
|
|
|
160
160
|
input, output, depth,
|
|
161
161
|
} = queue.pop()
|
|
162
162
|
const nextDepth = depth + 1
|
|
163
|
-
for (const key
|
|
164
|
-
if (typeof input[key] === 'function') continue
|
|
165
|
-
|
|
163
|
+
for (const key of Object.keys(input)) {
|
|
166
164
|
let child = input[key]
|
|
165
|
+
if (typeof child === 'function') continue
|
|
167
166
|
|
|
168
167
|
if (isBSON(child)) {
|
|
169
168
|
child = typeof child.toJSON === 'function' ? child.toJSON() : '?'
|
|
@@ -39,6 +39,7 @@ const {
|
|
|
39
39
|
TEST_SUITE,
|
|
40
40
|
TEST_HAS_DYNAMIC_NAME,
|
|
41
41
|
DYNAMIC_NAME_RE,
|
|
42
|
+
TEST_FINAL_STATUS,
|
|
42
43
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
43
44
|
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
44
45
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
@@ -319,6 +320,7 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
319
320
|
hasFailedAttemptToFixRetries,
|
|
320
321
|
isAtrRetry,
|
|
321
322
|
isModified,
|
|
323
|
+
finalStatus,
|
|
322
324
|
onDone,
|
|
323
325
|
}) => {
|
|
324
326
|
if (!span) return
|
|
@@ -379,6 +381,9 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
379
381
|
span.setTag(TEST_RETRY_REASON, TEST_RETRY_REASON_TYPES.efd)
|
|
380
382
|
}
|
|
381
383
|
}
|
|
384
|
+
if (finalStatus) {
|
|
385
|
+
span.setTag(TEST_FINAL_STATUS, finalStatus)
|
|
386
|
+
}
|
|
382
387
|
for (const step of steps) {
|
|
383
388
|
const stepStartTime = step.startTime.getTime()
|
|
384
389
|
const stepSpan = this.tracer.startSpan('playwright.step', {
|
|
@@ -451,6 +456,7 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
451
456
|
)
|
|
452
457
|
|
|
453
458
|
span.setTag(TEST_STATUS, 'skip')
|
|
459
|
+
span.setTag(TEST_FINAL_STATUS, 'skip')
|
|
454
460
|
|
|
455
461
|
if (isNew) {
|
|
456
462
|
span.setTag(TEST_IS_NEW, 'true')
|
|
@@ -157,6 +157,13 @@ class RouterPlugin extends WebPlugin {
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
function isMoreSpecificThan (routeA, routeB) {
|
|
160
|
+
// Concrete paths beat catch-all wildcards (`/*splat`, `/api/*`) on the same
|
|
161
|
+
// request so that `/foo/bar` wins over `/foo/*splat` regardless of length.
|
|
162
|
+
if (routeA && routeB) {
|
|
163
|
+
const aWild = hasWildcard(routeA)
|
|
164
|
+
const bWild = hasWildcard(routeB)
|
|
165
|
+
if (aWild !== bWild) return !aWild
|
|
166
|
+
}
|
|
160
167
|
if (!routeIsRegex(routeA) && routeIsRegex(routeB)) {
|
|
161
168
|
return true
|
|
162
169
|
}
|
|
@@ -167,4 +174,10 @@ function routeIsRegex (route) {
|
|
|
167
174
|
return route.includes('(/')
|
|
168
175
|
}
|
|
169
176
|
|
|
177
|
+
function hasWildcard (route) {
|
|
178
|
+
// RegExp routes are encoded as `(/.../)` and may legitimately contain `*`,
|
|
179
|
+
// so only treat plain string patterns as wildcards.
|
|
180
|
+
return !routeIsRegex(route) && route.includes('*')
|
|
181
|
+
}
|
|
182
|
+
|
|
170
183
|
module.exports = RouterPlugin
|
|
@@ -30,9 +30,10 @@ if (!global._ddtrace) {
|
|
|
30
30
|
configurable: true,
|
|
31
31
|
writable: true,
|
|
32
32
|
})
|
|
33
|
-
|
|
34
|
-
global._ddtrace.default = global._ddtrace
|
|
35
|
-
global._ddtrace.tracer = global._ddtrace
|
|
36
33
|
}
|
|
37
34
|
|
|
38
35
|
module.exports = global._ddtrace
|
|
36
|
+
// Static aliases so cjs-module-lexer surfaces them as ESM named exports
|
|
37
|
+
// (`import { tracer } from 'dd-trace'`).
|
|
38
|
+
module.exports.tracer = global._ddtrace
|
|
39
|
+
module.exports.default = global._ddtrace
|
|
@@ -70,7 +70,7 @@ class AIGuard extends NoopAIGuard {
|
|
|
70
70
|
constructor (tracer, config) {
|
|
71
71
|
super()
|
|
72
72
|
|
|
73
|
-
if (!config.apiKey || !config.
|
|
73
|
+
if (!config.apiKey || !config.DD_APP_KEY) {
|
|
74
74
|
log.error('AIGuard: missing api and/or app keys, use env DD_API_KEY and DD_APP_KEY')
|
|
75
75
|
this.#initialized = false
|
|
76
76
|
return
|
|
@@ -78,7 +78,7 @@ class AIGuard extends NoopAIGuard {
|
|
|
78
78
|
this.#tracer = tracer
|
|
79
79
|
this.#headers = {
|
|
80
80
|
'DD-API-KEY': config.apiKey,
|
|
81
|
-
'DD-APPLICATION-KEY': config.
|
|
81
|
+
'DD-APPLICATION-KEY': config.DD_APP_KEY,
|
|
82
82
|
'DD-AI-GUARD-VERSION': tracerVersion,
|
|
83
83
|
'DD-AI-GUARD-SOURCE': 'SDK',
|
|
84
84
|
'DD-AI-GUARD-LANGUAGE': 'nodejs',
|
|
@@ -8,6 +8,7 @@ const web = require('../plugins/util/web')
|
|
|
8
8
|
const { ipHeaderList } = require('../plugins/util/ip_extractor')
|
|
9
9
|
const { keepTrace } = require('../priority_sampler')
|
|
10
10
|
const { ASM } = require('../standalone/product')
|
|
11
|
+
const { isEmpty } = require('../util')
|
|
11
12
|
const { getActiveRequest } = require('./store')
|
|
12
13
|
const {
|
|
13
14
|
incrementWafInitMetric,
|
|
@@ -170,7 +171,9 @@ function getCollectedHeaders (req, res, shouldCollectEventHeaders, storedRespons
|
|
|
170
171
|
// Basic collection
|
|
171
172
|
if (!shouldCollectEventHeaders) return mandatoryCollectedHeaders
|
|
172
173
|
|
|
173
|
-
|
|
174
|
+
// Skip the spread when the stored side is empty -- common during the early
|
|
175
|
+
// request lifecycle when no upstream response headers have been captured.
|
|
176
|
+
const responseHeaders = isEmpty(storedResponseHeaders)
|
|
174
177
|
? res.getHeaders()
|
|
175
178
|
: { ...storedResponseHeaders, ...res.getHeaders() }
|
|
176
179
|
|
|
@@ -66,8 +66,18 @@ function removeAllBaggageItems () {
|
|
|
66
66
|
return EMPTY_STORE
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* @param {BaggageStore} items Frozen in place; do not mutate after.
|
|
71
|
+
*/
|
|
72
|
+
function setAllBaggageItems (items) {
|
|
73
|
+
Object.freeze(items)
|
|
74
|
+
baggageStorage.enterWith(items)
|
|
75
|
+
return items
|
|
76
|
+
}
|
|
77
|
+
|
|
69
78
|
module.exports = {
|
|
70
79
|
setBaggageItem,
|
|
80
|
+
setAllBaggageItems,
|
|
71
81
|
getBaggageItem,
|
|
72
82
|
getAllBaggageItems,
|
|
73
83
|
removeBaggageItem,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { getEnvironmentVariable } = require('../config/helper')
|
|
4
4
|
const { isTrue } = require('../util')
|
|
5
|
+
const { DD_MAJOR } = require('../../../../version')
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Returns the current Lage package name if the Lage package name override is enabled.
|
|
@@ -9,7 +10,7 @@ const { isTrue } = require('../util')
|
|
|
9
10
|
* @returns {string|undefined}
|
|
10
11
|
*/
|
|
11
12
|
function getLagePackageName () {
|
|
12
|
-
if (!isTrue(getEnvironmentVariable('DD_ENABLE_LAGE_PACKAGE_NAME'))) {
|
|
13
|
+
if (DD_MAJOR < 6 && !isTrue(getEnvironmentVariable('DD_ENABLE_LAGE_PACKAGE_NAME'))) {
|
|
13
14
|
return
|
|
14
15
|
}
|
|
15
16
|
|
|
@@ -7,39 +7,13 @@ const zlib = require('zlib')
|
|
|
7
7
|
const { storage } = require('../../../../datadog-core')
|
|
8
8
|
const log = require('../../log')
|
|
9
9
|
const { httpAgent, httpsAgent } = require('../../exporters/common/agents')
|
|
10
|
+
const {
|
|
11
|
+
RATE_LIMIT_MAX_WAIT_MS,
|
|
12
|
+
isRetriableNetworkError,
|
|
13
|
+
singleJitteredDelay,
|
|
14
|
+
} = require('../../exporters/common/retry')
|
|
10
15
|
const { urlToHttpOptions } = require('../../exporters/common/url-to-http-options-polyfill')
|
|
11
16
|
|
|
12
|
-
const RATE_LIMIT_MAX_WAIT_MS = 30_000
|
|
13
|
-
const RETRY_BASE_MS = 5000
|
|
14
|
-
const RETRY_JITTER_MS = 2500
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Calculates retry delay with jitter to prevent thundering herd.
|
|
18
|
-
* Delay is RETRY_BASE_MS + random(0, RETRY_JITTER_MS) (e.g. 5–7.5 seconds).
|
|
19
|
-
*
|
|
20
|
-
* @returns {number} Delay in milliseconds
|
|
21
|
-
*/
|
|
22
|
-
function getRetryDelay () {
|
|
23
|
-
return RETRY_BASE_MS + (Math.random() * RETRY_JITTER_MS)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Determines if a network error is retriable (transient failures only).
|
|
28
|
-
* ECONNREFUSED is retried because it can be transient (service starting up,
|
|
29
|
-
* restarts, rolling deploys, k8s pod/readiness transitions). ENOTFOUND is
|
|
30
|
-
* excluded as it indicates DNS failure or wrong host and is usually not transient.
|
|
31
|
-
*
|
|
32
|
-
* @param {Error} err - The error to check
|
|
33
|
-
* @returns {boolean}
|
|
34
|
-
*/
|
|
35
|
-
function isRetriableNetworkError (err) {
|
|
36
|
-
if (!err.code) return false
|
|
37
|
-
return err.code === 'ECONNREFUSED' ||
|
|
38
|
-
err.code === 'ECONNRESET' ||
|
|
39
|
-
err.code === 'ETIMEDOUT' ||
|
|
40
|
-
err.code === 'EPIPE'
|
|
41
|
-
}
|
|
42
|
-
|
|
43
17
|
function parseUrl (urlObjOrString) {
|
|
44
18
|
if (urlObjOrString !== null && typeof urlObjOrString === 'object') {
|
|
45
19
|
return urlToHttpOptions(urlObjOrString)
|
|
@@ -63,6 +37,10 @@ function parseUrl (urlObjOrString) {
|
|
|
63
37
|
* Destroys connections on errors to prevent reuse of bad connections. Preserves
|
|
64
38
|
* original status code across retries for telemetry.
|
|
65
39
|
*
|
|
40
|
+
* Retry timers stay ref'd. Test-runner plugins block the suite via
|
|
41
|
+
* `delay: true` channels until this callback fires; an unref'd retry would
|
|
42
|
+
* let the host exit first and the suite would never run.
|
|
43
|
+
*
|
|
66
44
|
* @param {string} data - Request body (e.g. JSON string)
|
|
67
45
|
* @param {object} options - { url, path?, method?, headers?, timeout? } (may be mutated)
|
|
68
46
|
* @param {Function} callback - (err, res, statusCode) => void
|
|
@@ -157,7 +135,7 @@ function request (data, options, callback) {
|
|
|
157
135
|
// ignore
|
|
158
136
|
}
|
|
159
137
|
hasRetried = true
|
|
160
|
-
setTimeout(makeRequest,
|
|
138
|
+
setTimeout(makeRequest, singleJitteredDelay())
|
|
161
139
|
return
|
|
162
140
|
}
|
|
163
141
|
|
|
@@ -177,7 +155,7 @@ function request (data, options, callback) {
|
|
|
177
155
|
// Retry on retriable network errors
|
|
178
156
|
if (!hasRetried && isRetriableNetworkError(err)) {
|
|
179
157
|
hasRetried = true
|
|
180
|
-
setTimeout(makeRequest,
|
|
158
|
+
setTimeout(makeRequest, singleJitteredDelay())
|
|
181
159
|
return
|
|
182
160
|
}
|
|
183
161
|
|
|
@@ -8,7 +8,6 @@ export interface ConfigProperties extends GeneratedConfig {
|
|
|
8
8
|
responsesEnabled: boolean
|
|
9
9
|
rules: PayloadTaggingRules
|
|
10
10
|
}
|
|
11
|
-
commitSHA: string | undefined
|
|
12
11
|
debug: boolean
|
|
13
12
|
instrumentationSource: 'manual' | 'ssi'
|
|
14
13
|
isCiVisibility: boolean
|
|
@@ -18,7 +17,6 @@ export interface ConfigProperties extends GeneratedConfig {
|
|
|
18
17
|
lookup: NonNullable<import('../../../../index').TracerOptions['lookup']>
|
|
19
18
|
readonly parsedDdTags: Record<string, string>
|
|
20
19
|
plugins: boolean
|
|
21
|
-
repositoryUrl: string | undefined
|
|
22
20
|
sampler: {
|
|
23
21
|
rateLimit: number
|
|
24
22
|
rules: import('../../../../index').SamplingRule[]
|