dd-trace 6.20.1 → 6.22.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 (225) hide show
  1. package/ci/diagnose.js +9 -14
  2. package/ci/init.js +1 -2
  3. package/ci/test-optimization-validation/ci-discovery.js +9 -4
  4. package/ci/test-optimization-validation/executable.js +1 -5
  5. package/ci/test-optimization-validation/framework-adapters/cypress.js +1 -4
  6. package/ci/test-optimization-validation/framework-adapters/vitest.js +1 -1
  7. package/ci/test-optimization-validation/generated-files.js +6 -4
  8. package/ci/test-optimization-validation/generated-test-contract.js +7 -3
  9. package/ci/test-optimization-validation/manifest-scaffold.js +7 -2
  10. package/ci/test-optimization-validation/manifest-schema.js +6 -7
  11. package/ci/test-optimization-validation/report-writer.js +6 -2
  12. package/ci/test-optimization-validation/runner-command.js +2 -4
  13. package/ci/test-optimization-validation/runner-contract.js +1 -2
  14. package/ci/test-optimization-validation/scenarios/auto-test-retries.js +1 -15
  15. package/ci/vitest-no-worker-init-setup.mjs +10 -7
  16. package/index.d.ts +116 -17
  17. package/initialize.mjs +1 -3
  18. package/openfeature/rules-based.d.ts +11 -0
  19. package/openfeature/rules-based.js +7 -0
  20. package/package.json +13 -11
  21. package/packages/datadog-core/src/storage.js +16 -0
  22. package/packages/datadog-esbuild/index.js +18 -12
  23. package/packages/datadog-instrumentations/src/aerospike.js +3 -3
  24. package/packages/datadog-instrumentations/src/ai.js +3 -3
  25. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -6
  26. package/packages/datadog-instrumentations/src/aws-sdk.js +21 -13
  27. package/packages/datadog-instrumentations/src/cassandra-driver.js +25 -20
  28. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +9 -23
  29. package/packages/datadog-instrumentations/src/cucumber.js +48 -33
  30. package/packages/datadog-instrumentations/src/elasticsearch.js +1 -5
  31. package/packages/datadog-instrumentations/src/electron.js +6 -6
  32. package/packages/datadog-instrumentations/src/fastify.js +13 -4
  33. package/packages/datadog-instrumentations/src/fs.js +2 -2
  34. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +9 -5
  35. package/packages/datadog-instrumentations/src/grpc/client.js +2 -0
  36. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +2 -2
  37. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +1 -2
  38. package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
  39. package/packages/datadog-instrumentations/src/helpers/register.js +36 -1
  40. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -0
  41. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +87 -6
  42. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +5 -6
  43. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +48 -31
  44. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +64 -11
  45. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
  46. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +42 -4
  47. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +7 -10
  48. package/packages/datadog-instrumentations/src/ioredis.js +7 -5
  49. package/packages/datadog-instrumentations/src/iovalkey.js +7 -5
  50. package/packages/datadog-instrumentations/src/jest.js +122 -11
  51. package/packages/datadog-instrumentations/src/knex.js +9 -5
  52. package/packages/datadog-instrumentations/src/mariadb-bundle.js +3 -3
  53. package/packages/datadog-instrumentations/src/mocha/common.js +20 -8
  54. package/packages/datadog-instrumentations/src/mocha/main.js +140 -43
  55. package/packages/datadog-instrumentations/src/mocha/utils.js +127 -38
  56. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +61 -34
  57. package/packages/datadog-instrumentations/src/mocha/worker.js +7 -5
  58. package/packages/datadog-instrumentations/src/mongodb-core.js +3 -3
  59. package/packages/datadog-instrumentations/src/mongoose.js +3 -3
  60. package/packages/datadog-instrumentations/src/nova-sonic/audio.js +173 -0
  61. package/packages/datadog-instrumentations/src/nova-sonic/index.js +209 -0
  62. package/packages/datadog-instrumentations/src/nova-sonic/session.js +483 -0
  63. package/packages/datadog-instrumentations/src/openai-realtime/index.js +18 -6
  64. package/packages/datadog-instrumentations/src/openai-realtime/session.js +1 -1
  65. package/packages/datadog-instrumentations/src/openai.js +7 -0
  66. package/packages/datadog-instrumentations/src/pino.js +5 -6
  67. package/packages/datadog-instrumentations/src/playwright.js +3 -0
  68. package/packages/datadog-instrumentations/src/redis.js +27 -13
  69. package/packages/datadog-instrumentations/src/router.js +30 -0
  70. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +2 -6
  71. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +7 -5
  72. package/packages/datadog-instrumentations/src/vitest-main.js +9 -17
  73. package/packages/datadog-instrumentations/src/vitest-util.js +2 -2
  74. package/packages/datadog-instrumentations/src/vitest-worker.js +1 -2
  75. package/packages/datadog-instrumentations/src/vitest.js +2 -2
  76. package/packages/datadog-instrumentations/src/webdriverio.js +118 -21
  77. package/packages/datadog-instrumentations/src/ws.js +1 -4
  78. package/packages/datadog-plugin-amqplib/src/client.js +1 -2
  79. package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -5
  80. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -2
  81. package/packages/datadog-plugin-aws-sdk/src/base.js +5 -8
  82. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +18 -0
  83. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/sonic.js +45 -0
  84. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
  85. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -5
  86. package/packages/datadog-plugin-azure-durable-functions/src/index.js +5 -4
  87. package/packages/datadog-plugin-azure-functions/src/index.js +5 -3
  88. package/packages/datadog-plugin-claude-agent-sdk/src/index.js +0 -2
  89. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +18 -5
  90. package/packages/datadog-plugin-cypress/src/index.js +6 -6
  91. package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -2
  92. package/packages/datadog-plugin-cypress/src/support.js +1 -4
  93. package/packages/datadog-plugin-express/src/code_origin.js +1 -2
  94. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -2
  95. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -2
  96. package/packages/datadog-plugin-graphql/src/execute.js +5 -5
  97. package/packages/datadog-plugin-grpc/src/util.js +1 -2
  98. package/packages/datadog-plugin-kafkajs/src/producer.js +1 -2
  99. package/packages/datadog-plugin-mocha/src/index.js +223 -39
  100. package/packages/datadog-plugin-mocha/src/webdriverio-video.js +453 -0
  101. package/packages/datadog-plugin-mongodb-core/src/query.js +8 -5
  102. package/packages/datadog-plugin-next/src/index.js +1 -2
  103. package/packages/datadog-plugin-openai/src/tracing.js +5 -2
  104. package/packages/datadog-plugin-openai-agents/src/integration.js +6 -2
  105. package/packages/datadog-plugin-openai-agents/src/processor.js +2 -4
  106. package/packages/datadog-plugin-postgres/src/index.js +24 -0
  107. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -5
  108. package/packages/datadog-plugin-rhea/src/consumer.js +1 -4
  109. package/packages/datadog-plugin-vitest/src/index.js +5 -2
  110. package/packages/datadog-shimmer/src/shimmer.js +5 -4
  111. package/packages/datadog-webpack/index.js +55 -43
  112. package/packages/datadog-webpack/src/loader.js +56 -8
  113. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -4
  114. package/packages/dd-trace/src/aiguard/messages/utils.js +1 -2
  115. package/packages/dd-trace/src/aiguard/redaction.js +8 -2
  116. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +3 -2
  117. package/packages/dd-trace/src/appsec/api_security/sampler.js +2 -6
  118. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -9
  119. package/packages/dd-trace/src/appsec/handlers/http-request.js +3 -9
  120. package/packages/dd-trace/src/appsec/handlers/http-response.js +7 -2
  121. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +4 -3
  122. package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +1 -4
  123. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +7 -1
  124. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -3
  125. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -3
  126. package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +1 -6
  127. package/packages/dd-trace/src/appsec/lambda.js +1 -3
  128. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +37 -27
  129. package/packages/dd-trace/src/appsec/rasp/lfi.js +9 -5
  130. package/packages/dd-trace/src/appsec/reporter.js +21 -1
  131. package/packages/dd-trace/src/appsec/stack_trace.js +1 -2
  132. package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +3 -6
  133. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +1 -1
  134. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +13 -7
  135. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +1 -2
  136. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +2 -8
  137. package/packages/dd-trace/src/ci-visibility/resolve-runfile-path.js +54 -0
  138. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +5 -8
  139. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +2 -53
  140. package/packages/dd-trace/src/config/config-types.d.ts +2 -2
  141. package/packages/dd-trace/src/config/defaults.js +5 -5
  142. package/packages/dd-trace/src/config/generated-config-types.d.ts +8 -0
  143. package/packages/dd-trace/src/config/helper.js +34 -3
  144. package/packages/dd-trace/src/config/index.js +10 -9
  145. package/packages/dd-trace/src/config/normalize-service.js +1 -1
  146. package/packages/dd-trace/src/config/parsers.js +2 -2
  147. package/packages/dd-trace/src/config/supported-configurations.json +35 -0
  148. package/packages/dd-trace/src/datastreams/processor.js +3 -2
  149. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -4
  150. package/packages/dd-trace/src/debugger/constants.js +1 -0
  151. package/packages/dd-trace/src/debugger/devtools_client/index.js +5 -3
  152. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +17 -12
  153. package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -2
  154. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -0
  155. package/packages/dd-trace/src/debugger/devtools_client/worker-error.js +21 -0
  156. package/packages/dd-trace/src/debugger/guardrail-metrics.js +2 -2
  157. package/packages/dd-trace/src/debugger/index.js +24 -2
  158. package/packages/dd-trace/src/debugger/probe_sampler.js +108 -11
  159. package/packages/dd-trace/src/dogstatsd.js +1 -1
  160. package/packages/dd-trace/src/encode/tags-processors.js +2 -2
  161. package/packages/dd-trace/src/exporters/common/request.js +2 -5
  162. package/packages/dd-trace/src/exporters/common/url.js +1 -1
  163. package/packages/dd-trace/src/id.js +15 -5
  164. package/packages/dd-trace/src/lambda/handler.js +179 -19
  165. package/packages/dd-trace/src/lambda/index.js +15 -1
  166. package/packages/dd-trace/src/lambda/runtime/patch.js +34 -7
  167. package/packages/dd-trace/src/llmobs/agent-manifest.js +296 -0
  168. package/packages/dd-trace/src/llmobs/audio-utils.js +3 -2
  169. package/packages/dd-trace/src/llmobs/constants/tags.js +13 -0
  170. package/packages/dd-trace/src/llmobs/experiments/experiment.js +1 -1
  171. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +15 -0
  172. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +1 -2
  173. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +1 -3
  174. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  175. package/packages/dd-trace/src/llmobs/plugins/nova-sonic.js +92 -0
  176. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +56 -2
  177. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +38 -0
  178. package/packages/dd-trace/src/llmobs/prompts/manager.js +5 -7
  179. package/packages/dd-trace/src/llmobs/prompts/prompt.js +1 -1
  180. package/packages/dd-trace/src/llmobs/sdk.js +23 -6
  181. package/packages/dd-trace/src/llmobs/span_processor.js +18 -8
  182. package/packages/dd-trace/src/llmobs/tagger.js +56 -3
  183. package/packages/dd-trace/src/llmobs/util.js +1 -2
  184. package/packages/dd-trace/src/llmobs/writers/base.js +7 -6
  185. package/packages/dd-trace/src/llmobs/writers/util.js +3 -1
  186. package/packages/dd-trace/src/log/log.js +1 -5
  187. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +30 -1
  188. package/packages/dd-trace/src/openfeature/configuration_source.js +17 -2
  189. package/packages/dd-trace/src/openfeature/debug-logging-hook.js +25 -0
  190. package/packages/dd-trace/src/openfeature/evaluation-telemetry-opt-out-hook.js +51 -0
  191. package/packages/dd-trace/src/openfeature/flagging_provider.js +59 -3
  192. package/packages/dd-trace/src/openfeature/remote_config.js +9 -0
  193. package/packages/dd-trace/src/openfeature/writers/util.js +43 -4
  194. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +1 -1
  195. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
  196. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -2
  197. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +1 -1
  198. package/packages/dd-trace/src/opentelemetry/resource-attributes.js +1 -1
  199. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +6 -23
  200. package/packages/dd-trace/src/opentracing/propagation/text_map.js +53 -13
  201. package/packages/dd-trace/src/otel-sampling.js +4 -5
  202. package/packages/dd-trace/src/otel-thread-ctx.js +81 -15
  203. package/packages/dd-trace/src/plugins/database.js +7 -16
  204. package/packages/dd-trace/src/plugins/util/git.js +9 -25
  205. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +1 -5
  206. package/packages/dd-trace/src/plugins/util/test.js +48 -4
  207. package/packages/dd-trace/src/priority_sampler.js +1 -2
  208. package/packages/dd-trace/src/profiling/profiler.js +4 -6
  209. package/packages/dd-trace/src/profiling/profilers/wall.js +4 -10
  210. package/packages/dd-trace/src/proxy.js +3 -2
  211. package/packages/dd-trace/src/ritm.js +4 -2
  212. package/packages/dd-trace/src/serverless.js +10 -0
  213. package/packages/dd-trace/src/span_processor.js +13 -17
  214. package/packages/dd-trace/src/storage-channels.js +112 -46
  215. package/packages/dd-trace/src/telemetry/endpoints.js +1 -3
  216. package/packages/dd-trace/src/util.js +29 -0
  217. package/packages/dd-trace/src/web-tags-cache.js +6 -2
  218. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  219. package/vendor/dist/@datadog/openfeature-node-server/rules-based/LICENSE +201 -0
  220. package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.d.ts +925 -0
  221. package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.js +1 -0
  222. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  223. package/vendor/dist/protobufjs/index.js +1 -1
  224. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  225. package/vendor/dist/shell-quote/index.js +1 -1
