dd-trace 5.114.0 → 5.116.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.electron.md +7 -0
- package/README.md +17 -0
- package/ci/diagnose.js +2100 -0
- package/ci/init.js +23 -2
- package/ci/runbook.md +198 -0
- package/ci/test-optimization-validation/approval-artifacts.js +143 -0
- package/ci/test-optimization-validation/approval.js +299 -0
- package/ci/test-optimization-validation/artifact-id.js +20 -0
- package/ci/test-optimization-validation/bounded-json.js +121 -0
- package/ci/test-optimization-validation/ci-command-candidate.js +83 -0
- package/ci/test-optimization-validation/ci-discovery.js +181 -0
- package/ci/test-optimization-validation/ci-remediation.js +210 -0
- package/ci/test-optimization-validation/cli.js +903 -0
- package/ci/test-optimization-validation/command-blocker.js +81 -0
- package/ci/test-optimization-validation/command-output-policy.js +206 -0
- package/ci/test-optimization-validation/command-runner.js +707 -0
- package/ci/test-optimization-validation/command-suitability.js +471 -0
- package/ci/test-optimization-validation/executable-approval.js +48 -0
- package/ci/test-optimization-validation/executable.js +480 -0
- package/ci/test-optimization-validation/generated-file-policy.js +63 -0
- package/ci/test-optimization-validation/generated-files.js +351 -0
- package/ci/test-optimization-validation/generated-verifier.js +196 -0
- package/ci/test-optimization-validation/init-probe-preload.js +163 -0
- package/ci/test-optimization-validation/init-probe.js +246 -0
- package/ci/test-optimization-validation/late-initialization.js +63 -0
- package/ci/test-optimization-validation/local-command.js +163 -0
- package/ci/test-optimization-validation/manifest-loader.js +133 -0
- package/ci/test-optimization-validation/manifest-scaffold.js +738 -0
- package/ci/test-optimization-validation/manifest-schema.js +862 -0
- package/ci/test-optimization-validation/offline-fixtures.js +327 -0
- package/ci/test-optimization-validation/offline-output.js +406 -0
- package/ci/test-optimization-validation/payload-normalizer.js +72 -0
- package/ci/test-optimization-validation/plan-writer.js +1120 -0
- package/ci/test-optimization-validation/preflight-runner.js +114 -0
- package/ci/test-optimization-validation/redaction.js +331 -0
- package/ci/test-optimization-validation/report-writer.js +1508 -0
- package/ci/test-optimization-validation/safe-files.js +203 -0
- package/ci/test-optimization-validation/scenarios/auto-test-retries.js +159 -0
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +657 -0
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +780 -0
- package/ci/test-optimization-validation/scenarios/early-flake-detection.js +141 -0
- package/ci/test-optimization-validation/scenarios/helpers.js +539 -0
- package/ci/test-optimization-validation/scenarios/test-management.js +218 -0
- package/ci/test-optimization-validation/setup-runner.js +97 -0
- package/ci/test-optimization-validation/static-diagnosis.js +159 -0
- package/ci/test-optimization-validation/test-output.js +129 -0
- package/ci/test-optimization-validation-manifest.schema.json +1 -0
- package/ci/validate-test-optimization.js +3 -0
- package/ext/exporters.js +1 -0
- package/index.d.ts +121 -8
- package/index.electron.js +3 -0
- package/init.js +18 -0
- package/initialize.mjs +1 -1
- package/loader-hook.mjs +28 -18
- package/openfeature.d.ts +1 -0
- package/openfeature.js +4 -0
- package/package.json +15 -8
- package/packages/datadog-instrumentations/src/ai.js +37 -26
- package/packages/datadog-instrumentations/src/child_process.js +1 -1
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -1
- package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +9 -3
- package/packages/datadog-instrumentations/src/cucumber.js +24 -10
- package/packages/datadog-instrumentations/src/cypress-config.js +398 -52
- package/packages/datadog-instrumentations/src/express.js +20 -2
- package/packages/datadog-instrumentations/src/fastify.js +7 -11
- package/packages/datadog-instrumentations/src/grpc/server.js +18 -0
- package/packages/datadog-instrumentations/src/helpers/hook.js +27 -6
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +8 -8
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +28 -2
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +8 -8
- package/packages/datadog-instrumentations/src/http2/server.js +143 -17
- package/packages/datadog-instrumentations/src/jest.js +36 -4
- package/packages/datadog-instrumentations/src/mariadb.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +0 -2
- package/packages/datadog-instrumentations/src/mocha/utils.js +0 -4
- package/packages/datadog-instrumentations/src/mongodb.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +3 -3
- package/packages/datadog-instrumentations/src/mquery.js +2 -2
- package/packages/datadog-instrumentations/src/mysql.js +1 -1
- package/packages/datadog-instrumentations/src/next.js +24 -0
- package/packages/datadog-instrumentations/src/nyc.js +0 -2
- package/packages/datadog-instrumentations/src/oracledb.js +2 -2
- package/packages/datadog-instrumentations/src/pg.js +2 -2
- package/packages/datadog-instrumentations/src/playwright.js +159 -22
- package/packages/datadog-instrumentations/src/process.js +3 -0
- package/packages/datadog-instrumentations/src/router.js +191 -106
- package/packages/datadog-instrumentations/src/selenium.js +52 -26
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +2 -2
- package/packages/datadog-instrumentations/src/vitest-main.js +0 -2
- package/packages/datadog-instrumentations/src/ws.js +2 -1
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +2 -1
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +41 -16
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +21 -21
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +14 -8
- package/packages/datadog-plugin-aws-sdk/src/util.js +2 -24
- package/packages/datadog-plugin-cucumber/src/index.js +1 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +24 -62
- package/packages/datadog-plugin-cypress/src/index.js +6 -1
- package/packages/datadog-plugin-cypress/src/support.js +92 -26
- package/packages/datadog-plugin-electron/src/ipc.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +177 -59
- package/packages/datadog-plugin-graphql/src/parse.js +22 -1
- package/packages/datadog-plugin-graphql/src/request.js +32 -1
- package/packages/datadog-plugin-graphql/src/utils.js +42 -0
- package/packages/datadog-plugin-graphql/src/validate.js +15 -3
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-http2/src/server.js +38 -6
- package/packages/datadog-plugin-jest/src/util.js +21 -6
- package/packages/datadog-plugin-langchain/src/handlers/embedding.js +2 -1
- package/packages/datadog-plugin-langchain/src/handlers/language_models.js +2 -1
- package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +177 -64
- package/packages/datadog-plugin-router/src/index.js +11 -2
- package/packages/datadog-plugin-selenium/src/index.js +5 -36
- package/packages/datadog-plugin-vitest/src/index.js +4 -2
- package/packages/datadog-plugin-ws/src/close.js +2 -1
- package/packages/datadog-plugin-ws/src/producer.js +2 -1
- package/packages/datadog-plugin-ws/src/receiver.js +2 -1
- package/packages/datadog-plugin-ws/src/server.js +2 -1
- package/packages/dd-trace/index.electron.js +3 -0
- package/packages/dd-trace/index.js +2 -37
- package/packages/dd-trace/src/aiguard/sdk.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
- package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +3 -1
- package/packages/dd-trace/src/azure_metadata.js +2 -1
- package/packages/dd-trace/src/bootstrap.js +39 -0
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +93 -28
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +160 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +288 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/payload-projection.js +84 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +369 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +60 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +55 -5
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +22 -6
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +5 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +62 -7
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -2
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +6 -2
- package/packages/dd-trace/src/ci-visibility/rum.js +7 -0
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +4 -0
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +1 -1
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +137 -9
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +173 -9
- package/packages/dd-trace/src/ci-visibility/test-screenshot.js +90 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +20 -0
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +10 -0
- package/packages/dd-trace/src/config/supported-configurations.json +83 -0
- package/packages/dd-trace/src/constants.js +7 -0
- package/packages/dd-trace/src/datastreams/pathway.js +6 -4
- package/packages/dd-trace/src/exporter.js +2 -0
- package/packages/dd-trace/src/exporters/common/client-library-headers.js +21 -0
- package/packages/dd-trace/src/exporters/common/request.js +59 -30
- package/packages/dd-trace/src/exporters/common/url.js +15 -1
- package/packages/dd-trace/src/feature-registry.js +29 -0
- package/packages/dd-trace/src/llmobs/constants/tags.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/client.js +113 -0
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +154 -0
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +283 -0
- package/packages/dd-trace/src/llmobs/experiments/index.js +152 -0
- package/packages/dd-trace/src/llmobs/experiments/noop.js +30 -0
- package/packages/dd-trace/src/llmobs/experiments/result.js +34 -0
- package/packages/dd-trace/src/llmobs/index.js +10 -2
- package/packages/dd-trace/src/llmobs/noop.js +6 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +20 -3
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +2 -2
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +8 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +48 -14
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +43 -0
- package/packages/dd-trace/src/llmobs/sdk.js +17 -0
- package/packages/dd-trace/src/llmobs/tagger.js +79 -7
- package/packages/dd-trace/src/llmobs/telemetry.js +2 -1
- package/packages/dd-trace/src/llmobs/util.js +32 -0
- package/packages/dd-trace/src/noop/proxy.js +5 -4
- package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +322 -0
- package/packages/dd-trace/src/openfeature/configuration_source.js +90 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +14 -23
- package/packages/dd-trace/src/openfeature/index.js +0 -2
- package/packages/dd-trace/src/openfeature/noop.js +1 -28
- package/packages/dd-trace/src/openfeature/register.js +35 -0
- package/packages/dd-trace/src/openfeature/remote_config.js +15 -18
- package/packages/dd-trace/src/openfeature/require-provider.js +18 -0
- package/packages/dd-trace/src/opentelemetry/span-ending-hook.js +10 -0
- package/packages/dd-trace/src/opentelemetry/span.js +5 -0
- package/packages/dd-trace/src/opentracing/propagation/log.js +11 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +80 -14
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +10 -1
- package/packages/dd-trace/src/opentracing/tracer.js +7 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +20 -6
- package/packages/dd-trace/src/plugins/util/git.js +3 -2
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +29 -5
- package/packages/dd-trace/src/plugins/util/test.js +65 -8
- package/packages/dd-trace/src/plugins/util/web.js +12 -0
- package/packages/dd-trace/src/proxy.js +99 -10
- package/packages/dd-trace/src/span_processor.js +5 -0
- package/packages/dd-trace/src/standalone/index.js +0 -36
- package/packages/dd-trace/src/telemetry/send-data.js +2 -2
- package/packages/dd-trace/src/util.js +26 -0
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
- 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-instrumentations/src/helpers/require-optional-peer.js +0 -17
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { channel, addHook } = require('../helpers/instrument')
|
|
4
4
|
const shimmer = require('../../../datadog-shimmer')
|
|
5
|
+
const { FOREIGN_HTTP2_SERVER } = require('../../../dd-trace/src/constants')
|
|
5
6
|
const types = require('./types')
|
|
6
7
|
|
|
7
8
|
const startChannel = channel('apm:grpc:server:request:start')
|
|
@@ -76,6 +77,22 @@ function wrapRegister (register) {
|
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
function wrapSetupHandlers (setupHandlers) {
|
|
81
|
+
return function (http2Server) {
|
|
82
|
+
// gRPC builds its transport on a core Node `Http2Server` and drives every
|
|
83
|
+
// call through its own span lifecycle over the raw 'stream' event. Mark the
|
|
84
|
+
// server so the http2 server instrumentation leaves it untraced; otherwise
|
|
85
|
+
// its re-enabled 'stream' path would wrap every gRPC call in a second
|
|
86
|
+
// web.request span and become the top frame. `_setupHandlers` is the single
|
|
87
|
+
// server-creation funnel across all supported versions (the older
|
|
88
|
+
// `createHttp2Server` does not exist), and it runs at bind time, before any
|
|
89
|
+
// request reaches the wrapped `emit`.
|
|
90
|
+
if (http2Server) http2Server[FOREIGN_HTTP2_SERVER] = true
|
|
91
|
+
|
|
92
|
+
return setupHandlers.apply(this, arguments)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
79
96
|
function createWrapEmit (call, ctx, onCancel) {
|
|
80
97
|
return function wrapEmit (emit) {
|
|
81
98
|
return function (event, arg1) {
|
|
@@ -149,6 +166,7 @@ function isEmitter (obj) {
|
|
|
149
166
|
|
|
150
167
|
addHook({ name: '@grpc/grpc-js', versions: ['>=1.0.3'], file: 'build/src/server.js' }, server => {
|
|
151
168
|
shimmer.wrap(server.Server.prototype, 'register', wrapRegister)
|
|
169
|
+
shimmer.wrap(server.Server.prototype, '_setupHandlers', wrapSetupHandlers)
|
|
152
170
|
|
|
153
171
|
return server
|
|
154
172
|
})
|
|
@@ -49,10 +49,19 @@ function Hook (modules, hookOptions, onrequire) {
|
|
|
49
49
|
this._patched = Object.create(null)
|
|
50
50
|
const patched = new WeakMap()
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* @param {object|Function|undefined} moduleExports
|
|
54
|
+
* @param {string} moduleName
|
|
55
|
+
* @param {string|undefined} moduleBaseDir
|
|
56
|
+
* @param {string|undefined} moduleVersion
|
|
57
|
+
* @param {boolean|undefined} isIitm
|
|
58
|
+
*/
|
|
52
59
|
const safeHook = (moduleExports, moduleName, moduleBaseDir, moduleVersion, isIitm) => {
|
|
53
60
|
const parts = [moduleBaseDir, moduleName].filter(Boolean)
|
|
54
61
|
const filename = path.join(...parts)
|
|
55
62
|
|
|
63
|
+
const defaultExport = isIitm && moduleExports.default
|
|
64
|
+
let defaultExportAliases
|
|
56
65
|
let defaultWrapResult
|
|
57
66
|
|
|
58
67
|
const wrappedOnrequire = (moduleExports, ...args) => {
|
|
@@ -77,17 +86,29 @@ function Hook (modules, hookOptions, onrequire) {
|
|
|
77
86
|
}
|
|
78
87
|
|
|
79
88
|
if (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
typeof moduleExports.default === 'function')
|
|
89
|
+
defaultExport &&
|
|
90
|
+
(typeof defaultExport === 'object' ||
|
|
91
|
+
typeof defaultExport === 'function')
|
|
84
92
|
) {
|
|
85
|
-
defaultWrapResult = wrappedOnrequire(
|
|
93
|
+
defaultWrapResult = wrappedOnrequire(defaultExport, moduleName, moduleBaseDir, moduleVersion, isIitm)
|
|
94
|
+
if (defaultWrapResult && defaultWrapResult !== defaultExport) {
|
|
95
|
+
defaultExportAliases = []
|
|
96
|
+
for (const exportName of Object.keys(moduleExports)) {
|
|
97
|
+
if (exportName !== 'default' && moduleExports[exportName] === defaultExport) {
|
|
98
|
+
defaultExportAliases.push(exportName)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
86
102
|
}
|
|
87
103
|
|
|
88
104
|
const newExports = wrappedOnrequire(moduleExports, moduleName, moduleBaseDir, moduleVersion, isIitm)
|
|
89
105
|
|
|
90
|
-
if (defaultWrapResult
|
|
106
|
+
if (defaultWrapResult && defaultExportAliases) {
|
|
107
|
+
newExports.default = defaultWrapResult
|
|
108
|
+
for (const exportName of defaultExportAliases) {
|
|
109
|
+
moduleExports[exportName] = defaultWrapResult
|
|
110
|
+
}
|
|
111
|
+
}
|
|
91
112
|
|
|
92
113
|
this._patched[filename] = true
|
|
93
114
|
|
|
@@ -4,19 +4,19 @@ const path = require('node:path')
|
|
|
4
4
|
|
|
5
5
|
// Build-time half of the optional-peer mechanism shared by the webpack and esbuild plugins.
|
|
6
6
|
//
|
|
7
|
-
// Runtime files load an optional peer through `requireOptionalPeer('name')`
|
|
8
|
-
//
|
|
9
|
-
// the feature never pulls in the peer's
|
|
10
|
-
// peer is installed at build time the user has opted in, so the plugins
|
|
11
|
-
// literal `require('name')` and let the bundler inline the peer, which
|
|
12
|
-
// the bundle is relocated
|
|
13
|
-
// build time stay opaque, so the rewrite is a no-op and the #8635 guarantee holds.
|
|
7
|
+
// Runtime files load an optional peer through a local `requireOptionalPeer('name')` wrapper.
|
|
8
|
+
// File tracers recognize its bound-require shape, while bundlers cannot follow the dynamic
|
|
9
|
+
// argument, so a build that does not opt into the feature never pulls in the peer's dependency
|
|
10
|
+
// chain (#8635). When the peer is installed at build time the user has opted in, so the plugins
|
|
11
|
+
// rewrite the call into a literal `require('name')` and let the bundler inline the peer, which
|
|
12
|
+
// keeps it working after the bundle is relocated without the peer on disk (#8980). Peers that
|
|
13
|
+
// are absent at build time stay opaque, so the rewrite is a no-op and the #8635 guarantee holds.
|
|
14
14
|
|
|
15
15
|
// Files that load an optional peer this way, as suffixes of the resolved module path. The same
|
|
16
16
|
// suffix matches the repo layout and `node_modules/dd-trace`. Add a file here to extend the
|
|
17
17
|
// mechanism to a new optional peer; no plugin change is needed.
|
|
18
18
|
const OPTIONAL_PEER_FILES = [
|
|
19
|
-
'packages/dd-trace/src/openfeature/
|
|
19
|
+
'packages/dd-trace/src/openfeature/require-provider.js',
|
|
20
20
|
]
|
|
21
21
|
|
|
22
22
|
// Captures the peer name from `requireOptionalPeer('name')` / `requireOptionalPeer("name")`.
|
package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
// Playwright
|
|
4
|
-
// Keep these rewrites limited to
|
|
3
|
+
// Playwright keeps several hook targets in private local classes/functions.
|
|
4
|
+
// Keep these rewrites limited to bundled internals that addHook cannot wrap.
|
|
5
5
|
module.exports = [
|
|
6
|
+
{
|
|
7
|
+
module: {
|
|
8
|
+
name: 'playwright',
|
|
9
|
+
versionRange: '>=1.38.0 <1.51.0',
|
|
10
|
+
filePath: 'lib/index.js',
|
|
11
|
+
},
|
|
12
|
+
functionQuery: {
|
|
13
|
+
className: 'ArtifactsRecorder',
|
|
14
|
+
methodName: '_createScreenshotAttachmentPath',
|
|
15
|
+
kind: 'Sync',
|
|
16
|
+
},
|
|
17
|
+
channelName: 'ArtifactsRecorder_createScreenshotAttachmentPath',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
module: {
|
|
21
|
+
name: 'playwright',
|
|
22
|
+
versionRange: '>=1.51.0',
|
|
23
|
+
filePath: 'lib/index.js',
|
|
24
|
+
},
|
|
25
|
+
functionQuery: {
|
|
26
|
+
className: 'SnapshotRecorder',
|
|
27
|
+
methodName: '_createAttachmentPath',
|
|
28
|
+
kind: 'Sync',
|
|
29
|
+
},
|
|
30
|
+
channelName: 'SnapshotRecorder_createAttachmentPath',
|
|
31
|
+
},
|
|
6
32
|
{
|
|
7
33
|
module: {
|
|
8
34
|
name: 'playwright',
|
|
@@ -4,7 +4,7 @@ const shimmer = require('../../../datadog-shimmer')
|
|
|
4
4
|
const { channel } = require('./instrument')
|
|
5
5
|
|
|
6
6
|
const routerMountPaths = new WeakMap() // to track mount paths for router instances
|
|
7
|
-
const
|
|
7
|
+
const layerMeta = new WeakMap() // per-layer middleware dispatch metadata (resolved name + route matchers)
|
|
8
8
|
const appMountedRouters = new WeakSet() // to track routers mounted via app.use()
|
|
9
9
|
|
|
10
10
|
const METHODS = [...require('http').METHODS.map(v => v.toLowerCase()), 'all']
|
|
@@ -70,7 +70,7 @@ function collectRoutesFromRouter (router, prefix) {
|
|
|
70
70
|
// Extract mount path from layer
|
|
71
71
|
const mountPath = typeof layer.path === 'string'
|
|
72
72
|
? layer.path
|
|
73
|
-
:
|
|
73
|
+
: getLayerMeta(layer)?.matchers?.[0]?.path || ''
|
|
74
74
|
|
|
75
75
|
const nestedPrefix = joinPath(prefix, mountPath)
|
|
76
76
|
if (nestedPrefix === null) continue
|
|
@@ -121,12 +121,12 @@ function getRouterMountPaths (router) {
|
|
|
121
121
|
return [...paths]
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
function
|
|
125
|
-
|
|
124
|
+
function setLayerMeta (layer, meta) {
|
|
125
|
+
layerMeta.set(layer, meta)
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
function
|
|
129
|
-
return
|
|
128
|
+
function getLayerMeta (layer) {
|
|
129
|
+
return layerMeta.get(layer)
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
function normalizeMethodName (method) {
|
|
@@ -224,8 +224,8 @@ module.exports = {
|
|
|
224
224
|
setRouterMountPath,
|
|
225
225
|
getRouterMountPaths,
|
|
226
226
|
joinPath,
|
|
227
|
-
|
|
228
|
-
|
|
227
|
+
setLayerMeta,
|
|
228
|
+
getLayerMeta,
|
|
229
229
|
markAppMounted,
|
|
230
230
|
isAppMounted,
|
|
231
231
|
normalizeRoutePath,
|
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
// Old instrumentation temporarily replaced with compatibility mode only instrumentation.
|
|
4
|
-
// See https://github.com/DataDog/dd-trace-js/issues/312
|
|
5
|
-
|
|
6
3
|
const {
|
|
7
4
|
channel,
|
|
8
5
|
addHook,
|
|
9
6
|
} = require('../helpers/instrument')
|
|
10
7
|
const shimmer = require('../../../datadog-shimmer')
|
|
8
|
+
const { FOREIGN_HTTP2_SERVER } = require('../../../dd-trace/src/constants')
|
|
11
9
|
|
|
12
10
|
const startServerCh = channel('apm:http2:server:request:start')
|
|
13
11
|
const errorServerCh = channel('apm:http2:server:request:error')
|
|
12
|
+
const adoptServerCh = channel('apm:http2:server:request:adopt')
|
|
14
13
|
const emitCh = channel('apm:http2:server:response:emit')
|
|
15
14
|
|
|
15
|
+
const HTTP2_HEADER_METHOD = ':method'
|
|
16
|
+
const HTTP2_HEADER_PATH = ':path'
|
|
17
|
+
const HTTP2_HEADER_STATUS = ':status'
|
|
18
|
+
|
|
19
|
+
// Streams whose server span was already created from the 'stream' event. The
|
|
20
|
+
// compatibility layer synthesizes 'request' from that same stream, so the
|
|
21
|
+
// 'request' branch consults this set to avoid creating a second span.
|
|
22
|
+
const tracedStreams = new WeakSet()
|
|
23
|
+
|
|
16
24
|
addHook({ name: 'http2' }, http2 => {
|
|
17
25
|
shimmer.wrap(http2, 'createSecureServer', wrapCreateServer)
|
|
18
26
|
shimmer.wrap(http2, 'createServer', wrapCreateServer)
|
|
@@ -36,34 +44,152 @@ function wrapResponseEmit (originalEmit, ctx) {
|
|
|
36
44
|
}
|
|
37
45
|
}
|
|
38
46
|
|
|
47
|
+
function wrapStreamEmit (originalEmit, ctx) {
|
|
48
|
+
// Named `emit`/arity-1 mirrors the stream method so the per-stream wrap skips
|
|
49
|
+
// its name/length rewrite. `this` is the Http2Stream; the plugin finishes on
|
|
50
|
+
// 'close', the same finish signal as the compatibility response.
|
|
51
|
+
return function emit (eventName) {
|
|
52
|
+
ctx.eventName = eventName
|
|
53
|
+
return emitCh.runStores(ctx, originalEmit, this, ...arguments)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
39
57
|
function wrapEmit (originalEmit) {
|
|
40
58
|
// Named `emit` mirrors the server method so the one-time wrap skips its name
|
|
41
59
|
// rewrite; rest params keep the per-event forwarding allocation-free.
|
|
42
60
|
return function emit (...args) {
|
|
43
|
-
|
|
61
|
+
// A server owned by another instrumentation (e.g. @grpc/grpc-js) drives its
|
|
62
|
+
// own span lifecycle over the raw 'stream' API, so tracing it here would add
|
|
63
|
+
// a spurious web.request span on top of that integration's span and steal
|
|
64
|
+
// the top frame. Skip it entirely; the mark is set at server creation, so
|
|
65
|
+
// this is one property read on servers we do trace.
|
|
66
|
+
if (!startServerCh.hasSubscribers || this[FOREIGN_HTTP2_SERVER]) {
|
|
44
67
|
return Reflect.apply(originalEmit, this, args)
|
|
45
68
|
}
|
|
46
69
|
|
|
47
70
|
const eventName = args[0]
|
|
48
|
-
if (eventName === '
|
|
71
|
+
if (eventName === 'stream') {
|
|
72
|
+
// The compatibility layer synthesizes 'request' from an internal 'stream'
|
|
73
|
+
// listener it registers exactly once when a 'request' listener is added,
|
|
74
|
+
// so `listenerCount('stream')` exceeds one only when the application also
|
|
75
|
+
// registered a raw-stream listener. Owning the span here for that case
|
|
76
|
+
// keeps it active while the application's stream listener runs; the
|
|
77
|
+
// synthesized 'request' that fires nested below then reuses it. A
|
|
78
|
+
// request-only server (no raw-stream listener) is left to the 'request'
|
|
79
|
+
// branch so the compatibility response keeps its richer req/res.
|
|
80
|
+
const requestListenerCount = this.listenerCount('request')
|
|
81
|
+
if (requestListenerCount === 0 || this.listenerCount('stream') > 1) {
|
|
82
|
+
const stream = args[1]
|
|
83
|
+
const headers = args[2]
|
|
84
|
+
const ctx = createStreamAdapter(stream, headers)
|
|
85
|
+
// Only a mixed server (a 'request' listener is present) synthesizes a
|
|
86
|
+
// real request off this stream and adopts the span later, so only then
|
|
87
|
+
// does the context need keying on the stream. A raw-stream-only server
|
|
88
|
+
// never adopts; leaving the flag unset keeps the stream->context write
|
|
89
|
+
// off its hot path.
|
|
90
|
+
ctx.adoptable = requestListenerCount !== 0
|
|
91
|
+
tracedStreams.add(stream)
|
|
92
|
+
|
|
93
|
+
return traceServerRequest(ctx, () => {
|
|
94
|
+
shimmer.wrap(stream, 'emit', emit => wrapStreamEmit(emit, ctx))
|
|
95
|
+
return Reflect.apply(originalEmit, this, args)
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
} else if (eventName === 'request') {
|
|
49
99
|
const req = args[1]
|
|
50
100
|
const res = args[2]
|
|
51
101
|
res.req = req
|
|
52
102
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
103
|
+
// A mixed server (raw-stream + 'request' listeners) already created the
|
|
104
|
+
// span from the 'stream' event above; the stream's single 'close' is the
|
|
105
|
+
// sole finish source, so skip creating a second span. The synthesized
|
|
106
|
+
// request/response are the real objects a user's 'request' handler and
|
|
107
|
+
// the finish `hooks.request` expect, so hand them to the existing
|
|
108
|
+
// stream-backed context rather than leaving it on the throwaway adapter.
|
|
109
|
+
if (tracedStreams.has(req.stream)) {
|
|
110
|
+
adoptServerCh.publish({ req, res })
|
|
111
|
+
} else {
|
|
112
|
+
const ctx = { req, res }
|
|
113
|
+
return traceServerRequest(ctx, () => {
|
|
114
|
+
shimmer.wrap(res, 'emit', emit => wrapResponseEmit(emit, ctx))
|
|
58
115
|
return Reflect.apply(originalEmit, this, args)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
errorServerCh.publish(ctx)
|
|
62
|
-
|
|
63
|
-
throw error
|
|
64
|
-
}
|
|
65
|
-
})
|
|
116
|
+
})
|
|
117
|
+
}
|
|
66
118
|
}
|
|
119
|
+
|
|
67
120
|
return Reflect.apply(originalEmit, this, args)
|
|
68
121
|
}
|
|
69
122
|
}
|
|
123
|
+
|
|
124
|
+
// Enter the request context and run `emitEvent` (the original `emit`, wrapped to
|
|
125
|
+
// publish per-event for the matching response/stream), publishing any synchronous
|
|
126
|
+
// throw from a user handler before letting it propagate.
|
|
127
|
+
/**
|
|
128
|
+
* @param {StreamRequestContext | { req: object, res: object }} ctx
|
|
129
|
+
* @param {() => unknown} emitEvent
|
|
130
|
+
*/
|
|
131
|
+
function traceServerRequest (ctx, emitEvent) {
|
|
132
|
+
return startServerCh.runStores(ctx, () => {
|
|
133
|
+
try {
|
|
134
|
+
return emitEvent()
|
|
135
|
+
} catch (error) {
|
|
136
|
+
ctx.error = error
|
|
137
|
+
errorServerCh.publish(ctx)
|
|
138
|
+
throw error
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The minimal req/res pair the shared web lifecycle (`web.js`) keys on, built
|
|
145
|
+
* from a core-API `Http2Stream`. The fields below are exactly the surface
|
|
146
|
+
* `web.js` / `url.js` / `ip_extractor.js` read for a stream-backed request; a
|
|
147
|
+
* new read added there must be mirrored here or it resolves to `undefined` on
|
|
148
|
+
* the core path only.
|
|
149
|
+
*
|
|
150
|
+
* @typedef {object} StreamRequestContext
|
|
151
|
+
* @property {object} req
|
|
152
|
+
* @property {import('node:http2').ServerHttp2Stream} req.stream branch key in `web.js`/`url.js`
|
|
153
|
+
* @property {import('node:http2').IncomingHttpHeaders} req.headers raw pseudo-header map
|
|
154
|
+
* @property {string} [req.method]
|
|
155
|
+
* @property {string} [req.url]
|
|
156
|
+
* @property {import('node:net').Socket} [req.socket] peer address source (OTel)
|
|
157
|
+
* @property {object} res
|
|
158
|
+
* @property {object} res.req back-reference used by `wrapResponseEmit`/finish
|
|
159
|
+
* @property {number} res.statusCode read at finish from `stream.sentHeaders`
|
|
160
|
+
* @property {(name: string) => string | number | string[] | undefined} res.getHeader response-header tagging
|
|
161
|
+
*/
|
|
162
|
+
|
|
163
|
+
// Present the core-API stream + pseudo-header map as the minimal req/res pair
|
|
164
|
+
// the shared web lifecycle (`web.js`) consumes. The response status and headers
|
|
165
|
+
// are getters because `stream.sentHeaders` is empty until `stream.respond()`
|
|
166
|
+
// runs in the user handler; a snapshot taken here would always be `undefined`.
|
|
167
|
+
/**
|
|
168
|
+
* @param {import('node:http2').ServerHttp2Stream} stream
|
|
169
|
+
* @param {import('node:http2').IncomingHttpHeaders} headers
|
|
170
|
+
* @returns {StreamRequestContext}
|
|
171
|
+
*/
|
|
172
|
+
function createStreamAdapter (stream, headers) {
|
|
173
|
+
const req = {
|
|
174
|
+
stream,
|
|
175
|
+
headers,
|
|
176
|
+
method: headers[HTTP2_HEADER_METHOD],
|
|
177
|
+
url: headers[HTTP2_HEADER_PATH],
|
|
178
|
+
socket: stream.session?.socket,
|
|
179
|
+
}
|
|
180
|
+
const res = {
|
|
181
|
+
req,
|
|
182
|
+
get statusCode () {
|
|
183
|
+
// A stream aborted before `stream.respond()` has no `:status`. The
|
|
184
|
+
// compatibility `Http2ServerResponse.statusCode` defaults to 200 in that
|
|
185
|
+
// case, so match it rather than report `undefined` (which `validateStatus`
|
|
186
|
+
// would treat as an error and which drops the `http.status_code` tag).
|
|
187
|
+
return stream.sentHeaders?.[HTTP2_HEADER_STATUS] ?? 200
|
|
188
|
+
},
|
|
189
|
+
getHeader (name) {
|
|
190
|
+
return stream.sentHeaders?.[name]
|
|
191
|
+
},
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return { req, res }
|
|
195
|
+
}
|
|
@@ -1858,13 +1858,39 @@ function resetSuiteSkippingRunState () {
|
|
|
1858
1858
|
coverageBackfillFiles = undefined
|
|
1859
1859
|
}
|
|
1860
1860
|
|
|
1861
|
+
function resetLibraryConfiguration () {
|
|
1862
|
+
knownTests = {}
|
|
1863
|
+
isCodeCoverageEnabled = false
|
|
1864
|
+
isCoverageReportUploadEnabled = false
|
|
1865
|
+
isItrEnabled = false
|
|
1866
|
+
isSuitesSkippingEnabled = false
|
|
1867
|
+
isEarlyFlakeDetectionEnabled = false
|
|
1868
|
+
earlyFlakeDetectionNumRetries = 0
|
|
1869
|
+
earlyFlakeDetectionSlowTestRetries = {}
|
|
1870
|
+
earlyFlakeDetectionFaultyThreshold = 30
|
|
1871
|
+
isEarlyFlakeDetectionFaulty = false
|
|
1872
|
+
isKnownTestsEnabled = false
|
|
1873
|
+
isTestManagementTestsEnabled = false
|
|
1874
|
+
testManagementTests = {}
|
|
1875
|
+
testManagementAttemptToFixRetries = 0
|
|
1876
|
+
isImpactedTestsEnabled = false
|
|
1877
|
+
modifiedFiles = {}
|
|
1878
|
+
repositoryRoot = undefined
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1861
1881
|
function applySuiteSkipping (originalTests, rootDir, frameworkVersion) {
|
|
1862
1882
|
if (!isItrEnabled || !isSuitesSkippingEnabled) return originalTests
|
|
1863
1883
|
|
|
1864
|
-
const
|
|
1865
|
-
const jestSuitesToRun = getJestSuitesToRun(skippableSuites, originalTests,
|
|
1884
|
+
const repositoryRoot = getRepositoryRootFromTest(originalTests[0], rootDir)
|
|
1885
|
+
const jestSuitesToRun = getJestSuitesToRun(skippableSuites, originalTests, repositoryRoot, rootDir)
|
|
1866
1886
|
hasFilteredSkippableSuites = true
|
|
1867
|
-
log.debug(
|
|
1887
|
+
log.debug(
|
|
1888
|
+
'Jest discovered %d suites; skipped %d using %d skippable candidates; %d suites will run.',
|
|
1889
|
+
originalTests.length,
|
|
1890
|
+
jestSuitesToRun.skippedSuites.length,
|
|
1891
|
+
skippableSuites.length,
|
|
1892
|
+
jestSuitesToRun.suitesToRun.length
|
|
1893
|
+
)
|
|
1868
1894
|
hasUnskippableSuites = jestSuitesToRun.hasUnskippableSuites
|
|
1869
1895
|
hasForcedToRunSuites = jestSuitesToRun.hasForcedToRunSuites
|
|
1870
1896
|
|
|
@@ -1877,7 +1903,7 @@ function applySuiteSkipping (originalTests, rootDir, frameworkVersion) {
|
|
|
1877
1903
|
? getTestContexts(originalTests)
|
|
1878
1904
|
: undefined
|
|
1879
1905
|
coverageBackfillFiles = isSuitesSkipped && isTiaCoverageBackfillEnabled() && hasSkippableSuitesCoverage()
|
|
1880
|
-
? getCoverageBackfillFiles(skippableSuitesCoverage,
|
|
1906
|
+
? getCoverageBackfillFiles(skippableSuitesCoverage, repositoryRoot, getTestSuitePath)
|
|
1881
1907
|
: undefined
|
|
1882
1908
|
|
|
1883
1909
|
itrSkippedSuitesCh.publish({ skippedSuites: jestSuitesToRun.skippedSuites, frameworkVersion })
|
|
@@ -2205,11 +2231,13 @@ function getCliWrapper (isNewJestVersion) {
|
|
|
2205
2231
|
resetSuiteSkippingRunState()
|
|
2206
2232
|
hasFinishedTestSession = false
|
|
2207
2233
|
|
|
2234
|
+
let shouldResetLibraryConfiguration = true
|
|
2208
2235
|
try {
|
|
2209
2236
|
const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh, {
|
|
2210
2237
|
frameworkVersion: jestVersion,
|
|
2211
2238
|
})
|
|
2212
2239
|
if (!err) {
|
|
2240
|
+
shouldResetLibraryConfiguration = false
|
|
2213
2241
|
isCodeCoverageEnabled = libraryConfig.isCodeCoverageEnabled
|
|
2214
2242
|
isCoverageReportUploadEnabled = libraryConfig.isCoverageReportUploadEnabled
|
|
2215
2243
|
isItrEnabled = libraryConfig.isItrEnabled
|
|
@@ -2225,6 +2253,10 @@ function getCliWrapper (isNewJestVersion) {
|
|
|
2225
2253
|
}
|
|
2226
2254
|
} catch (err) {
|
|
2227
2255
|
log.error('Jest library configuration error', err)
|
|
2256
|
+
} finally {
|
|
2257
|
+
if (shouldResetLibraryConfiguration) {
|
|
2258
|
+
resetLibraryConfiguration()
|
|
2259
|
+
}
|
|
2228
2260
|
}
|
|
2229
2261
|
|
|
2230
2262
|
const {
|
|
@@ -135,7 +135,7 @@ function wrapPoolMethod (createConnection) {
|
|
|
135
135
|
|
|
136
136
|
function wrapPoolGetConnectionMethod (getConnection) {
|
|
137
137
|
return function wrappedGetConnection (...args) {
|
|
138
|
-
const cb = args
|
|
138
|
+
const cb = args.at(-1)
|
|
139
139
|
if (typeof cb !== 'function') return getConnection.apply(this, args)
|
|
140
140
|
|
|
141
141
|
const ctx = {}
|
|
@@ -553,7 +553,6 @@ addHook({
|
|
|
553
553
|
|
|
554
554
|
const runner = run.apply(this, args)
|
|
555
555
|
|
|
556
|
-
// eslint-disable-next-line unicorn/no-array-for-each
|
|
557
556
|
this.files.forEach((path) => {
|
|
558
557
|
const isUnskippable = isMarkedAsUnskippable({ path })
|
|
559
558
|
if (isUnskippable) {
|
|
@@ -915,7 +914,6 @@ addHook({
|
|
|
915
914
|
status = 'skip'
|
|
916
915
|
} else {
|
|
917
916
|
// has to check every test in the test file
|
|
918
|
-
// eslint-disable-next-line unicorn/no-array-for-each
|
|
919
917
|
suitesInTestFile.forEach(suite => {
|
|
920
918
|
suite.eachTest(test => {
|
|
921
919
|
if (test.state === 'failed' || test.timedOut) {
|
|
@@ -229,7 +229,6 @@ function getSuitesByTestFile (root) {
|
|
|
229
229
|
suitesByTestFile[suite.file] = [suite]
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
|
-
// eslint-disable-next-line unicorn/no-array-for-each
|
|
233
232
|
suite.suites.forEach(suite => {
|
|
234
233
|
getSuites(suite)
|
|
235
234
|
})
|
|
@@ -920,7 +919,6 @@ function getOnPendingHandler () {
|
|
|
920
919
|
function getRunTestsWrapper (runTests, config) {
|
|
921
920
|
return function (suite) {
|
|
922
921
|
if (config.isTestManagementTestsEnabled) {
|
|
923
|
-
// eslint-disable-next-line unicorn/no-array-for-each
|
|
924
922
|
suite.tests.forEach((test) => {
|
|
925
923
|
const { isAttemptToFix, isDisabled, isQuarantined } = getTestProperties(test, config.testManagementTests)
|
|
926
924
|
if (isAttemptToFix && !test.isPending()) {
|
|
@@ -944,7 +942,6 @@ function getRunTestsWrapper (runTests, config) {
|
|
|
944
942
|
}
|
|
945
943
|
|
|
946
944
|
if (config.isImpactedTestsEnabled) {
|
|
947
|
-
// eslint-disable-next-line unicorn/no-array-for-each
|
|
948
945
|
suite.tests.forEach((test) => {
|
|
949
946
|
isModifiedCh.publish({
|
|
950
947
|
modifiedFiles: config.modifiedFiles,
|
|
@@ -968,7 +965,6 @@ function getRunTestsWrapper (runTests, config) {
|
|
|
968
965
|
|
|
969
966
|
if (config.isKnownTestsEnabled) {
|
|
970
967
|
// by the time we reach `this.on('test')`, it is too late. We need to add retries here
|
|
971
|
-
// eslint-disable-next-line unicorn/no-array-for-each
|
|
972
968
|
suite.tests.forEach((test) => {
|
|
973
969
|
if (!test.isPending() && isNewTest(test, config.knownTests)) {
|
|
974
970
|
test._ddIsNew = true
|
|
@@ -20,11 +20,11 @@ const collectionMethodsWithFilter = [
|
|
|
20
20
|
'replaceOne',
|
|
21
21
|
] // findOne is ignored because it calls to find
|
|
22
22
|
|
|
23
|
-
const collectionMethodsWithTwoFilters = [
|
|
23
|
+
const collectionMethodsWithTwoFilters = new Set([
|
|
24
24
|
'findOneAndUpdate',
|
|
25
25
|
'updateMany',
|
|
26
26
|
'updateOne',
|
|
27
|
-
]
|
|
27
|
+
])
|
|
28
28
|
|
|
29
29
|
const startCh = channel('datadog:mongodb:collection:filter:start')
|
|
30
30
|
|
|
@@ -36,7 +36,7 @@ addHook({ name: 'mongodb', versions: ['>=3.3 <5', '5', '>=6'] }, mongodb => {
|
|
|
36
36
|
for (const methodName of [...collectionMethodsWithFilter, ...collectionMethodsWithTwoFilters]) {
|
|
37
37
|
if (!(methodName in mongodb.Collection.prototype)) continue
|
|
38
38
|
|
|
39
|
-
const useTwoArguments = collectionMethodsWithTwoFilters.
|
|
39
|
+
const useTwoArguments = collectionMethodsWithTwoFilters.has(methodName)
|
|
40
40
|
|
|
41
41
|
shimmer.wrap(mongodb.Collection.prototype, methodName, method => {
|
|
42
42
|
return function (...args) {
|
|
@@ -55,11 +55,11 @@ const collectionMethodsWithFilter = [
|
|
|
55
55
|
'remove',
|
|
56
56
|
]
|
|
57
57
|
|
|
58
|
-
const collectionMethodsWithTwoFilters = [
|
|
58
|
+
const collectionMethodsWithTwoFilters = new Set([
|
|
59
59
|
'findOneAndUpdate',
|
|
60
60
|
'updateMany',
|
|
61
61
|
'updateOne',
|
|
62
|
-
]
|
|
62
|
+
])
|
|
63
63
|
|
|
64
64
|
addHook({
|
|
65
65
|
name: 'mongoose',
|
|
@@ -67,7 +67,7 @@ addHook({
|
|
|
67
67
|
file: 'lib/model.js',
|
|
68
68
|
}, Model => {
|
|
69
69
|
for (const methodName of [...collectionMethodsWithFilter, ...collectionMethodsWithTwoFilters]) {
|
|
70
|
-
const useTwoArguments = collectionMethodsWithTwoFilters.
|
|
70
|
+
const useTwoArguments = collectionMethodsWithTwoFilters.has(methodName)
|
|
71
71
|
if (!(methodName in Model)) continue
|
|
72
72
|
|
|
73
73
|
shimmer.wrap(Model, methodName, method => {
|
|
@@ -20,14 +20,14 @@ const methods = [
|
|
|
20
20
|
'where',
|
|
21
21
|
]
|
|
22
22
|
|
|
23
|
-
const methodsOptionalArgs = ['findOneAndUpdate']
|
|
23
|
+
const methodsOptionalArgs = new Set(['findOneAndUpdate'])
|
|
24
24
|
|
|
25
25
|
function getFilters (args, methodName) {
|
|
26
26
|
const [arg0, arg1] = args
|
|
27
27
|
|
|
28
28
|
const filters = arg0 !== null && typeof arg0 === 'object' ? [arg0] : []
|
|
29
29
|
|
|
30
|
-
if (arg1 !== null && typeof arg1 === 'object' && methodsOptionalArgs.
|
|
30
|
+
if (arg1 !== null && typeof arg1 === 'object' && methodsOptionalArgs.has(methodName)) {
|
|
31
31
|
filters.push(arg1)
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -84,7 +84,7 @@ addHook({ name: 'mysql', file: 'lib/Pool.js', versions: ['>=2'] }, Pool => {
|
|
|
84
84
|
const finish = () => finishPoolQueryCh.publish(ctx)
|
|
85
85
|
|
|
86
86
|
return startPoolQueryCh.runStores(ctx, () => {
|
|
87
|
-
const cb = args
|
|
87
|
+
const cb = args.at(-1)
|
|
88
88
|
if (typeof cb === 'function') {
|
|
89
89
|
args[args.length - 1] = shimmer.wrapCallback(cb, cb => function (...args) {
|
|
90
90
|
return finishPoolQueryCh.runStores(ctx, cb, this, ...args)
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const shimmer = require('../../datadog-shimmer')
|
|
4
|
+
const nomenclature = require('../../dd-trace/src/service-naming')
|
|
5
|
+
const spanEndingHook = require('../../dd-trace/src/opentelemetry/span-ending-hook')
|
|
6
|
+
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
4
7
|
const { channel, addHook } = require('./helpers/instrument')
|
|
5
8
|
|
|
6
9
|
const startChannel = channel('apm:next:request:start')
|
|
@@ -21,6 +24,27 @@ const NEXT_REQUEST_META = Symbol.for('NextInternalRequestMeta')
|
|
|
21
24
|
const META_IS_MIDDLEWARE = 'middlewareInvoke'
|
|
22
25
|
const encounteredMiddleware = new WeakSet()
|
|
23
26
|
|
|
27
|
+
// `next.span_type` value Next.js sets on its own OTel root request span; the whole detection surface.
|
|
28
|
+
const NEXT_BASE_SERVER_HANDLE_REQUEST = 'BaseServer.handleRequest'
|
|
29
|
+
|
|
30
|
+
// In OTel-bridge mode (`plugins: false` + `new tracer.TracerProvider().register()`) Next emits its
|
|
31
|
+
// own OTel spans and renames the root request span to `${method} ${route}` at finish, which the
|
|
32
|
+
// bridge routes into the DD operation name and leaves the resource as the bare method — the reverse
|
|
33
|
+
// of Datadog's contract. Correct it via the bridge's pre-finish hook. See span-ending-hook.js.
|
|
34
|
+
spanEndingHook.hook = (ddSpan) => {
|
|
35
|
+
const tags = ddSpan.context().getTags()
|
|
36
|
+
if (tags['next.span_type'] !== NEXT_BASE_SERVER_HANDLE_REQUEST) return
|
|
37
|
+
|
|
38
|
+
const method = tags['http.method']
|
|
39
|
+
const route = tags['next.route'] ?? tags['http.route']
|
|
40
|
+
// Next already wrote the RSC-aware `${method} ${route}` into `next.span_name`; prefer it so we
|
|
41
|
+
// mirror Next's own naming, and only construct the resource when it is absent.
|
|
42
|
+
const resource = tags['next.span_name'] ?? (route ? `${method} ${route}` : method)
|
|
43
|
+
|
|
44
|
+
ddSpan.setOperationName(nomenclature.opName('web', 'server', 'next'))
|
|
45
|
+
ddSpan.setTag(RESOURCE_NAME, resource)
|
|
46
|
+
}
|
|
47
|
+
|
|
24
48
|
function wrapHandleRequest (handleRequest) {
|
|
25
49
|
return function (req, res, pathname, query) {
|
|
26
50
|
return instrument(req, res, () => handleRequest.apply(this, arguments))
|