dd-trace 5.109.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 +25 -5
- package/ci/vitest-no-worker-init-setup.mjs +430 -0
- package/ext/tags.js +2 -0
- package/index.d.ts +79 -24
- package/initialize.mjs +5 -6
- package/loader-hook.mjs +154 -47
- package/package.json +20 -16
- package/packages/datadog-esbuild/index.js +26 -0
- package/packages/datadog-esbuild/src/utils.js +46 -3
- package/packages/datadog-instrumentations/src/connect.js +4 -3
- package/packages/datadog-instrumentations/src/fastify.js +4 -12
- package/packages/datadog-instrumentations/src/fs.js +8 -6
- package/packages/datadog-instrumentations/src/graphql.js +26 -484
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
- package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
- package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
- 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/helpers/rewriter/loader.mjs +18 -2
- package/packages/datadog-instrumentations/src/koa.js +3 -2
- package/packages/datadog-instrumentations/src/mariadb.js +4 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +14 -2
- package/packages/datadog-instrumentations/src/next.js +10 -10
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
- package/packages/datadog-instrumentations/src/playwright.js +1 -1
- package/packages/datadog-instrumentations/src/restify.js +4 -3
- package/packages/datadog-instrumentations/src/router.js +18 -5
- package/packages/datadog-instrumentations/src/selenium.js +1 -2
- package/packages/datadog-instrumentations/src/tedious.js +28 -0
- 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 -1598
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
- 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-aws-sdk/src/base.js +13 -9
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -91
- package/packages/datadog-plugin-cucumber/src/index.js +1 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -5
- 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/client.js +8 -3
- package/packages/datadog-plugin-http/src/index.js +6 -8
- package/packages/datadog-plugin-http2/src/client.js +5 -2
- package/packages/datadog-plugin-jest/src/index.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +1 -1
- package/packages/datadog-plugin-mysql/src/index.js +7 -2
- package/packages/datadog-plugin-openai/src/services.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
- package/packages/datadog-plugin-undici/src/index.js +5 -2
- package/packages/datadog-plugin-vitest/src/index.js +76 -64
- package/packages/datadog-shimmer/src/shimmer.js +37 -16
- package/packages/datadog-webpack/index.js +17 -1
- package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
- package/packages/dd-trace/src/aiguard/sdk.js +15 -3
- package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
- package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
- 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 +20 -7
- package/packages/dd-trace/src/appsec/lambda.js +135 -0
- package/packages/dd-trace/src/appsec/reporter.js +50 -10
- package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +27 -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/request.js +1 -17
- 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 +25 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +596 -58
- package/packages/dd-trace/src/config/helper.js +134 -33
- package/packages/dd-trace/src/config/index.js +39 -23
- package/packages/dd-trace/src/config/major-overrides.js +4 -2
- package/packages/dd-trace/src/config/parsers.js +19 -1
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +109 -57
- package/packages/dd-trace/src/constants.js +7 -0
- package/packages/dd-trace/src/datastreams/processor.js +11 -3
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -35
- package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
- package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
- package/packages/dd-trace/src/debugger/index.js +5 -0
- package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
- package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
- 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/exporters/common/request.js +1 -20
- package/packages/dd-trace/src/exporters/common/url.js +34 -0
- package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
- package/packages/dd-trace/src/guardrails/index.js +8 -5
- package/packages/dd-trace/src/index.js +6 -4
- package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
- package/packages/dd-trace/src/llmobs/index.js +17 -6
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +8 -6
- package/packages/dd-trace/src/llmobs/span_processor.js +5 -1
- package/packages/dd-trace/src/llmobs/tagger.js +58 -3
- package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
- package/packages/dd-trace/src/log/index.js +18 -20
- package/packages/dd-trace/src/openfeature/flagging_provider.js +2 -7
- package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
- package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
- 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/metrics/periodic_metric_reader.js +4 -3
- package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
- package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +44 -38
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
- package/packages/dd-trace/src/opentelemetry/span.js +14 -3
- package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
- package/packages/dd-trace/src/opentracing/span_context.js +17 -0
- package/packages/dd-trace/src/opentracing/tracer.js +6 -1
- package/packages/dd-trace/src/plugin_manager.js +14 -8
- package/packages/dd-trace/src/plugins/ci_plugin.js +21 -4
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
- package/packages/dd-trace/src/plugins/util/llm.js +6 -1
- package/packages/dd-trace/src/plugins/util/test.js +91 -9
- package/packages/dd-trace/src/plugins/util/url.js +68 -1
- package/packages/dd-trace/src/plugins/util/web.js +19 -36
- package/packages/dd-trace/src/priority_sampler.js +1 -13
- package/packages/dd-trace/src/profiling/config.js +129 -198
- package/packages/dd-trace/src/profiling/constants.js +4 -7
- package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
- package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
- package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
- package/packages/dd-trace/src/profiling/oom.js +71 -0
- package/packages/dd-trace/src/profiling/profiler.js +35 -23
- package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
- package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
- package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
- package/packages/dd-trace/src/proxy.js +8 -8
- package/packages/dd-trace/src/ritm.js +4 -2
- package/packages/dd-trace/src/serverless.js +1 -2
- package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
- package/packages/dd-trace/src/span_format.js +2 -1
- package/packages/dd-trace/src/span_processor.js +11 -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 +20 -18
- package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
- package/packages/dd-trace/src/tracer.js +15 -1
- package/packages/dd-trace/src/util.js +14 -0
- package/register.js +58 -1
- 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/pprof-format/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
- package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
|
@@ -1,504 +1,46 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { addHook, getHooks } = require('./helpers/instrument')
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
// Orchestrion rewriter handles wrapping of:
|
|
6
|
+
// - graphql: execute, parse, validate (CJS + ESM)
|
|
7
|
+
// - @graphql-tools/executor: execute, normalizedExecutor (CJS + ESM)
|
|
8
|
+
// See helpers/rewriter/instrumentations/graphql.js for the full config.
|
|
9
|
+
//
|
|
10
|
+
// The plugin (packages/datadog-plugin-graphql/src/execute.js) handles the
|
|
11
|
+
// `apm:graphql:execute:start` AppSec/WAF contract from inside its bindStart:
|
|
12
|
+
// publishing the channel synchronously runs subscribers, and an
|
|
13
|
+
// `abortController.abort()` from a subscriber is observed by replacing
|
|
14
|
+
// `ctx.arguments[0]` with an object whose getters throw AbortError. The
|
|
15
|
+
// orchestrion-emitted wrapper's `catch { ...; throw err }` block propagates
|
|
16
|
+
// that throw to the caller of graphql.execute. No outer wrap needed.
|
|
10
17
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
/** cached objects */
|
|
14
|
-
|
|
15
|
-
// `contexts` is the fast resolver-side lookup; `executeCtx` is the fallback
|
|
16
|
-
// when `contextValue` is a primitive and cannot key a WeakMap.
|
|
17
|
-
const contexts = new WeakMap()
|
|
18
|
-
const executeCtx = new AsyncLocalStorage()
|
|
19
|
-
// Tracks normalized args already instrumented in an outer wrap so graphql-yoga
|
|
20
|
-
// (which stacks `execute` + `normalizedExecutor`) only emits one span per call.
|
|
21
|
-
const instrumentedArgs = new WeakSet()
|
|
22
|
-
const documentSources = new WeakMap()
|
|
23
|
-
const patchedResolvers = new WeakSet()
|
|
24
|
-
const patchedTypes = new WeakSet()
|
|
25
|
-
|
|
26
|
-
/** CHANNELS */
|
|
27
|
-
|
|
28
|
-
// execute channels
|
|
29
|
-
const startExecuteCh = channel('apm:graphql:execute:start')
|
|
30
|
-
const finishExecuteCh = channel('apm:graphql:execute:finish')
|
|
31
|
-
const executeErrorCh = channel('apm:graphql:execute:error')
|
|
32
|
-
|
|
33
|
-
// resolve channels
|
|
34
|
-
const startResolveCh = channel('apm:graphql:resolve:start')
|
|
35
|
-
const finishResolveCh = channel('apm:graphql:resolve:finish')
|
|
36
|
-
const updateFieldCh = channel('apm:graphql:resolve:updateField')
|
|
37
|
-
const resolveErrorCh = channel('apm:graphql:resolve:error')
|
|
38
|
-
|
|
39
|
-
// parse channels
|
|
40
|
-
const parseStartCh = channel('apm:graphql:parser:start')
|
|
41
|
-
const parseFinishCh = channel('apm:graphql:parser:finish')
|
|
42
|
-
const parseErrorCh = channel('apm:graphql:parser:error')
|
|
43
|
-
|
|
44
|
-
// validate channels
|
|
45
|
-
const validateStartCh = channel('apm:graphql:validate:start')
|
|
46
|
-
const validateFinishCh = channel('apm:graphql:validate:finish')
|
|
47
|
-
const validateErrorCh = channel('apm:graphql:validate:error')
|
|
48
|
-
|
|
49
|
-
class AbortError extends Error {
|
|
50
|
-
constructor (message) {
|
|
51
|
-
super(message)
|
|
52
|
-
this.name = 'AbortError'
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const types = new Set(['query', 'mutation', 'subscription'])
|
|
57
|
-
|
|
58
|
-
function getOperation (document, operationName) {
|
|
59
|
-
if (!document || !Array.isArray(document.definitions)) {
|
|
60
|
-
return
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
for (const definition of document.definitions) {
|
|
64
|
-
if (definition && types.has(definition.operation) && (!operationName || definition.name?.value === operationName)) {
|
|
65
|
-
return definition
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function normalizeArgs (args, defaultFieldResolver) {
|
|
71
|
-
if (args.length !== 1) return normalizePositional(args, defaultFieldResolver)
|
|
72
|
-
|
|
73
|
-
const original = args[0]
|
|
74
|
-
const normalized = {
|
|
75
|
-
...original,
|
|
76
|
-
fieldResolver: wrapResolve(original.fieldResolver || defaultFieldResolver),
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
args[0] = normalized
|
|
80
|
-
return normalized
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function normalizePositional (args, defaultFieldResolver) {
|
|
84
|
-
args[6] = wrapResolve(args[6] || defaultFieldResolver) // fieldResolver
|
|
85
|
-
args.length = Math.max(args.length, 7)
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
schema: args[0],
|
|
89
|
-
document: args[1],
|
|
90
|
-
rootValue: args[2],
|
|
91
|
-
contextValue: args[3],
|
|
92
|
-
variableValues: args[4],
|
|
93
|
-
operationName: args[5],
|
|
94
|
-
fieldResolver: args[6],
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// `WeakMap.set` throws `TypeError` on a non-object key; `get`/`has`/`delete`
|
|
99
|
-
// silently miss. Skip the WeakMap entirely for non-keyable `contextValue`.
|
|
100
|
-
function isWeakMapKey (value) {
|
|
101
|
-
return value !== null && typeof value === 'object'
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function wrapParse (parse) {
|
|
105
|
-
return function (source) {
|
|
106
|
-
if (!parseStartCh.hasSubscribers) {
|
|
107
|
-
return parse.apply(this, arguments)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const ctx = { source }
|
|
111
|
-
return parseStartCh.runStores(ctx, () => {
|
|
112
|
-
try {
|
|
113
|
-
ctx.document = parse.apply(this, arguments)
|
|
114
|
-
const operation = getOperation(ctx.document)
|
|
115
|
-
|
|
116
|
-
if (!operation) return ctx.document
|
|
117
|
-
|
|
118
|
-
if (source) {
|
|
119
|
-
documentSources.set(ctx.document, source.body || source)
|
|
120
|
-
}
|
|
121
|
-
ctx.docSource = documentSources.get(ctx.document)
|
|
122
|
-
|
|
123
|
-
return ctx.document
|
|
124
|
-
} catch (err) {
|
|
125
|
-
void err.stack
|
|
126
|
-
ctx.error = err
|
|
127
|
-
parseErrorCh.publish(ctx)
|
|
128
|
-
|
|
129
|
-
throw err
|
|
130
|
-
} finally {
|
|
131
|
-
parseFinishCh.publish(ctx)
|
|
132
|
-
}
|
|
133
|
-
})
|
|
134
|
-
}
|
|
18
|
+
for (const hook of getHooks('graphql')) {
|
|
19
|
+
addHook(hook, exports => exports)
|
|
135
20
|
}
|
|
136
21
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (!validateStartCh.hasSubscribers) {
|
|
140
|
-
return validate.apply(this, arguments)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const ctx = { docSource: documentSources.get(document), document }
|
|
144
|
-
return validateStartCh.runStores(ctx, () => {
|
|
145
|
-
let errors
|
|
146
|
-
try {
|
|
147
|
-
errors = validate.apply(this, arguments)
|
|
148
|
-
if (errors && errors[0]) {
|
|
149
|
-
ctx.error = errors[0]
|
|
150
|
-
validateErrorCh.publish(ctx)
|
|
151
|
-
}
|
|
152
|
-
return errors
|
|
153
|
-
} catch (err) {
|
|
154
|
-
void err.stack
|
|
155
|
-
ctx.error = err
|
|
156
|
-
validateErrorCh.publish(ctx)
|
|
157
|
-
|
|
158
|
-
throw err
|
|
159
|
-
} finally {
|
|
160
|
-
ctx.errors = errors
|
|
161
|
-
validateFinishCh.publish(ctx)
|
|
162
|
-
}
|
|
163
|
-
})
|
|
164
|
-
}
|
|
22
|
+
for (const hook of getHooks('@graphql-tools/executor')) {
|
|
23
|
+
addHook(hook, exports => exports)
|
|
165
24
|
}
|
|
166
25
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return function () {
|
|
171
|
-
if (!startExecuteCh.hasSubscribers) {
|
|
172
|
-
return exe.apply(this, arguments)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// The outer wrap leaves its normalized args object in `arguments[0]`; on
|
|
176
|
-
// graphql-yoga's inner wrap that reference is already known here.
|
|
177
|
-
if (instrumentedArgs.has(arguments[0])) {
|
|
178
|
-
return exe.apply(this, arguments)
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const args = normalizeArgs(arguments, defaultFieldResolver)
|
|
182
|
-
const schema = args.schema
|
|
183
|
-
const document = args.document
|
|
184
|
-
const source = documentSources.get(document)
|
|
185
|
-
const contextValue = args.contextValue
|
|
186
|
-
const keyable = isWeakMapKey(contextValue)
|
|
187
|
-
const operation = getOperation(document, args.operationName)
|
|
188
|
-
|
|
189
|
-
if (keyable && contexts.has(contextValue)) {
|
|
190
|
-
return exe.apply(this, arguments)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const ctx = {
|
|
194
|
-
operation,
|
|
195
|
-
args,
|
|
196
|
-
docSource: source,
|
|
197
|
-
source,
|
|
198
|
-
fields: new Map(),
|
|
199
|
-
abortController: new AbortController(),
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Only the object form leaves a stable single-object handle in
|
|
203
|
-
// `arguments[0]` for the inner wrap to see.
|
|
204
|
-
if (args === arguments[0]) instrumentedArgs.add(args)
|
|
205
|
-
|
|
206
|
-
return startExecuteCh.runStores(ctx, () => {
|
|
207
|
-
if (schema) {
|
|
208
|
-
wrapFields(schema._queryType)
|
|
209
|
-
wrapFields(schema._mutationType)
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (keyable) contexts.set(contextValue, ctx)
|
|
213
|
-
|
|
214
|
-
const finish = (err, res) => {
|
|
215
|
-
if (finishResolveCh.hasSubscribers) finishResolvers(ctx)
|
|
216
|
-
|
|
217
|
-
const error = err || (res && res.errors && res.errors[0])
|
|
218
|
-
|
|
219
|
-
if (error) {
|
|
220
|
-
ctx.error = error
|
|
221
|
-
executeErrorCh.publish(ctx)
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
ctx.res = res
|
|
225
|
-
if (keyable) contexts.delete(contextValue)
|
|
226
|
-
instrumentedArgs.delete(args)
|
|
227
|
-
finishExecuteCh.publish(ctx)
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Skip the ALS entry on the common object-`contextValue` path; the
|
|
231
|
-
// resolver reaches `ctx` via the WeakMap there.
|
|
232
|
-
return keyable
|
|
233
|
-
? callInAsyncScope(exe, this, arguments, ctx.abortController, finish)
|
|
234
|
-
: executeCtx.run(ctx, () => callInAsyncScope(exe, this, arguments, ctx.abortController, finish))
|
|
235
|
-
})
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function wrapResolve (resolve) {
|
|
241
|
-
if (typeof resolve !== 'function' || patchedResolvers.has(resolve)) return resolve
|
|
242
|
-
|
|
243
|
-
function resolveAsync (source, args, contextValue, info) {
|
|
244
|
-
if (!startResolveCh.hasSubscribers) return resolve.apply(this, arguments)
|
|
245
|
-
|
|
246
|
-
// `WeakMap.get(primitive)` returns `undefined`, so the fallback covers
|
|
247
|
-
// executes that ran with a primitive `contextValue`.
|
|
248
|
-
const ctx = contexts.get(contextValue) ?? executeCtx.getStore()
|
|
249
|
-
|
|
250
|
-
/* istanbul ignore if: resolver invoked outside execute(), so no per-execute ctx was registered */
|
|
251
|
-
if (!ctx) return resolve.apply(this, arguments)
|
|
252
|
-
|
|
253
|
-
const field = assertField(ctx, info, args)
|
|
254
|
-
|
|
255
|
-
if (ctx.abortController.signal.aborted) {
|
|
256
|
-
publishResolverFinish(field, null)
|
|
257
|
-
throw new AbortError('Aborted')
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
try {
|
|
261
|
-
const result = resolve.call(this, source, args, contextValue, info)
|
|
262
|
-
if (result !== null && typeof result?.then === 'function') {
|
|
263
|
-
return result.then(
|
|
264
|
-
res => {
|
|
265
|
-
publishResolverFinish(field, null)
|
|
266
|
-
return res
|
|
267
|
-
},
|
|
268
|
-
error => {
|
|
269
|
-
publishResolverFinish(field, error)
|
|
270
|
-
throw error
|
|
271
|
-
}
|
|
272
|
-
)
|
|
273
|
-
}
|
|
274
|
-
publishResolverFinish(field, null)
|
|
275
|
-
return result
|
|
276
|
-
} catch (error) {
|
|
277
|
-
publishResolverFinish(field, error)
|
|
278
|
-
throw error
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
patchedResolvers.add(resolveAsync)
|
|
283
|
-
|
|
284
|
-
return resolveAsync
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* @param {{ ctx: object, error: unknown }} field
|
|
289
|
-
* @param {unknown} error
|
|
290
|
-
*/
|
|
291
|
-
function publishResolverFinish (field, error) {
|
|
292
|
-
const fieldCtx = field.ctx
|
|
293
|
-
fieldCtx.error = error
|
|
294
|
-
fieldCtx.field = field
|
|
295
|
-
updateFieldCh.publish(fieldCtx)
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
function callInAsyncScope (fn, thisArg, args, abortController, cb) {
|
|
299
|
-
if (abortController.signal.aborted) {
|
|
300
|
-
cb(null, null)
|
|
301
|
-
throw new AbortError('Aborted')
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
try {
|
|
305
|
-
const result = fn.apply(thisArg, args)
|
|
306
|
-
if (result !== null && typeof result?.then === 'function') {
|
|
307
|
-
return result.then(
|
|
308
|
-
res => {
|
|
309
|
-
cb(null, res)
|
|
310
|
-
return res
|
|
311
|
-
},
|
|
312
|
-
/* istanbul ignore next: graphql.execute() rejects only via custom executors (graphql-yoga / graphql-tools) */
|
|
313
|
-
error => {
|
|
314
|
-
cb(error)
|
|
315
|
-
throw error
|
|
316
|
-
}
|
|
317
|
-
)
|
|
318
|
-
}
|
|
319
|
-
cb(null, result)
|
|
320
|
-
return result
|
|
321
|
-
} catch (error) {
|
|
322
|
-
cb(error)
|
|
323
|
-
throw error
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* @typedef {{ prev: PathNode | undefined, key: string | number }} PathNode
|
|
329
|
-
*
|
|
330
|
-
* @typedef {{ error: unknown, ctx: object }} TrackedField
|
|
331
|
-
*/
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* @param {{
|
|
335
|
-
* fields: Map<object, TrackedField>,
|
|
336
|
-
* collapse: boolean,
|
|
337
|
-
* collapsedFields?: Map<string, TrackedField>,
|
|
338
|
-
* pathCache?: Map<PathNode, string>,
|
|
339
|
-
* }} rootCtx
|
|
340
|
-
* @param {import('graphql').GraphQLResolveInfo} info
|
|
341
|
-
* @param {Record<string, unknown>} args
|
|
342
|
-
*/
|
|
343
|
-
function assertField (rootCtx, info, args) {
|
|
344
|
-
const path = info.path
|
|
345
|
-
const collapse = rootCtx.collapse
|
|
346
|
-
|
|
347
|
-
const cache = rootCtx.pathCache ??= new Map()
|
|
348
|
-
const prev = path.prev
|
|
349
|
-
const key = path.key
|
|
350
|
-
const segment = collapse && typeof key !== 'string' ? '*' : key
|
|
351
|
-
|
|
352
|
-
const pathString = prev === undefined
|
|
353
|
-
? String(segment)
|
|
354
|
-
: (cache.get(prev) ?? buildCachedPathString(prev, cache, collapse)) + '.' + segment
|
|
355
|
-
cache.set(path, pathString)
|
|
356
|
-
|
|
357
|
-
const fieldCtx = {
|
|
358
|
-
rootCtx,
|
|
359
|
-
args,
|
|
360
|
-
path,
|
|
361
|
-
pathString,
|
|
362
|
-
fieldName: info.fieldName,
|
|
363
|
-
returnType: info.returnType,
|
|
364
|
-
fieldNode: info.fieldNodes[0],
|
|
365
|
-
variableValues: info.variableValues,
|
|
366
|
-
}
|
|
367
|
-
// Publish per resolver call, before the collapse / depth dedupe below.
|
|
368
|
-
// IAST mutates each call's own args object; if siblings 2..N skip the
|
|
369
|
-
// publish, those args objects never get tainted.
|
|
370
|
-
startResolveCh.publish(fieldCtx)
|
|
371
|
-
|
|
372
|
-
let collapsedFields
|
|
373
|
-
if (collapse) {
|
|
374
|
-
collapsedFields = rootCtx.collapsedFields ??= new Map()
|
|
375
|
-
const existing = collapsedFields.get(pathString)
|
|
376
|
-
// Subsequent siblings of a collapsed list share the first sibling's field
|
|
377
|
-
// so updateFieldCh fires for every call and the span's finishTime tracks
|
|
378
|
-
// the last sibling's completion, not the first.
|
|
379
|
-
if (existing !== undefined) return existing
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
const field = { error: null, ctx: fieldCtx }
|
|
383
|
-
rootCtx.fields.set(path, field)
|
|
384
|
-
if (collapsedFields !== undefined) collapsedFields.set(pathString, field)
|
|
385
|
-
return field
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Cold path for assertField. graphql-js inserts a synthetic array-index
|
|
390
|
-
* node between a list field and its items, and that node never reaches a
|
|
391
|
-
* resolver — so assertField has no chance to cache it. The first child of
|
|
392
|
-
* the list item that hits the path cache lands here to walk and populate
|
|
393
|
-
* back to a cached ancestor.
|
|
394
|
-
*
|
|
395
|
-
* @param {PathNode} path
|
|
396
|
-
* @param {Map<PathNode, string>} cache
|
|
397
|
-
* @param {boolean} collapse
|
|
398
|
-
*/
|
|
399
|
-
function buildCachedPathString (path, cache, collapse) {
|
|
400
|
-
const key = path.key
|
|
401
|
-
const segment = collapse && typeof key !== 'string' ? '*' : key
|
|
402
|
-
const prev = path.prev
|
|
403
|
-
|
|
404
|
-
const pathString = prev === undefined
|
|
405
|
-
? String(segment)
|
|
406
|
-
: (cache.get(prev) ?? buildCachedPathString(prev, cache, collapse)) + '.' + segment
|
|
407
|
-
cache.set(path, pathString)
|
|
408
|
-
return pathString
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
function wrapFields (type) {
|
|
412
|
-
if (!type || !type._fields || patchedTypes.has(type)) {
|
|
413
|
-
return
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
patchedTypes.add(type)
|
|
417
|
-
|
|
418
|
-
for (const field of Object.values(type._fields)) {
|
|
419
|
-
wrapFieldResolve(field)
|
|
420
|
-
wrapFieldType(field)
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
function wrapFieldResolve (field) {
|
|
425
|
-
if (!field || !field.resolve) return
|
|
426
|
-
field.resolve = wrapResolve(field.resolve)
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
function wrapFieldType (field) {
|
|
430
|
-
if (!field || !field.type) return
|
|
431
|
-
|
|
432
|
-
let unwrappedType = field.type
|
|
433
|
-
|
|
434
|
-
while (unwrappedType.ofType) {
|
|
435
|
-
unwrappedType = unwrappedType.ofType
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
wrapFields(unwrappedType)
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
function finishResolvers ({ fields }) {
|
|
442
|
-
for (const field of fields.values()) {
|
|
443
|
-
const fieldCtx = field.ctx
|
|
444
|
-
// A depth-gated field publishes startResolveCh for IAST/AppSec but the
|
|
445
|
-
// resolve plugin's start short-circuits before creating a span, so there
|
|
446
|
-
// is no span here to finish.
|
|
447
|
-
if (fieldCtx.currentStore === undefined) continue
|
|
448
|
-
fieldCtx.finishTime = field.finishTime
|
|
449
|
-
fieldCtx.field = field
|
|
450
|
-
if (field.error) {
|
|
451
|
-
fieldCtx.error = field.error
|
|
452
|
-
resolveErrorCh.publish(fieldCtx)
|
|
453
|
-
}
|
|
454
|
-
finishResolveCh.publish(fieldCtx)
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
addHook({ name: '@graphql-tools/executor', versions: ['>=0.0.14'] }, executor => {
|
|
459
|
-
// graphql-yoga uses the normalizedExecutor function, so we need to wrap both. There is no risk in wrapping both
|
|
460
|
-
// since the functions are closely related, and our wrappedExecute function prevents double calls with the
|
|
461
|
-
// contexts.has(contextValue) check.
|
|
462
|
-
shimmer.wrap(executor, 'execute', wrapExecute(executor))
|
|
463
|
-
shimmer.wrap(executor, 'normalizedExecutor', wrapExecute(executor))
|
|
464
|
-
return executor
|
|
465
|
-
})
|
|
466
|
-
|
|
467
|
-
// TODO(BridgeAR): graphql >=17.0.0-alpha.9 routes execute() through
|
|
468
|
-
// experimentalExecuteIncrementally(), bypassing this hook. The same
|
|
469
|
-
// function returns { initialResult, subsequentResults } for @defer /
|
|
470
|
-
// @stream which callInAsyncScope does not handle — execute finishes
|
|
471
|
-
// before the streamed payloads land.
|
|
472
|
-
addHook({ name: 'graphql', file: 'execution/execute.js', versions: ['>=0.10'] }, execute => {
|
|
473
|
-
shimmer.wrap(execute, 'execute', wrapExecute(execute))
|
|
474
|
-
return execute
|
|
475
|
-
})
|
|
476
|
-
|
|
477
|
-
addHook({ name: 'graphql', file: 'language/parser.js', versions: ['>=0.10'] }, parser => {
|
|
478
|
-
shimmer.wrap(parser, 'parse', wrapParse)
|
|
479
|
-
return parser
|
|
480
|
-
})
|
|
481
|
-
|
|
482
|
-
addHook({ name: 'graphql', file: 'validation/validate.js', versions: ['>=0.10'] }, validate => {
|
|
483
|
-
shimmer.wrap(validate, 'validate', wrapValidate)
|
|
484
|
-
|
|
485
|
-
return validate
|
|
486
|
-
})
|
|
26
|
+
// Module-load hooks: capture references on ddGlobal for cross-plugin access
|
|
27
|
+
// (read lazily inside each callback so agent.load() between mocha suites can
|
|
28
|
+
// rebind globalThis[dd-trace] without us stashing a stale reference).
|
|
487
29
|
|
|
488
30
|
addHook({ name: 'graphql', file: 'language/printer.js', versions: ['>=0.10'] }, printer => {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
// happened. Not sure how to handle this so for now use a global.
|
|
492
|
-
ddGlobal.graphql_printer = printer
|
|
31
|
+
const ddGlobal = globalThis[Symbol.for('dd-trace')]
|
|
32
|
+
if (ddGlobal) ddGlobal.graphql_printer = printer
|
|
493
33
|
return printer
|
|
494
34
|
})
|
|
495
35
|
|
|
496
36
|
addHook({ name: 'graphql', file: 'language/visitor.js', versions: ['>=0.10'] }, visitor => {
|
|
497
|
-
ddGlobal
|
|
37
|
+
const ddGlobal = globalThis[Symbol.for('dd-trace')]
|
|
38
|
+
if (ddGlobal) ddGlobal.graphql_visitor = visitor
|
|
498
39
|
return visitor
|
|
499
40
|
})
|
|
500
41
|
|
|
501
42
|
addHook({ name: 'graphql', file: 'utilities/index.js', versions: ['>=0.10'] }, utilities => {
|
|
502
|
-
ddGlobal
|
|
43
|
+
const ddGlobal = globalThis[Symbol.for('dd-trace')]
|
|
44
|
+
if (ddGlobal) ddGlobal.graphql_utilities = utilities
|
|
503
45
|
return utilities
|
|
504
46
|
})
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
const
|
|
3
|
+
// Queued before the logger is ready; flushed once the tracer knows its config.
|
|
4
|
+
const warnings = [] // conflicts; flushed under DD_TRACE_DEBUG
|
|
5
|
+
const loadOrderWarnings = [] // "loaded before dd-trace"; flushed under startupLogs
|
|
6
|
+
const frameworkWarnings = [] // curated (e.g. Next.js); flushed unconditionally
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Here we maintain a list of packages that an application
|
|
@@ -28,6 +29,24 @@ const potentialConflicts = new Set([
|
|
|
28
29
|
|
|
29
30
|
const extractPackageAndModulePath = require('./extract-package-and-module-path')
|
|
30
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Frameworks that load their own server code before any user code (and thus
|
|
34
|
+
* before a late `tracer.init()`) can run. When their server module is already
|
|
35
|
+
* in `require.cache` at init time, dd-trace was loaded too late to instrument
|
|
36
|
+
* them and the integration silently no-ops. Unlike the broad scan below, this
|
|
37
|
+
* set is high-signal enough to warn on by default. `file` is the module whose
|
|
38
|
+
* presence proves the server is already loaded; `guidance` is the bundler note
|
|
39
|
+
* appended to the warning.
|
|
40
|
+
*/
|
|
41
|
+
const earlyLoadFrameworks = new Map([
|
|
42
|
+
['next', {
|
|
43
|
+
// dist/server/next-server.js (>=11.1), dist/next-server/server/next-server.js (older)
|
|
44
|
+
file: 'next-server.js',
|
|
45
|
+
guidance: "add 'dd-trace' to `serverExternalPackages` (Next.js >=15) or " +
|
|
46
|
+
'`experimental.serverComponentsExternalPackages` (13-14) so it is not bundled',
|
|
47
|
+
}],
|
|
48
|
+
])
|
|
49
|
+
|
|
31
50
|
/**
|
|
32
51
|
* The lowest hanging fruit to debug an app that isn't tracing
|
|
33
52
|
* properly is to check that it is loaded before any modules
|
|
@@ -43,25 +62,53 @@ const extractPackageAndModulePath = require('./extract-package-and-module-path')
|
|
|
43
62
|
* app loads a package we instrument but outside of an
|
|
44
63
|
* unsupported version then a warning would still be displayed.
|
|
45
64
|
* This is OK as the tracer should be loaded earlier anyway.
|
|
65
|
+
*
|
|
66
|
+
* Curated frameworks (see `earlyLoadFrameworks`) surface unconditionally; the
|
|
67
|
+
* broad list of packages loaded before dd-trace is queued for the startupLogs-
|
|
68
|
+
* gated flush (`flushLoadOrderWarnings`).
|
|
46
69
|
*/
|
|
47
70
|
module.exports.checkForRequiredModules = function () {
|
|
48
71
|
const packages = require('./hooks')
|
|
49
72
|
const naughties = new Set()
|
|
73
|
+
const frameworksSeen = new Set()
|
|
50
74
|
let didWarn = false
|
|
51
75
|
|
|
52
76
|
for (const pathToModule of Object.keys(require.cache)) {
|
|
53
|
-
|
|
77
|
+
// require.cache keys use the platform separator; normalize so the
|
|
78
|
+
// `node_modules/<pkg>` parsing works on Windows (backslash paths).
|
|
79
|
+
const { pkg, path } = extractPackageAndModulePath(pathToModule.replaceAll('\\', '/'))
|
|
54
80
|
|
|
55
|
-
if (
|
|
56
|
-
|
|
81
|
+
if (pkg === null) continue
|
|
82
|
+
|
|
83
|
+
// A curated framework loads its own server before user code, so its server
|
|
84
|
+
// module being cached means dd-trace was too late to instrument it. These
|
|
85
|
+
// surface unconditionally (see flushFrameworkWarnings) with an actionable
|
|
86
|
+
// message, so they never fall through to the broad load-order list below.
|
|
87
|
+
const framework = earlyLoadFrameworks.get(pkg)
|
|
88
|
+
if (framework !== undefined) {
|
|
89
|
+
if (!frameworksSeen.has(pkg) && path?.endsWith(framework.file)) {
|
|
90
|
+
frameworksSeen.add(pkg)
|
|
91
|
+
frameworkWarnings.push(
|
|
92
|
+
`'${pkg}' was loaded before dd-trace, so the ${pkg} integration will not be applied. ` +
|
|
93
|
+
'Initialize dd-trace before your application starts — ' +
|
|
94
|
+
"NODE_OPTIONS='--require dd-trace/init' (CommonJS) or '--import dd-trace/initialize.mjs' (ESM) — " +
|
|
95
|
+
`and ${framework.guidance}.`
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
continue
|
|
99
|
+
}
|
|
57
100
|
|
|
58
|
-
|
|
101
|
+
if (naughties.has(pkg) || !(pkg in packages)) continue
|
|
102
|
+
|
|
103
|
+
loadOrderWarnings.push(
|
|
104
|
+
() => `Warning: Package '${pkg}' was loaded before dd-trace! This may break instrumentation.`
|
|
105
|
+
)
|
|
59
106
|
|
|
60
107
|
naughties.add(pkg)
|
|
61
108
|
didWarn = true
|
|
62
109
|
}
|
|
63
110
|
|
|
64
|
-
if (didWarn)
|
|
111
|
+
if (didWarn) loadOrderWarnings.push('Warning: Please ensure dd-trace is loaded before other modules.')
|
|
65
112
|
}
|
|
66
113
|
|
|
67
114
|
/**
|
|
@@ -104,3 +151,29 @@ module.exports.flushStartupLogs = function (log) {
|
|
|
104
151
|
log.warn(typeof entry === 'function' ? entry() : entry)
|
|
105
152
|
}
|
|
106
153
|
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Drains the broad "loaded before dd-trace" warnings collected by
|
|
157
|
+
* `checkForRequiredModules`. The tracer gates this on startupLogs (these are
|
|
158
|
+
* startup diagnostics), unlike the unconditional `flushFrameworkWarnings`.
|
|
159
|
+
* @param {(message: string) => void} warn
|
|
160
|
+
*/
|
|
161
|
+
module.exports.flushLoadOrderWarnings = function (warn) {
|
|
162
|
+
while (loadOrderWarnings.length) {
|
|
163
|
+
const entry = loadOrderWarnings.shift()
|
|
164
|
+
warn(typeof entry === 'function' ? entry() : entry)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Drains the framework warnings collected by `checkForRequiredModules`. The
|
|
170
|
+
* tracer surfaces these unconditionally (not gated on startupLogs or
|
|
171
|
+
* DD_TRACE_DEBUG), unlike the DD_TRACE_DEBUG-only `flushStartupLogs` queue,
|
|
172
|
+
* because the affected users run with neither enabled (#5430 / #5432).
|
|
173
|
+
* @param {(message: string) => void} warn
|
|
174
|
+
*/
|
|
175
|
+
module.exports.flushFrameworkWarnings = function (warn) {
|
|
176
|
+
while (frameworkWarnings.length) {
|
|
177
|
+
warn(frameworkWarnings.shift())
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -42,6 +42,31 @@ exports.tracingChannel = function (name) {
|
|
|
42
42
|
return tc
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Build a guarded publisher for a public error channel. A subscriber that
|
|
47
|
+
* re-enters the same wrapped dispatch while handling the error would otherwise
|
|
48
|
+
* republish here and recurse until the stack overflows. Each framework binds
|
|
49
|
+
* its own publisher, so the in-flight flag stays private to one channel: a
|
|
50
|
+
* genuinely nested error on a different framework's channel (a Koa app mounted
|
|
51
|
+
* inside Express) still reaches its subscribers instead of being dropped, and
|
|
52
|
+
* the guard costs a closure read rather than a per-publish channel lookup.
|
|
53
|
+
*
|
|
54
|
+
* @param {Channel} errorChannel
|
|
55
|
+
*/
|
|
56
|
+
exports.createErrorPublisher = function createErrorPublisher (errorChannel) {
|
|
57
|
+
let publishing = false
|
|
58
|
+
/** @param {object} message */
|
|
59
|
+
return function publishError (message) {
|
|
60
|
+
if (publishing) return
|
|
61
|
+
publishing = true
|
|
62
|
+
try {
|
|
63
|
+
errorChannel.publish(message)
|
|
64
|
+
} finally {
|
|
65
|
+
publishing = false
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
45
70
|
exports.getHooks = function getHooks (names) {
|
|
46
71
|
names = [names].flat()
|
|
47
72
|
|
|
@@ -55,9 +80,9 @@ exports.getHooks = function getHooks (names) {
|
|
|
55
80
|
* @param {object} args
|
|
56
81
|
* @param {string} args.name module name
|
|
57
82
|
* @param {string[]} [args.versions] array of semver range strings
|
|
58
|
-
* @param {string} [args.file] path to file within package to instrument. Defaults to 'index.js'.
|
|
83
|
+
* @param {string} [args.file] path to file within package to instrument. Defaults to 'index.js'.
|
|
59
84
|
* @param {string} [args.filePattern] pattern to match files within package to instrument
|
|
60
|
-
* @param {boolean} [args.patchDefault] whether to patch the default export. Defaults to true.
|
|
85
|
+
* @param {boolean} [args.patchDefault] whether to patch the default export. Defaults to true.
|
|
61
86
|
* @param {(moduleExports: unknown, version: string, isIitm?: boolean, hookMeta?: object) => unknown} [hook]
|
|
62
87
|
* Patches module exports
|
|
63
88
|
*/
|