dd-trace 5.124.0 → 5.126.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.
Files changed (208) hide show
  1. package/LICENSE-3rdparty.csv +3 -1
  2. package/ci/init.js +8 -3
  3. package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
  4. package/ci/test-optimization-validation/ci-remediation.js +6 -6
  5. package/ci/test-optimization-validation/command-blocker.js +7 -5
  6. package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
  7. package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
  8. package/ci/test-optimization-validation/package-check.js +1 -1
  9. package/ci/test-optimization-validation/report-writer.js +7 -5
  10. package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
  11. package/ci/vitest-no-worker-init-setup.mjs +13 -13
  12. package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
  13. package/index.d.ts +8 -0
  14. package/package.json +13 -10
  15. package/packages/datadog-esbuild/index.js +3 -1
  16. package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
  17. package/packages/datadog-instrumentations/src/fs.js +24 -20
  18. package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
  19. package/packages/datadog-instrumentations/src/graphql.js +16 -16
  20. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
  21. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  22. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +265 -0
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
  26. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
  30. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +14 -5
  31. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +251 -0
  32. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +11 -1
  33. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
  34. package/packages/datadog-instrumentations/src/jest.js +166 -45
  35. package/packages/datadog-instrumentations/src/knex.js +76 -0
  36. package/packages/datadog-instrumentations/src/mariadb.js +6 -0
  37. package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
  38. package/packages/datadog-instrumentations/src/mocha/main.js +66 -20
  39. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
  40. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
  41. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
  42. package/packages/datadog-instrumentations/src/net.js +14 -17
  43. package/packages/datadog-instrumentations/src/next.js +212 -62
  44. package/packages/datadog-instrumentations/src/openai.js +42 -40
  45. package/packages/datadog-instrumentations/src/pg.js +15 -0
  46. package/packages/datadog-instrumentations/src/playwright.js +113 -13
  47. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
  48. package/packages/datadog-instrumentations/src/sequelize.js +94 -2
  49. package/packages/datadog-instrumentations/src/undici.js +53 -4
  50. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +37 -3
  51. package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
  52. package/packages/datadog-instrumentations/src/vitest-worker.js +71 -14
  53. package/packages/datadog-instrumentations/src/webdriverio.js +965 -6
  54. package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
  55. package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
  56. package/packages/datadog-plugin-amqp10/src/util.js +2 -1
  57. package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
  58. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
  59. package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
  60. package/packages/datadog-plugin-cucumber/src/index.js +0 -2
  61. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
  62. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
  63. package/packages/datadog-plugin-graphql/src/execute.js +998 -196
  64. package/packages/datadog-plugin-graphql/src/index.js +27 -7
  65. package/packages/datadog-plugin-graphql/src/jit.js +136 -0
  66. package/packages/datadog-plugin-graphql/src/request.js +18 -27
  67. package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
  68. package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
  69. package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
  70. package/packages/datadog-plugin-graphql/src/utils.js +208 -120
  71. package/packages/datadog-plugin-graphql/src/validate.js +10 -23
  72. package/packages/datadog-plugin-jest/src/index.js +0 -6
  73. package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
  74. package/packages/datadog-plugin-mocha/src/index.js +403 -75
  75. package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
  76. package/packages/datadog-plugin-mysql/src/index.js +41 -15
  77. package/packages/datadog-plugin-net/src/tcp.js +3 -1
  78. package/packages/datadog-plugin-next/src/index.js +63 -29
  79. package/packages/datadog-plugin-playwright/src/index.js +167 -27
  80. package/packages/datadog-plugin-undici/src/index.js +27 -41
  81. package/packages/datadog-plugin-vitest/src/index.js +6 -12
  82. package/packages/dd-trace/src/aiguard/evaluation.js +2 -1
  83. package/packages/dd-trace/src/aiguard/messages/anthropic.js +8 -1
  84. package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
  85. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
  86. package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
  87. package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
  88. package/packages/dd-trace/src/appsec/graphql.js +6 -2
  89. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
  90. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
  91. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
  92. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +3 -1
  93. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +531 -95
  94. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
  95. package/packages/dd-trace/src/appsec/lambda.js +44 -26
  96. package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
  97. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
  98. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
  99. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
  100. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +5 -173
  101. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
  102. package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
  103. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
  104. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +394 -225
  105. package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
  106. package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
  107. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
  108. package/packages/dd-trace/src/ci-visibility/final-flush.js +2 -1
  109. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
  110. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
  111. package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -0
  112. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
  113. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +180 -0
  114. package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
  115. package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
  116. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
  117. package/packages/dd-trace/src/config/index.js +73 -13
  118. package/packages/dd-trace/src/config/parsers.js +11 -1
  119. package/packages/dd-trace/src/config/stable.js +3 -3
  120. package/packages/dd-trace/src/config/supported-configurations.json +22 -2
  121. package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
  122. package/packages/dd-trace/src/debugger/config.js +12 -1
  123. package/packages/dd-trace/src/debugger/constants.js +1 -0
  124. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
  125. package/packages/dd-trace/src/debugger/devtools_client/request-options.js +28 -0
  126. package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -40
  127. package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
  128. package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
  129. package/packages/dd-trace/src/debugger/index.js +23 -3
  130. package/packages/dd-trace/src/dogstatsd.js +510 -76
  131. package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
  132. package/packages/dd-trace/src/encode/0.4.js +5 -5
  133. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
  134. package/packages/dd-trace/src/evp_proxy/direct.js +3 -25
  135. package/packages/dd-trace/src/exporters/agent/index.js +2 -1
  136. package/packages/dd-trace/src/exporters/agent/writer.js +24 -7
  137. package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
  138. package/packages/dd-trace/src/exporters/agentless/writer.js +103 -119
  139. package/packages/dd-trace/src/exporters/common/agents.js +28 -6
  140. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
  141. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
  142. package/packages/dd-trace/src/exporters/common/proxy.js +52 -0
  143. package/packages/dd-trace/src/exporters/common/request.js +42 -14
  144. package/packages/dd-trace/src/exporters/common/url.js +40 -1
  145. package/packages/dd-trace/src/exporters/common/writer.js +9 -7
  146. package/packages/dd-trace/src/flush.js +6 -3
  147. package/packages/dd-trace/src/id.js +14 -0
  148. package/packages/dd-trace/src/index.js +5 -2
  149. package/packages/dd-trace/src/llmobs/experiments/dataset.js +2 -1
  150. package/packages/dd-trace/src/llmobs/index.js +32 -32
  151. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
  152. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
  153. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
  154. package/packages/dd-trace/src/llmobs/util.js +0 -33
  155. package/packages/dd-trace/src/llmobs/writers/base.js +49 -32
  156. package/packages/dd-trace/src/log/index.js +7 -1
  157. package/packages/dd-trace/src/{ci-visibility/log-submission → log-submission}/log-submission-plugin.js +48 -19
  158. package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
  159. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  160. package/packages/dd-trace/src/opentelemetry/logs/index.js +5 -24
  161. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
  162. package/packages/dd-trace/src/opentelemetry/metrics/index.js +15 -22
  163. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +14 -4
  164. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +10 -0
  165. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +5 -0
  166. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
  167. package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
  168. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  169. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
  170. package/packages/dd-trace/src/opentracing/propagation/text_map.js +101 -20
  171. package/packages/dd-trace/src/payload-tagging/index.js +2 -1
  172. package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
  173. package/packages/dd-trace/src/plugins/tracing.js +12 -10
  174. package/packages/dd-trace/src/plugins/util/ci.js +3 -1
  175. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
  176. package/packages/dd-trace/src/plugins/util/test.js +83 -20
  177. package/packages/dd-trace/src/plugins/util/url.js +2 -2
  178. package/packages/dd-trace/src/plugins/util/web.js +4 -9
  179. package/packages/dd-trace/src/priority_sampler.js +4 -0
  180. package/packages/dd-trace/src/profiling/config.js +1 -0
  181. package/packages/dd-trace/src/profiling/oom.js +7 -2
  182. package/packages/dd-trace/src/profiling/profiler.js +23 -13
  183. package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
  184. package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
  185. package/packages/dd-trace/src/proxy.js +75 -5
  186. package/packages/dd-trace/src/remote_config/index.js +319 -101
  187. package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
  188. package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
  189. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -1
  190. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -1
  191. package/packages/dd-trace/src/sampling_rule.js +39 -10
  192. package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +4 -8
  193. package/packages/dd-trace/src/serverless/vercel.js +53 -20
  194. package/packages/dd-trace/src/serverless.js +10 -3
  195. package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
  196. package/packages/dd-trace/src/span_processor.js +18 -3
  197. package/packages/dd-trace/src/span_stats.js +24 -28
  198. package/packages/dd-trace/src/startup-log.js +3 -2
  199. package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
  200. package/packages/dd-trace/src/telemetry/send-data.js +23 -20
  201. package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
  202. package/packages/dd-trace/src/tracer.js +4 -5
  203. package/packages/dd-trace/src/tracer_metadata.js +11 -3
  204. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  205. package/vendor/dist/protobufjs/index.js +1 -1
  206. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  207. package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
  208. package/packages/dd-trace/src/encode/agentless-json.js +0 -210
