dd-trace 5.108.0 → 5.110.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 (174) hide show
  1. package/ci/init.js +4 -3
  2. package/index.d.ts +88 -25
  3. package/loader-hook.mjs +88 -2
  4. package/package.json +19 -14
  5. package/packages/datadog-esbuild/index.js +26 -0
  6. package/packages/datadog-esbuild/src/utils.js +46 -3
  7. package/packages/datadog-instrumentations/src/ai.js +43 -48
  8. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js-context-methods.js +18 -0
  9. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +111 -0
  10. package/packages/datadog-instrumentations/src/aws-sdk.js +3 -1
  11. package/packages/datadog-instrumentations/src/connect.js +4 -3
  12. package/packages/datadog-instrumentations/src/electron.js +1 -1
  13. package/packages/datadog-instrumentations/src/fastify.js +4 -12
  14. package/packages/datadog-instrumentations/src/graphql.js +17 -6
  15. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
  16. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  17. package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
  18. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
  19. package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
  20. package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/aws-durable-execution-sdk-js.js +31 -0
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
  24. package/packages/datadog-instrumentations/src/http/client.js +12 -2
  25. package/packages/datadog-instrumentations/src/ioredis.js +0 -1
  26. package/packages/datadog-instrumentations/src/iovalkey.js +1 -2
  27. package/packages/datadog-instrumentations/src/koa.js +3 -2
  28. package/packages/datadog-instrumentations/src/mariadb.js +4 -2
  29. package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
  30. package/packages/datadog-instrumentations/src/next.js +44 -10
  31. package/packages/datadog-instrumentations/src/openai.js +77 -18
  32. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
  33. package/packages/datadog-instrumentations/src/playwright.js +1 -1
  34. package/packages/datadog-instrumentations/src/redis.js +0 -1
  35. package/packages/datadog-instrumentations/src/restify.js +4 -3
  36. package/packages/datadog-instrumentations/src/router.js +18 -5
  37. package/packages/datadog-instrumentations/src/selenium.js +1 -2
  38. package/packages/datadog-instrumentations/src/tedious.js +28 -0
  39. package/packages/datadog-instrumentations/src/vitest.js +323 -7
  40. package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
  41. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/checkpoint.js +31 -0
  42. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +55 -0
  43. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +114 -0
  44. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +128 -0
  45. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/index.js +19 -0
  46. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +224 -0
  47. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +43 -0
  48. package/packages/datadog-plugin-aws-sdk/src/base.js +14 -16
  49. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
  50. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +100 -37
  51. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +108 -99
  52. package/packages/datadog-plugin-bullmq/src/filter.js +35 -0
  53. package/packages/datadog-plugin-bullmq/src/producer.js +84 -4
  54. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +9 -1
  55. package/packages/datadog-plugin-fs/src/index.js +1 -0
  56. package/packages/datadog-plugin-http/src/client.js +8 -3
  57. package/packages/datadog-plugin-http2/src/client.js +5 -2
  58. package/packages/datadog-plugin-mysql/src/index.js +7 -2
  59. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
  60. package/packages/datadog-plugin-redis/src/index.js +1 -2
  61. package/packages/datadog-plugin-undici/src/index.js +5 -2
  62. package/packages/datadog-plugin-vitest/src/index.js +11 -2
  63. package/packages/datadog-webpack/index.js +17 -1
  64. package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
  65. package/packages/dd-trace/src/aiguard/channels.js +0 -1
  66. package/packages/dd-trace/src/aiguard/index.js +11 -49
  67. package/packages/dd-trace/src/aiguard/integrations/evaluate.js +46 -0
  68. package/packages/dd-trace/src/aiguard/integrations/openai.js +66 -0
  69. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +78 -0
  70. package/packages/{datadog-instrumentations/src/helpers/ai-messages.js → dd-trace/src/aiguard/messages/openai.js} +85 -193
  71. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +185 -0
  72. package/packages/dd-trace/src/aiguard/sdk.js +13 -1
  73. package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
  74. package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
  75. package/packages/dd-trace/src/appsec/channels.js +1 -0
  76. package/packages/dd-trace/src/appsec/downstream_requests.js +111 -58
  77. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +54 -12
  78. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +5 -1
  79. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +29 -4
  80. package/packages/dd-trace/src/appsec/index.js +10 -6
  81. package/packages/dd-trace/src/appsec/rasp/ssrf.js +19 -11
  82. package/packages/dd-trace/src/appsec/reporter.js +1 -0
  83. package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
  84. package/packages/dd-trace/src/appsec/telemetry/index.js +26 -0
  85. package/packages/dd-trace/src/ci-visibility/requests/request.js +1 -17
  86. package/packages/dd-trace/src/config/defaults.js +22 -3
  87. package/packages/dd-trace/src/config/generated-config-types.d.ts +538 -1
  88. package/packages/dd-trace/src/config/helper.js +133 -33
  89. package/packages/dd-trace/src/config/index.js +4 -1
  90. package/packages/dd-trace/src/config/parsers.js +19 -1
  91. package/packages/dd-trace/src/config/supported-configurations.json +45 -5
  92. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +49 -35
  93. package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
  94. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
  95. package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
  96. package/packages/dd-trace/src/debugger/index.js +5 -0
  97. package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
  98. package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
  99. package/packages/dd-trace/src/dogstatsd.js +15 -8
  100. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
  101. package/packages/dd-trace/src/exporters/agentless/index.js +7 -5
  102. package/packages/dd-trace/src/exporters/agentless/intake.js +43 -0
  103. package/packages/dd-trace/src/exporters/agentless/writer.js +5 -4
  104. package/packages/dd-trace/src/exporters/common/request.js +1 -20
  105. package/packages/dd-trace/src/exporters/common/url.js +34 -0
  106. package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
  107. package/packages/dd-trace/src/guardrails/index.js +8 -5
  108. package/packages/dd-trace/src/index.js +6 -4
  109. package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
  110. package/packages/dd-trace/src/llmobs/index.js +16 -5
  111. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
  112. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
  113. package/packages/dd-trace/src/llmobs/sdk.js +5 -3
  114. package/packages/dd-trace/src/llmobs/span_processor.js +4 -0
  115. package/packages/dd-trace/src/llmobs/tagger.js +55 -0
  116. package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
  117. package/packages/dd-trace/src/log/index.js +18 -20
  118. package/packages/dd-trace/src/openfeature/flagging_provider.js +3 -1
  119. package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
  120. package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
  121. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +4 -3
  122. package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
  123. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
  124. package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
  125. package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
  126. package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
  127. package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
  128. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +11 -4
  129. package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
  130. package/packages/dd-trace/src/opentelemetry/span.js +14 -3
  131. package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
  132. package/packages/dd-trace/src/opentelemetry/trace/index.js +2 -1
  133. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
  134. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
  135. package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
  136. package/packages/dd-trace/src/opentracing/span_context.js +17 -0
  137. package/packages/dd-trace/src/plugin_manager.js +7 -1
  138. package/packages/dd-trace/src/plugins/ci_plugin.js +36 -3
  139. package/packages/dd-trace/src/plugins/index.js +3 -0
  140. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
  141. package/packages/dd-trace/src/plugins/util/test.js +80 -0
  142. package/packages/dd-trace/src/plugins/util/url.js +68 -1
  143. package/packages/dd-trace/src/plugins/util/web.js +19 -36
  144. package/packages/dd-trace/src/priority_sampler.js +1 -13
  145. package/packages/dd-trace/src/profiling/config.js +129 -198
  146. package/packages/dd-trace/src/profiling/constants.js +4 -7
  147. package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
  148. package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
  149. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
  150. package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
  151. package/packages/dd-trace/src/profiling/oom.js +71 -0
  152. package/packages/dd-trace/src/profiling/profiler.js +35 -23
  153. package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
  154. package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
  155. package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
  156. package/packages/dd-trace/src/ritm.js +4 -2
  157. package/packages/dd-trace/src/serverless.js +1 -2
  158. package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
  159. package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +12 -0
  160. package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +12 -0
  161. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
  162. package/packages/dd-trace/src/span_format.js +2 -1
  163. package/packages/dd-trace/src/span_processor.js +6 -0
  164. package/packages/dd-trace/src/telemetry/send-data.js +12 -10
  165. package/packages/dd-trace/src/tracer.js +15 -1
  166. package/packages/dd-trace/src/util.js +14 -0
  167. package/register.js +58 -1
  168. package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
  169. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  170. package/vendor/dist/pprof-format/index.js +1 -1
  171. package/vendor/dist/protobufjs/index.js +1 -1
  172. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  173. package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +0 -284
  174. package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
