dd-trace 6.13.0 → 6.14.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 +3 -1
- package/ci/init.js +8 -3
- package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
- package/ci/test-optimization-validation/ci-remediation.js +6 -6
- package/ci/test-optimization-validation/command-blocker.js +7 -5
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
- package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
- package/ci/test-optimization-validation/package-check.js +1 -1
- package/ci/test-optimization-validation/report-writer.js +7 -5
- package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
- package/ci/vitest-no-worker-init-setup.mjs +13 -13
- package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
- package/package.json +13 -10
- package/packages/datadog-esbuild/index.js +3 -1
- package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
- package/packages/datadog-instrumentations/src/fs.js +24 -20
- package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
- package/packages/datadog-instrumentations/src/graphql.js +16 -16
- package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +265 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +13 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +251 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +11 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
- package/packages/datadog-instrumentations/src/jest.js +25 -20
- package/packages/datadog-instrumentations/src/knex.js +76 -0
- package/packages/datadog-instrumentations/src/mariadb.js +6 -0
- package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +25 -15
- package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
- package/packages/datadog-instrumentations/src/net.js +14 -17
- package/packages/datadog-instrumentations/src/next.js +212 -62
- package/packages/datadog-instrumentations/src/openai.js +42 -40
- package/packages/datadog-instrumentations/src/pg.js +15 -0
- package/packages/datadog-instrumentations/src/playwright.js +109 -13
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
- package/packages/datadog-instrumentations/src/sequelize.js +94 -2
- package/packages/datadog-instrumentations/src/undici.js +53 -4
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +37 -3
- package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
- package/packages/datadog-instrumentations/src/vitest-worker.js +71 -14
- package/packages/datadog-instrumentations/src/webdriverio.js +965 -6
- package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
- package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
- package/packages/datadog-plugin-amqp10/src/util.js +2 -1
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
- package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
- package/packages/datadog-plugin-cucumber/src/index.js +0 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
- package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
- package/packages/datadog-plugin-graphql/src/execute.js +998 -196
- package/packages/datadog-plugin-graphql/src/index.js +27 -7
- package/packages/datadog-plugin-graphql/src/jit.js +136 -0
- package/packages/datadog-plugin-graphql/src/request.js +18 -27
- package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
- package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
- package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
- package/packages/datadog-plugin-graphql/src/utils.js +208 -120
- package/packages/datadog-plugin-graphql/src/validate.js +10 -23
- package/packages/datadog-plugin-jest/src/index.js +0 -6
- package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
- package/packages/datadog-plugin-mocha/src/index.js +403 -75
- package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
- package/packages/datadog-plugin-mysql/src/index.js +41 -15
- package/packages/datadog-plugin-net/src/tcp.js +3 -1
- package/packages/datadog-plugin-next/src/index.js +63 -29
- package/packages/datadog-plugin-playwright/src/index.js +167 -27
- package/packages/datadog-plugin-undici/src/index.js +27 -41
- package/packages/datadog-plugin-vitest/src/index.js +6 -12
- package/packages/dd-trace/src/aiguard/evaluation.js +2 -1
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +8 -1
- package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
- package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
- package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
- package/packages/dd-trace/src/appsec/graphql.js +6 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +11 -36
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/lambda.js +44 -26
- package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +5 -173
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +394 -225
- package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
- package/packages/dd-trace/src/ci-visibility/final-flush.js +2 -1
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
- package/packages/dd-trace/src/ci-visibility/requests/video-request.js +176 -0
- package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
- package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
- package/packages/dd-trace/src/config/index.js +73 -13
- package/packages/dd-trace/src/config/parsers.js +11 -1
- package/packages/dd-trace/src/config/stable.js +3 -3
- package/packages/dd-trace/src/config/supported-configurations.json +20 -2
- package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
- package/packages/dd-trace/src/debugger/config.js +12 -1
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
- package/packages/dd-trace/src/debugger/devtools_client/request-options.js +33 -0
- package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -40
- package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
- package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
- package/packages/dd-trace/src/debugger/index.js +23 -3
- package/packages/dd-trace/src/dogstatsd.js +510 -76
- package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
- package/packages/dd-trace/src/encode/0.4.js +5 -5
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
- package/packages/dd-trace/src/evp_proxy/direct.js +27 -23
- package/packages/dd-trace/src/exporters/agent/index.js +2 -1
- package/packages/dd-trace/src/exporters/agent/writer.js +24 -7
- package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
- package/packages/dd-trace/src/exporters/agentless/writer.js +100 -118
- package/packages/dd-trace/src/exporters/common/agents.js +28 -6
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
- package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
- package/packages/dd-trace/src/exporters/common/request.js +31 -11
- package/packages/dd-trace/src/exporters/common/url.js +40 -1
- package/packages/dd-trace/src/exporters/common/writer.js +9 -7
- package/packages/dd-trace/src/flush.js +6 -3
- package/packages/dd-trace/src/id.js +14 -0
- package/packages/dd-trace/src/index.js +5 -2
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +2 -1
- package/packages/dd-trace/src/llmobs/index.js +32 -32
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
- package/packages/dd-trace/src/llmobs/util.js +0 -33
- package/packages/dd-trace/src/llmobs/writers/base.js +49 -32
- package/packages/dd-trace/src/log/index.js +7 -1
- package/packages/dd-trace/src/{ci-visibility/log-submission → log-submission}/log-submission-plugin.js +48 -19
- package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/index.js +5 -24
- package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +15 -22
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +14 -4
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +10 -0
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
- package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +93 -18
- package/packages/dd-trace/src/payload-tagging/index.js +2 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
- package/packages/dd-trace/src/plugins/tracing.js +12 -10
- package/packages/dd-trace/src/plugins/util/ci.js +3 -1
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
- package/packages/dd-trace/src/plugins/util/test.js +83 -20
- package/packages/dd-trace/src/plugins/util/url.js +2 -2
- package/packages/dd-trace/src/plugins/util/web.js +4 -9
- package/packages/dd-trace/src/profiling/config.js +1 -0
- package/packages/dd-trace/src/profiling/oom.js +7 -2
- package/packages/dd-trace/src/profiling/profiler.js +23 -13
- package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
- package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
- package/packages/dd-trace/src/proxy.js +75 -5
- package/packages/dd-trace/src/remote_config/index.js +319 -101
- package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
- package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -1
- package/packages/dd-trace/src/sampling_rule.js +36 -9
- package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +4 -8
- package/packages/dd-trace/src/serverless/vercel.js +53 -20
- package/packages/dd-trace/src/serverless.js +10 -3
- package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
- package/packages/dd-trace/src/span_stats.js +24 -28
- package/packages/dd-trace/src/startup-log.js +3 -2
- package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
- package/packages/dd-trace/src/telemetry/send-data.js +18 -16
- package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
- package/packages/dd-trace/src/tracer.js +4 -5
- package/packages/dd-trace/src/tracer_metadata.js +11 -3
- 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/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
- package/packages/dd-trace/src/encode/agentless-json.js +0 -210
|
@@ -1,9 +1,18 @@
|
|
|
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
|
+
/**
|
|
8
|
+
* @typedef {{
|
|
9
|
+
* database?: string,
|
|
10
|
+
* host?: string,
|
|
11
|
+
* port?: number,
|
|
12
|
+
* user?: string
|
|
13
|
+
* }} ConnectionConfig
|
|
14
|
+
*/
|
|
15
|
+
|
|
7
16
|
class MySQLPlugin extends DatabasePlugin {
|
|
8
17
|
static id = 'mysql'
|
|
9
18
|
static system = 'mysql'
|
|
@@ -33,13 +42,7 @@ class MySQLPlugin extends DatabasePlugin {
|
|
|
33
42
|
startTime: ctx.startTime,
|
|
34
43
|
type: 'sql',
|
|
35
44
|
kind: 'client',
|
|
36
|
-
meta:
|
|
37
|
-
'db.type': this.system,
|
|
38
|
-
'db.user': ctx.conf.user,
|
|
39
|
-
'db.name': ctx.conf.database,
|
|
40
|
-
'out.host': ctx.conf.host,
|
|
41
|
-
[CLIENT_PORT_KEY]: ctx.conf.port,
|
|
42
|
-
},
|
|
45
|
+
meta: connectionMeta(this.system, ctx.conf),
|
|
43
46
|
}, ctx)
|
|
44
47
|
})
|
|
45
48
|
|
|
@@ -51,6 +54,20 @@ class MySQLPlugin extends DatabasePlugin {
|
|
|
51
54
|
this.addError(ctx.error, span)
|
|
52
55
|
}
|
|
53
56
|
span.setTag(`${this.component}.pool.wait_time`, ctx.poolWaitTime)
|
|
57
|
+
if (ctx.connectionConfig !== undefined) {
|
|
58
|
+
span.addTags(connectionMeta(this.system, ctx.connectionConfig))
|
|
59
|
+
if (typeof this.config.service === 'function') {
|
|
60
|
+
const service = this.serviceName({
|
|
61
|
+
pluginConfig: this.config,
|
|
62
|
+
dbConfig: ctx.connectionConfig,
|
|
63
|
+
system: this.system,
|
|
64
|
+
})
|
|
65
|
+
if (service.name) {
|
|
66
|
+
this.setServiceName(span, service.name)
|
|
67
|
+
span.setTag(SVC_SRC_KEY, service.source)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
54
71
|
this.finish(ctx)
|
|
55
72
|
})
|
|
56
73
|
}
|
|
@@ -62,13 +79,7 @@ class MySQLPlugin extends DatabasePlugin {
|
|
|
62
79
|
resource: ctx.sql,
|
|
63
80
|
type: 'sql',
|
|
64
81
|
kind: 'client',
|
|
65
|
-
meta:
|
|
66
|
-
'db.type': this.system,
|
|
67
|
-
'db.user': ctx.conf.user,
|
|
68
|
-
'db.name': ctx.conf.database,
|
|
69
|
-
'out.host': ctx.conf.host,
|
|
70
|
-
[CLIENT_PORT_KEY]: ctx.conf.port,
|
|
71
|
-
},
|
|
82
|
+
meta: connectionMeta(this.system, ctx.conf),
|
|
72
83
|
}, ctx)
|
|
73
84
|
|
|
74
85
|
if (ctx.poolWaitTime !== undefined) {
|
|
@@ -81,4 +92,19 @@ class MySQLPlugin extends DatabasePlugin {
|
|
|
81
92
|
}
|
|
82
93
|
}
|
|
83
94
|
|
|
95
|
+
/**
|
|
96
|
+
* @param {string} system
|
|
97
|
+
* @param {ConnectionConfig} config
|
|
98
|
+
* @returns {Record<string, string|number|undefined>}
|
|
99
|
+
*/
|
|
100
|
+
function connectionMeta (system, config) {
|
|
101
|
+
return {
|
|
102
|
+
'db.type': system,
|
|
103
|
+
'db.user': config.user,
|
|
104
|
+
'db.name': config.database,
|
|
105
|
+
'out.host': config.host,
|
|
106
|
+
[CLIENT_PORT_KEY]: config.port,
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
84
110
|
module.exports = MySQLPlugin
|
|
@@ -28,10 +28,12 @@ class NetTCPPlugin extends ClientPlugin {
|
|
|
28
28
|
const host = ctx.options.host || 'localhost'
|
|
29
29
|
const port = ctx.options.port || 0
|
|
30
30
|
const family = ctx.options.family || 4
|
|
31
|
+
let resource = host
|
|
32
|
+
if (port) resource += `:${port}`
|
|
31
33
|
|
|
32
34
|
this.startSpan('tcp.connect', {
|
|
33
35
|
service: this.config.service,
|
|
34
|
-
resource
|
|
36
|
+
resource,
|
|
35
37
|
kind: 'client',
|
|
36
38
|
meta: {
|
|
37
39
|
'tcp.remote.host': host,
|
|
@@ -11,20 +11,53 @@ const errorPages = new Set(['/404', '/500', '/_error', '/_not-found', '/_not-fou
|
|
|
11
11
|
const reusedNextRequestStores = new WeakSet()
|
|
12
12
|
const nextParentRoutes = new WeakMap()
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {Record<string, unknown> & {
|
|
16
|
+
* span: import('../../dd-trace/src/opentracing/span'),
|
|
17
|
+
* req: import('node:http').IncomingMessage,
|
|
18
|
+
* backgroundRevalidationRequest?: import('node:http').IncomingMessage
|
|
19
|
+
* }} NextRequestStore
|
|
20
|
+
*
|
|
21
|
+
* @typedef {object} NextRequest
|
|
22
|
+
* @property {unknown} [error]
|
|
23
|
+
*
|
|
24
|
+
* @typedef {object} NextRequestContext
|
|
25
|
+
* @property {import('node:http').IncomingMessage} req
|
|
26
|
+
* @property {import('node:http').ServerResponse} res
|
|
27
|
+
* @property {NextRequest} [nextRequest]
|
|
28
|
+
* @property {boolean} [finishOnResponse]
|
|
29
|
+
* @property {NextRequestStore} [currentStore]
|
|
30
|
+
* @property {unknown} [error]
|
|
31
|
+
*
|
|
32
|
+
* @typedef {object} NextErrorContext
|
|
33
|
+
* @property {import('../../dd-trace/src/opentracing/span')} [span]
|
|
34
|
+
* @property {NextRequestStore} [currentStore]
|
|
35
|
+
* @property {import('node:http').IncomingMessage} [req]
|
|
36
|
+
* @property {unknown} error
|
|
37
|
+
*/
|
|
38
|
+
|
|
14
39
|
class NextPlugin extends ServerPlugin {
|
|
15
40
|
static id = 'next'
|
|
16
41
|
|
|
17
42
|
constructor (...args) {
|
|
18
43
|
super(...args)
|
|
44
|
+
this.addBind('apm:next:request:background-revalidation', req => ({
|
|
45
|
+
...storage('legacy').getStore(),
|
|
46
|
+
span: undefined,
|
|
47
|
+
backgroundRevalidationRequest: req,
|
|
48
|
+
}))
|
|
19
49
|
this.addSub('apm:next:page:load', message => this.pageLoad(message))
|
|
20
50
|
}
|
|
21
51
|
|
|
22
|
-
|
|
52
|
+
/** @param {NextRequestContext} ctx */
|
|
53
|
+
bindStart (ctx) {
|
|
54
|
+
const { req } = ctx
|
|
23
55
|
const store = storage('legacy').getStore()
|
|
24
56
|
const parentSpan = store?.span
|
|
25
57
|
if (parentSpan?._integrationName === this.constructor.id) {
|
|
26
58
|
const reusedStore = { ...store, span: parentSpan, req }
|
|
27
59
|
reusedNextRequestStores.add(reusedStore)
|
|
60
|
+
if (ctx.finishOnResponse) ctx.currentStore = reusedStore
|
|
28
61
|
return reusedStore
|
|
29
62
|
}
|
|
30
63
|
|
|
@@ -34,17 +67,19 @@ class NextPlugin extends ServerPlugin {
|
|
|
34
67
|
let serviceSource = this.config.service ? 'opt.plugin' : schemaServiceSource
|
|
35
68
|
if (!serviceName || serviceName === this.tracer._service) serviceSource = undefined
|
|
36
69
|
|
|
70
|
+
const tags = {
|
|
71
|
+
[COMPONENT]: this.constructor.id,
|
|
72
|
+
'service.name': serviceName,
|
|
73
|
+
'resource.name': req.method,
|
|
74
|
+
'span.type': 'web',
|
|
75
|
+
'span.kind': 'server',
|
|
76
|
+
'http.method': req.method,
|
|
77
|
+
}
|
|
78
|
+
if (serviceSource !== undefined) tags[SVC_SRC_KEY] = serviceSource
|
|
79
|
+
|
|
37
80
|
const span = this.tracer.startSpan(this.operationName(), {
|
|
38
81
|
childOf,
|
|
39
|
-
tags
|
|
40
|
-
[COMPONENT]: this.constructor.id,
|
|
41
|
-
'service.name': serviceName,
|
|
42
|
-
'resource.name': req.method,
|
|
43
|
-
'span.type': 'web',
|
|
44
|
-
'span.kind': 'server',
|
|
45
|
-
'http.method': req.method,
|
|
46
|
-
...(serviceSource === undefined ? undefined : { [SVC_SRC_KEY]: serviceSource }),
|
|
47
|
-
},
|
|
82
|
+
tags,
|
|
48
83
|
integrationName: this.constructor.id,
|
|
49
84
|
})
|
|
50
85
|
|
|
@@ -52,31 +87,31 @@ class NextPlugin extends ServerPlugin {
|
|
|
52
87
|
|
|
53
88
|
analyticsSampler.sample(span, this.config.measured, true)
|
|
54
89
|
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
return
|
|
90
|
+
const httpParentSpan = parentSpan?._integrationName === 'http' ? parentSpan : undefined
|
|
91
|
+
const currentStore = { ...store, span, req, httpParentSpan }
|
|
92
|
+
if (ctx.finishOnResponse) ctx.currentStore = currentStore
|
|
93
|
+
return currentStore
|
|
59
94
|
}
|
|
60
95
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
96
|
+
/** @param {NextErrorContext} ctx */
|
|
97
|
+
error ({ currentStore, span, req, error }) {
|
|
98
|
+
const store = currentStore ?? storage('legacy').getStore()
|
|
99
|
+
if (req && store?.backgroundRevalidationRequest === req) return
|
|
65
100
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
this.addError(error, span)
|
|
101
|
+
span ||= store?.span
|
|
102
|
+
if (span) this.addError(error, span)
|
|
70
103
|
}
|
|
71
104
|
|
|
72
|
-
|
|
73
|
-
|
|
105
|
+
/** @param {NextRequestContext} ctx */
|
|
106
|
+
finish (ctx) {
|
|
107
|
+
const { req, res, nextRequest = {} } = ctx
|
|
108
|
+
const store = ctx.currentStore ?? storage('legacy').getStore()
|
|
74
109
|
|
|
75
110
|
if (!store) return
|
|
76
111
|
if (reusedNextRequestStores.has(store)) return
|
|
77
112
|
|
|
78
113
|
const span = store.span
|
|
79
|
-
const error = span.context().getTag('error')
|
|
114
|
+
const error = ctx.error ?? span.context().getTag('error')
|
|
80
115
|
const requestError = req.error || nextRequest.error
|
|
81
116
|
|
|
82
117
|
if (requestError) {
|
|
@@ -107,7 +142,7 @@ class NextPlugin extends ServerPlugin {
|
|
|
107
142
|
|
|
108
143
|
if (!store) return
|
|
109
144
|
|
|
110
|
-
const { span, req,
|
|
145
|
+
const { span, req, httpParentSpan } = store
|
|
111
146
|
|
|
112
147
|
// safeguard against missing req in complicated timeout scenarios
|
|
113
148
|
if (!req) return
|
|
@@ -138,10 +173,9 @@ class NextPlugin extends ServerPlugin {
|
|
|
138
173
|
'resource.name': `${req.method} ${page}`.trim(),
|
|
139
174
|
'next.page': page,
|
|
140
175
|
})
|
|
141
|
-
|
|
142
|
-
web.setRouteOrEndpointTag(routeRequest)
|
|
176
|
+
web.setRouteOrEndpointTag(req)
|
|
143
177
|
setHttpParentRoute(httpParentSpan, req.method, page, isStatic)
|
|
144
|
-
web.setRoute(
|
|
178
|
+
web.setRoute(req, page)
|
|
145
179
|
}
|
|
146
180
|
|
|
147
181
|
configure (config) {
|
|
@@ -13,6 +13,12 @@ const {
|
|
|
13
13
|
getScreenshotUploadResult,
|
|
14
14
|
getScreenshotUploadTag,
|
|
15
15
|
} = require('../../dd-trace/src/ci-visibility/test-screenshot')
|
|
16
|
+
const {
|
|
17
|
+
VIDEO_UPLOAD_RESULT_ERROR,
|
|
18
|
+
VIDEO_UPLOAD_RESULT_UPLOADED,
|
|
19
|
+
getVideoUploadResult,
|
|
20
|
+
getVideoUploadTag,
|
|
21
|
+
} = require('../../dd-trace/src/ci-visibility/test-video')
|
|
16
22
|
|
|
17
23
|
const {
|
|
18
24
|
finishAllTraceSpans,
|
|
@@ -62,6 +68,8 @@ const { appClosing: appClosingTelemetry } = require('../../dd-trace/src/telemetr
|
|
|
62
68
|
const log = require('../../dd-trace/src/log')
|
|
63
69
|
|
|
64
70
|
const PLAYWRIGHT_FAILURE_SCREENSHOT_RE = /^test-failed-\d+\.png$/
|
|
71
|
+
const PLAYWRIGHT_VIDEO_CONTENT_TYPES = new Set(['video/mp4', 'video/webm'])
|
|
72
|
+
const noop = () => {}
|
|
65
73
|
|
|
66
74
|
/**
|
|
67
75
|
* Returns whether an attachment is an automatic Playwright failure screenshot.
|
|
@@ -76,10 +84,23 @@ function isPlaywrightFailureScreenshot (attachment) {
|
|
|
76
84
|
PLAYWRIGHT_FAILURE_SCREENSHOT_RE.test(basename(attachment.path))
|
|
77
85
|
}
|
|
78
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Returns whether an attachment is a Playwright test video.
|
|
89
|
+
*
|
|
90
|
+
* @param {object} attachment - Playwright test attachment
|
|
91
|
+
* @returns {boolean}
|
|
92
|
+
*/
|
|
93
|
+
function isPlaywrightFailureVideo (attachment) {
|
|
94
|
+
return attachment?.name === 'video' &&
|
|
95
|
+
PLAYWRIGHT_VIDEO_CONTENT_TYPES.has(attachment.contentType) &&
|
|
96
|
+
typeof attachment.path === 'string'
|
|
97
|
+
}
|
|
98
|
+
|
|
79
99
|
class PlaywrightPlugin extends CiPlugin {
|
|
80
100
|
static id = 'playwright'
|
|
81
101
|
|
|
82
102
|
#isFinalizingAfterError = false
|
|
103
|
+
#finishPendingTestFinishes
|
|
83
104
|
#pendingTestFinishCallbacks = new Map()
|
|
84
105
|
|
|
85
106
|
constructor (...args) {
|
|
@@ -89,7 +110,6 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
89
110
|
this.numFailedTests = 0
|
|
90
111
|
this.numFailedSuites = 0
|
|
91
112
|
this.pendingTestFinishes = 0
|
|
92
|
-
this.finishSession = undefined
|
|
93
113
|
|
|
94
114
|
this.addSub('ci:playwright:test:is-modified', ({
|
|
95
115
|
filePath,
|
|
@@ -101,29 +121,60 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
101
121
|
onDone(isModified)
|
|
102
122
|
})
|
|
103
123
|
|
|
104
|
-
this.addSub('ci:playwright:session:start', ({
|
|
124
|
+
this.addSub('ci:playwright:session:start', ({
|
|
125
|
+
isFailureScreenshotEnabled,
|
|
126
|
+
isFailureVideoEnabled,
|
|
127
|
+
isFailureVideoUploadSupported,
|
|
128
|
+
}) => {
|
|
105
129
|
this.#isFinalizingAfterError = false
|
|
106
|
-
|
|
130
|
+
const testOptimizationConfig = getConfig().testOptimization
|
|
107
131
|
|
|
108
|
-
if (!isFailureScreenshotEnabled) {
|
|
132
|
+
if (testOptimizationConfig.DD_TEST_FAILURE_SCREENSHOTS_ENABLED && !isFailureScreenshotEnabled) {
|
|
109
133
|
log.warn(
|
|
110
134
|
'%s %s',
|
|
111
135
|
'DD_TEST_FAILURE_SCREENSHOTS_ENABLED is true, but Playwright screenshot capture is disabled.',
|
|
112
136
|
'Set Playwright use.screenshot to "only-on-failure", "on-first-failure", or "on".'
|
|
113
137
|
)
|
|
114
138
|
}
|
|
139
|
+
if (testOptimizationConfig.DD_TEST_FAILURE_VIDEOS_ENABLED && !isFailureVideoUploadSupported) {
|
|
140
|
+
log.warn(
|
|
141
|
+
'%s %s',
|
|
142
|
+
'DD_TEST_FAILURE_VIDEOS_ENABLED is true, but Playwright video upload requires Playwright 1.38.0 or later.',
|
|
143
|
+
'Upgrade Playwright to enable failure video uploads.'
|
|
144
|
+
)
|
|
145
|
+
} else if (testOptimizationConfig.DD_TEST_FAILURE_VIDEOS_ENABLED && !isFailureVideoEnabled) {
|
|
146
|
+
log.warn(
|
|
147
|
+
'%s %s',
|
|
148
|
+
'DD_TEST_FAILURE_VIDEOS_ENABLED is true, but Playwright video capture is disabled.',
|
|
149
|
+
'Set Playwright use.video to "retain-on-failure" or "on".'
|
|
150
|
+
)
|
|
151
|
+
}
|
|
115
152
|
})
|
|
116
153
|
|
|
117
|
-
this.addSub('ci:playwright:session:configuration', ({
|
|
118
|
-
|
|
154
|
+
this.addSub('ci:playwright:session:configuration', ({
|
|
155
|
+
isFailureScreenshotEnabled,
|
|
156
|
+
isFailureVideoEnabled,
|
|
157
|
+
isFailureVideoUploadSupported,
|
|
158
|
+
}) => {
|
|
159
|
+
const testOptimizationConfig = getConfig().testOptimization
|
|
119
160
|
|
|
120
|
-
if (
|
|
161
|
+
if (testOptimizationConfig.DD_TEST_FAILURE_SCREENSHOTS_ENABLED && isFailureScreenshotEnabled &&
|
|
162
|
+
!this.tracer._exporter?.canUploadTestScreenshots?.()) {
|
|
121
163
|
log.warn(
|
|
122
164
|
'%s %s',
|
|
123
165
|
'DD_TEST_FAILURE_SCREENSHOTS_ENABLED is true, but Playwright screenshot upload is not supported',
|
|
124
166
|
'by the active Test Optimization transport.'
|
|
125
167
|
)
|
|
126
168
|
}
|
|
169
|
+
if (testOptimizationConfig.DD_TEST_FAILURE_VIDEOS_ENABLED && isFailureVideoEnabled &&
|
|
170
|
+
isFailureVideoUploadSupported &&
|
|
171
|
+
!this.tracer._exporter?.canUploadTestVideos?.()) {
|
|
172
|
+
log.warn(
|
|
173
|
+
'%s %s',
|
|
174
|
+
'DD_TEST_FAILURE_VIDEOS_ENABLED is true, but Playwright video upload is not supported',
|
|
175
|
+
'by the active Test Optimization transport.'
|
|
176
|
+
)
|
|
177
|
+
}
|
|
127
178
|
})
|
|
128
179
|
|
|
129
180
|
this.addSub('ci:playwright:session:finish', ({
|
|
@@ -140,13 +191,16 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
140
191
|
testSuiteSpan.setTag(TEST_STATUS, 'fail')
|
|
141
192
|
testSuiteSpan.setTag('error', error)
|
|
142
193
|
}
|
|
143
|
-
for (const [finishTest,
|
|
144
|
-
finishTest(
|
|
145
|
-
|
|
194
|
+
for (const [finishTest, pendingTestFinish] of this.#pendingTestFinishCallbacks) {
|
|
195
|
+
finishTest(
|
|
196
|
+
pendingTestFinish.hasScreenshot ? SCREENSHOT_UPLOAD_RESULT_ERROR : undefined,
|
|
197
|
+
pendingTestFinish.hasVideo ? VIDEO_UPLOAD_RESULT_ERROR : undefined
|
|
198
|
+
)
|
|
199
|
+
pendingTestFinish.abortController.abort()
|
|
146
200
|
}
|
|
147
201
|
}
|
|
148
202
|
|
|
149
|
-
const finishSession = () => {
|
|
203
|
+
const finishSession = (flushDone) => {
|
|
150
204
|
this.testModuleSpan.setTag(TEST_STATUS, status)
|
|
151
205
|
this.testSessionSpan.setTag(TEST_STATUS, status)
|
|
152
206
|
|
|
@@ -173,7 +227,6 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
173
227
|
this.testSessionSpan.setTag(TEST_MANAGEMENT_ENABLED, 'true')
|
|
174
228
|
}
|
|
175
229
|
|
|
176
|
-
this.tracer._exporter.exportDeferredTestSuiteSpans?.()
|
|
177
230
|
this.testModuleSpan.finish()
|
|
178
231
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
|
|
179
232
|
this.testSessionSpan.finish()
|
|
@@ -184,16 +237,16 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
184
237
|
autoInjected: !!this._tracerConfig.testOptimization.DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
|
|
185
238
|
})
|
|
186
239
|
appClosingTelemetry()
|
|
187
|
-
this.tracer._exporter.flush(
|
|
240
|
+
this.tracer._exporter.flush(flushDone)
|
|
188
241
|
this.numFailedTests = 0
|
|
189
242
|
this.numFailedSuites = 0
|
|
190
|
-
this.finishSession = undefined
|
|
191
243
|
}
|
|
192
244
|
|
|
193
245
|
if (this.pendingTestFinishes > 0) {
|
|
194
|
-
this
|
|
246
|
+
this.#finishPendingTestFinishes = () => this.tracer._exporter.flush(onDone)
|
|
247
|
+
finishSession(noop)
|
|
195
248
|
} else {
|
|
196
|
-
finishSession()
|
|
249
|
+
finishSession(onDone)
|
|
197
250
|
}
|
|
198
251
|
})
|
|
199
252
|
|
|
@@ -255,7 +308,6 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
255
308
|
this.numFailedSuites++
|
|
256
309
|
}
|
|
257
310
|
|
|
258
|
-
this.tracer._exporter.deferTestSuiteSpan?.(testSuiteSpan)
|
|
259
311
|
testSuiteSpan.finish()
|
|
260
312
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
|
|
261
313
|
})
|
|
@@ -267,7 +319,7 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
267
319
|
}
|
|
268
320
|
})
|
|
269
321
|
|
|
270
|
-
this.addSub('ci:playwright:worker:report', ({ serializedTraces, screenshots }) => {
|
|
322
|
+
this.addSub('ci:playwright:worker:report', ({ serializedTraces, screenshots, videos }) => {
|
|
271
323
|
const traces = JSON.parse(serializedTraces)
|
|
272
324
|
const formattedTraces = []
|
|
273
325
|
let formattedTestSpan
|
|
@@ -326,33 +378,70 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
326
378
|
}
|
|
327
379
|
}
|
|
328
380
|
|
|
329
|
-
if (!formattedTestSpan || !screenshots || this.#isFinalizingAfterError) {
|
|
381
|
+
if (!formattedTestSpan || (!screenshots && !videos) || this.#isFinalizingAfterError) {
|
|
330
382
|
exportTraces()
|
|
331
383
|
return
|
|
332
384
|
}
|
|
333
385
|
|
|
334
386
|
this.pendingTestFinishes++
|
|
335
387
|
const abortController = new AbortController()
|
|
336
|
-
const finishTest = (screenshotUploadResult) => {
|
|
388
|
+
const finishTest = (screenshotUploadResult, videoUploadResult) => {
|
|
337
389
|
if (!this.#pendingTestFinishCallbacks.delete(finishTest)) return
|
|
338
390
|
|
|
339
391
|
const screenshotUploadTag = getScreenshotUploadTag(screenshotUploadResult)
|
|
340
392
|
if (screenshotUploadTag) {
|
|
341
393
|
formattedTestSpan.meta[screenshotUploadTag] = 'true'
|
|
342
394
|
}
|
|
395
|
+
const videoUploadTag = getVideoUploadTag(videoUploadResult)
|
|
396
|
+
if (videoUploadTag) {
|
|
397
|
+
formattedTestSpan.meta[videoUploadTag] = 'true'
|
|
398
|
+
}
|
|
343
399
|
exportTraces()
|
|
344
400
|
this.pendingTestFinishes--
|
|
345
|
-
if (this.pendingTestFinishes === 0 && this
|
|
346
|
-
this
|
|
401
|
+
if (this.pendingTestFinishes === 0 && this.#finishPendingTestFinishes) {
|
|
402
|
+
const finishPendingTestFinishes = this.#finishPendingTestFinishes
|
|
403
|
+
this.#finishPendingTestFinishes = undefined
|
|
404
|
+
finishPendingTestFinishes()
|
|
347
405
|
}
|
|
348
406
|
}
|
|
349
|
-
|
|
350
|
-
|
|
407
|
+
const pendingTestFinish = {
|
|
408
|
+
abortController,
|
|
409
|
+
hasScreenshot: false,
|
|
410
|
+
hasVideo: false,
|
|
411
|
+
}
|
|
412
|
+
this.#pendingTestFinishCallbacks.set(finishTest, pendingTestFinish)
|
|
413
|
+
let pendingMedia = 0
|
|
414
|
+
let screenshotUploadResult
|
|
415
|
+
let videoUploadResult
|
|
416
|
+
const finishMedia = () => {
|
|
417
|
+
pendingMedia--
|
|
418
|
+
if (pendingMedia === 0) finishTest(screenshotUploadResult, videoUploadResult)
|
|
419
|
+
}
|
|
420
|
+
const screenshotUploadStarted = this.uploadTestScreenshots({
|
|
351
421
|
screenshots,
|
|
352
422
|
traceId: formattedTestSpan.trace_id.toString(10),
|
|
353
423
|
signal: abortController.signal,
|
|
354
|
-
},
|
|
355
|
-
|
|
424
|
+
}, (uploadResult) => {
|
|
425
|
+
screenshotUploadResult = uploadResult
|
|
426
|
+
finishMedia()
|
|
427
|
+
})
|
|
428
|
+
if (screenshotUploadStarted) {
|
|
429
|
+
pendingMedia++
|
|
430
|
+
pendingTestFinish.hasScreenshot = true
|
|
431
|
+
}
|
|
432
|
+
const videoUploadStarted = this.uploadTestVideos({
|
|
433
|
+
videos,
|
|
434
|
+
traceId: formattedTestSpan.trace_id.toString(10),
|
|
435
|
+
signal: abortController.signal,
|
|
436
|
+
}, (uploadResult) => {
|
|
437
|
+
videoUploadResult = uploadResult
|
|
438
|
+
finishMedia()
|
|
439
|
+
})
|
|
440
|
+
if (videoUploadStarted) {
|
|
441
|
+
pendingMedia++
|
|
442
|
+
pendingTestFinish.hasVideo = true
|
|
443
|
+
}
|
|
444
|
+
if (pendingMedia > 0) return
|
|
356
445
|
|
|
357
446
|
finishTest()
|
|
358
447
|
})
|
|
@@ -630,7 +719,58 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
630
719
|
}
|
|
631
720
|
pendingUploads--
|
|
632
721
|
if (pendingUploads === 0) {
|
|
633
|
-
|
|
722
|
+
const uploadResult = getScreenshotUploadResult(uploadResults)
|
|
723
|
+
queueMicrotask(() => onDone(uploadResult))
|
|
724
|
+
}
|
|
725
|
+
})
|
|
726
|
+
}
|
|
727
|
+
return true
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Uploads failure videos for a Playwright test attempt.
|
|
732
|
+
*
|
|
733
|
+
* @param {object} options - Upload options
|
|
734
|
+
* @param {Array<object>} options.videos - Playwright test attachments
|
|
735
|
+
* @param {string} options.traceId - Test trace id used as the video key
|
|
736
|
+
* @param {AbortSignal} options.signal - Signal used to cancel uploads during error finalization
|
|
737
|
+
* @param {(result: string|undefined) => void} onDone - Completion callback
|
|
738
|
+
* @returns {boolean} Whether at least one upload was started
|
|
739
|
+
*/
|
|
740
|
+
uploadTestVideos ({ videos, traceId, signal }, onDone) {
|
|
741
|
+
const exporter = this.tracer?._exporter
|
|
742
|
+
if (!Array.isArray(videos) || !videos.length ||
|
|
743
|
+
!exporter?.canUploadTestVideos?.() || !exporter.uploadTestVideo) {
|
|
744
|
+
return false
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const videoPaths = new Set()
|
|
748
|
+
for (const video of videos) {
|
|
749
|
+
if (isPlaywrightFailureVideo(video)) videoPaths.add(video.path)
|
|
750
|
+
}
|
|
751
|
+
if (!videoPaths.size) return false
|
|
752
|
+
|
|
753
|
+
const uploadResults = new Array(videoPaths.size)
|
|
754
|
+
let pendingUploads = videoPaths.size
|
|
755
|
+
let index = 0
|
|
756
|
+
for (const filePath of videoPaths) {
|
|
757
|
+
const resultIndex = index++
|
|
758
|
+
exporter.uploadTestVideo({
|
|
759
|
+
filePath,
|
|
760
|
+
traceId,
|
|
761
|
+
idempotencyKey: `${traceId}:${basename(filePath)}`,
|
|
762
|
+
capturedAtMs: Date.now(),
|
|
763
|
+
signal,
|
|
764
|
+
}, (error, uploaded = true) => {
|
|
765
|
+
if (uploaded) {
|
|
766
|
+
uploadResults[resultIndex] = error
|
|
767
|
+
? VIDEO_UPLOAD_RESULT_ERROR
|
|
768
|
+
: VIDEO_UPLOAD_RESULT_UPLOADED
|
|
769
|
+
}
|
|
770
|
+
pendingUploads--
|
|
771
|
+
if (pendingUploads === 0) {
|
|
772
|
+
const uploadResult = getVideoUploadResult(uploadResults)
|
|
773
|
+
queueMicrotask(() => onDone(uploadResult))
|
|
634
774
|
}
|
|
635
775
|
})
|
|
636
776
|
}
|