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
|
@@ -3,69 +3,106 @@
|
|
|
3
3
|
const { LRUCache } = require('../../../vendor/dist/lru-cache')
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* @typedef {{ signature
|
|
6
|
+
* @typedef {{ signature: string, type: import('graphql').OperationTypeNode, name?: string }} RequestOperation
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
// Mercurius funnels every operation through `fastifyGraphQl`, but the parsed
|
|
12
|
-
// document — and therefore the operation signature/type/name — is only known
|
|
13
|
-
// once mercurius parses internally. The top-level request span opens before
|
|
14
|
-
// that, and on the JIT warm path neither parse/validate nor execute fires, so
|
|
15
|
-
// the span would otherwise be left with only the provisional resource. The cold
|
|
16
|
-
// path caches the computed metadata; the request boundary reads it back on the
|
|
17
|
-
// warm path. Bounded so a flood of distinct queries can't grow it without limit.
|
|
18
|
-
//
|
|
19
|
-
// The key is the operation name plus the raw query text, not the source alone:
|
|
20
|
-
// mercurius keys its document LRU by source but compiles the JIT for a single
|
|
21
|
-
// `operationName`, and the compiled query then serves that operation for every
|
|
22
|
-
// later request that shares the source — regardless of the `operationName` those
|
|
23
|
-
// requests ask for. A source-only key would hand a warm request for operation B
|
|
24
|
-
// the metadata of whichever operation was cached last for that source (A),
|
|
25
|
-
// mislabeling the span. Operation names cannot contain a newline, so it is a
|
|
26
|
-
// safe separator that keeps the two parts from colliding.
|
|
27
|
-
const requestOperationCache = new LRUCache({ max: 500 })
|
|
28
|
-
|
|
29
|
-
// Mercurius also accepts a pre-parsed document AST as the source, which reaches
|
|
30
|
-
// the request boundary as an object rather than query text — so there is no
|
|
31
|
-
// string to key the LRU by. Mercurius keys its own document LRU by that source
|
|
32
|
-
// object's identity, and the same object reaches the boundary on the warm path,
|
|
33
|
-
// so a WeakMap keyed by the caller-owned document recovers the metadata without
|
|
34
|
-
// mutating the document and releases with it. The value carries the requested
|
|
35
|
-
// operation name so a JIT-only sibling selection is not handed another
|
|
36
|
-
// operation's metadata (same reason the string cache keys by operation name).
|
|
37
|
-
/** @type {WeakMap<object, Map<string | undefined, RequestOperation>>} */
|
|
38
|
-
const documentOperationCache = new WeakMap()
|
|
9
|
+
const requestCacheMax = 500
|
|
39
10
|
|
|
40
11
|
/**
|
|
41
|
-
* @
|
|
42
|
-
*
|
|
12
|
+
* @typedef {{
|
|
13
|
+
* document: WeakRef<import('graphql').DocumentNode>,
|
|
14
|
+
* operations: Map<string, RequestOperation>
|
|
15
|
+
* }} DocumentOperations
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {{
|
|
20
|
+
* documentOperations: WeakMap<object, DocumentOperations>,
|
|
21
|
+
* documents: import('lru-cache')<string, WeakRef<import('graphql').DocumentNode>>,
|
|
22
|
+
* operations: import('lru-cache')<string, RequestOperation>
|
|
23
|
+
* }} RequestCache
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** @type {WeakMap<object, RequestCache>} */
|
|
27
|
+
const requestCaches = new WeakMap()
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {object | undefined} owner
|
|
31
|
+
* @param {boolean | number | undefined} configuredLimit
|
|
32
|
+
* @returns {RequestCache | undefined}
|
|
33
|
+
*/
|
|
34
|
+
function getRequestCache (owner, configuredLimit) {
|
|
35
|
+
if (!owner || configuredLimit === false || (typeof configuredLimit === 'number' && configuredLimit <= 0)) return
|
|
36
|
+
|
|
37
|
+
let requestCache = requestCaches.get(owner)
|
|
38
|
+
if (requestCache !== undefined) return requestCache
|
|
39
|
+
|
|
40
|
+
const max = typeof configuredLimit === 'number'
|
|
41
|
+
? Math.min(Math.floor(configuredLimit), requestCacheMax)
|
|
42
|
+
: requestCacheMax
|
|
43
|
+
if (max <= 0) return
|
|
44
|
+
|
|
45
|
+
requestCache = {
|
|
46
|
+
documentOperations: new WeakMap(),
|
|
47
|
+
documents: new LRUCache({ max }),
|
|
48
|
+
operations: new LRUCache({ max }),
|
|
49
|
+
}
|
|
50
|
+
requestCaches.set(owner, requestCache)
|
|
51
|
+
return requestCache
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {string | null | undefined} operationName
|
|
56
|
+
* @param {boolean} calculateSignature
|
|
43
57
|
* @returns {string}
|
|
44
58
|
*/
|
|
45
|
-
function
|
|
46
|
-
return `${
|
|
59
|
+
function operationCacheKey (operationName, calculateSignature) {
|
|
60
|
+
return `${calculateSignature === false ? 0 : 1}\n${operationName ?? ''}`
|
|
47
61
|
}
|
|
48
62
|
|
|
49
63
|
/**
|
|
50
|
-
* @param {unknown} source
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* @param {
|
|
64
|
+
* @param {unknown} source
|
|
65
|
+
* @param {string | null | undefined} operationName
|
|
66
|
+
* @param {boolean} calculateSignature
|
|
67
|
+
* @param {RequestCache | undefined} requestCache
|
|
54
68
|
* @returns {RequestOperation | undefined}
|
|
55
69
|
*/
|
|
56
|
-
function getCachedRequestOperation (source, operationName) {
|
|
70
|
+
function getCachedRequestOperation (source, operationName, calculateSignature, requestCache) {
|
|
71
|
+
if (requestCache === undefined) return
|
|
72
|
+
|
|
73
|
+
const operationKey = operationCacheKey(operationName, calculateSignature)
|
|
57
74
|
if (typeof source === 'string') {
|
|
58
|
-
|
|
75
|
+
// GraphQL operation names cannot contain newlines, so source text cannot collide with this prefix.
|
|
76
|
+
const key = `${operationKey}\n${source}`
|
|
77
|
+
let operation = requestCache.operations.get(key)
|
|
78
|
+
if (operation !== undefined) return operation
|
|
79
|
+
|
|
80
|
+
const document = requestCache.documents.get(source)?.deref()
|
|
81
|
+
if (document === undefined) return
|
|
82
|
+
|
|
83
|
+
operation = getRequestOperation(document, operationName, calculateSignature)
|
|
84
|
+
if (operation === undefined) return
|
|
85
|
+
|
|
86
|
+
requestCache.operations.set(key, operation)
|
|
87
|
+
return operation
|
|
59
88
|
}
|
|
60
89
|
if (source === null || typeof source !== 'object') return
|
|
61
|
-
|
|
90
|
+
|
|
91
|
+
const cached = requestCache.documentOperations.get(source)
|
|
92
|
+
if (cached === undefined) return
|
|
93
|
+
|
|
94
|
+
let operation = cached.operations.get(operationKey)
|
|
95
|
+
const document = cached.document.deref()
|
|
96
|
+
if (operation !== undefined || document === undefined) return operation
|
|
97
|
+
|
|
98
|
+
operation = getRequestOperation(document, operationName, calculateSignature)
|
|
99
|
+
if (operation === undefined) return
|
|
100
|
+
|
|
101
|
+
cached.operations.set(operationKey, operation)
|
|
102
|
+
return operation
|
|
62
103
|
}
|
|
63
104
|
|
|
64
105
|
/**
|
|
65
|
-
* A string source keys the text LRU; a document AST keys the WeakMap. Any other
|
|
66
|
-
* shape has no usable key — mercurius rejects it before execute, so the warm
|
|
67
|
-
* path never reaches the request span for it either.
|
|
68
|
-
*
|
|
69
106
|
* @param {unknown} source
|
|
70
107
|
* @returns {source is string | object}
|
|
71
108
|
*/
|
|
@@ -74,104 +111,139 @@ function isCacheableSource (source) {
|
|
|
74
111
|
}
|
|
75
112
|
|
|
76
113
|
/**
|
|
77
|
-
* Select the operation definition matching `operationName`, or the first one
|
|
78
|
-
* when no name is given (graphql/mercurius default selection).
|
|
79
|
-
*
|
|
80
114
|
* @param {import('graphql').DocumentNode | undefined} document
|
|
81
|
-
* @param {string | undefined} operationName
|
|
115
|
+
* @param {string | null | undefined} operationName
|
|
82
116
|
* @returns {import('graphql').OperationDefinitionNode | undefined}
|
|
83
117
|
*/
|
|
84
118
|
function getOperation (document, operationName) {
|
|
85
|
-
/* istanbul ignore if: validate/execute only call this with a parsed GraphQL document. */
|
|
86
119
|
if (!document || !Array.isArray(document.definitions)) return
|
|
87
120
|
|
|
121
|
+
let operation
|
|
88
122
|
for (const definition of document.definitions) {
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
123
|
+
if (definition.kind !== 'OperationDefinition') continue
|
|
124
|
+
|
|
125
|
+
if (operationName != null) {
|
|
126
|
+
if (definition.name?.value === operationName) return definition
|
|
127
|
+
continue
|
|
92
128
|
}
|
|
129
|
+
|
|
130
|
+
if (operation !== undefined) return
|
|
131
|
+
operation = definition
|
|
93
132
|
}
|
|
133
|
+
|
|
134
|
+
return operation
|
|
94
135
|
}
|
|
95
136
|
|
|
96
137
|
/**
|
|
97
|
-
* Refine the top-level graphql.request span (mercurius) from the parsed
|
|
98
|
-
* document and cache the metadata so the JIT warm path — where no sub-span
|
|
99
|
-
* fires — can recover the same tags at the request boundary.
|
|
100
|
-
*
|
|
101
|
-
* This runs at the first boundary that has the document (validate on the cold
|
|
102
|
-
* path, which also precedes a pre-execute validation failure). It is idempotent
|
|
103
|
-
* across the later execute boundary via the `ddRequestRefined` flag, and a
|
|
104
|
-
* no-op for graphql-js/apollo/yoga, which never open a request span.
|
|
105
|
-
*
|
|
106
|
-
* Every named operation in the document is cached, not just the selected one:
|
|
107
|
-
* a multi-operation document parses once, and a later request may select a
|
|
108
|
-
* sibling operation that mercurius then serves exclusively through its JIT path
|
|
109
|
-
* (no execute span), so its metadata has to be ready before that happens.
|
|
110
|
-
*
|
|
111
138
|
* @param {import('../../dd-trace/src/opentracing/span') | undefined} requestSpan
|
|
112
|
-
* @param {
|
|
113
|
-
* @param {
|
|
114
|
-
*
|
|
115
|
-
* cache is keyed by it, not by the parsed document, so the request boundary
|
|
116
|
-
* recovers the metadata on the warm path from the same value mercurius keys
|
|
117
|
-
* its own document LRU by. Any other shape has no usable key and is not
|
|
118
|
-
* cached (the warm path never reaches this span for it either).
|
|
119
|
-
* @param {string | undefined} operationName - The requested operation name.
|
|
120
|
-
* @param {boolean} calculateSignature - The graphql plugin's `signature` config.
|
|
139
|
+
* @param {string} signature
|
|
140
|
+
* @param {import('graphql').OperationTypeNode | undefined} type
|
|
141
|
+
* @param {string | undefined} name
|
|
121
142
|
*/
|
|
122
|
-
function
|
|
123
|
-
|
|
124
|
-
if (!requestSpan || requestSpan.ddRequestRefined || !document) return
|
|
143
|
+
function refineRequestSpanMetadata (requestSpan, signature, type, name) {
|
|
144
|
+
if (!requestSpan || requestSpan.ddRequestRefined) return
|
|
125
145
|
requestSpan.ddRequestRefined = true
|
|
126
146
|
|
|
127
|
-
const operation = getOperation(document, operationName)
|
|
128
|
-
const type = operation?.operation
|
|
129
|
-
const name = operation?.name?.value
|
|
130
|
-
const signature = getSignature(document, name, type, calculateSignature)
|
|
131
|
-
|
|
132
147
|
if (signature) requestSpan.setTag('resource.name', signature)
|
|
133
148
|
if (type) requestSpan.setTag('graphql.operation.type', type)
|
|
134
149
|
if (name) requestSpan.setTag('graphql.operation.name', name)
|
|
150
|
+
}
|
|
135
151
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
152
|
+
/**
|
|
153
|
+
* @param {{
|
|
154
|
+
* ddRequestRefined?: boolean,
|
|
155
|
+
* setTag: (key: string, value: string) => unknown
|
|
156
|
+
* } | undefined} requestSpan
|
|
157
|
+
* @param {import('graphql').DocumentNode | undefined} document
|
|
158
|
+
* @param {unknown} requestSource
|
|
159
|
+
* @param {string | null | undefined} operationName
|
|
160
|
+
* @param {boolean} calculateSignature
|
|
161
|
+
* @param {boolean} validated
|
|
162
|
+
* @param {RequestCache | undefined} requestCache
|
|
163
|
+
*/
|
|
164
|
+
function refineRequestSpan (
|
|
165
|
+
requestSpan,
|
|
166
|
+
document,
|
|
167
|
+
requestSource,
|
|
168
|
+
operationName,
|
|
169
|
+
calculateSignature,
|
|
170
|
+
validated,
|
|
171
|
+
requestCache
|
|
172
|
+
) {
|
|
173
|
+
/* istanbul ignore if: validate only refines after the request span and parsed document exist. */
|
|
174
|
+
if (!requestSpan || requestSpan.ddRequestRefined || !document) return
|
|
141
175
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
176
|
+
const operation = getRequestOperation(document, operationName, calculateSignature)
|
|
177
|
+
if (operation === undefined) return
|
|
178
|
+
|
|
179
|
+
const { signature, type, name } = operation
|
|
180
|
+
refineRequestSpanMetadata(requestSpan, signature, type, name)
|
|
181
|
+
|
|
182
|
+
if (!validated || !requestCache || !isCacheableSource(requestSource)) return
|
|
183
|
+
cacheRequestOperation(
|
|
184
|
+
requestCache,
|
|
185
|
+
requestSource,
|
|
186
|
+
operationName,
|
|
187
|
+
calculateSignature,
|
|
188
|
+
operation,
|
|
189
|
+
document
|
|
190
|
+
)
|
|
155
191
|
}
|
|
156
192
|
|
|
157
193
|
/**
|
|
158
|
-
* @param {
|
|
159
|
-
*
|
|
160
|
-
* @param {string | undefined} operationName
|
|
194
|
+
* @param {RequestCache} requestCache
|
|
195
|
+
* @param {string | object} source
|
|
196
|
+
* @param {string | null | undefined} operationName
|
|
197
|
+
* @param {boolean} calculateSignature
|
|
161
198
|
* @param {RequestOperation} operation
|
|
199
|
+
* @param {import('graphql').DocumentNode} document
|
|
162
200
|
*/
|
|
163
|
-
function cacheRequestOperation (
|
|
201
|
+
function cacheRequestOperation (
|
|
202
|
+
requestCache,
|
|
203
|
+
source,
|
|
204
|
+
operationName,
|
|
205
|
+
calculateSignature,
|
|
206
|
+
operation,
|
|
207
|
+
document
|
|
208
|
+
) {
|
|
209
|
+
const operationKey = operationCacheKey(operationName, calculateSignature)
|
|
210
|
+
const documentRef = new WeakRef(document)
|
|
164
211
|
if (typeof source === 'string') {
|
|
165
|
-
|
|
212
|
+
requestCache.operations.set(`${operationKey}\n${source}`, operation)
|
|
213
|
+
requestCache.documents.set(source, documentRef)
|
|
166
214
|
return
|
|
167
215
|
}
|
|
168
216
|
|
|
169
|
-
let
|
|
170
|
-
if (
|
|
171
|
-
|
|
172
|
-
|
|
217
|
+
let cached = requestCache.documentOperations.get(source)
|
|
218
|
+
if (cached === undefined) {
|
|
219
|
+
cached = {
|
|
220
|
+
document: documentRef,
|
|
221
|
+
operations: new Map(),
|
|
222
|
+
}
|
|
223
|
+
requestCache.documentOperations.set(source, cached)
|
|
224
|
+
} else {
|
|
225
|
+
cached.document = documentRef
|
|
226
|
+
}
|
|
227
|
+
cached.operations.set(operationKey, operation)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* @param {import('graphql').DocumentNode} document
|
|
232
|
+
* @param {string | null | undefined} operationName
|
|
233
|
+
* @param {boolean} calculateSignature
|
|
234
|
+
* @returns {RequestOperation | undefined}
|
|
235
|
+
*/
|
|
236
|
+
function getRequestOperation (document, operationName, calculateSignature) {
|
|
237
|
+
const operation = getOperation(document, operationName)
|
|
238
|
+
if (operation === undefined) return
|
|
239
|
+
|
|
240
|
+
const type = operation.operation
|
|
241
|
+
const name = operation.name?.value
|
|
242
|
+
return {
|
|
243
|
+
signature: getSignature(document, name, type, calculateSignature),
|
|
244
|
+
type,
|
|
245
|
+
name,
|
|
173
246
|
}
|
|
174
|
-
operations.set(operationName, operation)
|
|
175
247
|
}
|
|
176
248
|
|
|
177
249
|
/**
|
|
@@ -268,8 +340,25 @@ function isApolloHealthCheck (operation) {
|
|
|
268
340
|
selection.directives?.length === 0
|
|
269
341
|
}
|
|
270
342
|
|
|
343
|
+
/**
|
|
344
|
+
* @param {import('graphql').GraphQLOutputType} type
|
|
345
|
+
* @returns {string}
|
|
346
|
+
*/
|
|
347
|
+
function getBaseTypeName (type) {
|
|
348
|
+
let current = type
|
|
349
|
+
while ('ofType' in current) current = current.ofType
|
|
350
|
+
return current.name
|
|
351
|
+
}
|
|
352
|
+
|
|
271
353
|
let tools
|
|
272
354
|
|
|
355
|
+
/**
|
|
356
|
+
* @param {import('graphql').DocumentNode} document
|
|
357
|
+
* @param {string | undefined} operationName
|
|
358
|
+
* @param {import('graphql').OperationTypeNode} operationType
|
|
359
|
+
* @param {boolean} [calculate]
|
|
360
|
+
* @returns {string}
|
|
361
|
+
*/
|
|
273
362
|
function getSignature (document, operationName, operationType, calculate) {
|
|
274
363
|
if (calculate !== false && tools !== false) {
|
|
275
364
|
try {
|
|
@@ -286,22 +375,21 @@ function getSignature (document, operationName, operationType, calculate) {
|
|
|
286
375
|
}
|
|
287
376
|
}
|
|
288
377
|
|
|
289
|
-
if (
|
|
290
|
-
|
|
291
|
-
return `${operationType} ${operationName}`
|
|
292
|
-
}
|
|
293
|
-
return operationType
|
|
378
|
+
if (operationName) {
|
|
379
|
+
return `${operationType} ${operationName}`
|
|
294
380
|
}
|
|
295
|
-
|
|
296
|
-
return operationName ?? ''
|
|
381
|
+
return operationType
|
|
297
382
|
}
|
|
298
383
|
|
|
299
384
|
module.exports = {
|
|
300
385
|
extractErrorIntoSpanEvent,
|
|
386
|
+
getBaseTypeName,
|
|
301
387
|
getCachedRequestOperation,
|
|
302
388
|
getOperation,
|
|
389
|
+
getRequestCache,
|
|
303
390
|
getSignature,
|
|
304
391
|
isApolloHealthCheck,
|
|
305
392
|
isApolloHealthCheckSource,
|
|
306
393
|
refineRequestSpan,
|
|
394
|
+
refineRequestSpanMetadata,
|
|
307
395
|
}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { storage } = require('../../datadog-core')
|
|
4
3
|
const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
|
|
5
4
|
const GraphQLParsePlugin = require('./parse')
|
|
6
5
|
const { extractErrorIntoSpanEvent, isApolloHealthCheck, refineRequestSpan } = require('./utils')
|
|
7
6
|
|
|
8
|
-
const legacyStorage = storage('legacy')
|
|
9
|
-
|
|
10
7
|
class GraphQLValidatePlugin extends TracingPlugin {
|
|
11
8
|
static id = 'graphql'
|
|
12
9
|
static operation = 'validate'
|
|
@@ -28,26 +25,6 @@ class GraphQLValidatePlugin extends TracingPlugin {
|
|
|
28
25
|
const docSource = document ? GraphQLParsePlugin.documentSources.get(document) : undefined
|
|
29
26
|
const source = this.config.source && document && docSource
|
|
30
27
|
|
|
31
|
-
// Refine the top-level graphql.request span (mercurius) from the parsed
|
|
32
|
-
// document. validate is the first boundary that has it and precedes both
|
|
33
|
-
// execute and any pre-execute rejection (unknown field, GET mutation), so a
|
|
34
|
-
// failing request still ends up with a resource and operation tags. The
|
|
35
|
-
// request span, its operation name, and the raw source ride the active
|
|
36
|
-
// store the request boundary entered (validate's own `ctx.currentStore` is
|
|
37
|
-
// not populated yet). The cache is keyed by that raw source, not the parsed
|
|
38
|
-
// document — for a pre-parsed AST mercurius validates a structuredClone, so
|
|
39
|
-
// the document here is a different object from the one the boundary saw and
|
|
40
|
-
// recovers on the warm path. No-op for graphql-js/apollo/yoga, which never
|
|
41
|
-
// open a request span.
|
|
42
|
-
const requestStore = legacyStorage.getStore()
|
|
43
|
-
refineRequestSpan(
|
|
44
|
-
requestStore?.graphqlRequestSpan,
|
|
45
|
-
document,
|
|
46
|
-
requestStore?.graphqlRequestSource,
|
|
47
|
-
requestStore?.graphqlRequestOperationName,
|
|
48
|
-
this.config.signature
|
|
49
|
-
)
|
|
50
|
-
|
|
51
28
|
this.startSpan('graphql.validate', {
|
|
52
29
|
service: this.config.service,
|
|
53
30
|
type: 'graphql',
|
|
@@ -68,6 +45,16 @@ class GraphQLValidatePlugin extends TracingPlugin {
|
|
|
68
45
|
const errors = ctx.result
|
|
69
46
|
const span = ctx?.currentStore?.span || this.activeSpan
|
|
70
47
|
|
|
48
|
+
refineRequestSpan(
|
|
49
|
+
ctx.currentStore?.graphqlRequestSpan,
|
|
50
|
+
document,
|
|
51
|
+
ctx.currentStore?.graphqlRequestSource,
|
|
52
|
+
ctx.currentStore?.graphqlRequestOperationName,
|
|
53
|
+
this.config.signature,
|
|
54
|
+
!errors?.length,
|
|
55
|
+
ctx.currentStore?.graphqlRequestCache
|
|
56
|
+
)
|
|
57
|
+
|
|
71
58
|
if (errors?.length) {
|
|
72
59
|
span.setTag('error', errors[0])
|
|
73
60
|
for (const err of errors) {
|
|
@@ -113,7 +113,6 @@ class JestPlugin extends CiPlugin {
|
|
|
113
113
|
hasUnskippableSuites,
|
|
114
114
|
hasForcedToRunSuites,
|
|
115
115
|
error,
|
|
116
|
-
isTestSessionFinalizationError,
|
|
117
116
|
isEarlyFlakeDetectionEnabled,
|
|
118
117
|
isEarlyFlakeDetectionFaulty,
|
|
119
118
|
isTestManagementTestsEnabled,
|
|
@@ -124,9 +123,6 @@ class JestPlugin extends CiPlugin {
|
|
|
124
123
|
this.testModuleSpan.setTag(TEST_STATUS, status)
|
|
125
124
|
|
|
126
125
|
if (error) {
|
|
127
|
-
if (isTestSessionFinalizationError) {
|
|
128
|
-
this.tracer._exporter.setDeferredTestSuiteError?.(error)
|
|
129
|
-
}
|
|
130
126
|
this.testSessionSpan.setTag('error', error)
|
|
131
127
|
this.testModuleSpan.setTag('error', error)
|
|
132
128
|
}
|
|
@@ -163,7 +159,6 @@ class JestPlugin extends CiPlugin {
|
|
|
163
159
|
this.testSessionSpan.setTag(TEST_MANAGEMENT_ENABLED, 'true')
|
|
164
160
|
}
|
|
165
161
|
|
|
166
|
-
this.tracer._exporter.exportDeferredTestSuiteSpans?.()
|
|
167
162
|
this.testModuleSpan.finish()
|
|
168
163
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
|
|
169
164
|
this.testSessionSpan.finish()
|
|
@@ -334,7 +329,6 @@ class JestPlugin extends CiPlugin {
|
|
|
334
329
|
this.pendingTestSuiteFinishes.add(pendingFinish)
|
|
335
330
|
|
|
336
331
|
const finish = () => {
|
|
337
|
-
this.tracer._exporter.deferTestSuiteSpan?.(testSuiteSpan)
|
|
338
332
|
testSuiteSpan.finish()
|
|
339
333
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
|
|
340
334
|
// Suites potentially run in a different process than the session,
|
|
@@ -31,6 +31,13 @@ class BaseLangChainTracingPlugin extends TracingPlugin {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
error (ctx) {
|
|
35
|
+
// NodeInterrupt or GraphInterrupt "errors" for control flow, do not mark as real errors
|
|
36
|
+
if (ctx.error?.is_bubble_up) return
|
|
37
|
+
|
|
38
|
+
super.error(ctx)
|
|
39
|
+
}
|
|
40
|
+
|
|
34
41
|
bindStart (ctx) {
|
|
35
42
|
// TODO(bengl): All this renaming is just so we don't have to change the existing handlers
|
|
36
43
|
ctx.args = ctx.arguments
|