@@ -92,8 +92,10 @@ function Hook (modules, options, onrequire) {
92
92
  let name, basedir, hooks
93
93
  // return known patched modules immediately
94
94
  if (cache[moduleId]) {
95
- // require.cache was potentially altered externally
96
- const cacheEntry = require.cache[filename]
95
+ // Use Node's cache: bundlers replace require.cache with their own factory cache,
96
+ // whose not-yet-evaluated external modules can still have empty exports.
97
+ // @ts-expect-error - Module._cache is not typed
98
+ const cacheEntry = Module._cache[filename]
97
99
  if (cacheEntry && cacheEntry.exports !== cache[moduleId].original) {
98
100
  return cacheEntry.exports
99
101
  }
@@ -3,6 +3,10 @@
3
3
  const { getEnvironmentVariable, getValueFromEnvSources } = require('./config/helper')
4
4
 
5
5
  const IS_AWS_LAMBDA_MICROVM = getEnvironmentVariable('AWS_LAMBDA_MICROVM_IMAGE_ARN') !== undefined
6
+ // Standard Lambda, whose sandbox freezes between invocations. Excludes Lambda Lite (native-http),
7
+ // Managed Instances (lambda-managed-instances) and MicroVMs.
8
+ const IS_STANDARD_AWS_LAMBDA = !IS_AWS_LAMBDA_MICROVM && ['on-demand', 'provisioned-concurrency', 'snap-start']
9
+ .includes(getEnvironmentVariable('AWS_LAMBDA_INITIALIZATION_TYPE'))
6
10
  const isVercelAtStartup = getEnvironmentVariable('VERCEL') === '1'
7
11
 
8
12
  function getIsGCPFunction () {
@@ -28,6 +32,10 @@ function enableGCPPubSubPushSubscription () {
28
32
  getValueFromEnvSources('DD_TRACE_GCP_PUBSUB_PUSH_ENABLED')
29
33
  }
30
34
 
35
+ function getIsAzureContainerApp () {
36
+ return getEnvironmentVariable('CONTAINER_APP_NAME') !== undefined
37
+ }
38
+
31
39
  function getIsAzureFunction () {
32
40
  return getEnvironmentVariable('FUNCTIONS_EXTENSION_VERSION') !== undefined &&
33
41
  getEnvironmentVariable('FUNCTIONS_WORKER_RUNTIME') !== undefined
@@ -99,11 +107,13 @@ module.exports = {
99
107
  supportsServerlessTelemetryRetention,
100
108
  createServerlessDeliveryTracker,
101
109
  getIsGCPFunction,
110
+ getIsAzureContainerApp,
102
111
  getIsAzureFunction,
103
112
  enableGCPPubSubPushSubscription,
104
113
  getIsFlexConsumptionAzureFunction,
105
114
  initializeServerlessTelemetry,
106
115
  IS_AWS_LAMBDA_MICROVM,
116
+ IS_STANDARD_AWS_LAMBDA,
107
117
  // true only for a Node that bundles its own OpenSSL, whose CSPRNG keeps the snapshot's DRBG
108
118
  // state across a MicroVM clone resume
109
119
  NODE_BUNDLES_OPENSSL: process.config.variables.node_shared_openssl === false,
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { AUTO_REJECT } = require('../../../ext/priority')
3
+ const { AUTO_KEEP, AUTO_REJECT } = require('../../../ext/priority')
4
4
  const log = require('./log')
5
5
  const spanFormat = require('./span_format')
6
6
  const SpanSampler = require('./span_sampler')
@@ -15,32 +15,23 @@ const finishedSpans = new WeakSet()
15
15
 
16
16
  let otelSampling
17
17
 
18
- /**
19
- * Adds first-class OTLP trace context to a DD-formatted span.
20
- *
21
- * @param {import('./opentracing/span')} span
22
- * @param {boolean} isFirstSpanInChunk
23
- * @param {string | false} processTagsValue
24
- */
25
- function formatOtlpSpan (span, isFirstSpanInChunk, processTagsValue) {
26
- const formattedSpan = spanFormat(span, isFirstSpanInChunk, processTagsValue)
27
- const context = span.context()
18
+ /** @param {import('./opentracing/span_context')} context */
19
+ function serializeOtlpTraceState (context) {
28
20
  const traceState = context._tracestate?.clone() ?? new TraceState()
29
21
  otelSampling ??= require('./otel-sampling')
30
22
  otelSampling.updateOtelTraceState(context, traceState)
31
- formattedSpan.trace_state = traceState.toString()
32
- return formattedSpan
23
+ return traceState.toString()
33
24
  }
34
25
 
35
26
  class SpanProcessor {
36
- #formatSpan
27
+ #exportOtlpTraces
37
28
 
38
29
  constructor (exporter, prioritySampler, config, otlpStatsExporter, exportOtlpTraces) {
39
30
  this._exporter = exporter
40
31
  this._prioritySampler = prioritySampler
41
32
  this._config = config
42
33
  this._killAll = false
43
- this.#formatSpan = exportOtlpTraces ? formatOtlpSpan : spanFormat
34
+ this.#exportOtlpTraces = exportOtlpTraces
44
35
 
45
36
  if (config.stats?.DD_TRACE_STATS_COMPUTATION_ENABLED &&
46
37
  !config.appsec?.DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED) {
@@ -95,13 +86,18 @@ class SpanProcessor {
95
86
  let isFirstSpanInChunk = true
96
87
  const stampApmDisabled = this._config.apmTracingEnabled === false
97
88
  const discard = this.#isDiscarded(spanContext)
98
- const formatSpan = this.#formatSpan
89
+ const exportOtlpTraces = this.#exportOtlpTraces && !(spanContext._sampling.priority < AUTO_KEEP)
90
+ let traceState
99
91
 
100
92
  for (const span of started) {
101
93
  if (span._duration === undefined) {
102
94
  active.push(span)
103
95
  } else if (!discard) {
104
- const formattedSpan = formatSpan(span, isFirstSpanInChunk, this._processTags)
96
+ const formattedSpan = spanFormat(span, isFirstSpanInChunk, this._processTags)
97
+ if (exportOtlpTraces) {
98
+ traceState ??= serializeOtlpTraceState(spanContext)
99
+ formattedSpan.trace_state = traceState
100
+ }
105
101
  if (stampApmDisabled) {
106
102
  formattedSpan.metrics[APM_TRACING_ENABLED_KEY] = 0
107
103
  }
@@ -2,16 +2,17 @@
2
2
 
3
3
  const dc = require('dc-polyfill')
4
4
  const { storage } = require('../../datadog-core')
5
+ const { isACFActive } = require('../../datadog-core/src/storage')
5
6
 
6
7
  // Channels that surface tracer-storage events to interested consumers
7
8
  // (currently the wall profiler and the OTEP-4947 thread context writer).
8
9
  //
9
10
  // dd-trace:storage:enter — fires when the active legacy-storage store
10
- // changes. Published by the shimmer installed in ensureChannelsActivated().
11
- // dd-trace:storage:before — fires from an async_hooks "before" callback in
12
- // non-AsyncContextFrame mode. Used by the wall profiler to refresh the
13
- // sample context at the start of each async resource callback. Has no
14
- // publisher in ACF mode.
11
+ // changes. Published by the shimmer installed in acquireChannels().
12
+ // dd-trace:storage:before — fires from an async_hooks "before" callback while
13
+ // at least one consumer that acquired the channels needs it. Used by the
14
+ // wall profiler in its non-AsyncContextFrame (single holder) mode to refresh
15
+ // the sample context at the start of each async resource callback.
15
16
  // dd-trace:span:finish, dd-trace:span:tags:update — published by the
16
17
  // tracer core (opentracing/span.js). Re-exported here as a convenience
17
18
  // so consumers have a single import path for storage-related channels.
@@ -25,55 +26,119 @@ function getActiveSpan () {
25
26
  return store && store.span
26
27
  }
27
28
 
28
- let channelsActivated = false
29
- // Idempotent. The asyncContextFrameEnabled argument from the first caller
30
- // wins; subsequent calls are no-ops regardless of their argument. In
31
- // practice all callers observe the same global ACF state.
32
- function ensureChannelsActivated (asyncContextFrameEnabled) {
33
- if (channelsActivated) return
29
+ // The legacy storage instrumentation feeding enterCh and beforeCh is installed
30
+ // lazily via acquireChannels() / releaseChannels(), ref-counted across
31
+ // consumers, so that once the last consumer stops (e.g. the profiler is turned
32
+ // off remotely), instrumentation is undone.
33
+ // Consumers MUST balance every acquireChannels() call with a matching
34
+ // releaseChannels() when they no longer use them.
35
+ let refCount = 0
36
+ // The async hook has a separate count: it's only needed by some consumers, and
37
+ // is a per-callback cost, so it's disabled as soon as the last of them releases.
38
+ let beforeRefCount = 0
39
+ let inRun = false
40
+ let beforeHook
34
41
 
35
- const shimmer = require('../../datadog-shimmer')
42
+ // Wrappers currently installed on legacy storage, keyed by method name. Each
43
+ // entry records the method's own property descriptor from before wrapping
44
+ // (undefined when the method was inherited from the prototype) so it can be
45
+ // restored exactly.
46
+ const installedWrappers = new Map()
47
+
48
+ function wrapEnterWith (original) {
49
+ return function (store) {
50
+ const retVal = original.call(this, store)
51
+ if (!inRun && refCount !== 0) enterCh.publish()
52
+ return retVal
53
+ }
54
+ }
36
55
 
37
- // We need to instrument enterWith() on the legacy storage — that's the storage
38
- // carrying span data and the only one consumers of these channels care about.
39
- const legacyStorage = storage('legacy')
40
- let inRun = false
41
- shimmer.wrap(legacyStorage, 'enterWith', function (original) {
42
- return function (store) {
43
- const retVal = original.call(this, store)
44
- if (!inRun) enterCh.publish()
56
+ function wrapRun (original) {
57
+ const shimmer = require('../../datadog-shimmer')
58
+ return function (store, callback, ...args) {
59
+ if (refCount === 0) return original.call(this, store, callback, ...args)
60
+ const wrappedCb = shimmer.wrapFunction(callback, cb => function (...args) {
61
+ inRun = false
62
+ enterCh.publish()
63
+ const retVal = cb.apply(this, args)
64
+ inRun = true
45
65
  return retVal
46
- }
47
- })
66
+ })
67
+ inRun = true
68
+ const retVal = original.call(this, store, wrappedCb, ...args)
69
+ enterCh.publish()
70
+ inRun = false
71
+ return retVal
72
+ }
73
+ }
48
74
 
49
- // When not using AsyncContextFrame, we need additional instrumentation.
50
- if (!asyncContextFrameEnabled) {
51
- // We need async_hooks.createHook to create a "before" callback.
52
- const { createHook } = require('async_hooks')
53
- createHook({ before: () => beforeCh.publish() }).enable()
75
+ function installWrapper (target, name, wrapper) {
76
+ if (installedWrappers.has(name)) return
77
+ const descriptor = Object.getOwnPropertyDescriptor(target, name)
78
+ require('../../datadog-shimmer').wrap(target, name, wrapper)
79
+ installedWrappers.set(name, { descriptor, wrapped: target[name] })
80
+ }
54
81
 
82
+ function uninstallWrappers (target) {
83
+ for (const [name, { descriptor, wrapped }] of installedWrappers) {
84
+ // If someone else wrapped the method on top of ours, restoring would
85
+ // silently drop their wrapper too. Leave ours in place instead; it
86
+ // short-circuits while released, and gets reused on the next acquisition.
87
+ if (target[name] !== wrapped) continue
88
+ if (descriptor === undefined) {
89
+ delete target[name]
90
+ } else {
91
+ Object.defineProperty(target, name, descriptor)
92
+ }
93
+ installedWrappers.delete(name)
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Acquires a reference to the channel publishers. The first reference installs
99
+ * the legacy storage instrumentation publishing on enterCh.
100
+ *
101
+ * @param {boolean} needsBeforeHook Whether the consumer needs beforeCh events.
102
+ * The same value must be passed to the matching releaseChannels() call.
103
+ */
104
+ function acquireChannels (needsBeforeHook) {
105
+ if (refCount++ === 0) {
106
+ // We need to instrument enterWith() on the legacy storage — that's the storage
107
+ // carrying span data and the only one consumers of these channels care about.
108
+ const legacyStorage = storage('legacy')
109
+ installWrapper(legacyStorage, 'enterWith', wrapEnterWith)
55
110
  // In ACF-based implementation run() delegates to enterWith() so it doesn't
56
- // need to be separately instrumented. in non-ACF implementation run()
111
+ // need to be separately instrumented. In non-ACF implementation run()
57
112
  // doesn't delegate to enterWith(), so separate instrumentation is necessary.
58
- shimmer.wrap(legacyStorage, 'run', function (original) {
59
- return function (store, callback, ...args) {
60
- const wrappedCb = shimmer.wrapFunction(callback, cb => function (...args) {
61
- inRun = false
62
- enterCh.publish()
63
- const retVal = cb.apply(this, args)
64
- inRun = true
65
- return retVal
66
- })
67
- inRun = true
68
- const retVal = original.call(this, store, wrappedCb, ...args)
69
- enterCh.publish()
70
- inRun = false
71
- return retVal
72
- }
73
- })
113
+ if (!isACFActive) {
114
+ installWrapper(legacyStorage, 'run', wrapRun)
115
+ }
74
116
  }
75
117
 
76
- channelsActivated = true
118
+ if (needsBeforeHook && beforeRefCount++ === 0) {
119
+ beforeHook ??= require('async_hooks').createHook({ before: () => beforeCh.publish() })
120
+ beforeHook.enable()
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Releases one acquireChannels() reference. The async hook is disabled when the
126
+ * last reference needing it is released, and the legacy storage instrumentation
127
+ * is removed when the last reference overall is released.
128
+ *
129
+ * @param {boolean} needsBeforeHook The value passed to the matching
130
+ * acquireChannels() call.
131
+ */
132
+ function releaseChannels (needsBeforeHook) {
133
+ if (refCount === 0) return
134
+
135
+ if (needsBeforeHook && beforeRefCount !== 0 && --beforeRefCount === 0) {
136
+ beforeHook.disable()
137
+ }
138
+
139
+ if (--refCount === 0) {
140
+ uninstallWrappers(storage('legacy'))
141
+ }
77
142
  }
78
143
 
79
144
  module.exports = {
@@ -82,5 +147,6 @@ module.exports = {
82
147
  spanFinishCh,
83
148
  tagsUpdateCh,
84
149
  getActiveSpan,
85
- ensureChannelsActivated,
150
+ acquireChannels,
151
+ releaseChannels,
86
152
  }
@@ -95,10 +95,8 @@ function onFastifyRoute (routeData) {
95
95
  * @param {{ method?: string, path?: string }} param0
96
96
  */
97
97
  function onExpressRoute ({ method, path }) {
98
- if (!method || !path) return
99
-
100
98
  // If wildcard already recorded for this path, skip specific methods
101
- if (wildcardEndpoints.has(path)) return
99
+ if (!method || !path || wildcardEndpoints.has(path)) return
102
100
 
103
101
  recordEndpoint(method, path, 'express.request')
104
102
 
@@ -1,6 +1,14 @@
1
1
  'use strict'
2
2
 
3
3
  const path = require('path')
4
+ const { types: { isTypedArray } } = require('node:util')
5
+
6
+ /**
7
+ * @typedef {Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array |
8
+ * Float32Array | Float64Array | BigInt64Array | BigUint64Array | DataView} BinaryView
9
+ */
10
+
11
+ const getTypedArrayLength = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Uint8Array.prototype), 'length').get
4
12
 
5
13
  /**
6
14
  * `for-in` with an early return is the only allocation-free shape for
@@ -12,11 +20,31 @@ const path = require('path')
12
20
  * @param {object | undefined} obj
13
21
  */
14
22
  function isEmpty (obj) {
23
+ if (Array.isArray(obj) || ArrayBuffer.isView(obj)) {
24
+ // `for-in` materializes every index key up front, which exhausts memory on large bodies, so return as soon as
25
+ // there are elements. Empty ones have no index keys to materialize, so check their own keys, which still detects
26
+ // named properties without walking a Buffer's enumerable prototype methods.
27
+ const length = Array.isArray(obj) ? obj.length : typedArrayLength(obj)
28
+ // eslint-disable-next-line no-restricted-syntax
29
+ return length === 0 && Object.keys(obj).length === 0
30
+ }
31
+ // `for-in` over a string also materializes every index key, so large raw string bodies need the same shortcut.
32
+ if (typeof obj === 'string') return obj.length === 0
15
33
  // eslint-disable-next-line no-unreachable-loop
16
34
  for (const _ in obj) return false
17
35
  return true
18
36
  }
19
37
 
38
+ /**
39
+ * Reads the element count through the intrinsic getter, which, unlike `view.length`, an instance or subclass cannot
40
+ * shadow with application code. A `DataView` exposes no indexed elements, so it counts as zero.
41
+ *
42
+ * @param {BinaryView} view
43
+ */
44
+ function typedArrayLength (view) {
45
+ return isTypedArray(view) ? getTypedArrayLength.call(view) : 0
46
+ }
47
+
20
48
  function isTrue (str) {
21
49
  str = String(str).toLowerCase()
22
50
  return str === 'true' || str === '1'
@@ -167,4 +195,5 @@ module.exports = {
167
195
  normalizePluginEnvName,
168
196
  formatKnuthRate,
169
197
  truncateString,
198
+ typedArrayLength,
170
199
  }
@@ -120,8 +120,12 @@ function onTagsUpdate (span) {
120
120
  // the ancestor's own update is what settles the endpoint. Skipped entirely
121
121
  // when nobody is listening, keeping this the same early return it used to be
122
122
  // for consumers that only care about web-tag discovery.
123
- if (!endpointResolvedCh.hasSubscribers || cached.endpointFinal || cached.webTags !== tags) return
124
- if (finalEndpoint(tags) === undefined) return
123
+ if (
124
+ !endpointResolvedCh.hasSubscribers || cached.endpointFinal || cached.webTags !== tags ||
125
+ finalEndpoint(tags) === undefined
126
+ ) {
127
+ return
128
+ }
125
129
  cached.endpointFinal = true
126
130
  endpointResolvedCh.publish(span)
127
131
  }