@@ -5,8 +5,10 @@ const { DD_MAJOR } = require('../../../version')
5
5
  const CompositePlugin = require('../../dd-trace/src/plugins/composite')
6
6
  const log = require('../../dd-trace/src/log')
7
7
  const GraphQLExecutePlugin = require('./execute')
8
+ const GraphQLJitExecutePlugin = require('./jit')
8
9
  const GraphQLParsePlugin = require('./parse')
9
10
  const GraphQLRequestPlugin = require('./request')
11
+ const { GraphQLResolveErrorPlugin, GraphQLToolsResolveErrorPlugin } = require('./resolve-error')
10
12
  const GraphQLValidatePlugin = require('./validate')
11
13
 
12
14
  class GraphQLPlugin extends CompositePlugin {
@@ -14,16 +16,16 @@ class GraphQLPlugin extends CompositePlugin {
14
16
  static get plugins () {
15
17
  return {
16
18
  execute: GraphQLExecutePlugin,
19
+ jit: GraphQLJitExecutePlugin,
17
20
  parse: GraphQLParsePlugin,
18
21
  // Top-level request span for drivers (mercurius) that funnel through a
19
22
  // single entry point and parse/execute internally. graphql-js, apollo,
20
23
  // and yoga produce no such channel, so the plugin simply never fires for
21
24
  // them.
22
25
  request: GraphQLRequestPlugin,
26
+ resolveError: GraphQLResolveErrorPlugin,
27
+ toolsResolveError: GraphQLToolsResolveErrorPlugin,
23
28
  validate: GraphQLValidatePlugin,
24
- // resolve plugin is absorbed into execute: per-field data is recorded
25
- // synchronously in wrapResolve, and all graphql.resolve spans are
26
- // materialized at execute end.
27
29
  }
28
30
  }
29
31
 
@@ -93,13 +95,31 @@ const noopHooks = { execute: noop, parse: noop, validate: noop, resolve: undefin
93
95
  function getHooks ({ hooks }) {
94
96
  if (!hooks) return noopHooks
95
97
  return {
96
- execute: hooks.execute ?? noop,
97
- parse: hooks.parse ?? noop,
98
- validate: hooks.validate ?? noop,
98
+ execute: guardHook('execute', hooks.execute) ?? noop,
99
+ parse: guardHook('parse', hooks.parse) ?? noop,
100
+ validate: guardHook('validate', hooks.validate) ?? noop,
99
101
  // No noop fallback: `resolve` runs per-field (hot path); the plugin
100
102
  // gates with `if (this.config.hooks.resolve)` so the absent-hook case
101
103
  // skips both the call and the payload-object allocation.
102
- resolve: hooks.resolve,
104
+ resolve: guardHook('resolve', hooks.resolve),
105
+ }
106
+ }
107
+
108
+ /**
109
+ * @param {'execute' | 'parse' | 'resolve' | 'validate'} name
110
+ * @param {((...args: unknown[]) => unknown) | undefined} hook
111
+ * @returns {((...args: unknown[]) => unknown) | undefined}
112
+ */
113
+ function guardHook (name, hook) {
114
+ if (typeof hook !== 'function') return
115
+
116
+ const errorMessage = `Error in the graphql ${name} hook`
117
+ return function () {
118
+ try {
119
+ return hook.apply(this, arguments)
120
+ } catch (error) {
121
+ log.error(errorMessage, error)
122
+ }
103
123
  }
104
124
  }
105
125
 
@@ -0,0 +1,136 @@
1
+ 'use strict'
2
+
3
+ const { storage } = require('../../datadog-core')
4
+ const createGraphqlJitRuntime = require('../../datadog-instrumentations/src/helpers/graphql-jit-runtime')
5
+ const GraphQLExecutePlugin = require('./execute')
6
+ const { getBaseTypeName } = require('./utils')
7
+
8
+ const {
9
+ readJitDefaultInScope,
10
+ recordJitResolverError,
11
+ resolveCompiledJitField,
12
+ resolveJitDefaultInvocation,
13
+ unwrapJitResolve,
14
+ wrapJitResolve,
15
+ } = GraphQLExecutePlugin
16
+
17
+ /**
18
+ * @typedef {import('graphql').ExecutionArgs} ExecutionArguments
19
+ * @typedef {import('../../datadog-instrumentations/src/helpers/graphql-jit-runtime').JitPlan} JitPlan
20
+ * @typedef {Record<string, import('graphql').GraphQLFieldResolver<unknown, unknown>>} GraphQLResolverMap
21
+ * @typedef {{
22
+ * arguments: [unknown, unknown, Record<string, unknown> | undefined],
23
+ * currentStore: Record<string, unknown>,
24
+ * ddArgs?: ExecutionArguments,
25
+ * ddDocument: import('graphql').DocumentNode,
26
+ * ddOperationName?: string,
27
+ * ddPlan?: JitPlan,
28
+ * ddResolvers?: GraphQLResolverMap,
29
+ * ddSchema: import('graphql').GraphQLSchema
30
+ * }} JitExecutionContext
31
+ */
32
+
33
+ const graphqlJitCompilePrefix = 'tracing:orchestrion:graphql-jit:compile'
34
+ const legacyStorage = storage('legacy')
35
+ const patchedResolverMaps = new WeakSet()
36
+
37
+ const { configureCompilationContext, runtime: jitRuntime } = createGraphqlJitRuntime({
38
+ createFieldMetadata,
39
+ readDefaultInScope: readJitDefaultInScope,
40
+ recordResolverError: recordJitResolverError,
41
+ resolveDefaultInvocation: resolveJitDefaultInvocation,
42
+ resolveField: resolveCompiledJitField,
43
+ startExecution,
44
+ unwrapResolver: unwrapJitResolve,
45
+ wrapResolver: wrapJitResolve,
46
+ })
47
+
48
+ class GraphQLJitExecutePlugin extends GraphQLExecutePlugin {
49
+ static prefix = 'tracing:orchestrion:graphql-jit:apm:graphql:execute'
50
+ static extraPrefixes = []
51
+
52
+ addTraceSubs () {
53
+ super.addTraceSubs()
54
+ this.addSub(`${graphqlJitCompilePrefix}:end`, configureCompilationContext)
55
+ }
56
+
57
+ /**
58
+ * @param {JitExecutionContext} ctx
59
+ * @returns {ExecutionArguments}
60
+ */
61
+ readExecutionArgs (ctx) {
62
+ const [rootValue, contextValue, variableValues] = ctx.arguments
63
+ return {
64
+ schema: ctx.ddSchema,
65
+ document: ctx.ddDocument,
66
+ rootValue,
67
+ contextValue,
68
+ variableValues,
69
+ operationName: ctx.ddOperationName,
70
+ }
71
+ }
72
+
73
+ /**
74
+ * @param {JitExecutionContext} ctx
75
+ * @param {ExecutionArguments} args
76
+ */
77
+ wrapExecutionResolvers (ctx, args) {
78
+ const { ddResolvers: resolvers } = ctx
79
+ if (ctx.ddPlan === undefined && resolvers && !patchedResolverMaps.has(resolvers)) {
80
+ patchedResolverMaps.add(resolvers)
81
+ for (const name of Object.keys(resolvers)) {
82
+ resolvers[name] = wrapJitResolve(resolvers[name])
83
+ }
84
+ }
85
+ ctx.ddArgs = args
86
+ }
87
+
88
+ /**
89
+ * @param {JitExecutionContext} ctx
90
+ * @param {unknown} _contextValue
91
+ * @param {object} rootCtx
92
+ */
93
+ storeRootContext (ctx, _contextValue, rootCtx) {
94
+ ctx.currentStore.graphqlRootCtx = rootCtx
95
+ }
96
+ }
97
+
98
+ /**
99
+ * @param {Record<string, unknown> | undefined} variableValues
100
+ * @returns {object | undefined}
101
+ */
102
+ function startExecution (variableValues) {
103
+ const rootCtx = legacyStorage.getStore()?.graphqlRootCtx
104
+ if (!rootCtx) return
105
+
106
+ rootCtx.variableValues = variableValues
107
+ rootCtx.jitRuntime = jitRuntime
108
+ return rootCtx
109
+ }
110
+
111
+ /**
112
+ * @param {string} parentTypeName
113
+ * @param {string} fieldName
114
+ * @param {import('graphql').GraphQLOutputType} returnType
115
+ * @param {string} collapsedPath
116
+ * @returns {{
117
+ * baseTypeName: string,
118
+ * resource: string,
119
+ * tags: Record<string, string>
120
+ * }}
121
+ */
122
+ function createFieldMetadata (parentTypeName, fieldName, returnType, collapsedPath) {
123
+ const baseTypeName = getBaseTypeName(returnType)
124
+ return {
125
+ baseTypeName,
126
+ resource: `${fieldName}:${returnType}`,
127
+ tags: {
128
+ 'graphql.field.coordinates': `${parentTypeName}.${fieldName}`,
129
+ 'graphql.field.name': fieldName,
130
+ 'graphql.field.path': collapsedPath,
131
+ 'graphql.field.type': baseTypeName,
132
+ },
133
+ }
134
+ }
135
+
136
+ module.exports = GraphQLJitExecutePlugin
@@ -1,12 +1,20 @@
1
1
  'use strict'
2
2
 
3
3
  const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
4
- const { extractErrorIntoSpanEvent, getCachedRequestOperation, isApolloHealthCheckSource } = require('./utils')
4
+ const {
5
+ extractErrorIntoSpanEvent,
6
+ getCachedRequestOperation,
7
+ getRequestCache,
8
+ isApolloHealthCheckSource,
9
+ } = require('./utils')
10
+
11
+ /** @typedef {ReturnType<typeof getRequestCache>} RequestCache */
5
12
 
6
13
  /**
7
14
  * @typedef {object} GraphQLRequestStore
8
15
  * @property {import('../../dd-trace/src/opentracing/span')} [span]
9
16
  * @property {import('../../dd-trace/src/opentracing/span')} [graphqlRequestSpan]
17
+ * @property {RequestCache} [graphqlRequestCache]
10
18
  * @property {string} [graphqlRequestOperationName]
11
19
  * @property {unknown} [graphqlRequestSource]
12
20
  */
@@ -17,24 +25,25 @@ const { extractErrorIntoSpanEvent, getCachedRequestOperation, isApolloHealthChec
17
25
  * @property {GraphQLRequestStore} [currentStore]
18
26
  * @property {GraphQLRequestStore} [parentStore]
19
27
  * @property {boolean} [ddSkipped]
28
+ * @property {boolean | number} [ddCacheLimit]
20
29
  * @property {{ errors?: import('graphql').GraphQLError[] }} [result]
30
+ * @property {object} [self]
21
31
  */
22
32
 
23
33
  // Top-level GraphQL request span for drivers that funnel every operation
24
34
  // through a single entry point but parse/validate/execute internally (mercurius
25
35
  // today). It parents the `graphql.parse`/`graphql.validate`/`graphql.execute`
26
36
  // (or JIT) sub-spans and carries the request text + operation name/type, which
27
- // otherwise have no home when the query is JIT-compiled and `graphql.execute`
28
- // never fires.
37
+ // otherwise have no home if GraphQL-JIT instrumentation is unavailable.
29
38
  //
30
39
  // The entry boundary only hands us the raw `source` (string or pre-parsed AST)
31
40
  // and `operationName`; the parsed document — and therefore the precise
32
41
  // operation signature — is only known once mercurius parses internally. On the
33
42
  // cold path the `validate` sub-plugin refines the resource/operation tags onto
34
43
  // this span via `ctx.currentStore.graphqlRequestSpan` once the document is
35
- // available, so we never re-parse on the hot path. On the JIT warm path no
36
- // sub-span fires, so we recover the same tags from the cache the cold path
37
- // populated, keyed by source + operationName.
44
+ // available. The normal and JIT execute plugins use the same refinement path
45
+ // when present. A bounded cache preserves the metadata when a warm JIT query
46
+ // skips validation and GraphQL-JIT instrumentation is unavailable or disabled.
38
47
  class GraphQLRequestPlugin extends TracingPlugin {
39
48
  static id = 'graphql'
40
49
  static operation = 'request'
@@ -55,25 +64,13 @@ class GraphQLRequestPlugin extends TracingPlugin {
55
64
  }
56
65
 
57
66
  const operationName = ctx.arguments?.[3]
67
+ const requestCache = getRequestCache(ctx.self, ctx.ddCacheLimit)
58
68
 
59
- // `source` is the request text on the common path, but mercurius also
60
- // accepts a pre-parsed document AST; only a string is the query text, and
61
- // `graphql.source` carries only the text form.
62
69
  const docSource = typeof source === 'string' ? source : undefined
63
-
64
- // Warm (JIT-compiled) path: execute never fires, so recover the operation
65
- // signature/type the cold path cached, keyed by source + operationName —
66
- // by query text for a string, by document identity for a pre-parsed AST.
67
- // Empty on the cold path — validate hasn't refined yet — where the request
68
- // span is refined from the parsed document instead.
69
- const cached = getCachedRequestOperation(source, operationName)
70
+ const cached = getCachedRequestOperation(source, operationName, this.config.signature, requestCache)
70
71
 
71
72
  const span = this.startSpan(this.operationName({ id: 'request' }), {
72
73
  service: this.config.service || this.serviceName(),
73
- // The cached signature is the precise resource; otherwise provisional and
74
- // refined by the validate sub-plugin once the document is parsed.
75
- // `operationName` is the best name at the boundary; falls back to the
76
- // operation signature once validate sees the document.
77
74
  resource: cached?.signature || operationName || undefined,
78
75
  kind: this.constructor.kind,
79
76
  type: this.constructor.type,
@@ -84,14 +81,8 @@ class GraphQLRequestPlugin extends TracingPlugin {
84
81
  },
85
82
  }, ctx)
86
83
 
87
- // Hand the span, the requested operation name, and the raw source to the
88
- // validate sub-plugin running inside this store so it can refine the
89
- // resource + operation tags from the parsed document (validate is the first
90
- // boundary that has it) and cache them keyed by the source the request
91
- // boundary saw. The raw source is the cache key — validate sees mercurius's
92
- // internally parsed document, not the caller's source, and for a pre-parsed
93
- // AST the two are different objects.
94
84
  ctx.currentStore.graphqlRequestSpan = span
85
+ ctx.currentStore.graphqlRequestCache = requestCache
95
86
  ctx.currentStore.graphqlRequestOperationName = operationName
96
87
  ctx.currentStore.graphqlRequestSource = source
97
88
 
@@ -0,0 +1,108 @@
1
+ 'use strict'
2
+
3
+ const { storage } = require('../../datadog-core')
4
+ const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
5
+
6
+ const legacyStorage = storage('legacy')
7
+ const normalizedFalsyErrors = new WeakSet()
8
+
9
+ class GraphQLResolveErrorPlugin extends TracingPlugin {
10
+ static id = 'graphql'
11
+ static prefix = 'tracing:orchestrion:graphql:apm:graphql:resolve:error'
12
+
13
+ /**
14
+ * @param {{ arguments?: unknown[], result?: unknown }} ctx
15
+ */
16
+ start (ctx) {
17
+ const args = ctx.arguments
18
+ if (args?.length === 1) return
19
+
20
+ const error = args?.[0]
21
+ const errorPath = error?.path
22
+ const path = Array.isArray(errorPath) ? errorPath : args?.[2]
23
+ if (path) {
24
+ recordResolveErrorForPath(normalizedFalsyErrors.delete(error) ? undefined : error, path, args?.[1])
25
+ }
26
+ }
27
+
28
+ /**
29
+ * @param {{ arguments?: unknown[], result?: unknown }} ctx
30
+ */
31
+ end (ctx) {
32
+ const args = ctx.arguments
33
+ if (args?.length !== 1 || args[0]) return
34
+
35
+ const error = ctx.result
36
+ if (error !== null && typeof error === 'object') normalizedFalsyErrors.add(error)
37
+ }
38
+ }
39
+
40
+ class GraphQLToolsResolveErrorPlugin extends TracingPlugin {
41
+ static id = 'graphql'
42
+ static prefix = 'tracing:orchestrion:@graphql-tools/executor:apm:graphql:resolve:error'
43
+
44
+ /**
45
+ * @param {{ arguments?: unknown[] }} ctx
46
+ */
47
+ start (ctx) {
48
+ const error = ctx.arguments?.[0]
49
+ if (error?.path) {
50
+ recordResolveErrorForPath(error, error.path)
51
+ }
52
+ }
53
+
54
+ // `start` owns attribution while the normalized error path is available.
55
+ error () {}
56
+ }
57
+
58
+ /**
59
+ * @param {unknown} error
60
+ * @param {(string | number)[]} path
61
+ * @param {readonly object[]} [fieldNodes]
62
+ */
63
+ function recordResolveErrorForPath (error, path, fieldNodes) {
64
+ const fieldNode = fieldNodes?.[0]
65
+ const rootCtx = legacyStorage.getStore()?.graphqlRootCtx
66
+ for (let field = rootCtx?.resolveFields; field; field = field.nextResolveField) {
67
+ if ((!fieldNode || field.fieldNode === fieldNode) && matchesPath(field.pathString, path)) {
68
+ recordResolveError(field, error)
69
+ return
70
+ }
71
+ }
72
+ }
73
+
74
+ /**
75
+ * @param {string} fieldPath
76
+ * @param {(string | number)[]} path
77
+ * @returns {boolean}
78
+ */
79
+ function matchesPath (fieldPath, path) {
80
+ let pathString = ''
81
+ for (let index = 0; index < path.length; index++) {
82
+ if (index !== 0) pathString += '.'
83
+ pathString += typeof path[index] === 'number' ? '*' : path[index]
84
+ }
85
+ return pathString === fieldPath
86
+ }
87
+
88
+ /**
89
+ * @param {object} field
90
+ * @param {unknown} error
91
+ */
92
+ function recordResolveError (field, error) {
93
+ if (field.error !== undefined) return
94
+
95
+ const recordedError = error || new Error('GraphQL resolver rejected without an error')
96
+ field.error = recordedError
97
+ field.span.setTag('error', recordedError)
98
+ if (field.resolveHookContext) {
99
+ field.resolveHookContext.error = recordedError
100
+ field.resolveHookContext.result = undefined
101
+ }
102
+ }
103
+
104
+ module.exports = {
105
+ GraphQLResolveErrorPlugin,
106
+ GraphQLToolsResolveErrorPlugin,
107
+ recordResolveError,
108
+ }
@@ -6,8 +6,7 @@ const transforms = require('./transforms')
6
6
  // Apollo Server / Yoga / Mercurius hand back the same parsed `DocumentNode`
7
7
  // from their own document caches per execute, so memoizing the signature on
8
8
  // the document keeps the visit/print pipeline off the hot path. The inner
9
- // Map keys on operationName since `separateOperations` picks a different
10
- // sub-document for each operation.
9
+ // Map keys on operationName since each operation has a different sub-document.
11
10
  const cache = new WeakMap()
12
11
 
13
12
  function defaultEngineReportingSignature (ast, operationName) {
@@ -8,11 +8,53 @@ const printer = ddGlobal.graphql_printer
8
8
  const utilities = ddGlobal.graphql_utilities
9
9
 
10
10
  function dropUnusedDefinitions (ast, operationName) {
11
- const separated = utilities.separateOperations(ast)[operationName]
12
- if (!separated) {
11
+ const operation = utilities.getOperationAST(ast, operationName)
12
+ if (!operation) {
13
13
  return ast
14
14
  }
15
- return separated
15
+
16
+ const fragments = new Map()
17
+ for (const definition of ast.definitions) {
18
+ if (definition.kind === 'FragmentDefinition') {
19
+ fragments.set(definition.name.value, definition)
20
+ }
21
+ }
22
+
23
+ const dependencies = new Set()
24
+ collectFragmentDependencies(operation.selectionSet, fragments, dependencies)
25
+
26
+ const definitions = []
27
+ for (const definition of ast.definitions) {
28
+ if (definition === operation ||
29
+ (definition.kind === 'FragmentDefinition' && dependencies.has(definition.name.value))) {
30
+ definitions.push(definition)
31
+ }
32
+ }
33
+
34
+ return { kind: ast.kind, definitions }
35
+ }
36
+
37
+ /**
38
+ * @param {import('graphql').SelectionSetNode} selectionSet
39
+ * @param {Map<string, import('graphql').FragmentDefinitionNode>} fragments
40
+ * @param {Set<string>} dependencies
41
+ */
42
+ function collectFragmentDependencies (selectionSet, fragments, dependencies) {
43
+ visitor.visit(selectionSet, {
44
+ /**
45
+ * @param {import('graphql').FragmentSpreadNode} node
46
+ */
47
+ FragmentSpread (node) {
48
+ const name = node.name.value
49
+ if (dependencies.has(name)) return
50
+
51
+ dependencies.add(name)
52
+ const fragment = fragments.get(name)
53
+ if (fragment) {
54
+ collectFragmentDependencies(fragment.selectionSet, fragments, dependencies)
55
+ }
56
+ },
57
+ })
16
58
  }
17
59
 
18
60
  // One walk replaces Apollo's `hideLiterals` + `removeAliases` + `sortAST` +