dd-trace 6.10.0 → 6.12.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 +5 -0
- package/README.md +0 -17
- package/index.d.ts +128 -6
- package/initialize.mjs +28 -17
- package/openfeature.js +2 -2
- package/package.json +27 -23
- package/packages/datadog-esbuild/index.js +0 -34
- package/packages/datadog-instrumentations/src/anthropic.js +210 -15
- package/packages/datadog-instrumentations/src/aws-sdk.js +9 -1
- package/packages/datadog-instrumentations/src/browser-bunyan.js +11 -0
- package/packages/datadog-instrumentations/src/bunyan.js +3 -16
- package/packages/datadog-instrumentations/src/cucumber.js +30 -15
- package/packages/datadog-instrumentations/src/cypress-config.js +182 -16
- package/packages/datadog-instrumentations/src/cypress-legacy-finalizers.js +30 -0
- package/packages/datadog-instrumentations/src/cypress.js +2 -0
- package/packages/datadog-instrumentations/src/fastify.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +22 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +9 -4
- package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
- package/packages/datadog-instrumentations/src/jest.js +25 -17
- package/packages/datadog-instrumentations/src/mariadb.js +278 -21
- package/packages/datadog-instrumentations/src/mocha/main.js +539 -55
- package/packages/datadog-instrumentations/src/mocha/utils.js +135 -19
- package/packages/datadog-instrumentations/src/playwright-reporter.js +247 -0
- package/packages/datadog-instrumentations/src/playwright.js +182 -35
- package/packages/datadog-instrumentations/src/router.js +2 -0
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +6 -8
- package/packages/datadog-instrumentations/src/vitest-main.js +11 -3
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +163 -44
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -8
- package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
- package/packages/datadog-plugin-browser-bunyan/src/index.js +9 -0
- package/packages/datadog-plugin-bunyan/src/index.js +5 -8
- package/packages/datadog-plugin-cucumber/src/index.js +11 -0
- package/packages/datadog-plugin-cypress/src/after-run.js +5 -1
- package/packages/datadog-plugin-cypress/src/after-spec.js +5 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +81 -29
- package/packages/datadog-plugin-cypress/src/finalization.js +76 -0
- package/packages/datadog-plugin-cypress/src/index.js +12 -6
- package/packages/datadog-plugin-cypress/src/plugin.js +16 -3
- package/packages/datadog-plugin-dd-trace-api/src/index.js +4 -0
- package/packages/datadog-plugin-electron/src/index.js +2 -0
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +6 -0
- package/packages/datadog-plugin-mocha/src/index.js +56 -1
- package/packages/datadog-plugin-nats/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/tracing.js +41 -30
- package/packages/datadog-plugin-openai-agents/src/integration.js +1 -0
- package/packages/datadog-plugin-playwright/src/index.js +46 -13
- package/packages/datadog-plugin-vitest/src/index.js +6 -0
- package/packages/datadog-webpack/index.js +0 -11
- package/packages/dd-trace/index.js +0 -1
- package/packages/dd-trace/src/agent/info.js +6 -5
- package/packages/dd-trace/src/aiguard/client.js +100 -1
- package/packages/dd-trace/src/aiguard/errors.js +41 -0
- package/packages/dd-trace/src/aiguard/evaluation.js +357 -0
- package/packages/dd-trace/src/aiguard/integrations/anthropic.js +70 -0
- package/packages/dd-trace/src/aiguard/integrations/index.js +4 -0
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +423 -0
- package/packages/dd-trace/src/aiguard/messages/openai.js +1 -24
- package/packages/dd-trace/src/aiguard/messages/utils.js +28 -0
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +1 -11
- package/packages/dd-trace/src/aiguard/sdk.js +22 -278
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
- package/packages/dd-trace/src/appsec/reporter.js +2 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +13 -0
- package/packages/dd-trace/src/appsec/telemetry/rasp.js +16 -0
- package/packages/dd-trace/src/appsec/telemetry/waf.js +24 -0
- package/packages/dd-trace/src/carrier.js +107 -48
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +1 -0
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +5 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +29 -3
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +27 -9
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +21 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +164 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +26 -8
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +365 -32
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +6 -4
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +201 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -3
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -5
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +7 -4
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +39 -0
- package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -6
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +10 -4
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +5 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
- package/packages/dd-trace/src/config/index.js +13 -2
- package/packages/dd-trace/src/config/major-overrides.js +7 -0
- package/packages/dd-trace/src/config/supported-configurations.json +14 -0
- package/packages/dd-trace/src/datastreams/index.js +2 -0
- package/packages/dd-trace/src/datastreams/manager.js +9 -2
- package/packages/dd-trace/src/datastreams/processor.js +15 -13
- package/packages/dd-trace/src/datastreams/size.js +5 -0
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/index.js +5 -11
- package/packages/dd-trace/src/debugger/index.js +4 -2
- package/packages/dd-trace/src/debugger/inspect-segment.js +154 -0
- package/packages/dd-trace/src/evp_proxy/constants.js +8 -0
- package/packages/dd-trace/src/evp_proxy/direct.js +64 -0
- package/packages/dd-trace/src/evp_proxy/discovery.js +133 -0
- package/packages/dd-trace/src/evp_proxy/path.js +22 -0
- package/packages/dd-trace/src/exporters/agent/writer.js +49 -9
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +6 -4
- package/packages/dd-trace/src/exporters/common/request.js +5 -2
- package/packages/dd-trace/src/exporters/common/writer.js +11 -6
- package/packages/dd-trace/src/llmobs/experiments/client.js +167 -5
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +484 -92
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +302 -29
- package/packages/dd-trace/src/llmobs/experiments/index.js +75 -64
- package/packages/dd-trace/src/llmobs/experiments/noop.js +135 -7
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +101 -9
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -3
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -2
- package/packages/dd-trace/src/noop/proxy.js +3 -4
- package/packages/dd-trace/src/openfeature/constants/constants.js +0 -18
- package/packages/dd-trace/src/openfeature/flagging_provider.js +18 -2
- package/packages/dd-trace/src/openfeature/index.js +7 -4
- package/packages/dd-trace/src/openfeature/remote_config.js +1 -1
- package/packages/dd-trace/src/openfeature/writers/base.js +153 -11
- package/packages/dd-trace/src/openfeature/writers/exposures.js +62 -14
- package/packages/dd-trace/src/openfeature/writers/util.js +97 -15
- package/packages/dd-trace/src/opentelemetry/context_manager.js +7 -7
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +2 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger.js +67 -11
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +14 -3
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +25 -12
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +2 -5
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +96 -43
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +22 -13
- package/packages/dd-trace/src/opentracing/tracer.js +5 -2
- package/packages/dd-trace/src/otel-thread-ctx.js +387 -0
- package/packages/dd-trace/src/plugin_manager.js +3 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/jest.js +4 -3
- package/packages/dd-trace/src/plugins/util/test.js +9 -9
- package/packages/dd-trace/src/plugins/util/web.js +3 -0
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +37 -90
- package/packages/dd-trace/src/profiling/webspan-utils.js +45 -0
- package/packages/dd-trace/src/proxy.js +38 -29
- package/packages/dd-trace/src/ritm.js +9 -2
- package/packages/dd-trace/src/serverless.js +37 -0
- package/packages/dd-trace/src/span_format.js +1 -0
- package/packages/dd-trace/src/span_stats.js +30 -3
- package/packages/dd-trace/src/storage-channels.js +86 -0
- package/packages/dd-trace/src/telemetry/metrics.js +62 -8
- package/packages/dd-trace/src/telemetry/send-data.js +1 -0
- package/packages/dd-trace/src/tracer.js +9 -2
- package/packages/dd-trace/src/tracer_metadata.js +14 -1
- package/packages/dd-trace/src/web-tags-cache.js +132 -0
- package/vendor/dist/@datadog/openfeature-node-server/LICENSE +201 -0
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -0
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/https-proxy-agent/LICENSE +22 -0
- package/vendor/dist/https-proxy-agent/index.js +4 -0
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/proxy-from-env/LICENSE +20 -0
- package/vendor/dist/proxy-from-env/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +0 -65
- package/packages/datadog-webpack/src/optional-peer-loader.js +0 -17
- package/packages/dd-trace/src/feature-registry.js +0 -29
- package/packages/dd-trace/src/openfeature/register.js +0 -35
- package/packages/dd-trace/src/openfeature/require-provider.js +0 -18
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const getConfig = require('../../../config')
|
|
3
|
-
const
|
|
3
|
+
const { EVP_SUBDOMAIN_HEADER_NAME } = require('../../../evp_proxy/constants')
|
|
4
|
+
const { joinEVPProxyPath } = require('../../../evp_proxy/path')
|
|
4
5
|
const { safeJSONStringify } = require('../../../exporters/common/util')
|
|
5
6
|
const log = require('../../../log')
|
|
6
7
|
|
|
7
|
-
const { AgentlessCiVisibilityEncoder } = require('../../../encode/agentless-ci-visibility')
|
|
8
|
-
const BaseWriter = require('../../../exporters/common/writer')
|
|
9
8
|
const {
|
|
10
9
|
incrementCountMetric,
|
|
11
10
|
distributionMetric,
|
|
@@ -15,17 +14,35 @@ const {
|
|
|
15
14
|
TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS,
|
|
16
15
|
TELEMETRY_ENDPOINT_PAYLOAD_DROPPED,
|
|
17
16
|
} = require('../../../ci-visibility/telemetry')
|
|
17
|
+
const { AgentlessCiVisibilityEncoder } = require('../../../encode/agentless-ci-visibility')
|
|
18
|
+
const BaseWriter = require('../../../exporters/common/writer')
|
|
19
|
+
const request = require('../request')
|
|
20
|
+
const TestOptimizationRequestTracker = require('./request-tracker')
|
|
18
21
|
|
|
19
22
|
class Writer extends BaseWriter {
|
|
23
|
+
#requestTracker
|
|
24
|
+
|
|
20
25
|
constructor ({ url, tags, evpProxyPrefix = '' }) {
|
|
21
26
|
super(...arguments)
|
|
27
|
+
this.#requestTracker = new TestOptimizationRequestTracker(this)
|
|
22
28
|
const { 'runtime-id': runtimeId, env, service } = tags
|
|
23
29
|
this._url = url
|
|
24
30
|
this._encoder = new AgentlessCiVisibilityEncoder(this, { runtimeId, env, service })
|
|
25
31
|
this._evpProxyPrefix = evpProxyPrefix
|
|
26
32
|
}
|
|
27
33
|
|
|
28
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Flushes buffered events, waiting for tracked requests during finalization.
|
|
36
|
+
*
|
|
37
|
+
* @param {(error?: Error) => void} [done]
|
|
38
|
+
* @param {{ deadline?: number }} [options]
|
|
39
|
+
* @returns {void}
|
|
40
|
+
*/
|
|
41
|
+
flush (done, options) {
|
|
42
|
+
this.#requestTracker.flush(done, options)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
_sendPayload (data, _, done, flushOptions) {
|
|
29
46
|
const options = {
|
|
30
47
|
path: '/api/v2/citestcycle',
|
|
31
48
|
method: 'POST',
|
|
@@ -35,12 +52,13 @@ class Writer extends BaseWriter {
|
|
|
35
52
|
},
|
|
36
53
|
timeout: 15_000,
|
|
37
54
|
url: this._url,
|
|
55
|
+
deadline: flushOptions?.deadline,
|
|
38
56
|
}
|
|
39
57
|
|
|
40
58
|
if (this._evpProxyPrefix) {
|
|
41
|
-
options.path =
|
|
59
|
+
options.path = joinEVPProxyPath(this._evpProxyPrefix, '/api/v2/citestcycle')
|
|
42
60
|
delete options.headers['dd-api-key']
|
|
43
|
-
options.headers[
|
|
61
|
+
options.headers[EVP_SUBDOMAIN_HEADER_NAME] = 'citestcycle-intake'
|
|
44
62
|
}
|
|
45
63
|
|
|
46
64
|
// eslint-disable-next-line eslint-rules/eslint-log-printf-style
|
|
@@ -51,7 +69,7 @@ class Writer extends BaseWriter {
|
|
|
51
69
|
incrementCountMetric(TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS, { endpoint: 'test_cycle' })
|
|
52
70
|
distributionMetric(TELEMETRY_ENDPOINT_PAYLOAD_BYTES, { endpoint: 'test_cycle' }, Buffer.byteLength(data))
|
|
53
71
|
|
|
54
|
-
request
|
|
72
|
+
this.#requestTracker.send(request, data, options, (err, res, statusCode) => {
|
|
55
73
|
distributionMetric(
|
|
56
74
|
TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_MS,
|
|
57
75
|
{ endpoint: 'test_cycle' },
|
|
@@ -67,7 +85,7 @@ class Writer extends BaseWriter {
|
|
|
67
85
|
{ endpoint: 'test_cycle' }
|
|
68
86
|
)
|
|
69
87
|
log.error('Error sending CI agentless payload', err)
|
|
70
|
-
done()
|
|
88
|
+
done(err)
|
|
71
89
|
return
|
|
72
90
|
}
|
|
73
91
|
log.debug('Response from the intake:', res)
|
|
@@ -12,13 +12,16 @@ const { getTestManagementTests: getTestManagementTestsRequest } =
|
|
|
12
12
|
require('../test-management/get-test-management-tests')
|
|
13
13
|
const { writeSettingsToCache } = require('../test-optimization-cache')
|
|
14
14
|
const { CACHE_MISS, TestOptimizationHttpCache } = require('../test-optimization-http-cache')
|
|
15
|
+
const { incrementCountMetric, TELEMETRY_EVENTS_ENQUEUED_FOR_SERIALIZATION } = require('../telemetry')
|
|
15
16
|
const { uploadCoverageReport: uploadCoverageReportRequest } = require('../requests/upload-coverage-report')
|
|
16
17
|
const { uploadTestScreenshot: uploadTestScreenshotRequest } = require('../requests/upload-test-screenshot')
|
|
17
18
|
const { parsers } = require('../../config/parsers')
|
|
18
19
|
const log = require('../../log')
|
|
20
|
+
const spanFormat = require('../../span_format')
|
|
19
21
|
const { getSegment } = require('../../util')
|
|
20
22
|
const BufferingExporter = require('../../exporters/common/buffering-exporter')
|
|
21
23
|
const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../../plugins/util/tags')
|
|
24
|
+
const { TEST_STATUS } = require('../../plugins/util/test')
|
|
22
25
|
const { sendGitMetadata: sendGitMetadataRequest } = require('./git/git_metadata')
|
|
23
26
|
|
|
24
27
|
const hostname = getHostname()
|
|
@@ -57,6 +60,17 @@ function getIsTestSessionTrace (trace) {
|
|
|
57
60
|
const GIT_UPLOAD_TIMEOUT = 60_000 // 60 seconds
|
|
58
61
|
const CAN_USE_CI_VIS_PROTOCOL_TIMEOUT = GIT_UPLOAD_TIMEOUT
|
|
59
62
|
const MAX_COVERAGE_REPORT_FLAGS = 32
|
|
63
|
+
const FINAL_FLUSH_TIMEOUT = 10_000
|
|
64
|
+
const FINAL_FLUSH_FALLBACK_DELAY = 100
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @returns {Error}
|
|
68
|
+
*/
|
|
69
|
+
function createFinalFlushTimeoutError () {
|
|
70
|
+
const error = new Error('Timed out waiting for Test Optimization to flush')
|
|
71
|
+
error.code = 'ERR_DD_TEST_OPTIMIZATION_FLUSH_TIMEOUT'
|
|
72
|
+
return error
|
|
73
|
+
}
|
|
60
74
|
|
|
61
75
|
function appendLogTag (tags, key, value) {
|
|
62
76
|
if (value !== undefined) {
|
|
@@ -87,6 +101,12 @@ function getLogTags (logMessage, { env, version }, gitRepositoryUrl, gitCommitSh
|
|
|
87
101
|
}
|
|
88
102
|
|
|
89
103
|
class CiVisibilityExporter extends BufferingExporter {
|
|
104
|
+
#finalFlush
|
|
105
|
+
#deferredTestSessionTraces = []
|
|
106
|
+
#pendingScreenshotUploads = new Set()
|
|
107
|
+
#screenshotFlushWaiters = new Set()
|
|
108
|
+
#deferredTestSuiteSpans = new Map()
|
|
109
|
+
|
|
90
110
|
constructor (config, options = {}) {
|
|
91
111
|
super(config)
|
|
92
112
|
this._timer = undefined
|
|
@@ -137,18 +157,7 @@ class CiVisibilityExporter extends BufferingExporter {
|
|
|
137
157
|
}
|
|
138
158
|
})
|
|
139
159
|
|
|
140
|
-
|
|
141
|
-
if (this._writer) {
|
|
142
|
-
this._writer.flush()
|
|
143
|
-
}
|
|
144
|
-
if (this._coverageWriter) {
|
|
145
|
-
this._coverageWriter.flush()
|
|
146
|
-
}
|
|
147
|
-
if (this._logsWriter) {
|
|
148
|
-
this._logsWriter.flush()
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(flush.bind(this))
|
|
160
|
+
globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(() => this.flush(() => {}))
|
|
152
161
|
}
|
|
153
162
|
|
|
154
163
|
shouldRequestSkippableSuites () {
|
|
@@ -399,18 +408,189 @@ class CiVisibilityExporter extends BufferingExporter {
|
|
|
399
408
|
}
|
|
400
409
|
|
|
401
410
|
export (trace) {
|
|
411
|
+
this.#resetFinalFlush()
|
|
412
|
+
|
|
413
|
+
if (this.#deferredTestSuiteSpans.size === 0) {
|
|
414
|
+
this.#exportTrace(trace)
|
|
415
|
+
return
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
let hasDeferredTestSuiteSpan = false
|
|
419
|
+
let immediateTrace
|
|
420
|
+
for (let index = 0; index < trace.length; index++) {
|
|
421
|
+
const formattedSpan = trace[index]
|
|
422
|
+
const spanId = formattedSpan.span_id?.toString()
|
|
423
|
+
const deferredTestSuiteSpan = this.#deferredTestSuiteSpans.get(spanId)
|
|
424
|
+
if (deferredTestSuiteSpan) {
|
|
425
|
+
if (!hasDeferredTestSuiteSpan && index > 0) immediateTrace = trace.slice(0, index)
|
|
426
|
+
hasDeferredTestSuiteSpan = true
|
|
427
|
+
if (deferredTestSuiteSpan.emitted || (this._isInitialized && !this.canReportSessionTraces())) {
|
|
428
|
+
this.#deferredTestSuiteSpans.delete(spanId)
|
|
429
|
+
} else {
|
|
430
|
+
deferredTestSuiteSpan.formattedSpan = formattedSpan
|
|
431
|
+
}
|
|
432
|
+
} else if (hasDeferredTestSuiteSpan) {
|
|
433
|
+
immediateTrace ??= []
|
|
434
|
+
immediateTrace.push(formattedSpan)
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (!hasDeferredTestSuiteSpan) this.#exportTrace(trace)
|
|
439
|
+
else if (immediateTrace) this.#exportTrace(immediateTrace)
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Exports spans that are not retained for late updates to session, module, or suite events.
|
|
444
|
+
*
|
|
445
|
+
* @param {Array<object>} trace
|
|
446
|
+
* @returns {void}
|
|
447
|
+
*/
|
|
448
|
+
#exportTrace (trace) {
|
|
402
449
|
// Until it's initialized, we just store the traces as is
|
|
403
450
|
if (!this._isInitialized) {
|
|
404
451
|
this._traceBuffer.push(trace)
|
|
405
452
|
return
|
|
406
453
|
}
|
|
407
|
-
|
|
454
|
+
const isTestSessionTrace = getIsTestSessionTrace(trace)
|
|
455
|
+
if (!this.canReportSessionTraces() && isTestSessionTrace) {
|
|
408
456
|
return
|
|
409
457
|
}
|
|
410
|
-
this._export(trace)
|
|
458
|
+
if (this._export(trace, undefined, undefined, isTestSessionTrace) === false && isTestSessionTrace) {
|
|
459
|
+
this.#deferredTestSessionTraces.push(trace)
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Retains a completed suite until finalization so a later framework error can still update it.
|
|
465
|
+
*
|
|
466
|
+
* @param {import('../../opentracing/span')} testSuiteSpan
|
|
467
|
+
* @returns {void}
|
|
468
|
+
*/
|
|
469
|
+
deferTestSuiteSpan (testSuiteSpan) {
|
|
470
|
+
this.#resetFinalFlush()
|
|
471
|
+
this.#deferredTestSuiteSpans.set(testSuiteSpan.context()._spanId.toString(), {
|
|
472
|
+
span: testSuiteSpan,
|
|
473
|
+
formattedSpan: undefined,
|
|
474
|
+
emitted: false,
|
|
475
|
+
})
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Retains formatted test suite events received from test framework workers until finalization.
|
|
480
|
+
*
|
|
481
|
+
* @param {Array<object>} trace
|
|
482
|
+
* @returns {void}
|
|
483
|
+
*/
|
|
484
|
+
exportTraceWithDeferredTestSuite (trace) {
|
|
485
|
+
for (const formattedSpan of trace) {
|
|
486
|
+
if (formattedSpan.type !== 'test_suite_end') continue
|
|
487
|
+
|
|
488
|
+
this.#deferredTestSuiteSpans.set(formattedSpan.span_id.toString(), {
|
|
489
|
+
span: undefined,
|
|
490
|
+
formattedSpan,
|
|
491
|
+
emitted: false,
|
|
492
|
+
})
|
|
493
|
+
}
|
|
494
|
+
this.export(trace)
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Applies a late framework error to every completed suite retained by the current finalization boundary.
|
|
499
|
+
*
|
|
500
|
+
* @param {Error} error
|
|
501
|
+
* @returns {void}
|
|
502
|
+
*/
|
|
503
|
+
setDeferredTestSuiteError (error) {
|
|
504
|
+
for (const { span, formattedSpan, emitted } of this.#deferredTestSuiteSpans.values()) {
|
|
505
|
+
if (emitted) continue
|
|
506
|
+
if (span) {
|
|
507
|
+
span.setTag(TEST_STATUS, 'fail')
|
|
508
|
+
span.setTag('error', error)
|
|
509
|
+
} else if (formattedSpan) {
|
|
510
|
+
formattedSpan.meta[TEST_STATUS] = 'fail'
|
|
511
|
+
spanFormat.addError(formattedSpan, error)
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Appends a completed suite while preserving the ordinary writer call shape outside finalization.
|
|
518
|
+
*
|
|
519
|
+
* @param {object} formattedSpan
|
|
520
|
+
* @param {{ deadline?: number }} [options]
|
|
521
|
+
* @returns {boolean|undefined}
|
|
522
|
+
*/
|
|
523
|
+
#appendDeferredTestSuiteSpan (formattedSpan, options) {
|
|
524
|
+
const appended = options === undefined
|
|
525
|
+
? this._writer.append([formattedSpan])
|
|
526
|
+
: this._writer.append([formattedSpan], options)
|
|
527
|
+
|
|
528
|
+
if (appended !== false && this._config.isCiVisibility) {
|
|
529
|
+
incrementCountMetric(TELEMETRY_EVENTS_ENQUEUED_FOR_SERIALIZATION)
|
|
530
|
+
}
|
|
531
|
+
return appended
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Appends completed suites before their module and session parents are exported.
|
|
536
|
+
*
|
|
537
|
+
* @param {{ deadline?: number }} [options]
|
|
538
|
+
* @returns {void}
|
|
539
|
+
*/
|
|
540
|
+
exportDeferredTestSuiteSpans (options) {
|
|
541
|
+
if (!this._writer || !this.canReportSessionTraces()) return
|
|
542
|
+
|
|
543
|
+
for (const [spanId, deferredTestSuiteSpan] of this.#deferredTestSuiteSpans) {
|
|
544
|
+
const { span, formattedSpan, emitted } = deferredTestSuiteSpan
|
|
545
|
+
if (emitted) continue
|
|
546
|
+
const updatedSpan = span && spanFormat(span)
|
|
547
|
+
if (span && formattedSpan) {
|
|
548
|
+
formattedSpan.error = updatedSpan.error
|
|
549
|
+
Object.assign(formattedSpan.meta, updatedSpan.meta)
|
|
550
|
+
Object.assign(formattedSpan.metrics, updatedSpan.metrics)
|
|
551
|
+
if (this.#appendDeferredTestSuiteSpan(formattedSpan, options) === false) continue
|
|
552
|
+
this.#deferredTestSuiteSpans.delete(spanId)
|
|
553
|
+
} else if (formattedSpan) {
|
|
554
|
+
if (this.#appendDeferredTestSuiteSpan(formattedSpan, options) === false) continue
|
|
555
|
+
this.#deferredTestSuiteSpans.delete(spanId)
|
|
556
|
+
} else {
|
|
557
|
+
if (this.#appendDeferredTestSuiteSpan(updatedSpan, options) === false) continue
|
|
558
|
+
deferredTestSuiteSpan.emitted = true
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Discards completed suites that the selected transport cannot deliver.
|
|
565
|
+
*
|
|
566
|
+
* @returns {void}
|
|
567
|
+
*/
|
|
568
|
+
resetDeferredTestSuiteSpans () {
|
|
569
|
+
this.#deferredTestSuiteSpans.clear()
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Retries session, module, and suite traces rejected by writer backpressure within the final deadline.
|
|
574
|
+
*
|
|
575
|
+
* @param {{ deadline?: number }} options final-flush options
|
|
576
|
+
* @returns {void}
|
|
577
|
+
*/
|
|
578
|
+
#exportDeferredTestSessionTraces (options) {
|
|
579
|
+
if (!this._writer || !this.canReportSessionTraces()) return
|
|
580
|
+
|
|
581
|
+
let retainedCount = 0
|
|
582
|
+
|
|
583
|
+
for (const trace of this.#deferredTestSessionTraces) {
|
|
584
|
+
if (this._writer.append(trace, options) === false) {
|
|
585
|
+
this.#deferredTestSessionTraces[retainedCount++] = trace
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
this.#deferredTestSessionTraces.length = retainedCount
|
|
411
589
|
}
|
|
412
590
|
|
|
413
591
|
exportCoverage (formattedCoverage) {
|
|
592
|
+
this.#resetFinalFlush()
|
|
593
|
+
|
|
414
594
|
// Until it's initialized, we just store the coverages as is
|
|
415
595
|
if (!this._isInitialized) {
|
|
416
596
|
this._coverageBuffer.push(formattedCoverage)
|
|
@@ -455,6 +635,7 @@ class CiVisibilityExporter extends BufferingExporter {
|
|
|
455
635
|
return
|
|
456
636
|
}
|
|
457
637
|
|
|
638
|
+
this.#resetFinalFlush()
|
|
458
639
|
this._export(
|
|
459
640
|
this.formatLogMessage(testEnvironmentMetadata, logMessage),
|
|
460
641
|
this._logsWriter,
|
|
@@ -462,32 +643,140 @@ class CiVisibilityExporter extends BufferingExporter {
|
|
|
462
643
|
)
|
|
463
644
|
}
|
|
464
645
|
|
|
465
|
-
flush (done
|
|
466
|
-
|
|
467
|
-
|
|
646
|
+
flush (done) {
|
|
647
|
+
const isFinalFlush = typeof done === 'function'
|
|
648
|
+
const onDone = done || (() => {})
|
|
649
|
+
let finalFlush
|
|
650
|
+
|
|
651
|
+
if (isFinalFlush && this.#finalFlush) {
|
|
652
|
+
if (this.#finalFlush.completed) onDone(this.#finalFlush.error)
|
|
653
|
+
else this.#finalFlush.callbacks.push(onDone)
|
|
654
|
+
return
|
|
468
655
|
}
|
|
469
656
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
this.
|
|
473
|
-
this.
|
|
474
|
-
|
|
475
|
-
|
|
657
|
+
if (isFinalFlush && !this._isInitialized &&
|
|
658
|
+
this._traceBuffer.length === 0 && this._coverageBuffer.length === 0 &&
|
|
659
|
+
this.#deferredTestSuiteSpans.size === 0 && this.#deferredTestSessionTraces.length === 0 &&
|
|
660
|
+
this.#pendingScreenshotUploads.size === 0) {
|
|
661
|
+
onDone()
|
|
662
|
+
return
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
if (isFinalFlush) {
|
|
666
|
+
finalFlush = {
|
|
667
|
+
callbacks: [onDone],
|
|
668
|
+
completed: false,
|
|
669
|
+
error: undefined,
|
|
670
|
+
}
|
|
671
|
+
this.#finalFlush = finalFlush
|
|
672
|
+
}
|
|
476
673
|
|
|
477
|
-
|
|
674
|
+
const deadline = isFinalFlush ? Date.now() + FINAL_FLUSH_TIMEOUT : undefined
|
|
675
|
+
let hasCompleted = false
|
|
676
|
+
let initializationTimeoutId
|
|
677
|
+
|
|
678
|
+
const fallbackTimeoutId = isFinalFlush
|
|
679
|
+
? setTimeout(() => {
|
|
680
|
+
complete(createFinalFlushTimeoutError())
|
|
681
|
+
}, FINAL_FLUSH_TIMEOUT + FINAL_FLUSH_FALLBACK_DELAY)
|
|
682
|
+
: undefined
|
|
683
|
+
|
|
684
|
+
const complete = (error) => {
|
|
685
|
+
if (hasCompleted) return
|
|
686
|
+
hasCompleted = true
|
|
687
|
+
clearTimeout(fallbackTimeoutId)
|
|
688
|
+
clearTimeout(initializationTimeoutId)
|
|
689
|
+
this.#screenshotFlushWaiters.delete(flushWriters)
|
|
690
|
+
if (error) log.error('Error flushing Test Optimization data', error)
|
|
691
|
+
if (!isFinalFlush) {
|
|
692
|
+
onDone(error)
|
|
693
|
+
return
|
|
694
|
+
}
|
|
478
695
|
|
|
479
|
-
|
|
480
|
-
|
|
696
|
+
finalFlush.completed = true
|
|
697
|
+
finalFlush.error = error
|
|
698
|
+
const callbacks = finalFlush.callbacks
|
|
699
|
+
finalFlush.callbacks = []
|
|
700
|
+
for (const callback of callbacks) {
|
|
701
|
+
try {
|
|
702
|
+
callback(error)
|
|
703
|
+
} catch (callbackError) {
|
|
704
|
+
log.error('Error completing Test Optimization flush callback', callbackError)
|
|
705
|
+
}
|
|
706
|
+
}
|
|
481
707
|
}
|
|
482
708
|
|
|
483
|
-
const
|
|
484
|
-
|
|
709
|
+
const flushWriters = () => {
|
|
710
|
+
if (isFinalFlush && this.#pendingScreenshotUploads.size !== 0) {
|
|
711
|
+
this.#screenshotFlushWaiters.add(flushWriters)
|
|
712
|
+
return
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const options = deadline === undefined ? undefined : { deadline }
|
|
716
|
+
if (isFinalFlush) {
|
|
717
|
+
this.exportDeferredTestSuiteSpans(options)
|
|
718
|
+
this.#exportDeferredTestSessionTraces(options)
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const writers = [
|
|
722
|
+
this._writer,
|
|
723
|
+
this._coverageWriter,
|
|
724
|
+
this._logsWriter,
|
|
725
|
+
].filter(Boolean)
|
|
726
|
+
|
|
727
|
+
let remaining = writers.length
|
|
728
|
+
let flushError
|
|
729
|
+
|
|
485
730
|
if (remaining === 0) {
|
|
486
|
-
|
|
731
|
+
complete()
|
|
732
|
+
return
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
const onFlushComplete = (error) => {
|
|
736
|
+
flushError ||= error
|
|
737
|
+
remaining -= 1
|
|
738
|
+
if (remaining === 0) complete(flushError)
|
|
487
739
|
}
|
|
740
|
+
|
|
741
|
+
for (const writer of writers) writer.flush(onFlushComplete, options)
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
if (isFinalFlush && this._initializationRequest) {
|
|
745
|
+
const initializationRequest = this._initializationRequest
|
|
746
|
+
const { controller, options } = initializationRequest
|
|
747
|
+
initializationRequest.finalFlush = finalFlush
|
|
748
|
+
options.deadline = deadline
|
|
749
|
+
initializationTimeoutId = setTimeout(() => {
|
|
750
|
+
const error = createFinalFlushTimeoutError()
|
|
751
|
+
if (initializationRequest.finalFlush === finalFlush) controller.abort(error)
|
|
752
|
+
complete(error)
|
|
753
|
+
}, Math.max(0, deadline - Date.now()))
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if (!isFinalFlush) {
|
|
757
|
+
if (this._isInitialized) flushWriters()
|
|
758
|
+
else complete()
|
|
759
|
+
return
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if (this._isInitialized) {
|
|
763
|
+
flushWriters()
|
|
764
|
+
return
|
|
488
765
|
}
|
|
489
766
|
|
|
490
|
-
|
|
767
|
+
this._canUseCiVisProtocolPromise.then(() => {
|
|
768
|
+
clearTimeout(initializationTimeoutId)
|
|
769
|
+
if (!hasCompleted) flushWriters()
|
|
770
|
+
})
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Allows later test activity to establish a new finalization boundary.
|
|
775
|
+
*
|
|
776
|
+
* @returns {void}
|
|
777
|
+
*/
|
|
778
|
+
#resetFinalFlush () {
|
|
779
|
+
this.#finalFlush = undefined
|
|
491
780
|
}
|
|
492
781
|
|
|
493
782
|
exportUncodedCoverages () {
|
|
@@ -572,13 +861,55 @@ class CiVisibilityExporter extends BufferingExporter {
|
|
|
572
861
|
* @param {string} options.traceId - Test trace id used as the screenshot key
|
|
573
862
|
* @param {string} options.idempotencyKey - Stable per-artifact key, reused on retry
|
|
574
863
|
* @param {number} options.capturedAtMs - Capture time in epoch milliseconds
|
|
864
|
+
* @param {AbortSignal} [options.signal] - Additional signal used to cancel the upload
|
|
575
865
|
* @param {Function} callback - Callback function (err)
|
|
576
866
|
*/
|
|
577
|
-
uploadTestScreenshot ({ filePath, traceId, idempotencyKey, capturedAtMs }, callback) {
|
|
867
|
+
uploadTestScreenshot ({ filePath, traceId, idempotencyKey, capturedAtMs, signal }, callback) {
|
|
578
868
|
if (!this._testScreenshotUploadUrl) {
|
|
579
869
|
return callback(new Error('Test screenshot upload URL not configured'))
|
|
580
870
|
}
|
|
581
871
|
|
|
872
|
+
this.#resetFinalFlush()
|
|
873
|
+
const controller = new AbortController()
|
|
874
|
+
const deadline = Date.now() + FINAL_FLUSH_TIMEOUT
|
|
875
|
+
let settled = false
|
|
876
|
+
const complete = (error) => {
|
|
877
|
+
if (settled) return
|
|
878
|
+
settled = true
|
|
879
|
+
clearTimeout(timeoutId)
|
|
880
|
+
signal?.removeEventListener('abort', onAbort)
|
|
881
|
+
|
|
882
|
+
try {
|
|
883
|
+
callback(error)
|
|
884
|
+
} finally {
|
|
885
|
+
this.#pendingScreenshotUploads.delete(controller)
|
|
886
|
+
if (this.#pendingScreenshotUploads.size === 0) {
|
|
887
|
+
const waiters = [...this.#screenshotFlushWaiters]
|
|
888
|
+
this.#screenshotFlushWaiters.clear()
|
|
889
|
+
for (const waiter of waiters) queueMicrotask(waiter)
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
const onAbort = () => {
|
|
894
|
+
const error = signal.reason || Object.assign(new Error('Test screenshot upload aborted'), { code: 'ABORT_ERR' })
|
|
895
|
+
controller.abort(error)
|
|
896
|
+
complete(error)
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
this.#pendingScreenshotUploads.add(controller)
|
|
900
|
+
signal?.addEventListener('abort', onAbort, { once: true })
|
|
901
|
+
const timeoutId = setTimeout(() => {
|
|
902
|
+
const error = createFinalFlushTimeoutError()
|
|
903
|
+
controller.abort(error)
|
|
904
|
+
complete(error)
|
|
905
|
+
}, FINAL_FLUSH_TIMEOUT)
|
|
906
|
+
timeoutId.unref?.()
|
|
907
|
+
|
|
908
|
+
if (signal?.aborted) {
|
|
909
|
+
onAbort()
|
|
910
|
+
return
|
|
911
|
+
}
|
|
912
|
+
|
|
582
913
|
uploadTestScreenshotRequest({
|
|
583
914
|
filePath,
|
|
584
915
|
traceId,
|
|
@@ -587,7 +918,9 @@ class CiVisibilityExporter extends BufferingExporter {
|
|
|
587
918
|
url: this._testScreenshotUploadUrl,
|
|
588
919
|
isEvpProxy: !!this._isUsingEvpProxy,
|
|
589
920
|
evpProxyPrefix: this.evpProxyPrefix,
|
|
590
|
-
|
|
921
|
+
deadline,
|
|
922
|
+
signal: controller.signal,
|
|
923
|
+
}, complete)
|
|
591
924
|
}
|
|
592
925
|
}
|
|
593
926
|
|
|
@@ -4,6 +4,8 @@ const fs = require('fs')
|
|
|
4
4
|
const path = require('path')
|
|
5
5
|
|
|
6
6
|
const getConfig = require('../../../config')
|
|
7
|
+
const { EVP_SUBDOMAIN_HEADER_NAME } = require('../../../evp_proxy/constants')
|
|
8
|
+
const { joinEVPProxyPath } = require('../../../evp_proxy/path')
|
|
7
9
|
const FormData = require('../../../exporters/common/form-data')
|
|
8
10
|
const request = require('../../../exporters/common/request')
|
|
9
11
|
|
|
@@ -83,8 +85,8 @@ function getCommitsToUpload ({ url, repositoryUrl, latestCommits, isEvpProxy, ev
|
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
if (isEvpProxy) {
|
|
86
|
-
options.path =
|
|
87
|
-
options.headers[
|
|
88
|
+
options.path = joinEVPProxyPath(evpProxyPrefix, '/api/v2/git/repository/search_commits')
|
|
89
|
+
options.headers[EVP_SUBDOMAIN_HEADER_NAME] = 'api'
|
|
88
90
|
delete options.headers['dd-api-key']
|
|
89
91
|
}
|
|
90
92
|
|
|
@@ -179,8 +181,8 @@ function uploadPackFile ({ url, isEvpProxy, evpProxyPrefix, packFileToUpload, re
|
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
if (isEvpProxy) {
|
|
182
|
-
options.path =
|
|
183
|
-
options.headers[
|
|
184
|
+
options.path = joinEVPProxyPath(evpProxyPrefix, '/api/v2/git/repository/packfile')
|
|
185
|
+
options.headers[EVP_SUBDOMAIN_HEADER_NAME] = 'api'
|
|
184
186
|
delete options.headers['dd-api-key']
|
|
185
187
|
}
|
|
186
188
|
|