dd-trace 5.110.0 → 5.111.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/README.md +13 -8
- package/ci/init.js +21 -2
- package/ci/vitest-no-worker-init-setup.mjs +430 -0
- package/ext/tags.js +2 -0
- package/index.d.ts +13 -0
- package/initialize.mjs +5 -6
- package/loader-hook.mjs +76 -55
- package/package.json +9 -9
- package/packages/datadog-instrumentations/src/fs.js +8 -6
- package/packages/datadog-instrumentations/src/graphql.js +26 -495
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/mongoose.js +14 -2
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1293 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +1567 -0
- package/packages/datadog-instrumentations/src/vitest-util.js +249 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +753 -0
- package/packages/datadog-instrumentations/src/vitest.js +11 -1855
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +18 -10
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
- package/packages/datadog-plugin-cucumber/src/index.js +1 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +10 -4
- package/packages/datadog-plugin-cypress/src/support.js +45 -1
- package/packages/datadog-plugin-express/src/code_origin.js +1 -1
- package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +627 -11
- package/packages/datadog-plugin-graphql/src/index.js +20 -8
- package/packages/datadog-plugin-graphql/src/parse.js +24 -4
- package/packages/datadog-plugin-graphql/src/validate.js +16 -5
- package/packages/datadog-plugin-http/src/index.js +6 -8
- package/packages/datadog-plugin-jest/src/index.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/services.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +69 -63
- package/packages/datadog-shimmer/src/shimmer.js +37 -16
- package/packages/dd-trace/src/aiguard/sdk.js +2 -2
- package/packages/dd-trace/src/appsec/api_security/sampler.js +3 -3
- package/packages/dd-trace/src/appsec/channels.js +3 -1
- package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
- package/packages/dd-trace/src/appsec/graphql.js +5 -5
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +26 -19
- package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
- package/packages/dd-trace/src/appsec/index.js +10 -1
- package/packages/dd-trace/src/appsec/lambda.js +135 -0
- package/packages/dd-trace/src/appsec/reporter.js +49 -10
- package/packages/dd-trace/src/appsec/telemetry/index.js +1 -1
- package/packages/dd-trace/src/appsec/waf/index.js +16 -4
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +55 -13
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
- package/packages/dd-trace/src/config/defaults.js +3 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +61 -57
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +35 -22
- package/packages/dd-trace/src/config/major-overrides.js +4 -2
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +90 -56
- package/packages/dd-trace/src/constants.js +7 -0
- package/packages/dd-trace/src/datastreams/processor.js +11 -3
- package/packages/dd-trace/src/exporters/agent/index.js +1 -1
- package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
- package/packages/dd-trace/src/llmobs/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +3 -3
- package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
- package/packages/dd-trace/src/llmobs/tagger.js +3 -3
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +38 -39
- package/packages/dd-trace/src/opentelemetry/trace/index.js +4 -0
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
- package/packages/dd-trace/src/opentracing/tracer.js +6 -1
- package/packages/dd-trace/src/plugin_manager.js +7 -7
- package/packages/dd-trace/src/plugins/ci_plugin.js +12 -3
- package/packages/dd-trace/src/plugins/util/llm.js +6 -1
- package/packages/dd-trace/src/plugins/util/test.js +11 -9
- package/packages/dd-trace/src/profiling/exporter_cli.js +2 -2
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
- package/packages/dd-trace/src/proxy.js +8 -8
- package/packages/dd-trace/src/span_processor.js +5 -6
- package/packages/dd-trace/src/span_stats.js +96 -78
- package/packages/dd-trace/src/startup-log.js +2 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
- package/packages/dd-trace/src/telemetry/index.js +2 -2
- package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
- package/packages/dd-trace/src/telemetry/send-data.js +8 -8
- package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
- package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const pick = require('../../datadog-core/src/utils/src/pick')
|
|
4
|
+
const { DD_MAJOR } = require('../../../version')
|
|
4
5
|
const CompositePlugin = require('../../dd-trace/src/plugins/composite')
|
|
5
6
|
const log = require('../../dd-trace/src/log')
|
|
6
7
|
const GraphQLExecutePlugin = require('./execute')
|
|
7
8
|
const GraphQLParsePlugin = require('./parse')
|
|
8
9
|
const GraphQLValidatePlugin = require('./validate')
|
|
9
|
-
const GraphQLResolvePlugin = require('./resolve')
|
|
10
10
|
|
|
11
11
|
class GraphQLPlugin extends CompositePlugin {
|
|
12
12
|
static id = 'graphql'
|
|
@@ -15,7 +15,9 @@ class GraphQLPlugin extends CompositePlugin {
|
|
|
15
15
|
execute: GraphQLExecutePlugin,
|
|
16
16
|
parse: GraphQLParsePlugin,
|
|
17
17
|
validate: GraphQLValidatePlugin,
|
|
18
|
-
resolve:
|
|
18
|
+
// resolve plugin is absorbed into execute: per-field data is recorded
|
|
19
|
+
// synchronously in wrapResolve, and all graphql.resolve spans are
|
|
20
|
+
// materialized at execute end.
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
23
|
|
|
@@ -30,11 +32,15 @@ class GraphQLPlugin extends CompositePlugin {
|
|
|
30
32
|
// config validator helpers
|
|
31
33
|
|
|
32
34
|
function validateConfig (config) {
|
|
35
|
+
const collapse = config.collapse === undefined || !!config.collapse
|
|
33
36
|
return {
|
|
34
37
|
...config,
|
|
35
38
|
depth: getDepth(config),
|
|
36
39
|
variables: getVariablesFilter(config),
|
|
37
|
-
collapse
|
|
40
|
+
collapse,
|
|
41
|
+
// v5 counted collapsed list indices toward `depth`, so the same query reached a
|
|
42
|
+
// different depth depending on `collapse`. v6 counts selection-set depth only.
|
|
43
|
+
countListIndices: DD_MAJOR < 6 && collapse,
|
|
38
44
|
hooks: getHooks(config),
|
|
39
45
|
}
|
|
40
46
|
}
|
|
@@ -60,13 +66,19 @@ function getVariablesFilter (config) {
|
|
|
60
66
|
}
|
|
61
67
|
|
|
62
68
|
const noop = () => {}
|
|
69
|
+
const noopHooks = { execute: noop, parse: noop, validate: noop, resolve: undefined }
|
|
63
70
|
|
|
64
71
|
function getHooks ({ hooks }) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
if (!hooks) return noopHooks
|
|
73
|
+
return {
|
|
74
|
+
execute: hooks.execute ?? noop,
|
|
75
|
+
parse: hooks.parse ?? noop,
|
|
76
|
+
validate: hooks.validate ?? noop,
|
|
77
|
+
// No noop fallback: `resolve` runs per-field (hot path); the plugin
|
|
78
|
+
// gates with `if (this.config.hooks.resolve)` so the absent-hook case
|
|
79
|
+
// skips both the call and the payload-object allocation.
|
|
80
|
+
resolve: hooks.resolve,
|
|
81
|
+
}
|
|
70
82
|
}
|
|
71
83
|
|
|
72
84
|
module.exports = GraphQLPlugin
|
|
@@ -2,34 +2,54 @@
|
|
|
2
2
|
|
|
3
3
|
const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
|
|
4
4
|
|
|
5
|
+
const documentSources = new WeakMap()
|
|
6
|
+
|
|
5
7
|
class GraphQLParsePlugin extends TracingPlugin {
|
|
6
8
|
static id = 'graphql'
|
|
7
9
|
static operation = 'parser'
|
|
10
|
+
static prefix = 'tracing:orchestrion:graphql:apm:graphql:parser'
|
|
8
11
|
|
|
9
12
|
bindStart (ctx) {
|
|
13
|
+
const source = ctx.arguments?.[0]
|
|
14
|
+
|
|
10
15
|
this.startSpan('graphql.parse', {
|
|
11
16
|
service: this.config.service,
|
|
12
17
|
type: 'graphql',
|
|
13
18
|
meta: {},
|
|
14
19
|
}, ctx)
|
|
15
20
|
|
|
21
|
+
ctx.ddSource = source
|
|
22
|
+
|
|
16
23
|
return ctx.currentStore
|
|
17
24
|
}
|
|
18
25
|
|
|
19
|
-
|
|
20
|
-
const
|
|
26
|
+
end (ctx) {
|
|
27
|
+
const source = ctx.ddSource
|
|
28
|
+
const document = ctx.result
|
|
21
29
|
const span = ctx?.currentStore?.span || this.activeSpan
|
|
22
30
|
|
|
23
|
-
|
|
31
|
+
let docSource
|
|
32
|
+
if (document) {
|
|
33
|
+
if (source) {
|
|
34
|
+
docSource = source.body || source
|
|
35
|
+
documentSources.set(document, docSource)
|
|
36
|
+
} else {
|
|
37
|
+
docSource = documentSources.get(document)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (this.config.source && docSource) {
|
|
24
42
|
span.setTag('graphql.source', docSource)
|
|
25
43
|
}
|
|
26
44
|
|
|
27
45
|
this.config.hooks.parse(span, source, document)
|
|
28
46
|
|
|
29
|
-
|
|
47
|
+
span.finish()
|
|
30
48
|
|
|
31
49
|
return ctx.parentStore
|
|
32
50
|
}
|
|
33
51
|
}
|
|
34
52
|
|
|
53
|
+
GraphQLParsePlugin.documentSources = documentSources
|
|
54
|
+
|
|
35
55
|
module.exports = GraphQLParsePlugin
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
|
|
4
|
+
const GraphQLParsePlugin = require('./parse')
|
|
4
5
|
const { extractErrorIntoSpanEvent } = require('./utils')
|
|
5
6
|
|
|
6
7
|
class GraphQLValidatePlugin extends TracingPlugin {
|
|
7
8
|
static id = 'graphql'
|
|
8
9
|
static operation = 'validate'
|
|
10
|
+
static prefix = 'tracing:orchestrion:graphql:apm:graphql:validate'
|
|
9
11
|
|
|
10
12
|
bindStart (ctx) {
|
|
11
|
-
|
|
13
|
+
// validate(schema, documentAST, rules, options, typeInfo)
|
|
14
|
+
const document = ctx.arguments?.[1]
|
|
15
|
+
const docSource = document ? GraphQLParsePlugin.documentSources.get(document) : undefined
|
|
12
16
|
const source = this.config.source && document && docSource
|
|
13
17
|
|
|
14
18
|
this.startSpan('graphql.validate', {
|
|
@@ -19,19 +23,26 @@ class GraphQLValidatePlugin extends TracingPlugin {
|
|
|
19
23
|
},
|
|
20
24
|
}, ctx)
|
|
21
25
|
|
|
26
|
+
ctx.ddDocument = document
|
|
27
|
+
|
|
22
28
|
return ctx.currentStore
|
|
23
29
|
}
|
|
24
30
|
|
|
25
|
-
|
|
26
|
-
const
|
|
31
|
+
end (ctx) {
|
|
32
|
+
const document = ctx.ddDocument
|
|
33
|
+
const errors = ctx.result
|
|
27
34
|
const span = ctx?.currentStore?.span || this.activeSpan
|
|
35
|
+
|
|
28
36
|
this.config.hooks.validate(span, document, errors)
|
|
29
|
-
|
|
37
|
+
|
|
38
|
+
if (errors?.length) {
|
|
39
|
+
span.setTag('error', errors[0])
|
|
30
40
|
for (const err of errors) {
|
|
31
41
|
extractErrorIntoSpanEvent(this._tracerConfig, span, err)
|
|
32
42
|
}
|
|
33
43
|
}
|
|
34
|
-
|
|
44
|
+
|
|
45
|
+
span.finish()
|
|
35
46
|
|
|
36
47
|
return ctx.parentStore
|
|
37
48
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const PushSubscriptionPlugin = require('../../datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription')
|
|
4
3
|
const CompositePlugin = require('../../dd-trace/src/plugins/composite')
|
|
5
4
|
const { enableGCPPubSubPushSubscription } = require('../../dd-trace/src/serverless')
|
|
6
5
|
const log = require('../../dd-trace/src/log')
|
|
@@ -17,14 +16,13 @@ class HttpPlugin extends CompositePlugin {
|
|
|
17
16
|
static get plugins () {
|
|
18
17
|
const plugins = {}
|
|
19
18
|
|
|
20
|
-
// Load push subscription plugin first (if enabled) for GCP Cloud Run
|
|
19
|
+
// Load the push subscription plugin first (if enabled) for GCP Cloud Run.
|
|
20
|
+
// The require stays inside the gate so the pubsub plugin graph is not pulled
|
|
21
|
+
// into every process that instruments http — only GCP Cloud Run reaches it.
|
|
21
22
|
if (enableGCPPubSubPushSubscription()) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
} catch (e) {
|
|
26
|
-
log.debug('Failed to load GCP Pub/Sub Push Subscription plugin:', e)
|
|
27
|
-
}
|
|
23
|
+
plugins['pubsub-push-subscription'] =
|
|
24
|
+
require('../../datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription')
|
|
25
|
+
log.debug('Loaded GCP Pub/Sub Push Subscription plugin for HTTP requests')
|
|
28
26
|
}
|
|
29
27
|
|
|
30
28
|
plugins.server = HttpServerPlugin
|
|
@@ -177,7 +177,7 @@ class JestPlugin extends CiPlugin {
|
|
|
177
177
|
|
|
178
178
|
this.telemetry.count(TELEMETRY_TEST_SESSION, {
|
|
179
179
|
provider: this.ciProviderName,
|
|
180
|
-
autoInjected: !!this._tracerConfig.DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
|
|
180
|
+
autoInjected: !!this._tracerConfig.testOptimization.DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
|
|
181
181
|
})
|
|
182
182
|
|
|
183
183
|
appClosingTelemetry()
|
|
@@ -427,7 +427,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
427
427
|
finishAllTraceSpans(this.testSessionSpan)
|
|
428
428
|
this.telemetry.count(TELEMETRY_TEST_SESSION, {
|
|
429
429
|
provider: this.ciProviderName,
|
|
430
|
-
autoInjected: !!this._tracerConfig.DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
|
|
430
|
+
autoInjected: !!this._tracerConfig.testOptimization.DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
|
|
431
431
|
})
|
|
432
432
|
}
|
|
433
433
|
this.libraryConfig = null
|
|
@@ -24,12 +24,12 @@ module.exports.init = function (tracerConfig) {
|
|
|
24
24
|
})
|
|
25
25
|
: new NoopDogStatsDClient()
|
|
26
26
|
|
|
27
|
-
logger = tracerConfig && tracerConfig.
|
|
27
|
+
logger = tracerConfig && tracerConfig.DD_API_KEY
|
|
28
28
|
? new ExternalLogger({
|
|
29
29
|
ddsource: 'openai',
|
|
30
30
|
hostname: tracerConfig.hostname,
|
|
31
31
|
service: tracerConfig.service,
|
|
32
|
-
apiKey: tracerConfig.
|
|
32
|
+
apiKey: tracerConfig.DD_API_KEY,
|
|
33
33
|
interval: FLUSH_INTERVAL,
|
|
34
34
|
})
|
|
35
35
|
: new NoopExternalLogger()
|
|
@@ -108,7 +108,7 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
108
108
|
finishAllTraceSpans(this.testSessionSpan)
|
|
109
109
|
this.telemetry.count(TELEMETRY_TEST_SESSION, {
|
|
110
110
|
provider: this.ciProviderName,
|
|
111
|
-
autoInjected: !!this._tracerConfig.DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
|
|
111
|
+
autoInjected: !!this._tracerConfig.testOptimization.DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
|
|
112
112
|
})
|
|
113
113
|
appClosingTelemetry()
|
|
114
114
|
this.tracer._exporter.flush(onDone)
|
|
@@ -30,12 +30,12 @@ const {
|
|
|
30
30
|
TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX,
|
|
31
31
|
TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED,
|
|
32
32
|
TEST_HAS_FAILED_ALL_RETRIES,
|
|
33
|
-
getLibraryCapabilitiesTags,
|
|
33
|
+
getLibraryCapabilitiesTags: getDefaultLibraryCapabilitiesTags,
|
|
34
34
|
TEST_RETRY_REASON_TYPES,
|
|
35
|
-
isModifiedTest,
|
|
36
35
|
TEST_IS_MODIFIED,
|
|
37
36
|
TEST_HAS_DYNAMIC_NAME,
|
|
38
37
|
TEST_FINAL_STATUS,
|
|
38
|
+
TEST_IS_TEST_FRAMEWORK_WORKER,
|
|
39
39
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
40
40
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
41
41
|
const {
|
|
@@ -70,49 +70,6 @@ class VitestPlugin extends CiPlugin {
|
|
|
70
70
|
})
|
|
71
71
|
})
|
|
72
72
|
|
|
73
|
-
this.addSub('ci:vitest:test:is-new', ({ knownTests, testSuiteAbsolutePath, testName, onDone }) => {
|
|
74
|
-
// if for whatever reason the worker does not receive valid known tests, we don't consider it as new
|
|
75
|
-
if (!knownTests.vitest) {
|
|
76
|
-
return onDone(false)
|
|
77
|
-
}
|
|
78
|
-
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
79
|
-
const testsForThisTestSuite = knownTests.vitest[testSuite] || []
|
|
80
|
-
onDone(!testsForThisTestSuite.includes(testName))
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
this.addSub('ci:vitest:test:is-attempt-to-fix', ({
|
|
84
|
-
testManagementTests,
|
|
85
|
-
testSuiteAbsolutePath,
|
|
86
|
-
testName,
|
|
87
|
-
onDone,
|
|
88
|
-
}) => {
|
|
89
|
-
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
90
|
-
const { isAttemptToFix } = this.getTestProperties(testManagementTests, testSuite, testName)
|
|
91
|
-
|
|
92
|
-
onDone(isAttemptToFix ?? false)
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
this.addSub('ci:vitest:test:is-disabled', ({ testManagementTests, testSuiteAbsolutePath, testName, onDone }) => {
|
|
96
|
-
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
97
|
-
const { isDisabled } = this.getTestProperties(testManagementTests, testSuite, testName)
|
|
98
|
-
|
|
99
|
-
onDone(isDisabled)
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
this.addSub('ci:vitest:test:is-quarantined', ({ testManagementTests, testSuiteAbsolutePath, testName, onDone }) => {
|
|
103
|
-
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
104
|
-
const { isQuarantined } = this.getTestProperties(testManagementTests, testSuite, testName)
|
|
105
|
-
|
|
106
|
-
onDone(isQuarantined)
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
this.addSub('ci:vitest:test:is-modified', ({ modifiedFiles, testSuiteAbsolutePath, onDone }) => {
|
|
110
|
-
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
111
|
-
const isModified = isModifiedTest(testSuite, 0, 0, modifiedFiles, this.constructor.id)
|
|
112
|
-
|
|
113
|
-
onDone(isModified)
|
|
114
|
-
})
|
|
115
|
-
|
|
116
73
|
this.addSub('ci:vitest:is-early-flake-detection-faulty', ({
|
|
117
74
|
knownTests,
|
|
118
75
|
testFilepaths,
|
|
@@ -141,12 +98,16 @@ class VitestPlugin extends CiPlugin {
|
|
|
141
98
|
isRetryReasonAttemptToFix,
|
|
142
99
|
isRetryReasonAtr,
|
|
143
100
|
isModified,
|
|
101
|
+
isTestFrameworkWorker,
|
|
102
|
+
requestErrorTags,
|
|
144
103
|
} = ctx
|
|
145
104
|
|
|
146
105
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
147
|
-
const store = storage('legacy').getStore()
|
|
106
|
+
const store = ctx.currentStore || storage('legacy').getStore()
|
|
107
|
+
const testSuiteSpan = store?.testSuiteSpan || this.testSuiteSpan
|
|
148
108
|
|
|
149
109
|
const extraTags = {
|
|
110
|
+
...requestErrorTags,
|
|
150
111
|
[TEST_SOURCE_FILE]: testSuite,
|
|
151
112
|
}
|
|
152
113
|
if (isRetry) {
|
|
@@ -179,11 +140,14 @@ class VitestPlugin extends CiPlugin {
|
|
|
179
140
|
if (isModified) {
|
|
180
141
|
extraTags[TEST_IS_MODIFIED] = 'true'
|
|
181
142
|
}
|
|
143
|
+
if (isTestFrameworkWorker) {
|
|
144
|
+
extraTags[TEST_IS_TEST_FRAMEWORK_WORKER] = 'true'
|
|
145
|
+
}
|
|
182
146
|
|
|
183
147
|
const span = this.startTestSpan(
|
|
184
148
|
testName,
|
|
185
149
|
testSuite,
|
|
186
|
-
|
|
150
|
+
testSuiteSpan,
|
|
187
151
|
extraTags
|
|
188
152
|
)
|
|
189
153
|
|
|
@@ -204,7 +168,7 @@ class VitestPlugin extends CiPlugin {
|
|
|
204
168
|
})
|
|
205
169
|
|
|
206
170
|
this.addBind('ci:vitest:test:finish-time', (ctx) => {
|
|
207
|
-
const { status, task, attemptToFixPassed, attemptToFixFailed } = ctx
|
|
171
|
+
const { status, task, attemptToFixPassed, attemptToFixFailed, duration } = ctx
|
|
208
172
|
const span = ctx.currentStore?.span
|
|
209
173
|
|
|
210
174
|
// we store the finish time to finish at a later hook
|
|
@@ -218,7 +182,8 @@ class VitestPlugin extends CiPlugin {
|
|
|
218
182
|
span.setTag(TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED, 'false')
|
|
219
183
|
}
|
|
220
184
|
|
|
221
|
-
|
|
185
|
+
const finishTime = typeof duration === 'number' ? span._startTime + duration : span._getTime()
|
|
186
|
+
this.taskToFinishTime.set(task, finishTime)
|
|
222
187
|
|
|
223
188
|
ctx.parentStore = ctx.currentStore
|
|
224
189
|
ctx.currentStore = { ...ctx.currentStore, span }
|
|
@@ -291,19 +256,29 @@ class VitestPlugin extends CiPlugin {
|
|
|
291
256
|
finishAllTraceSpans(span)
|
|
292
257
|
})
|
|
293
258
|
|
|
294
|
-
this.addSub('ci:vitest:test:skip', ({
|
|
259
|
+
this.addSub('ci:vitest:test:skip', ({
|
|
260
|
+
testName,
|
|
261
|
+
testSuiteAbsolutePath,
|
|
262
|
+
isNew,
|
|
263
|
+
isDisabled,
|
|
264
|
+
isTestFrameworkWorker,
|
|
265
|
+
requestErrorTags,
|
|
266
|
+
testSuiteSpan,
|
|
267
|
+
}) => {
|
|
295
268
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
296
269
|
const testSpan = this.startTestSpan(
|
|
297
270
|
testName,
|
|
298
271
|
testSuite,
|
|
299
|
-
this.testSuiteSpan,
|
|
272
|
+
testSuiteSpan || this.testSuiteSpan,
|
|
300
273
|
{
|
|
274
|
+
...requestErrorTags,
|
|
301
275
|
[TEST_SOURCE_FILE]: testSuite,
|
|
302
276
|
[TEST_SOURCE_START]: 1, // we can't get the proper start line in vitest
|
|
303
277
|
[TEST_STATUS]: 'skip',
|
|
304
278
|
[TEST_FINAL_STATUS]: 'skip',
|
|
305
279
|
...(isDisabled ? { [TEST_MANAGEMENT_IS_DISABLED]: 'true' } : {}),
|
|
306
280
|
...(isNew ? { [TEST_IS_NEW]: 'true' } : {}),
|
|
281
|
+
...(isTestFrameworkWorker ? { [TEST_IS_TEST_FRAMEWORK_WORKER]: 'true' } : {}),
|
|
307
282
|
}
|
|
308
283
|
)
|
|
309
284
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'test', this.getTestTelemetryTags(testSpan))
|
|
@@ -311,7 +286,15 @@ class VitestPlugin extends CiPlugin {
|
|
|
311
286
|
})
|
|
312
287
|
|
|
313
288
|
this.addBind('ci:vitest:test-suite:start', (ctx) => {
|
|
314
|
-
const {
|
|
289
|
+
const {
|
|
290
|
+
codeOwnersEntries,
|
|
291
|
+
repositoryRoot,
|
|
292
|
+
requestErrorTags,
|
|
293
|
+
testSuiteAbsolutePath,
|
|
294
|
+
frameworkVersion,
|
|
295
|
+
isTestFrameworkWorker,
|
|
296
|
+
isVitestNoWorkerInitActive,
|
|
297
|
+
} = ctx
|
|
315
298
|
|
|
316
299
|
const testCommand = ctx.testCommand || 'vitest run'
|
|
317
300
|
const { testSessionId, testModuleId } = ctx
|
|
@@ -329,13 +312,16 @@ class VitestPlugin extends CiPlugin {
|
|
|
329
312
|
// test suites run in a different process, so they also need to init the metadata dictionary
|
|
330
313
|
const testSessionName = getTestSessionName(this.config, trimmedCommand, this.testEnvironmentMetadata)
|
|
331
314
|
if (this.tracer._exporter.addMetadataTags) {
|
|
315
|
+
const libraryCapabilitiesTags = this.getLibraryCapabilitiesTags(frameworkVersion, {
|
|
316
|
+
isVitestNoWorkerInitActive,
|
|
317
|
+
})
|
|
332
318
|
this.tracer._exporter.addMetadataTags({
|
|
333
319
|
[TEST_LEVELS_METADATA]: {
|
|
334
320
|
[TEST_COMMAND]: testCommand,
|
|
335
321
|
[TEST_SESSION_NAME]: testSessionName,
|
|
336
322
|
...getTestLevelsMetadataTags(this.testEnvironmentMetadata),
|
|
337
323
|
},
|
|
338
|
-
test:
|
|
324
|
+
test: libraryCapabilitiesTags,
|
|
339
325
|
})
|
|
340
326
|
}
|
|
341
327
|
|
|
@@ -348,9 +334,13 @@ class VitestPlugin extends CiPlugin {
|
|
|
348
334
|
testSuite,
|
|
349
335
|
'vitest'
|
|
350
336
|
),
|
|
337
|
+
...requestErrorTags,
|
|
351
338
|
[TEST_SOURCE_FILE]: testSuite,
|
|
352
339
|
[TEST_SOURCE_START]: 1,
|
|
353
340
|
}
|
|
341
|
+
if (isTestFrameworkWorker) {
|
|
342
|
+
testSuiteMetadata[TEST_IS_TEST_FRAMEWORK_WORKER] = 'true'
|
|
343
|
+
}
|
|
354
344
|
|
|
355
345
|
const codeOwners = this.getCodeOwners(testSuiteMetadata)
|
|
356
346
|
if (codeOwners) {
|
|
@@ -374,13 +364,17 @@ class VitestPlugin extends CiPlugin {
|
|
|
374
364
|
return ctx.currentStore
|
|
375
365
|
})
|
|
376
366
|
|
|
377
|
-
this.addSub('ci:vitest:test-suite:finish', ({ testSuiteSpan, status, onFinish }) => {
|
|
367
|
+
this.addSub('ci:vitest:test-suite:finish', ({ testSuiteSpan, status, deferFlush, onFinish }) => {
|
|
378
368
|
if (testSuiteSpan) {
|
|
379
369
|
testSuiteSpan.setTag(TEST_STATUS, status)
|
|
380
370
|
testSuiteSpan.finish()
|
|
381
371
|
finishAllTraceSpans(testSuiteSpan)
|
|
382
372
|
}
|
|
383
373
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
|
|
374
|
+
if (deferFlush) {
|
|
375
|
+
onFinish()
|
|
376
|
+
return
|
|
377
|
+
}
|
|
384
378
|
this.tracer._exporter.flush(onFinish)
|
|
385
379
|
if (this.runningTestProbe) {
|
|
386
380
|
this.removeDiProbe(this.runningTestProbe)
|
|
@@ -409,9 +403,15 @@ class VitestPlugin extends CiPlugin {
|
|
|
409
403
|
isEarlyFlakeDetectionEnabled,
|
|
410
404
|
isEarlyFlakeDetectionFaulty,
|
|
411
405
|
isTestManagementTestsEnabled,
|
|
406
|
+
requestErrorTags,
|
|
412
407
|
vitestPool,
|
|
408
|
+
isVitestNoWorkerInitActive,
|
|
413
409
|
onFinish,
|
|
414
410
|
}) => {
|
|
411
|
+
for (const [tag, value] of Object.entries(requestErrorTags || {})) {
|
|
412
|
+
this.testSessionSpan.setTag(tag, value)
|
|
413
|
+
this.testModuleSpan.setTag(tag, value)
|
|
414
|
+
}
|
|
415
415
|
this.testSessionSpan.setTag(TEST_STATUS, status)
|
|
416
416
|
this.testModuleSpan.setTag(TEST_STATUS, status)
|
|
417
417
|
if (error) {
|
|
@@ -438,12 +438,12 @@ class VitestPlugin extends CiPlugin {
|
|
|
438
438
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
|
|
439
439
|
this.testSessionSpan.finish()
|
|
440
440
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'session', {
|
|
441
|
-
hasFailedTestReplay: this.libraryConfig?.isDiEnabled
|
|
441
|
+
hasFailedTestReplay: this.libraryConfig?.isDiEnabled && !isVitestNoWorkerInitActive ? true : undefined,
|
|
442
442
|
})
|
|
443
443
|
finishAllTraceSpans(this.testSessionSpan)
|
|
444
444
|
this.telemetry.count(TELEMETRY_TEST_SESSION, {
|
|
445
445
|
provider: this.ciProviderName,
|
|
446
|
-
autoInjected: !!this._tracerConfig.DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
|
|
446
|
+
autoInjected: !!this._tracerConfig.testOptimization.DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
|
|
447
447
|
})
|
|
448
448
|
this.tracer._exporter.flush(onFinish)
|
|
449
449
|
})
|
|
@@ -453,6 +453,19 @@ class VitestPlugin extends CiPlugin {
|
|
|
453
453
|
})
|
|
454
454
|
}
|
|
455
455
|
|
|
456
|
+
/**
|
|
457
|
+
* Returns Vitest library capability metadata tags.
|
|
458
|
+
* @param {string} frameworkVersion - The Vitest version.
|
|
459
|
+
* @param {object} [ctx] - Diagnostic channel context.
|
|
460
|
+
* @param {boolean} [ctx.isVitestNoWorkerInitActive] - Whether no-worker init is active for this run.
|
|
461
|
+
* @returns {Record<string, string|undefined>}
|
|
462
|
+
*/
|
|
463
|
+
getLibraryCapabilitiesTags (frameworkVersion, ctx = {}) {
|
|
464
|
+
return getDefaultLibraryCapabilitiesTags(this.constructor.id, frameworkVersion, {
|
|
465
|
+
omitFailedTestReplay: ctx.isVitestNoWorkerInitActive,
|
|
466
|
+
})
|
|
467
|
+
}
|
|
468
|
+
|
|
456
469
|
/**
|
|
457
470
|
* Handles the coverage report by discovering and uploading it if enabled.
|
|
458
471
|
* @param {string} rootDir - The root directory where coverage reports are located.
|
|
@@ -469,13 +482,6 @@ class VitestPlugin extends CiPlugin {
|
|
|
469
482
|
onDone,
|
|
470
483
|
})
|
|
471
484
|
}
|
|
472
|
-
|
|
473
|
-
getTestProperties (testManagementTests, testSuite, testName) {
|
|
474
|
-
const { attempt_to_fix: isAttemptToFix, disabled: isDisabled, quarantined: isQuarantined } =
|
|
475
|
-
testManagementTests?.vitest?.suites?.[testSuite]?.tests?.[testName]?.properties || {}
|
|
476
|
-
|
|
477
|
-
return { isAttemptToFix, isDisabled, isQuarantined }
|
|
478
|
-
}
|
|
479
485
|
}
|
|
480
486
|
|
|
481
487
|
module.exports = VitestPlugin
|
|
@@ -119,11 +119,14 @@ function wrapCallback (original, wrapper) {
|
|
|
119
119
|
* object.
|
|
120
120
|
* @param {string | symbol} name - The property key of the method to wrap.
|
|
121
121
|
* @param {(original: Function) => (...args: unknown[]) => unknown} wrapper - The wrapper function.
|
|
122
|
-
* @param {{ replaceGetter?: boolean }} [options] -
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
122
|
+
* @param {{ replaceGetter?: boolean }} [options] - By default the getter is
|
|
123
|
+
* wrapped in place, so each property access runs the wrapper. A getter+setter
|
|
124
|
+
* pair keeps its setter; a setter-only property throws. If `replaceGetter` is
|
|
125
|
+
* true, the getter is instead accessed once and replaced with one returning the
|
|
126
|
+
* resolved wrapped value — for a lazy getter+setter pair (e.g. Node 20's
|
|
127
|
+
* `fs.opendir`) the setter is rebuilt to materialize a writable data property on
|
|
128
|
+
* assignment, keeping the descriptor observationally identical for downstream
|
|
129
|
+
* consumers. Use with care! This may only be done when the getter has no side effect.
|
|
127
130
|
* @returns {Record<string | symbol, unknown> | Function | undefined} The target object with
|
|
128
131
|
* the wrapped method.
|
|
129
132
|
*/
|
|
@@ -151,17 +154,13 @@ function wrap (target, name, wrapper, options) {
|
|
|
151
154
|
enumerable: false,
|
|
152
155
|
}
|
|
153
156
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
// this is not a common use case.
|
|
159
|
-
throw new Error(options?.replaceGetter
|
|
160
|
-
? 'Replacing a getter/setter pair is not supported. Implement if required.'
|
|
161
|
-
: 'Replacing setters is not supported. Implement if required.')
|
|
157
|
+
// A setter-only property has nothing to wrap. Instrumenting the setter is not
|
|
158
|
+
// implemented; a getter+setter pair is handled below.
|
|
159
|
+
if (descriptor.set && !descriptor.get) {
|
|
160
|
+
throw new Error('Replacing setters is not supported. Implement if required.')
|
|
162
161
|
}
|
|
163
162
|
|
|
164
|
-
const original = descriptor.value ?? options?.replaceGetter ? target[name] : descriptor.get
|
|
163
|
+
const original = (descriptor.value ?? options?.replaceGetter) ? target[name] : descriptor.get
|
|
165
164
|
|
|
166
165
|
assertMethod(target, name, original)
|
|
167
166
|
|
|
@@ -176,8 +175,30 @@ function wrap (target, name, wrapper, options) {
|
|
|
176
175
|
}
|
|
177
176
|
descriptor.value = wrapped
|
|
178
177
|
} else {
|
|
179
|
-
if (descriptor.
|
|
180
|
-
//
|
|
178
|
+
if (descriptor.set && options?.replaceGetter) {
|
|
179
|
+
// A lazy accessor pair (e.g. Node 20's `fs.opendir`). `original` already
|
|
180
|
+
// resolved the value through the getter. Keep the property an accessor pair
|
|
181
|
+
// so the shape stays observationally identical — a downstream consumer may
|
|
182
|
+
// read the descriptor or assign to it on a specific Node.js version. The
|
|
183
|
+
// getter returns the wrapped value; the setter mirrors the native lazy
|
|
184
|
+
// contract (assignment self-replaces the property with a writable data
|
|
185
|
+
// property holding the assigned value), so a caller that overwrites the
|
|
186
|
+
// method gets exactly what they set, unwrapped, as before. The descriptor
|
|
187
|
+
// is the original accessor descriptor (no `value`/`writable` slots), so
|
|
188
|
+
// reassigning `get`/`set` keeps it a valid accessor descriptor.
|
|
189
|
+
descriptor.get = () => wrapped
|
|
190
|
+
descriptor.set = function (value) {
|
|
191
|
+
Object.defineProperty(this, name, {
|
|
192
|
+
configurable: descriptor.configurable,
|
|
193
|
+
enumerable: descriptor.enumerable,
|
|
194
|
+
writable: true,
|
|
195
|
+
value,
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
} else if (descriptor.get) {
|
|
199
|
+
// Wrap the getter in place; for a getter+setter pair the original setter
|
|
200
|
+
// stays untouched. `replaceGetter` (no side effect on read) instead returns
|
|
201
|
+
// the value resolved once into `wrapped`.
|
|
181
202
|
descriptor.get = options?.replaceGetter ? () => wrapped : wrapped
|
|
182
203
|
} else {
|
|
183
204
|
descriptor.value = wrapped
|
|
@@ -93,14 +93,14 @@ class AIGuard extends NoopAIGuard {
|
|
|
93
93
|
constructor (tracer, config) {
|
|
94
94
|
super()
|
|
95
95
|
|
|
96
|
-
if (!config.
|
|
96
|
+
if (!config.DD_API_KEY || !config.DD_APP_KEY) {
|
|
97
97
|
log.error('AIGuard: missing api and/or app keys, use env DD_API_KEY and DD_APP_KEY')
|
|
98
98
|
this.#initialized = false
|
|
99
99
|
return
|
|
100
100
|
}
|
|
101
101
|
this.#tracer = tracer
|
|
102
102
|
this.#headers = {
|
|
103
|
-
'DD-API-KEY': config.
|
|
103
|
+
'DD-API-KEY': config.DD_API_KEY,
|
|
104
104
|
'DD-APPLICATION-KEY': config.DD_APP_KEY,
|
|
105
105
|
'DD-AI-GUARD-VERSION': tracerVersion,
|
|
106
106
|
'DD-AI-GUARD-SOURCE': 'SDK',
|
|
@@ -31,11 +31,11 @@ class NoopTTLCache {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
function configure ({ appsec, apmTracingEnabled }) {
|
|
34
|
-
enabled = appsec.
|
|
34
|
+
enabled = appsec.DD_API_SECURITY_ENABLED
|
|
35
35
|
asmStandaloneEnabled = apmTracingEnabled === false
|
|
36
|
-
sampledRequests = appsec.
|
|
36
|
+
sampledRequests = appsec.DD_API_SECURITY_SAMPLE_DELAY === 0
|
|
37
37
|
? new NoopTTLCache()
|
|
38
|
-
: new TTLCache({ max: MAX_SIZE, ttl: appsec.
|
|
38
|
+
: new TTLCache({ max: MAX_SIZE, ttl: appsec.DD_API_SECURITY_SAMPLE_DELAY * 1000 })
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
function disable () {
|
|
@@ -27,6 +27,8 @@ module.exports = {
|
|
|
27
27
|
httpClientResponseFinish: dc.channel('apm:http:client:response:finish'),
|
|
28
28
|
incomingHttpRequestEnd: dc.channel('dd-trace:incomingHttpRequestEnd'),
|
|
29
29
|
incomingHttpRequestStart: dc.channel('dd-trace:incomingHttpRequestStart'),
|
|
30
|
+
lambdaStartInvocation: dc.channel('datadog:lambda:start-invocation'),
|
|
31
|
+
lambdaEndInvocation: dc.channel('datadog:lambda:end-invocation'),
|
|
30
32
|
multerParser: dc.channel('datadog:multer:read:finish'),
|
|
31
33
|
mysql2OuterQueryStart: dc.channel('datadog:mysql2:outerquery:start'),
|
|
32
34
|
nextBodyParsed: dc.channel('apm:next:body-parsed'),
|
|
@@ -43,7 +45,7 @@ module.exports = {
|
|
|
43
45
|
routerParam: dc.channel('datadog:router:param:start'),
|
|
44
46
|
setCookieChannel: dc.channel('datadog:iast:set-cookie'),
|
|
45
47
|
setUncaughtExceptionCaptureCallbackStart: dc.channel('datadog:process:setUncaughtExceptionCaptureCallback:start'),
|
|
46
|
-
|
|
48
|
+
startGraphqlResolver: dc.channel('datadog:graphql:resolver:start'),
|
|
47
49
|
stripeCheckoutSessionCreate: dc.channel('datadog:stripe:checkoutSession:create:finish'),
|
|
48
50
|
stripeConstructEvent: dc.channel('datadog:stripe:constructEvent:finish'),
|
|
49
51
|
stripePaymentIntentCreate: dc.channel('datadog:stripe:paymentIntent:create:finish'),
|