dd-trace 6.8.0 → 6.9.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/ci/diagnose.js +27 -25
- package/ci/init.js +6 -6
- package/ci/runbook.md +114 -20
- package/ci/test-optimization-validation/approval-artifacts.js +36 -3
- package/ci/test-optimization-validation/approval.js +78 -22
- package/ci/test-optimization-validation/blocker-category.js +24 -0
- package/ci/test-optimization-validation/ci-discovery.js +23 -17
- package/ci/test-optimization-validation/ci-package-scripts.js +34 -6
- package/ci/test-optimization-validation/ci-remediation.js +1 -1
- package/ci/test-optimization-validation/cli.js +280 -52
- package/ci/test-optimization-validation/command-blocker.js +159 -20
- package/ci/test-optimization-validation/command-output-policy.js +9 -3
- package/ci/test-optimization-validation/command-runner.js +9 -7
- package/ci/test-optimization-validation/environment.js +4 -2
- package/ci/test-optimization-validation/executable.js +15 -13
- package/ci/test-optimization-validation/execution-lock.js +70 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber-validation.json +3 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +657 -5
- package/ci/test-optimization-validation/framework-adapters/vitest.js +517 -0
- package/ci/test-optimization-validation/generated-files.js +38 -28
- package/ci/test-optimization-validation/generated-test-contract.js +8 -4
- package/ci/test-optimization-validation/generated-verifier.js +24 -17
- package/ci/test-optimization-validation/literal-glob.js +52 -0
- package/ci/test-optimization-validation/manifest-loader.js +3 -0
- package/ci/test-optimization-validation/manifest-scaffold.js +300 -50
- package/ci/test-optimization-validation/manifest-schema.js +80 -17
- package/ci/test-optimization-validation/offline-fixtures.js +13 -4
- package/ci/test-optimization-validation/package-check.js +94 -0
- package/ci/test-optimization-validation/plan-writer.js +183 -10
- package/ci/test-optimization-validation/preflight-runner.js +110 -31
- package/ci/test-optimization-validation/project-build-artifact.js +31 -0
- package/ci/test-optimization-validation/redaction.js +18 -27
- package/ci/test-optimization-validation/report-writer.js +182 -48
- package/ci/test-optimization-validation/result-semantics.js +20 -3
- package/ci/test-optimization-validation/runner-command.js +40 -20
- package/ci/test-optimization-validation/runner-contract.js +79 -43
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +59 -5
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
- package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
- package/ci/test-optimization-validation/static-diagnosis.js +10 -3
- package/ci/test-optimization-validation/test-output.js +2 -1
- package/ci/test-optimization-validation/validation-blocker.js +21 -0
- package/ci/vitest-no-worker-init-setup.mjs +30 -34
- package/index.d.ts +98 -1
- package/init.js +1 -17
- package/initialize.mjs +11 -0
- package/loader-hook.mjs +6 -4
- package/package.json +5 -3
- package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
- package/packages/datadog-esbuild/src/utils.js +16 -1
- package/packages/datadog-instrumentations/src/ai.js +61 -34
- package/packages/datadog-instrumentations/src/bluebird.js +6 -6
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +81 -5
- package/packages/datadog-instrumentations/src/cucumber.js +124 -47
- package/packages/datadog-instrumentations/src/dns.js +21 -2
- package/packages/datadog-instrumentations/src/electron.js +1 -0
- package/packages/datadog-instrumentations/src/fastify.js +3 -1
- package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +33 -9
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +118 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +61 -0
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +37 -6
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +979 -233
- package/packages/datadog-instrumentations/src/mocha/main.js +107 -9
- package/packages/datadog-instrumentations/src/mocha/utils.js +64 -42
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +25 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -18
- package/packages/datadog-instrumentations/src/mysql.js +1 -1
- package/packages/datadog-instrumentations/src/next.js +155 -23
- package/packages/datadog-instrumentations/src/openai.js +14 -0
- package/packages/datadog-instrumentations/src/pg.js +180 -6
- package/packages/datadog-instrumentations/src/playwright.js +173 -79
- package/packages/datadog-instrumentations/src/promise-js.js +4 -4
- package/packages/datadog-instrumentations/src/promise.js +3 -3
- package/packages/datadog-instrumentations/src/router.js +195 -19
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -10
- package/packages/datadog-instrumentations/src/vitest-main.js +360 -56
- package/packages/datadog-instrumentations/src/vitest-util.js +42 -7
- package/packages/datadog-instrumentations/src/vitest-worker.js +234 -17
- package/packages/datadog-instrumentations/src/webdriverio.js +194 -18
- package/packages/datadog-instrumentations/src/when.js +3 -3
- package/packages/datadog-instrumentations/src/ws.js +5 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -3
- package/packages/datadog-plugin-cucumber/src/index.js +5 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +50 -46
- package/packages/datadog-plugin-cypress/src/support.js +7 -4
- package/packages/datadog-plugin-electron/src/net.js +10 -4
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +1 -18
- package/packages/datadog-plugin-jest/src/util.js +2 -0
- package/packages/datadog-plugin-kafkajs/src/producer.js +2 -1
- package/packages/datadog-plugin-mocha/src/index.js +318 -1
- package/packages/datadog-plugin-next/src/index.js +57 -9
- package/packages/datadog-plugin-openai/src/tracing.js +4 -0
- package/packages/datadog-plugin-pg/src/index.js +65 -1
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +95 -7
- package/packages/datadog-shimmer/src/shimmer.js +6 -2
- package/packages/dd-trace/src/aiguard/sdk.js +3 -0
- package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
- package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
- package/packages/dd-trace/src/appsec/handlers/http-request.js +215 -0
- package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
- package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
- package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +48 -476
- package/packages/dd-trace/src/appsec/sdk/track_event.js +9 -0
- package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
- package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +27 -13
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +17 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +21 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +1 -1
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
- package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -0
- package/packages/dd-trace/src/config/helper.js +2 -0
- package/packages/dd-trace/src/config/index.js +16 -5
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +8 -0
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
- package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
- package/packages/dd-trace/src/guardrails/log.js +1 -10
- package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
- package/packages/dd-trace/src/llmobs/experiments/index.js +33 -25
- package/packages/dd-trace/src/llmobs/experiments/util.js +4 -2
- package/packages/dd-trace/src/llmobs/noop.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +149 -68
- package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
- package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
- package/packages/dd-trace/src/log/channels.js +1 -9
- package/packages/dd-trace/src/log/levels.js +12 -0
- package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
- package/packages/dd-trace/src/opentelemetry/span.js +4 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +21 -5
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/ci.js +6 -0
- package/packages/dd-trace/src/plugins/util/git.js +6 -4
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +2 -2
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
- package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
- package/packages/dd-trace/src/plugins/util/test.js +319 -75
- package/packages/dd-trace/src/plugins/util/url.js +1 -1
- package/packages/dd-trace/src/plugins/util/web.js +23 -1
- package/packages/dd-trace/src/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
- package/packages/dd-trace/src/proxy.js +1 -0
- package/packages/dd-trace/src/ritm.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
- package/packages/dd-trace/src/sampling_rule.js +4 -2
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
- package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
- package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
- package/vendor/dist/pprof-format/index.js +1 -1
- package/version.js +10 -8
- /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { fileURLToPath } = require('node:url')
|
|
4
|
+
|
|
5
|
+
const { channel } = require('dc-polyfill')
|
|
6
|
+
|
|
3
7
|
const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
|
|
4
8
|
const { storage } = require('../../datadog-core')
|
|
9
|
+
const log = require('../../dd-trace/src/log')
|
|
10
|
+
const {
|
|
11
|
+
sendWebdriverioWorkerMessage,
|
|
12
|
+
SUITE_FINISH,
|
|
13
|
+
} = require('../../datadog-instrumentations/src/mocha/webdriverio-protocol')
|
|
5
14
|
|
|
6
15
|
const {
|
|
7
16
|
TEST_STATUS,
|
|
@@ -51,6 +60,94 @@ const {
|
|
|
51
60
|
TELEMETRY_TEST_SESSION,
|
|
52
61
|
} = require('../../dd-trace/src/ci-visibility/telemetry')
|
|
53
62
|
|
|
63
|
+
const jasmineAdapterRunAsyncEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineAdapter_run:asyncEnd'
|
|
64
|
+
const jasmineDoneCh = 'ci:webdriverio:jasmine:done'
|
|
65
|
+
const jasmineReporterSpecDoneEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineReporter_specDone:end'
|
|
66
|
+
const jasmineReporterSpecStartedEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineReporter_specStarted:end'
|
|
67
|
+
const jasmineReporterSuiteDoneEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineReporter_suiteDone:end'
|
|
68
|
+
const jasmineReporterSuiteStartedEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineReporter_suiteStarted:end'
|
|
69
|
+
const jasmineTestFunctionStartCh = 'tracing:orchestrion:@wdio/utils:testFrameworkFnWrapper:start'
|
|
70
|
+
const testFinishCh = channel('ci:mocha:test:finish')
|
|
71
|
+
const WEBDRIVERIO_JASMINE_ADAPTER = 'jasmine'
|
|
72
|
+
const workerFinishCh = channel('ci:mocha:worker:finish')
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @typedef {object} WebdriverioJasmineResult
|
|
76
|
+
* @property {string|undefined} id
|
|
77
|
+
* @property {string|undefined} description
|
|
78
|
+
* @property {Array<{message?: string, stack?: string}>|undefined} failedExpectations
|
|
79
|
+
* @property {string|undefined} file
|
|
80
|
+
* @property {string|undefined} filename
|
|
81
|
+
* @property {string|undefined} fullName
|
|
82
|
+
* @property {string|undefined} parentSuiteId
|
|
83
|
+
* @property {string|undefined} status
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Normalizes a WebdriverIO Jasmine spec identifier to a filesystem path.
|
|
88
|
+
*
|
|
89
|
+
* @param {string|undefined} file
|
|
90
|
+
* @returns {string|undefined}
|
|
91
|
+
*/
|
|
92
|
+
function normalizeJasmineFile (file) {
|
|
93
|
+
return file?.startsWith('file://') ? fileURLToPath(file) : file
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Maps a Jasmine result status to the Test Optimization status vocabulary.
|
|
98
|
+
*
|
|
99
|
+
* @param {string|undefined} status
|
|
100
|
+
* @returns {'pass'|'fail'|'skip'}
|
|
101
|
+
*/
|
|
102
|
+
function getJasmineStatus (status) {
|
|
103
|
+
if (status === 'passed') return 'pass'
|
|
104
|
+
if (status === 'failed') return 'fail'
|
|
105
|
+
return 'skip'
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Converts Jasmine's failed-expectation shape to an Error.
|
|
110
|
+
*
|
|
111
|
+
* @param {WebdriverioJasmineResult} result
|
|
112
|
+
* @returns {Error|undefined}
|
|
113
|
+
*/
|
|
114
|
+
function getJasmineError (result) {
|
|
115
|
+
const failedExpectation = result.failedExpectations?.[0]
|
|
116
|
+
if (!failedExpectation) return
|
|
117
|
+
|
|
118
|
+
const error = new Error(failedExpectation.message)
|
|
119
|
+
if (failedExpectation.stack) {
|
|
120
|
+
error.stack = failedExpectation.stack
|
|
121
|
+
}
|
|
122
|
+
return error
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Resolves the spec file responsible for a run-level Jasmine failure.
|
|
127
|
+
*
|
|
128
|
+
* @param {WebdriverioJasmineResult|undefined} result
|
|
129
|
+
* @param {string[]} specs
|
|
130
|
+
* @returns {string|undefined}
|
|
131
|
+
*/
|
|
132
|
+
function getJasmineFailureFile (result, specs) {
|
|
133
|
+
const resultFile = normalizeJasmineFile(result?.file || result?.filename)
|
|
134
|
+
if (resultFile) {
|
|
135
|
+
return resultFile
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const stack = result?.failedExpectations?.[0]?.stack
|
|
139
|
+
if (stack) {
|
|
140
|
+
for (const spec of specs) {
|
|
141
|
+
const file = normalizeJasmineFile(spec)
|
|
142
|
+
if (file && stack.includes(file)) {
|
|
143
|
+
return file
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return specs.length === 1 ? normalizeJasmineFile(specs[0]) : undefined
|
|
149
|
+
}
|
|
150
|
+
|
|
54
151
|
class MochaPlugin extends CiPlugin {
|
|
55
152
|
static id = 'mocha'
|
|
56
153
|
|
|
@@ -60,10 +157,98 @@ class MochaPlugin extends CiPlugin {
|
|
|
60
157
|
this._testTitleToParams = {}
|
|
61
158
|
this.sourceRoot = process.cwd()
|
|
62
159
|
|
|
63
|
-
this.addSub('ci:mocha:worker:configuration', ({
|
|
160
|
+
this.addSub('ci:mocha:worker:configuration', ({
|
|
161
|
+
libraryConfig,
|
|
162
|
+
repositoryRoot,
|
|
163
|
+
specs,
|
|
164
|
+
testFramework,
|
|
165
|
+
testFrameworkAdapter,
|
|
166
|
+
}) => {
|
|
64
167
|
this.libraryConfig = libraryConfig
|
|
65
168
|
this.testFramework = testFramework
|
|
169
|
+
this.testFrameworkAdapter = testFrameworkAdapter
|
|
66
170
|
this._setRepositoryRoot(repositoryRoot)
|
|
171
|
+
if (testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
|
|
172
|
+
this._webdriverioJasmineState = {
|
|
173
|
+
currentResult: undefined,
|
|
174
|
+
specs: specs || [],
|
|
175
|
+
suiteErrors: new Map(),
|
|
176
|
+
suiteFiles: new Map(),
|
|
177
|
+
suiteStatuses: new Map(),
|
|
178
|
+
tests: new Map(),
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
this.addBind(jasmineTestFunctionStartCh, (ctx) => {
|
|
184
|
+
if (this.testFrameworkAdapter !== WEBDRIVERIO_JASMINE_ADAPTER) {
|
|
185
|
+
return storage('legacy').getStore()
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const result = this._webdriverioJasmineState?.currentResult
|
|
189
|
+
const type = ctx.arguments?.[1]
|
|
190
|
+
if (type === 'Test' || (type === 'Hook' && result)) {
|
|
191
|
+
return this.#startWebdriverioJasmineTest(result)
|
|
192
|
+
}
|
|
193
|
+
return storage('legacy').getStore()
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
this.addSub(jasmineReporterSuiteStartedEndCh, (ctx) => {
|
|
197
|
+
if (this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
|
|
198
|
+
const suite = ctx.arguments?.[0]
|
|
199
|
+
const file = normalizeJasmineFile(suite?.filename)
|
|
200
|
+
if (suite?.id && file) {
|
|
201
|
+
this._webdriverioJasmineState.suiteFiles.set(suite.id, file)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
this.addSub(jasmineReporterSuiteDoneEndCh, (ctx) => {
|
|
207
|
+
if (this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
|
|
208
|
+
const state = this._webdriverioJasmineState
|
|
209
|
+
const suite = ctx.arguments?.[0]
|
|
210
|
+
const error = suite && getJasmineError(suite)
|
|
211
|
+
const file = normalizeJasmineFile(
|
|
212
|
+
suite?.filename ||
|
|
213
|
+
state.suiteFiles.get(suite?.id) ||
|
|
214
|
+
(state.specs.length === 1 ? state.specs[0] : undefined)
|
|
215
|
+
)
|
|
216
|
+
if (error && file) {
|
|
217
|
+
state.suiteErrors.set(file, error)
|
|
218
|
+
state.suiteStatuses.set(file, 'fail')
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
this.addSub(jasmineDoneCh, ({ result } = {}) => {
|
|
224
|
+
if (this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
|
|
225
|
+
const state = this._webdriverioJasmineState
|
|
226
|
+
const error = result && getJasmineError(result)
|
|
227
|
+
const file = getJasmineFailureFile(result, state.specs)
|
|
228
|
+
if (error && file) {
|
|
229
|
+
state.suiteErrors.set(file, error)
|
|
230
|
+
state.suiteStatuses.set(file, 'fail')
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
this.addSub(jasmineReporterSpecStartedEndCh, (ctx) => {
|
|
236
|
+
if (this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
|
|
237
|
+
this._webdriverioJasmineState.currentResult = ctx.arguments?.[0]
|
|
238
|
+
this.#startWebdriverioJasmineTest(ctx.arguments?.[0], ctx.self?._specs, ctx.self?.startedSuite)
|
|
239
|
+
}
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
this.addSub(jasmineReporterSpecDoneEndCh, (ctx) => {
|
|
243
|
+
if (this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
|
|
244
|
+
this.#finishWebdriverioJasmineTest(ctx.arguments?.[0], ctx.self?._specs)
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
this.addSub(jasmineAdapterRunAsyncEndCh, (ctx) => {
|
|
249
|
+
if (this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
|
|
250
|
+
this.#finishWebdriverioJasmineWorker(ctx)
|
|
251
|
+
}
|
|
67
252
|
})
|
|
68
253
|
|
|
69
254
|
this.addSub('ci:mocha:test-suite:code-coverage', ({ coverageFiles, suiteFile }) => {
|
|
@@ -484,6 +669,138 @@ class MochaPlugin extends CiPlugin {
|
|
|
484
669
|
})
|
|
485
670
|
}
|
|
486
671
|
|
|
672
|
+
/**
|
|
673
|
+
* Starts a Jasmine test span around WebdriverIO's test-function wrapper.
|
|
674
|
+
*
|
|
675
|
+
* @param {WebdriverioJasmineResult|undefined} result
|
|
676
|
+
* @param {string[]} [specs]
|
|
677
|
+
* @param {{filename?: string}|undefined} currentSuite
|
|
678
|
+
* @returns {object|undefined}
|
|
679
|
+
*/
|
|
680
|
+
#startWebdriverioJasmineTest (result, specs, currentSuite) {
|
|
681
|
+
const state = this._webdriverioJasmineState
|
|
682
|
+
const currentStore = storage('legacy').getStore()
|
|
683
|
+
if (!state || !result?.id) {
|
|
684
|
+
return currentStore
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
const existingTest = state.tests.get(result.id)
|
|
688
|
+
if (existingTest) {
|
|
689
|
+
return existingTest.currentStore
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
const candidateSpecs = specs || state.specs
|
|
693
|
+
const testSuiteAbsolutePath = normalizeJasmineFile(
|
|
694
|
+
state.suiteFiles.get(result.parentSuiteId) ||
|
|
695
|
+
currentSuite?.filename ||
|
|
696
|
+
(candidateSpecs.length === 1 ? candidateSpecs[0] : undefined) ||
|
|
697
|
+
result.file ||
|
|
698
|
+
result.filename ||
|
|
699
|
+
candidateSpecs[0]
|
|
700
|
+
)
|
|
701
|
+
if (!testSuiteAbsolutePath) {
|
|
702
|
+
return currentStore
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
const span = this.startTestSpan({
|
|
706
|
+
testName: result.fullName || result.description,
|
|
707
|
+
testSuiteAbsolutePath,
|
|
708
|
+
title: result.description,
|
|
709
|
+
})
|
|
710
|
+
const testStore = { ...currentStore, span }
|
|
711
|
+
state.tests.set(result.id, {
|
|
712
|
+
currentStore: testStore,
|
|
713
|
+
span,
|
|
714
|
+
testSuiteAbsolutePath,
|
|
715
|
+
})
|
|
716
|
+
this.activeTestSpan = span
|
|
717
|
+
|
|
718
|
+
return testStore
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Finishes a Jasmine test after its reporter receives the final result.
|
|
723
|
+
*
|
|
724
|
+
* @param {WebdriverioJasmineResult|undefined} result
|
|
725
|
+
* @param {string[]} [specs]
|
|
726
|
+
* @returns {void}
|
|
727
|
+
*/
|
|
728
|
+
#finishWebdriverioJasmineTest (result, specs) {
|
|
729
|
+
const state = this._webdriverioJasmineState
|
|
730
|
+
if (!state || !result?.id) {
|
|
731
|
+
return
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
this.#startWebdriverioJasmineTest(result, specs)
|
|
735
|
+
const test = state.tests.get(result.id)
|
|
736
|
+
if (!test) {
|
|
737
|
+
return
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const status = getJasmineStatus(result.status)
|
|
741
|
+
const error = getJasmineError(result)
|
|
742
|
+
if (error) {
|
|
743
|
+
test.span.setTag('error', error)
|
|
744
|
+
}
|
|
745
|
+
testFinishCh.publish({ span: test.span, status })
|
|
746
|
+
state.tests.delete(result.id)
|
|
747
|
+
if (state.currentResult?.id === result.id) {
|
|
748
|
+
state.currentResult = undefined
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
const previousStatus = state.suiteStatuses.get(test.testSuiteAbsolutePath)
|
|
752
|
+
if (status === 'fail' || !previousStatus || previousStatus === 'skip') {
|
|
753
|
+
state.suiteStatuses.set(test.testSuiteAbsolutePath, status)
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Reports Jasmine suite statuses and flushes worker traces before its run settles.
|
|
759
|
+
*
|
|
760
|
+
* @param {{
|
|
761
|
+
* resolveCallback?: (onDone: () => void) => void,
|
|
762
|
+
* rejectCallback?: (onDone: () => void) => void
|
|
763
|
+
* }} context
|
|
764
|
+
* @returns {void}
|
|
765
|
+
*/
|
|
766
|
+
#finishWebdriverioJasmineWorker (context) {
|
|
767
|
+
const state = this._webdriverioJasmineState
|
|
768
|
+
const results = []
|
|
769
|
+
const reportedFiles = new Set()
|
|
770
|
+
for (const [file, status] of state.suiteStatuses) {
|
|
771
|
+
const error = state.suiteErrors.get(file)
|
|
772
|
+
const result = { file, status }
|
|
773
|
+
if (error) {
|
|
774
|
+
result.error = {
|
|
775
|
+
message: error.message,
|
|
776
|
+
stack: error.stack,
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
results.push(result)
|
|
780
|
+
reportedFiles.add(file)
|
|
781
|
+
}
|
|
782
|
+
for (const spec of state.specs) {
|
|
783
|
+
const file = normalizeJasmineFile(spec)
|
|
784
|
+
if (!reportedFiles.has(file)) {
|
|
785
|
+
results.push({ file, status: 'skip' })
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
const waitForWorker = onDone => {
|
|
790
|
+
sendWebdriverioWorkerMessage({
|
|
791
|
+
origin: 'datadog',
|
|
792
|
+
name: SUITE_FINISH,
|
|
793
|
+
content: { results },
|
|
794
|
+
}, error => {
|
|
795
|
+
if (error) {
|
|
796
|
+
log.error('WebdriverIO Test Optimization IPC error', error)
|
|
797
|
+
}
|
|
798
|
+
}, () => workerFinishCh.publish({ onDone }))
|
|
799
|
+
}
|
|
800
|
+
context.resolveCallback = waitForWorker
|
|
801
|
+
context.rejectCallback = waitForWorker
|
|
802
|
+
}
|
|
803
|
+
|
|
487
804
|
startTestSpan (testInfo) {
|
|
488
805
|
const {
|
|
489
806
|
testName,
|
|
@@ -5,8 +5,11 @@ const { storage } = require('../../datadog-core')
|
|
|
5
5
|
const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
|
|
6
6
|
const { COMPONENT, SVC_SRC_KEY } = require('../../dd-trace/src/constants')
|
|
7
7
|
const web = require('../../dd-trace/src/plugins/util/web')
|
|
8
|
+
const { HTTP_ROUTE, RESOURCE_NAME } = require('../../../ext/tags')
|
|
8
9
|
|
|
9
10
|
const errorPages = new Set(['/404', '/500', '/_error', '/_not-found', '/_not-found/page'])
|
|
11
|
+
const reusedNextRequestStores = new WeakSet()
|
|
12
|
+
const nextParentRoutes = new WeakMap()
|
|
10
13
|
|
|
11
14
|
class NextPlugin extends ServerPlugin {
|
|
12
15
|
static id = 'next'
|
|
@@ -18,7 +21,14 @@ class NextPlugin extends ServerPlugin {
|
|
|
18
21
|
|
|
19
22
|
bindStart ({ req, res }) {
|
|
20
23
|
const store = storage('legacy').getStore()
|
|
21
|
-
const
|
|
24
|
+
const parentSpan = store?.span
|
|
25
|
+
if (parentSpan?._integrationName === this.constructor.id) {
|
|
26
|
+
const reusedStore = { ...store, span: parentSpan, req }
|
|
27
|
+
reusedNextRequestStores.add(reusedStore)
|
|
28
|
+
return reusedStore
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const childOf = parentSpan || web.extractIncomingServerContext(this.tracer, req.headers)
|
|
22
32
|
const { name: schemaServiceName, source: schemaServiceSource } = this.serviceName()
|
|
23
33
|
const serviceName = this.config.service || schemaServiceName
|
|
24
34
|
let serviceSource = this.config.service ? 'opt.plugin' : schemaServiceSource
|
|
@@ -42,7 +52,10 @@ class NextPlugin extends ServerPlugin {
|
|
|
42
52
|
|
|
43
53
|
analyticsSampler.sample(span, this.config.measured, true)
|
|
44
54
|
|
|
45
|
-
|
|
55
|
+
const isHttpParent = parentSpan?._integrationName === 'http'
|
|
56
|
+
const httpParentSpan = isHttpParent ? parentSpan : undefined
|
|
57
|
+
const httpParentReq = isHttpParent ? web.getRequest(parentSpan) : undefined
|
|
58
|
+
return { ...store, span, req, httpParentSpan, httpParentReq }
|
|
46
59
|
}
|
|
47
60
|
|
|
48
61
|
error ({ span, error }) {
|
|
@@ -60,6 +73,7 @@ class NextPlugin extends ServerPlugin {
|
|
|
60
73
|
const store = storage('legacy').getStore()
|
|
61
74
|
|
|
62
75
|
if (!store) return
|
|
76
|
+
if (reusedNextRequestStores.has(store)) return
|
|
63
77
|
|
|
64
78
|
const span = store.span
|
|
65
79
|
const error = span.context().getTag('error')
|
|
@@ -88,12 +102,12 @@ class NextPlugin extends ServerPlugin {
|
|
|
88
102
|
span.finish()
|
|
89
103
|
}
|
|
90
104
|
|
|
91
|
-
pageLoad ({ page, isAppPath = false, isStatic = false }) {
|
|
105
|
+
pageLoad ({ page, isAppPath = false, isStatic = false, isFilesystemPath = isAppPath }) {
|
|
92
106
|
const store = storage('legacy').getStore()
|
|
93
107
|
|
|
94
108
|
if (!store) return
|
|
95
109
|
|
|
96
|
-
const { span, req } = store
|
|
110
|
+
const { span, req, httpParentReq, httpParentSpan } = store
|
|
97
111
|
|
|
98
112
|
// safeguard against missing req in complicated timeout scenarios
|
|
99
113
|
if (!req) return
|
|
@@ -106,10 +120,11 @@ class NextPlugin extends ServerPlugin {
|
|
|
106
120
|
return
|
|
107
121
|
}
|
|
108
122
|
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
123
|
+
// Compiled runtimes may publish a normalized pathname rather than a filesystem path.
|
|
124
|
+
if (isFilesystemPath && !isErrorPage) {
|
|
125
|
+
if (isAppPath) page = normalizeAppPath(page)
|
|
126
|
+
page = normalizeIndexPage(page)
|
|
127
|
+
}
|
|
113
128
|
|
|
114
129
|
// handle static resource
|
|
115
130
|
if (isStatic) {
|
|
@@ -123,7 +138,10 @@ class NextPlugin extends ServerPlugin {
|
|
|
123
138
|
'resource.name': `${req.method} ${page}`.trim(),
|
|
124
139
|
'next.page': page,
|
|
125
140
|
})
|
|
126
|
-
|
|
141
|
+
const routeRequest = httpParentReq || req
|
|
142
|
+
web.setRouteOrEndpointTag(routeRequest)
|
|
143
|
+
setHttpParentRoute(httpParentSpan, req.method, page, isStatic)
|
|
144
|
+
web.setRoute(routeRequest, page)
|
|
127
145
|
}
|
|
128
146
|
|
|
129
147
|
configure (config) {
|
|
@@ -131,6 +149,36 @@ class NextPlugin extends ServerPlugin {
|
|
|
131
149
|
}
|
|
132
150
|
}
|
|
133
151
|
|
|
152
|
+
function normalizeIndexPage (page) {
|
|
153
|
+
if (typeof page !== 'string') return page
|
|
154
|
+
if (page === '/index') return '/'
|
|
155
|
+
if (page.endsWith('/index')) return page.slice(0, -'/index'.length) || '/'
|
|
156
|
+
return page
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function normalizeAppPath (page) {
|
|
160
|
+
if (typeof page !== 'string') return page
|
|
161
|
+
|
|
162
|
+
for (const suffix of ['/page', '/route']) {
|
|
163
|
+
if (page === suffix) return '/'
|
|
164
|
+
if (page.endsWith(suffix)) return page.slice(0, -suffix.length) || '/'
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return page
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function setHttpParentRoute (span, method, page, isStatic) {
|
|
171
|
+
if (!span) return
|
|
172
|
+
const currentRoute = span.context().getTag(HTTP_ROUTE)
|
|
173
|
+
|
|
174
|
+
// Only refine a route that Next itself set; static resources are fallbacks.
|
|
175
|
+
if (currentRoute && (nextParentRoutes.get(span) !== currentRoute || isStatic)) return
|
|
176
|
+
|
|
177
|
+
span.setTag(HTTP_ROUTE, page)
|
|
178
|
+
span.setTag(RESOURCE_NAME, `${method} ${page}`.trim())
|
|
179
|
+
nextParentRoutes.set(span, page)
|
|
180
|
+
}
|
|
181
|
+
|
|
134
182
|
function normalizeConfig (config) {
|
|
135
183
|
const hooks = getHooks(config)
|
|
136
184
|
const validateStatus = typeof config.validateStatus === 'function'
|
|
@@ -627,6 +627,10 @@ function lookupOperationEndpoint (operationId, methodName, url) {
|
|
|
627
627
|
* This function essentially normalizes the OpenAI method interface. Many methods accept
|
|
628
628
|
* a single object argument. The remaining ones take individual arguments. This function
|
|
629
629
|
* turns the individual arguments into an object to make extracting properties consistent.
|
|
630
|
+
*
|
|
631
|
+
* @param {string} methodName SDK method name, e.g. `createChatCompletion` or `chat.completions.create`
|
|
632
|
+
* @param {unknown[]} args arguments the SDK method was called with
|
|
633
|
+
* @returns {Record<string, unknown>}
|
|
630
634
|
*/
|
|
631
635
|
function normalizeRequestPayload (methodName, args) {
|
|
632
636
|
switch (methodName) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { storage } = require('../../datadog-core')
|
|
4
|
-
const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
|
|
4
|
+
const { CLIENT_PORT_KEY, SVC_SRC_KEY } = require('../../dd-trace/src/constants')
|
|
5
5
|
const DatabasePlugin = require('../../dd-trace/src/plugins/database')
|
|
6
6
|
|
|
7
7
|
class PGPlugin extends DatabasePlugin {
|
|
@@ -16,6 +16,52 @@ class PGPlugin extends DatabasePlugin {
|
|
|
16
16
|
ctx.parentStore = storage('legacy').getStore()
|
|
17
17
|
})
|
|
18
18
|
this.addBind('apm:pg:pool:connect:finish', ctx => ctx.parentStore)
|
|
19
|
+
|
|
20
|
+
this.addSub('apm:pg:pool:acquire:start', ctx => {
|
|
21
|
+
const params = ctx.poolOptions
|
|
22
|
+
const operationName = this.operationName({ operation: 'pool.acquire' })
|
|
23
|
+
const deferServiceResolution = typeof this.config.service === 'function'
|
|
24
|
+
const service = deferServiceResolution
|
|
25
|
+
? undefined
|
|
26
|
+
: this.serviceName({ pluginConfig: this.config, params })
|
|
27
|
+
ctx.deferServiceResolution = deferServiceResolution
|
|
28
|
+
|
|
29
|
+
this.startSpan(operationName, {
|
|
30
|
+
service,
|
|
31
|
+
resource: operationName,
|
|
32
|
+
type: 'sql',
|
|
33
|
+
kind: 'client',
|
|
34
|
+
meta: {
|
|
35
|
+
'db.type': 'postgres',
|
|
36
|
+
...connectionMeta(params),
|
|
37
|
+
},
|
|
38
|
+
}, ctx)
|
|
39
|
+
})
|
|
40
|
+
this.addSub('apm:pg:pool:acquire:finish', ctx => {
|
|
41
|
+
const span = ctx.currentStore?.span
|
|
42
|
+
if (span === undefined) return
|
|
43
|
+
|
|
44
|
+
if (ctx.error) {
|
|
45
|
+
this.addError(ctx.error, span)
|
|
46
|
+
}
|
|
47
|
+
span.setTag('db.pool.wait_time_ms', ctx.poolWaitTime)
|
|
48
|
+
// `Pool` options carry only what the caller passed, so anything pg resolves later - a
|
|
49
|
+
// connection string, the default port, `PG*` environment variables - is known once the
|
|
50
|
+
// client exists.
|
|
51
|
+
if (ctx.params !== undefined) {
|
|
52
|
+
span.addTags(connectionMeta(ctx.params))
|
|
53
|
+
if (ctx.deferServiceResolution) {
|
|
54
|
+
const service = this.serviceName({ pluginConfig: this.config, params: ctx.params })
|
|
55
|
+
// A `service` callback may return no name, and the start-time fallback already carries
|
|
56
|
+
// the schema default every other pg span falls back to.
|
|
57
|
+
if (service.name) {
|
|
58
|
+
this.setServiceName(span, service.name)
|
|
59
|
+
span.setTag(SVC_SRC_KEY, service.source)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
this.finish(ctx)
|
|
64
|
+
})
|
|
19
65
|
}
|
|
20
66
|
|
|
21
67
|
bindStart (ctx) {
|
|
@@ -42,10 +88,28 @@ class PGPlugin extends DatabasePlugin {
|
|
|
42
88
|
span.setTag('db.stream', 1)
|
|
43
89
|
}
|
|
44
90
|
|
|
91
|
+
if (ctx.poolWaitTime !== undefined) {
|
|
92
|
+
span.setTag('db.pool.wait_time_ms', ctx.poolWaitTime)
|
|
93
|
+
}
|
|
94
|
+
|
|
45
95
|
ctx.injected = this.injectDbmQuery(span, originalText, service.name, !!query.name)
|
|
46
96
|
|
|
47
97
|
return ctx.currentStore
|
|
48
98
|
}
|
|
49
99
|
}
|
|
50
100
|
|
|
101
|
+
/**
|
|
102
|
+
* `Pool.options` and `Client.connectionParameters` are pg internals, so either can be missing.
|
|
103
|
+
*
|
|
104
|
+
* @param {{ database?: string, user?: string, host?: string, port?: number } | undefined} params
|
|
105
|
+
*/
|
|
106
|
+
function connectionMeta (params) {
|
|
107
|
+
return {
|
|
108
|
+
'db.name': params?.database,
|
|
109
|
+
'db.user': params?.user,
|
|
110
|
+
'out.host': params?.host,
|
|
111
|
+
[CLIENT_PORT_KEY]: params?.port,
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
51
115
|
module.exports = PGPlugin
|
|
@@ -485,7 +485,7 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
485
485
|
{
|
|
486
486
|
hasCodeOwners: !!span.context().getTag(TEST_CODE_OWNERS),
|
|
487
487
|
isNew,
|
|
488
|
-
isRum: isRUMActive,
|
|
488
|
+
isRum: isRUMActive === 'true' || undefined,
|
|
489
489
|
browserDriver: 'playwright',
|
|
490
490
|
isQuarantined,
|
|
491
491
|
isDisabled,
|