@@ -0,0 +1,65 @@
1
+ 'use strict'
2
+
3
+ const path = require('node:path')
4
+
5
+ // Build-time half of the optional-peer mechanism shared by the webpack and esbuild plugins.
6
+ //
7
+ // Runtime files load an optional peer through `requireOptionalPeer('name')` (see
8
+ // `require-optional-peer.js`), which bundlers cannot follow, so a build that does not opt into
9
+ // the feature never pulls in the peer's (possibly optional) dependency chain (#8635). When the
10
+ // peer is installed at build time the user has opted in, so the plugins rewrite the call into a
11
+ // literal `require('name')` and let the bundler inline the peer, which keeps it working after
12
+ // the bundle is relocated to a tree without the peer on disk (#8980). Peers that are absent at
13
+ // build time stay opaque, so the rewrite is a no-op and the #8635 guarantee holds.
14
+
15
+ // Files that load an optional peer this way, as suffixes of the resolved module path. The same
16
+ // suffix matches the repo layout and `node_modules/dd-trace`. Add a file here to extend the
17
+ // mechanism to a new optional peer; no plugin change is needed.
18
+ const OPTIONAL_PEER_FILES = [
19
+ 'packages/dd-trace/src/openfeature/flagging_provider.js',
20
+ ]
21
+
22
+ // Captures the peer name from `requireOptionalPeer('name')` / `requireOptionalPeer("name")`.
23
+ const OPTIONAL_PEER_LOAD = /requireOptionalPeer\((['"])(.+?)\1\)/g
24
+
25
+ // esbuild's `onLoad` needs a path filter; match the basenames so the callback only fires for
26
+ // the candidate files, then `matchesOptionalPeerFile` confirms the full suffix on a normalized
27
+ // path (basenames carry no separators, so the filter is OS-agnostic).
28
+ const OPTIONAL_PEER_FILTER = new RegExp(
29
+ `(?:${OPTIONAL_PEER_FILES.map((file) => path.basename(file).replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`)).join('|')})$`
30
+ )
31
+
32
+ /**
33
+ * @param {string} normalizedResource - Resolved module path with forward slashes
34
+ * @returns {boolean} Whether the module is one of the optional-peer loader files
35
+ */
36
+ function matchesOptionalPeerFile (normalizedResource) {
37
+ return OPTIONAL_PEER_FILES.some((suffix) => normalizedResource.endsWith(suffix))
38
+ }
39
+
40
+ /**
41
+ * Rewrites each `requireOptionalPeer('name')` whose peer resolves from `fromDir` into a literal
42
+ * `require('name')` so the bundler inlines it. Peers that do not resolve stay opaque, so a build
43
+ * without the peer keeps the #8635 guarantee.
44
+ *
45
+ * @param {string} source - Source of an optional-peer loader file
46
+ * @param {string} fromDir - Directory to resolve the optional peers from
47
+ * @returns {string} Source with installed optional peers turned into literal requires
48
+ */
49
+ function rewriteOptionalPeerLoads (source, fromDir) {
50
+ return source.replaceAll(OPTIONAL_PEER_LOAD, (match, _quote, request) => {
51
+ try {
52
+ require.resolve(request, { paths: [fromDir] })
53
+ } catch {
54
+ return match
55
+ }
56
+ return `require('${request}')`
57
+ })
58
+ }
59
+
60
+ module.exports = {
61
+ OPTIONAL_PEER_FILES,
62
+ OPTIONAL_PEER_FILTER,
63
+ matchesOptionalPeerFile,
64
+ rewriteOptionalPeerLoads,
65
+ }
@@ -13,8 +13,9 @@ const Hook = require('./hook')
13
13
  const { isRelativeRequire } = require('./shared-utils')
14
14
  const rewriter = require('./rewriter')
15
15
 
16
- const DD_TRACE_DISABLED_INSTRUMENTATIONS = getValueFromEnvSources('DD_TRACE_DISABLED_INSTRUMENTATIONS') || ''
17
- const DD_TRACE_DEBUG = getValueFromEnvSources('DD_TRACE_DEBUG') || ''
16
+ const DD_TRACE_DISABLED_INSTRUMENTATIONS =
17
+ getValueFromEnvSources('DD_TRACE_DISABLED_INSTRUMENTATIONS')
18
+ const DD_TRACE_DEBUG = getValueFromEnvSources('DD_TRACE_DEBUG')
18
19
 
19
20
  const hooks = require('./hooks')
20
21
  const instrumentations = require('./instrumentations')
@@ -36,8 +37,9 @@ if (!disabledInstrumentations.has('process')) {
36
37
  require('../process')
37
38
  }
38
39
 
39
- if (DD_TRACE_DEBUG && DD_TRACE_DEBUG.toLowerCase() !== 'false') {
40
- checkRequireCache.checkForRequiredModules()
40
+ const debugEnabled = DD_TRACE_DEBUG
41
+ checkRequireCache.checkForRequiredModules()
42
+ if (debugEnabled) {
41
43
  setImmediate(checkRequireCache.checkForPotentialConflicts)
42
44
  }
43
45
 
@@ -0,0 +1,17 @@
1
+ 'use strict'
2
+
3
+ // Loads an optional peer through a require bundlers cannot follow: the package name is an
4
+ // argument, not a `require('literal')`, so a build that does not install the peer never pulls
5
+ // in its (possibly optional) dependency chain (#8635). When the peer is installed at build
6
+ // time, the webpack and esbuild plugins rewrite `requireOptionalPeer('name')` into a literal
7
+ // `require('name')` so the peer is bundled and survives the bundle being relocated without it
8
+ // on disk (#8980). See `optional-peer-bundler.js` for the build-time half.
9
+
10
+ /**
11
+ * @param {string} request - Module specifier of the optional peer
12
+ */
13
+ module.exports = function requireOptionalPeer (request) {
14
+ // eslint-disable-next-line camelcase, no-undef
15
+ const runtimeRequire = typeof __non_webpack_require__ === 'function' ? __non_webpack_require__ : require
16
+ return runtimeRequire(request)
17
+ }
@@ -0,0 +1,31 @@
1
+ 'use strict'
2
+
3
+ const contextMethods = require('../../../aws-durable-execution-sdk-js-context-methods')
4
+
5
+ const baseModule = { name: '@aws/durable-execution-sdk-js', versionRange: '>=1.1.0' }
6
+
7
+ const buildEntries = filePath => {
8
+ const module = { ...baseModule, filePath }
9
+ return [
10
+ {
11
+ module,
12
+ functionQuery: { functionName: 'runHandler', kind: 'Async' },
13
+ channelName: 'withDurableExecution',
14
+ },
15
+ ...contextMethods.map(methodName => ({
16
+ module,
17
+ functionQuery: { className: 'DurableContextImpl', methodName, kind: 'Sync' },
18
+ channelName: `DurableContextImpl_${methodName}`,
19
+ })),
20
+ {
21
+ module,
22
+ functionQuery: { className: 'CheckpointManager', methodName: 'checkpoint', kind: 'Async' },
23
+ channelName: 'CheckpointManager_checkpoint',
24
+ },
25
+ ]
26
+ }
27
+
28
+ module.exports = [
29
+ ...buildEntries('dist/index.mjs'),
30
+ ...buildEntries('dist-cjs/index.js'),
31
+ ]
@@ -8,4 +8,5 @@ module.exports = [
8
8
  ...require('./langgraph'),
9
9
  ...require('./modelcontextprotocol-sdk'),
10
10
  ...require('./playwright'),
11
+ ...require('./aws-durable-execution-sdk-js'),
11
12
  ]
@@ -3,9 +3,25 @@ import { rewrite } from './index.js'
3
3
  async function load (url, context, nextLoad) {
4
4
  const result = await nextLoad(url, context)
5
5
 
6
- result.source = rewrite(result.source, url, context.format)
6
+ return rewriteResult(result, url, context)
7
+ }
8
+
9
+ function loadSync (url, context, nextLoad) {
10
+ const result = nextLoad(url, context)
11
+
12
+ return rewriteResult(result, url, context)
13
+ }
14
+
15
+ function rewriteResult (result, url, context) {
16
+ const format = result.format || context.format
17
+
18
+ // CommonJS source is rewritten by Module._compile. Rewriting it here too
19
+ // double-instruments CommonJS entrypoints loaded through sync hooks.
20
+ if (format === 'commonjs') return result
21
+
22
+ result.source = rewrite(result.source, url, format)
7
23
 
8
24
  return result
9
25
  }
10
26
 
11
- export { load }
27
+ export { load, loadSync }
@@ -14,6 +14,7 @@ const finishChannel = channel('apm:http:client:request:finish')
14
14
  const endChannel = channel('apm:http:client:request:end')
15
15
  const asyncStartChannel = channel('apm:http:client:request:asyncStart')
16
16
  const errorChannel = channel('apm:http:client:request:error')
17
+ const responseStartChannel = channel('apm:http:client:response:start')
17
18
  const responseFinishChannel = channel('apm:http:client:response:finish')
18
19
 
19
20
  addHook({ name: 'http' }, hookFn)
@@ -78,10 +79,15 @@ function setupResponseInstrumentation (ctx, res) {
78
79
  let dataReadStarted = false
79
80
 
80
81
  const { shouldCollectBody } = ctx
81
- const bodyChunks = shouldCollectBody ? [] : null
82
+
83
+ let bodyChunks = null
84
+
85
+ if (shouldCollectBody) {
86
+ bodyChunks = []
87
+ }
82
88
 
83
89
  const collectChunk = chunk => {
84
- if (!shouldCollectBody || !chunk) return
90
+ if (!bodyChunks || !chunk) return
85
91
 
86
92
  if (typeof chunk === 'string') {
87
93
  bodyChunks.push(chunk)
@@ -229,6 +235,10 @@ function patch (http, methodName) {
229
235
  res.once('end', finish)
230
236
  res.once(errorMonitor, finish)
231
237
 
238
+ if (responseStartChannel.hasSubscribers) {
239
+ responseStartChannel.publish({ ctx, res })
240
+ }
241
+
232
242
  const instrumentation = setupResponseInstrumentation(ctx, res)
233
243
 
234
244
  if (!instrumentation) {
@@ -26,7 +26,6 @@ function wrapRedis (Redis) {
26
26
  }
27
27
 
28
28
  const ctx = {
29
- db: options.db,
30
29
  command: command.name,
31
30
  args: command.args,
32
31
  connectionOptions,
@@ -18,10 +18,9 @@ addHook({ name: 'iovalkey', versions: ['>=0.0.1'] }, Valkey => {
18
18
 
19
19
  const options = this.options || {}
20
20
  const connectionName = options.connectionName
21
- const db = options.db
22
21
  const connectionOptions = { host: options.host, port: options.port }
23
22
 
24
- const ctx = { db, command: command.name, args: command.args, connectionOptions, connectionName }
23
+ const ctx = { command: command.name, args: command.args, connectionOptions, connectionName }
25
24
  return startCh.runStores(ctx, () => {
26
25
  command.promise.then(() => finish(finishCh, errorCh, ctx), err => finish(finishCh, errorCh, ctx, err))
27
26
 
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const shimmer = require('../../datadog-shimmer')
4
- const { addHook, channel } = require('./helpers/instrument')
4
+ const { addHook, channel, createErrorPublisher } = require('./helpers/instrument')
5
5
 
6
6
  const enterChannel = channel('apm:koa:middleware:enter')
7
7
  const exitChannel = channel('apm:koa:middleware:exit')
@@ -10,6 +10,7 @@ const nextChannel = channel('apm:koa:middleware:next')
10
10
  const finishChannel = channel('apm:koa:middleware:finish')
11
11
  const handleChannel = channel('apm:koa:request:handle')
12
12
  const routeChannel = channel('apm:koa:request:route')
13
+ const publishError = createErrorPublisher(errorChannel)
13
14
 
14
15
  const originals = new WeakMap()
15
16
 
@@ -132,7 +133,7 @@ function fulfill (ctx, error) {
132
133
  const route = ctx.routePath
133
134
 
134
135
  if (error) {
135
- errorChannel.publish({ req, error })
136
+ publishError({ req, error })
136
137
  }
137
138
 
138
139
  // TODO: make sure that the parent class cannot override this in `enter`
@@ -160,14 +160,16 @@ addHook({ name, file: 'lib/cmd/execute.js', versions: ['>=3'] }, (Execute) => {
160
160
  return wrapCommand(Execute)
161
161
  })
162
162
 
163
- // in 3.4.1 getConnection method start to use callbacks instead of promises
163
+ // mariadb 3.4.1 refactored the pool: getConnection switched from promises to
164
+ // callbacks and _createConnection was renamed to _createPoolConnection.
164
165
  addHook({ name, file: 'lib/pool.js', versions: ['>=3.4.1'] }, (Pool) => {
165
166
  shimmer.wrap(Pool.prototype, 'getConnection', wrapPoolGetConnectionMethod)
167
+ shimmer.wrap(Pool.prototype, '_createPoolConnection', wrapPoolMethod)
166
168
 
167
169
  return Pool
168
170
  })
169
171
 
170
- addHook({ name, file: 'lib/pool.js', versions: ['>=3'] }, (Pool) => {
172
+ addHook({ name, file: 'lib/pool.js', versions: ['>=3 <3.4.1'] }, (Pool) => {
171
173
  shimmer.wrap(Pool.prototype, '_createConnection', wrapPoolMethod)
172
174
 
173
175
  return Pool
@@ -1093,9 +1093,9 @@ addHook({
1093
1093
  shimmer.wrap(BufferedWorkerPool.prototype, 'run', run => async function (testSuiteAbsolutePath, workerArgs) {
1094
1094
  if (!testFinishCh.hasSubscribers ||
1095
1095
  (!config.isKnownTestsEnabled &&
1096
- !config.isTestManagementTestsEnabled &&
1097
- !config.isImpactedTestsEnabled &&
1098
- !config.isFlakyTestRetriesEnabled)) {
1096
+ !config.isTestManagementTestsEnabled &&
1097
+ !config.isImpactedTestsEnabled &&
1098
+ !config.isFlakyTestRetriesEnabled)) {
1099
1099
  return run.apply(this, arguments)
1100
1100
  }
1101
1101
 
@@ -141,19 +141,19 @@ function instrument (req, res, handler, error) {
141
141
  requests.add(req)
142
142
 
143
143
  const ctx = { req, res }
144
- // Parse query parameters from request URL
145
144
  if (queryParsedChannel.hasSubscribers && req.url) {
146
- // req.url is only the relative path (/foo?bar=baz) and new URL() needs a full URL
147
- // so we give it a dummy base
148
- const { searchParams } = new URL(req.url, 'http://dummy')
149
- const query = {}
150
- for (const key of searchParams.keys()) {
151
- if (!query[key]) {
152
- query[key] = searchParams.getAll(key)
145
+ const queryIndex = req.url.indexOf('?')
146
+ if (queryIndex !== -1) {
147
+ const searchParams = new URLSearchParams(req.url.slice(queryIndex + 1))
148
+ const query = {}
149
+ for (const key of searchParams.keys()) {
150
+ if (!query[key]) {
151
+ query[key] = searchParams.getAll(key)
152
+ }
153
153
  }
154
- }
155
154
 
156
- queryParsedChannel.publish({ query })
155
+ queryParsedChannel.publish({ query })
156
+ }
157
157
  }
158
158
 
159
159
  return startChannel.runStores(ctx, () => {
@@ -220,6 +220,40 @@ addHook({
220
220
  return NextRequestAdapter
221
221
  })
222
222
 
223
+ // From next 15.4.1 each app-route build inlines its own copy of `fromNodeNextRequest`, so the hook
224
+ // above no longer maps the node request to the app-route NextRequest and a thrown handler error
225
+ // cannot reach `finish`. The app-route runtime reports real errors (redirect/notFound and other
226
+ // control-flow signals excluded by next) through `RouteModule.onRequestError`, which next loads from
227
+ // a precompiled `app-route*.runtime.{dev,prod}.js` bundle regardless of how the route chunk is
228
+ // bundled. The bundler/experimental part of the name is matched with a pattern rather than
229
+ // enumerated, so a variant next adds later is picked up without a code change.
230
+ const patchedAppRouteModules = new WeakSet()
231
+
232
+ function wrapOnRequestError (onRequestError) {
233
+ return function (req, error) {
234
+ if (error) {
235
+ errorChannel.publish({ error })
236
+ }
237
+ return onRequestError.apply(this, arguments)
238
+ }
239
+ }
240
+
241
+ function instrumentAppRouteRuntime (runtime) {
242
+ const AppRouteRouteModule = runtime.AppRouteRouteModule
243
+ const proto = AppRouteRouteModule?.prototype
244
+ if (proto && typeof proto.onRequestError === 'function' && !patchedAppRouteModules.has(AppRouteRouteModule)) {
245
+ patchedAppRouteModules.add(AppRouteRouteModule)
246
+ shimmer.wrap(proto, 'onRequestError', wrapOnRequestError)
247
+ }
248
+ return runtime
249
+ }
250
+
251
+ addHook({
252
+ name: 'next',
253
+ versions: ['>=15.4.1'],
254
+ filePattern: String.raw`dist/compiled/next-server/app-route[\w-]*\.runtime\.(?:dev|prod)\.js$`,
255
+ }, instrumentAppRouteRuntime)
256
+
223
257
  addHook({
224
258
  name: 'next',
225
259
  versions: ['>=11.1'],
@@ -3,11 +3,52 @@
3
3
  const dc = require('dc-polyfill')
4
4
  const shimmer = require('../../datadog-shimmer')
5
5
  const { addHook } = require('./helpers/instrument')
6
- const aiGuard = require('./helpers/openai-ai-guard')
7
6
 
8
7
  const ch = dc.tracingChannel('apm:openai:request')
9
8
  const onStreamedChunkCh = dc.channel('apm:openai:request:chunk')
10
9
 
10
+ // Provider lifecycle channels. Payloads stay OpenAI-native:
11
+ // before { args, parentSpan, abortController, pending }
12
+ // after { args, body, parentSpan, abortController, pending }
13
+ const chatCompletionsBeforeChannel = dc.channel('dd-trace:openai:chat.completions:before')
14
+ const chatCompletionsAfterChannel = dc.channel('dd-trace:openai:chat.completions:after')
15
+ const responsesBeforeChannel = dc.channel('dd-trace:openai:responses:before')
16
+ const responsesAfterChannel = dc.channel('dd-trace:openai:responses:after')
17
+
18
+ const LIFECYCLE_CHANNELS = {
19
+ 'chat.completions': {
20
+ before: chatCompletionsBeforeChannel,
21
+ after: chatCompletionsAfterChannel,
22
+ },
23
+ responses: {
24
+ before: responsesBeforeChannel,
25
+ after: responsesAfterChannel,
26
+ },
27
+ }
28
+
29
+ /**
30
+ * Publishes a provider-native lifecycle payload to a cancelable lifecycle channel.
31
+ *
32
+ * Subscribers push async work into `pending` synchronously during publication and
33
+ * abort `abortController` with an error before the pushed promise resolves to block.
34
+ *
35
+ * @param {object} channel
36
+ * @param {object} payload
37
+ * @returns {Promise<void>}
38
+ */
39
+ function publishLifecycle (channel, payload) {
40
+ const abortController = new AbortController()
41
+ const ctx = { ...payload, abortController, pending: [] }
42
+
43
+ channel.publish(ctx)
44
+
45
+ return Promise.all(ctx.pending).then(() => {
46
+ if (abortController.signal.aborted) {
47
+ throw abortController.signal.reason
48
+ }
49
+ })
50
+ }
51
+
11
52
  const V4_PACKAGE_SHIMS = [
12
53
  {
13
54
  file: 'resources/chat/completions',
@@ -217,17 +258,15 @@ for (const extension of extensions) {
217
258
 
218
259
  for (const methodName of methods) {
219
260
  shimmer.wrap(targetPrototype, methodName, methodFn => function (...args) {
220
- if (!ch.start.hasSubscribers && !aiGuard.hasSubscribers()) {
221
- return methodFn.apply(this, args)
222
- }
223
261
  // The OpenAI library lets you set `stream: true` on the options arg to any method
224
262
  // However, we only want to handle streamed responses in specific cases
225
263
  // chat.completions and completions
226
264
  const stream = streamedResponse && getOption(args, 'stream', false)
227
265
 
228
- const guard = aiGuard.createGuard(baseResource, args[0], stream)
266
+ const channels = stream ? null : LIFECYCLE_CHANNELS[baseResource]
267
+ const hasLifecycle = !!channels && (channels.before.hasSubscribers || channels.after.hasSubscribers)
229
268
 
230
- if (!ch.start.hasSubscribers && !guard) {
269
+ if (!ch.start.hasSubscribers && !hasLifecycle) {
231
270
  return methodFn.apply(this, args)
232
271
  }
233
272
 
@@ -240,12 +279,22 @@ for (const extension of extensions) {
240
279
  }
241
280
 
242
281
  return ch.start.runStores(ctx, () => {
243
- // Explicit childOf rather than async-context: the _thenUnwrap/parse path
244
- // decouples the lazy evaluation from the active scope at call time.
245
- if (guard) guard.parentSpan = ctx.currentStore?.span
282
+ // Capture the parent span explicitly: the _thenUnwrap/parse path decouples
283
+ // the lazy evaluation from the active scope at call time.
284
+ const parentSpan = hasLifecycle ? ctx.currentStore?.span : undefined
246
285
 
247
286
  const apiProm = methodFn.apply(this, args)
248
287
 
288
+ const beforeChannel = hasLifecycle && channels.before.hasSubscribers ? channels.before : null
289
+ const afterChannel = hasLifecycle && channels.after.hasSubscribers ? channels.after : null
290
+ let beforeVerdict
291
+ const getBeforeVerdict = beforeChannel
292
+ ? function getBeforeVerdict () {
293
+ beforeVerdict ??= publishLifecycle(beforeChannel, { args, parentSpan })
294
+ return beforeVerdict
295
+ }
296
+ : null
297
+
249
298
  if (baseResource === 'chat.completions' && typeof apiProm._thenUnwrap === 'function') {
250
299
  // this should only ever be invoked from a client.beta.chat.completions.parse call
251
300
  shimmer.wrap(apiProm, '_thenUnwrap', origApiPromThenUnwrap => function (...args) {
@@ -259,7 +308,9 @@ for (const extension of extensions) {
259
308
  const parsedPromise = origApiPromParse.apply(this, args)
260
309
  .then(body => Promise.all([this.responsePromise, body]))
261
310
 
262
- return handleUnwrappedAPIPromise(parsedPromise, ctx, stream, guard)
311
+ return handleUnwrappedAPIPromise(
312
+ parsedPromise, ctx, stream, getBeforeVerdict, afterChannel, parentSpan
313
+ )
263
314
  })
264
315
 
265
316
  return unwrappedPromise
@@ -272,10 +323,16 @@ for (const extension of extensions) {
272
323
  const parsedPromise = origApiPromParse.apply(this, args)
273
324
  .then(body => Promise.all([this.responsePromise, body]))
274
325
 
275
- return handleUnwrappedAPIPromise(parsedPromise, ctx, stream, guard)
326
+ return handleUnwrappedAPIPromise(parsedPromise, ctx, stream, getBeforeVerdict, afterChannel, parentSpan)
276
327
  })
277
328
 
278
- if (guard) aiGuard.wrapAsResponse(apiProm, guard)
329
+ // Gate `.asResponse()` callers on the before verdict so raw-response paths still block.
330
+ if (beforeChannel && typeof apiProm.asResponse === 'function') {
331
+ shimmer.wrap(apiProm, 'asResponse', origAsResponse => function (...args) {
332
+ const responsePromise = origAsResponse.apply(this, args)
333
+ return Promise.all([getBeforeVerdict(), responsePromise]).then(([, response]) => response)
334
+ })
335
+ }
279
336
 
280
337
  ch.end.publish(ctx)
281
338
 
@@ -288,10 +345,12 @@ for (const extension of extensions) {
288
345
  }
289
346
  }
290
347
 
291
- function handleUnwrappedAPIPromise (apiProm, ctx, stream, guard) {
292
- const guardedApiProm = guard ? aiGuard.gateParse(apiProm, guard) : apiProm
348
+ function handleUnwrappedAPIPromise (apiProm, ctx, stream, getBeforeVerdict, afterChannel, parentSpan) {
349
+ const gatedApiProm = getBeforeVerdict
350
+ ? Promise.all([getBeforeVerdict(), apiProm]).then(([, result]) => result)
351
+ : apiProm
293
352
 
294
- return guardedApiProm
353
+ return gatedApiProm
295
354
  .then(([{ response, options }, body]) => {
296
355
  if (stream) {
297
356
  if (body.iterator) {
@@ -313,14 +372,14 @@ function handleUnwrappedAPIPromise (apiProm, ctx, stream, guard) {
313
372
  },
314
373
  }
315
374
 
316
- if (!guard) {
375
+ if (!afterChannel) {
317
376
  finish(ctx, responseData)
318
377
  return body
319
378
  }
320
379
 
321
380
  // Finish after evaluation so a block propagates the error to openai.request
322
- // and the span wraps its ai_guard child instead of closing before it.
323
- return aiGuard.evaluateOutput(guard, body).then(() => {
381
+ // and the span wraps its child instead of closing before it.
382
+ return publishLifecycle(afterChannel, { args: ctx.args, body, parentSpan }).then(() => {
324
383
  finish(ctx, responseData)
325
384
  return body
326
385
  })
@@ -3,7 +3,6 @@
3
3
  const shimmer = require('../../datadog-shimmer')
4
4
  const tracer = require('../../dd-trace')
5
5
  const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
6
- const { isFalse, isTrue } = require('../../dd-trace/src/util')
7
6
  const { addHook } = require('./helpers/instrument')
8
7
 
9
8
  if (isOtelSdkEnabled()) {
@@ -21,9 +20,11 @@ if (isOtelSdkEnabled()) {
21
20
 
22
21
  function isOtelSdkEnabled () {
23
22
  // Datadog explicit opt-out wins over every OTel signal; check it first.
24
- const ddTraceOtelEnabled = getValueFromEnvSources('DD_TRACE_OTEL_ENABLED')
25
- if (isFalse(ddTraceOtelEnabled)) return false
26
- const otelSdkDisabled = getValueFromEnvSources('OTEL_SDK_DISABLED')
27
- if (isTrue(otelSdkDisabled)) return false
28
- return isTrue(ddTraceOtelEnabled) || isFalse(otelSdkDisabled)
23
+ // skipDefault: an unset option must stay undefined so the OTel signal still decides — the
24
+ // registered defaults (false / true) would otherwise force-disable before that check.
25
+ const ddTraceOtelEnabled = getValueFromEnvSources('DD_TRACE_OTEL_ENABLED', true)
26
+ if (ddTraceOtelEnabled === false) return false
27
+ const otelSdkDisabled = getValueFromEnvSources('OTEL_SDK_DISABLED', true)
28
+ if (otelSdkDisabled) return false
29
+ return ddTraceOtelEnabled || otelSdkDisabled === false
29
30
  }
@@ -59,7 +59,7 @@ const testSuiteToTestStatuses = new Map()
59
59
  const testSuiteToErrors = new Map()
60
60
  const testsToTestStatuses = new Map()
61
61
 
62
- const RUM_FLUSH_WAIT_TIME = Number(getValueFromEnvSources('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS')) || 500
62
+ const RUM_FLUSH_WAIT_TIME = getValueFromEnvSources('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS')
63
63
  const DD_PROPERTIES_TIMEOUT = 5000
64
64
 
65
65
  let applyRepeatEachIndex = null
@@ -147,7 +147,6 @@ function getStartCtx (client, command, args, argsStartIndex) {
147
147
  }
148
148
 
149
149
  return {
150
- db: client.selected_db,
151
150
  command,
152
151
  args,
153
152
  argsStartIndex,
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const shimmer = require('../../datadog-shimmer')
4
- const { addHook, channel } = require('./helpers/instrument')
4
+ const { addHook, channel, createErrorPublisher } = require('./helpers/instrument')
5
5
  const handlers = ['use', 'pre']
6
6
  const methods = ['del', 'get', 'head', 'opts', 'post', 'put', 'patch']
7
7
 
@@ -11,6 +11,7 @@ const enterChannel = channel('apm:restify:middleware:enter')
11
11
  const exitChannel = channel('apm:restify:middleware:exit')
12
12
  const finishChannel = channel('apm:restify:middleware:finish')
13
13
  const nextChannel = channel('apm:restify:middleware:next')
14
+ const publishError = createErrorPublisher(errorChannel)
14
15
 
15
16
  function wrapSetupRequest (setupRequest) {
16
17
  return function (req, res) {
@@ -53,7 +54,7 @@ function wrapFn (fn) {
53
54
  finishChannel.publish({ req })
54
55
  return result
55
56
  }).catch(function (error) {
56
- errorChannel.publish({ req, error })
57
+ publishError({ req, error })
57
58
  nextChannel.publish({ req })
58
59
  finishChannel.publish({ req })
59
60
  throw error
@@ -61,7 +62,7 @@ function wrapFn (fn) {
61
62
  }
62
63
  return result
63
64
  } catch (error) {
64
- errorChannel.publish({ req, error })
65
+ publishError({ req, error })
65
66
  nextChannel.publish({ req })
66
67
  finishChannel.publish({ req })
67
68
  throw error