dd-trace 5.132.0 → 5.134.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 (231) 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-runner-setup.mjs +27 -0
  16. package/ci/vitest-no-worker-init-setup.mjs +10 -7
  17. package/index.d.ts +41 -19
  18. package/initialize.mjs +1 -3
  19. package/openfeature/rules-based.d.ts +11 -0
  20. package/openfeature/rules-based.js +7 -0
  21. package/package.json +25 -22
  22. package/packages/datadog-core/src/storage.js +16 -0
  23. package/packages/datadog-esbuild/index.js +18 -12
  24. package/packages/datadog-instrumentations/index.js +2 -1
  25. package/packages/datadog-instrumentations/src/aerospike.js +3 -3
  26. package/packages/datadog-instrumentations/src/ai.js +3 -3
  27. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -6
  28. package/packages/datadog-instrumentations/src/aws-sdk.js +21 -13
  29. package/packages/datadog-instrumentations/src/cassandra-driver.js +25 -20
  30. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +9 -23
  31. package/packages/datadog-instrumentations/src/cucumber.js +65 -41
  32. package/packages/datadog-instrumentations/src/elasticsearch.js +1 -5
  33. package/packages/datadog-instrumentations/src/electron.js +6 -6
  34. package/packages/datadog-instrumentations/src/fastify.js +13 -4
  35. package/packages/datadog-instrumentations/src/fs.js +2 -2
  36. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +9 -5
  37. package/packages/datadog-instrumentations/src/grpc/client.js +2 -0
  38. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +28 -9
  39. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +1 -2
  40. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  41. package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
  42. package/packages/datadog-instrumentations/src/helpers/register.js +45 -1
  43. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -0
  44. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +89 -6
  45. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +5 -6
  46. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/jest.js +12 -0
  47. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +48 -31
  48. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/vitest.js +8 -0
  49. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +7 -7
  50. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +5 -0
  51. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +42 -4
  52. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +7 -10
  53. package/packages/datadog-instrumentations/src/ioredis.js +7 -5
  54. package/packages/datadog-instrumentations/src/iovalkey.js +7 -5
  55. package/packages/datadog-instrumentations/src/jest.js +72 -67
  56. package/packages/datadog-instrumentations/src/knex.js +9 -5
  57. package/packages/datadog-instrumentations/src/mariadb-bundle.js +3 -3
  58. package/packages/datadog-instrumentations/src/mocha/common.js +20 -8
  59. package/packages/datadog-instrumentations/src/mocha/main.js +140 -43
  60. package/packages/datadog-instrumentations/src/mocha/utils.js +38 -4
  61. package/packages/datadog-instrumentations/src/mocha/worker.js +1 -2
  62. package/packages/datadog-instrumentations/src/mongodb-core.js +3 -3
  63. package/packages/datadog-instrumentations/src/mongoose.js +3 -3
  64. package/packages/datadog-instrumentations/src/nova-sonic/audio.js +173 -0
  65. package/packages/datadog-instrumentations/src/nova-sonic/index.js +209 -0
  66. package/packages/datadog-instrumentations/src/nova-sonic/session.js +492 -0
  67. package/packages/datadog-instrumentations/src/openai-realtime/index.js +18 -6
  68. package/packages/datadog-instrumentations/src/openai-realtime/session.js +1 -1
  69. package/packages/datadog-instrumentations/src/openai.js +7 -0
  70. package/packages/datadog-instrumentations/src/pino.js +5 -6
  71. package/packages/datadog-instrumentations/src/playwright.js +2 -0
  72. package/packages/datadog-instrumentations/src/redis.js +27 -13
  73. package/packages/datadog-instrumentations/src/router.js +30 -0
  74. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +2 -6
  75. package/packages/datadog-instrumentations/src/stripe.js +23 -12
  76. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +8 -5
  77. package/packages/datadog-instrumentations/src/vitest-main.js +16 -18
  78. package/packages/datadog-instrumentations/src/vitest-util.js +7 -4
  79. package/packages/datadog-instrumentations/src/vitest-worker.js +1 -2
  80. package/packages/datadog-instrumentations/src/vitest.js +9 -1
  81. package/packages/datadog-instrumentations/src/webdriverio.js +5 -8
  82. package/packages/datadog-instrumentations/src/ws.js +1 -4
  83. package/packages/datadog-plugin-amqplib/src/client.js +1 -2
  84. package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -5
  85. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -2
  86. package/packages/datadog-plugin-aws-sdk/src/base.js +5 -8
  87. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +18 -0
  88. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/sonic.js +45 -0
  89. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
  90. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -5
  91. package/packages/datadog-plugin-azure-durable-functions/src/index.js +5 -4
  92. package/packages/datadog-plugin-azure-functions/src/index.js +5 -3
  93. package/packages/datadog-plugin-claude-agent-sdk/src/index.js +0 -2
  94. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +4 -5
  95. package/packages/datadog-plugin-cypress/src/index.js +6 -6
  96. package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -2
  97. package/packages/datadog-plugin-cypress/src/support.js +3 -4
  98. package/packages/datadog-plugin-express/src/code_origin.js +1 -2
  99. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -2
  100. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -2
  101. package/packages/datadog-plugin-graphql/src/execute.js +5 -5
  102. package/packages/datadog-plugin-grpc/src/util.js +1 -2
  103. package/packages/datadog-plugin-kafkajs/src/producer.js +1 -2
  104. package/packages/datadog-plugin-mocha/src/index.js +7 -10
  105. package/packages/datadog-plugin-mocha/src/webdriverio-video.js +8 -4
  106. package/packages/datadog-plugin-mongodb-core/src/query.js +8 -5
  107. package/packages/datadog-plugin-next/src/index.js +1 -2
  108. package/packages/datadog-plugin-openai/src/tracing.js +5 -2
  109. package/packages/datadog-plugin-openai-agents/src/integration.js +6 -2
  110. package/packages/datadog-plugin-openai-agents/src/processor.js +2 -4
  111. package/packages/datadog-plugin-postgres/src/index.js +24 -0
  112. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -5
  113. package/packages/datadog-plugin-rhea/src/consumer.js +1 -4
  114. package/packages/datadog-plugin-vitest/src/index.js +5 -2
  115. package/packages/datadog-shimmer/src/shimmer.js +5 -4
  116. package/packages/datadog-webpack/index.js +55 -43
  117. package/packages/datadog-webpack/src/loader.js +56 -8
  118. package/packages/dd-trace/src/aiguard/evaluation.js +143 -1
  119. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -4
  120. package/packages/dd-trace/src/aiguard/messages/utils.js +1 -2
  121. package/packages/dd-trace/src/aiguard/redaction.js +8 -2
  122. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +3 -2
  123. package/packages/dd-trace/src/appsec/api_security/sampler.js +2 -6
  124. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -9
  125. package/packages/dd-trace/src/appsec/handlers/http-request.js +3 -9
  126. package/packages/dd-trace/src/appsec/handlers/http-response.js +7 -2
  127. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +4 -3
  128. package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +1 -4
  129. package/packages/dd-trace/src/appsec/iast/path-line.js +2 -1
  130. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +7 -1
  131. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -3
  132. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -3
  133. package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +1 -6
  134. package/packages/dd-trace/src/appsec/lambda.js +1 -3
  135. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +37 -27
  136. package/packages/dd-trace/src/appsec/rasp/lfi.js +9 -5
  137. package/packages/dd-trace/src/appsec/reporter.js +21 -1
  138. package/packages/dd-trace/src/appsec/stack_trace.js +4 -4
  139. package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +3 -6
  140. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +3 -2
  141. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +1 -1
  142. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +13 -7
  143. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +1 -2
  144. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +2 -8
  145. package/packages/dd-trace/src/ci-visibility/resolve-runfile-path.js +54 -0
  146. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +5 -8
  147. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +2 -53
  148. package/packages/dd-trace/src/config/config-types.d.ts +2 -2
  149. package/packages/dd-trace/src/config/defaults.js +5 -5
  150. package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
  151. package/packages/dd-trace/src/config/helper.js +34 -3
  152. package/packages/dd-trace/src/config/index.js +55 -9
  153. package/packages/dd-trace/src/config/normalize-service.js +1 -1
  154. package/packages/dd-trace/src/config/parsers.js +2 -2
  155. package/packages/dd-trace/src/config/supported-configurations.json +44 -0
  156. package/packages/dd-trace/src/datastreams/processor.js +3 -2
  157. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -4
  158. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +6 -5
  159. package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -5
  160. package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -0
  161. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +4 -6
  162. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -0
  163. package/packages/dd-trace/src/debugger/guardrail-metrics.js +2 -2
  164. package/packages/dd-trace/src/debugger/unsupported-runtime.js +16 -0
  165. package/packages/dd-trace/src/dogstatsd.js +1 -1
  166. package/packages/dd-trace/src/encode/tags-processors.js +2 -2
  167. package/packages/dd-trace/src/exporters/common/request.js +1 -5
  168. package/packages/dd-trace/src/id.js +14 -1
  169. package/packages/dd-trace/src/lambda/handler.js +179 -19
  170. package/packages/dd-trace/src/lambda/index.js +15 -1
  171. package/packages/dd-trace/src/lambda/runtime/patch.js +34 -7
  172. package/packages/dd-trace/src/llmobs/agent-manifest.js +19 -2
  173. package/packages/dd-trace/src/llmobs/audio-utils.js +3 -2
  174. package/packages/dd-trace/src/llmobs/constants/tags.js +15 -0
  175. package/packages/dd-trace/src/llmobs/experiments/experiment.js +1 -1
  176. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +15 -0
  177. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +1 -2
  178. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +1 -3
  179. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  180. package/packages/dd-trace/src/llmobs/plugins/nova-sonic.js +92 -0
  181. package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +5 -0
  182. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +64 -2
  183. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +38 -0
  184. package/packages/dd-trace/src/llmobs/prompts/manager.js +5 -7
  185. package/packages/dd-trace/src/llmobs/prompts/prompt.js +1 -1
  186. package/packages/dd-trace/src/llmobs/sdk.js +15 -4
  187. package/packages/dd-trace/src/llmobs/span_processor.js +13 -2
  188. package/packages/dd-trace/src/llmobs/tagger.js +20 -10
  189. package/packages/dd-trace/src/llmobs/util.js +11 -6
  190. package/packages/dd-trace/src/llmobs/writers/base.js +4 -5
  191. package/packages/dd-trace/src/log/log.js +1 -5
  192. package/packages/dd-trace/src/openfeature/evaluation-telemetry-opt-out-hook.js +51 -0
  193. package/packages/dd-trace/src/openfeature/flagging_provider.js +48 -21
  194. package/packages/dd-trace/src/openfeature/index.js +6 -0
  195. package/packages/dd-trace/src/openfeature/writers/util.js +4 -4
  196. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +1 -1
  197. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
  198. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -2
  199. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +1 -1
  200. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +24 -24
  201. package/packages/dd-trace/src/opentracing/propagation/text_map.js +53 -13
  202. package/packages/dd-trace/src/otel-sampling.js +4 -5
  203. package/packages/dd-trace/src/otel-thread-ctx.js +79 -13
  204. package/packages/dd-trace/src/plugin_manager.js +1 -2
  205. package/packages/dd-trace/src/plugins/database.js +7 -16
  206. package/packages/dd-trace/src/plugins/tracing.js +0 -1
  207. package/packages/dd-trace/src/plugins/util/git.js +99 -59
  208. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +1 -5
  209. package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -1
  210. package/packages/dd-trace/src/plugins/util/test.js +50 -4
  211. package/packages/dd-trace/src/plugins/util/web.js +90 -20
  212. package/packages/dd-trace/src/priority_sampler.js +1 -2
  213. package/packages/dd-trace/src/profiling/profiler.js +4 -6
  214. package/packages/dd-trace/src/proxy.js +1 -2
  215. package/packages/dd-trace/src/ritm.js +4 -2
  216. package/packages/dd-trace/src/serverless.js +5 -0
  217. package/packages/dd-trace/src/span_processor.js +13 -17
  218. package/packages/dd-trace/src/span_stats.js +4 -1
  219. package/packages/dd-trace/src/telemetry/endpoints.js +1 -3
  220. package/packages/dd-trace/src/telemetry/logs/log-collector.js +1 -1
  221. package/packages/dd-trace/src/util.js +37 -4
  222. package/packages/dd-trace/src/web-tags-cache.js +6 -2
  223. package/vendor/dist/@apm-js-collab/code-transformer/index.js +8 -8
  224. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  225. package/vendor/dist/@datadog/openfeature-node-server/rules-based/LICENSE +201 -0
  226. package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.d.ts +925 -0
  227. package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.js +1 -0
  228. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  229. package/vendor/dist/protobufjs/index.js +1 -1
  230. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  231. package/vendor/dist/shell-quote/index.js +1 -1
@@ -0,0 +1,51 @@
1
+ 'use strict'
2
+
3
+ const PARAMETER = 'dd_evaluation_telemetry_opt_out'
4
+
5
+ /** Gates an owned finally-only telemetry hook without changing evaluation or exposure handling. */
6
+ class EvaluationTelemetryOptOutHook {
7
+ #delegate
8
+ #decisions = new WeakMap()
9
+
10
+ /** @param {import('@openfeature/server-sdk').Hook} delegate */
11
+ constructor (delegate) {
12
+ this.#delegate = delegate
13
+ }
14
+
15
+ /** @param {import('@openfeature/core').HookContext} hookContext */
16
+ before (hookContext) {
17
+ // Hook context identity is stable across stages, including each MultiProvider branch.
18
+ this.#decisions.set(hookContext, isOptedOut(hookContext.context))
19
+ }
20
+
21
+ /** @param {import('@openfeature/core').HookContext} hookContext */
22
+ error (hookContext) {
23
+ // A customer before hook can fail before this hook runs. Capture opt-out before customer error hooks change it.
24
+ if (!this.#decisions.has(hookContext)) {
25
+ this.#decisions.set(hookContext, isOptedOut(hookContext.context))
26
+ }
27
+ }
28
+
29
+ /**
30
+ * @param {import('@openfeature/core').HookContext} hookContext
31
+ * @param {import('@openfeature/core').EvaluationDetails<import('@openfeature/core').FlagValue>} evaluationDetails
32
+ * @param {import('@openfeature/core').HookHints} [hints]
33
+ */
34
+ finally (hookContext, evaluationDetails, hints) {
35
+ const optedOut = this.#decisions.get(hookContext) === true || isOptedOut(hookContext.context)
36
+ this.#decisions.delete(hookContext)
37
+ if (!optedOut) return this.#delegate.finally?.(hookContext, evaluationDetails, hints)
38
+ }
39
+ }
40
+
41
+ /** @param {import('@openfeature/core').EvaluationContext} context */
42
+ function isOptedOut (context) {
43
+ try {
44
+ return context?.[PARAMETER] === true
45
+ } catch {
46
+ // An unreadable attribute is not boolean true and must not change the evaluation result.
47
+ return false
48
+ }
49
+ }
50
+
51
+ module.exports = EvaluationTelemetryOptOutHook
@@ -9,6 +9,7 @@ const configurationSource = require('./configuration_source')
9
9
  const { EXPOSURE_CHANNEL } = require('./constants/constants')
10
10
  const DebugLoggingHook = require('./debug-logging-hook')
11
11
  const EvalMetricsHook = require('./eval-metrics-hook')
12
+ const EvaluationTelemetryOptOutHook = require('./evaluation-telemetry-opt-out-hook')
12
13
  const SpanEnrichmentHook = require('./span-enrichment-hook')
13
14
  const FlagEvalEVPHook = require('./writers/flag-eval-evp-hook')
14
15
 
@@ -17,6 +18,9 @@ const FlagEvalEVPHook = require('./writers/flag-eval-evp-hook')
17
18
  * Extends DatadogNodeServerProvider to add tracer integration and configuration management.
18
19
  */
19
20
  class FlaggingProvider extends DatadogNodeServerProvider {
21
+ // Track this wrapper's logging lifecycle without reading the upstream provider's private state.
22
+ #waitingForConfiguration = false
23
+
20
24
  /** @type {SpanEnrichmentHook | undefined} */
21
25
  #spanEnrichmentHook
22
26
 
@@ -36,8 +40,13 @@ class FlaggingProvider extends DatadogNodeServerProvider {
36
40
  initializationTimeoutMs: config.featureFlags.DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS,
37
41
  })
38
42
 
43
+ const enableOptOut =
44
+ config.featureFlags.DD_FEATURE_FLAGS_EVALUATION_TELEMETRY_PER_SUBJECT_OPT_OUT_ENABLED === true
45
+ /** @param {import('@openfeature/server-sdk').Hook} hook */
46
+ const telemetryHook = hook => enableOptOut ? new EvaluationTelemetryOptOutHook(hook) : hook
47
+
39
48
  if (config.DD_METRICS_OTEL_ENABLED === true) {
40
- this.hooks.push(new EvalMetricsHook(config))
49
+ this.hooks.push(telemetryHook(new EvalMetricsHook(config)))
41
50
  } else {
42
51
  log.debug('Feature Flags: evaluation metrics disabled; set %s=true to enable', 'DD_METRICS_OTEL_ENABLED')
43
52
  }
@@ -49,7 +58,7 @@ class FlaggingProvider extends DatadogNodeServerProvider {
49
58
  if (config.featureFlags.DD_EXPERIMENTAL_FLAGGING_PROVIDER_SPAN_ENRICHMENT_ENABLED) {
50
59
  this.#spanEnrichmentHook = new SpanEnrichmentHook(tracer)
51
60
  // @ts-expect-error The upstream constructor always initializes its optional hooks property.
52
- this.hooks.push(this.#spanEnrichmentHook)
61
+ this.hooks.push(telemetryHook(this.#spanEnrichmentHook))
53
62
  log.info('%s span enrichment enabled', this.constructor.name)
54
63
  } else {
55
64
  log.info('%s span enrichment disabled', this.constructor.name)
@@ -60,7 +69,7 @@ class FlaggingProvider extends DatadogNodeServerProvider {
60
69
 
61
70
  if (config.featureFlags?.DD_FLAGGING_EVALUATION_COUNTS_ENABLED !== false) {
62
71
  this.#flagEvalEVPHook = new FlagEvalEVPHook(config)
63
- this.hooks.push(this.#flagEvalEVPHook)
72
+ this.hooks.push(telemetryHook(this.#flagEvalEVPHook))
64
73
  }
65
74
 
66
75
  this.#configurationSource = configurationSource.create(config, this.setConfiguration.bind(this))
@@ -74,6 +83,7 @@ class FlaggingProvider extends DatadogNodeServerProvider {
74
83
  initialize (context) {
75
84
  log.debug('Feature Flags: waiting for provider initialization...')
76
85
 
86
+ this.#waitingForConfiguration = !this.getConfiguration()
77
87
  const promise = super.initialize(context)
78
88
 
79
89
  // `DatadogNodeServerProvider#initialize` starts a timer that is never unref'd, which would
@@ -82,28 +92,45 @@ class FlaggingProvider extends DatadogNodeServerProvider {
82
92
  // TODO: remove once `@datadog/openfeature-node-server` unrefs this timer itself.
83
93
  this.initController?.timeoutId?.unref?.()
84
94
 
85
- // Only observes the outcome for logging; `promise` itself is returned unmodified below.
86
- // Guarded so a logger failure can never leave the derived promise unhandled.
87
- promise.then(
88
- () => {
89
- try {
90
- log.debug('Feature Flags: provider initialized successfully')
91
- } catch { /* logging failure must not crash the process */ }
92
- },
93
- (error) => {
94
- try {
95
- // errorWithoutTelemetry avoids inflating telemetry volume for every routine init timeout.
96
- log.errorWithoutTelemetry(
97
- 'Feature Flags: provider failed to initialize: %s',
98
- error instanceof Error ? error.message : String(error)
99
- )
100
- } catch { /* logging failure must not crash the process */ }
101
- }
102
- )
95
+ if (this.getConfiguration()) this.#logInitialConfiguration()
103
96
 
104
97
  return promise
105
98
  }
106
99
 
100
+ /**
101
+ * @param {Parameters<DatadogNodeServerProvider['setConfiguration']>[0]} configuration
102
+ */
103
+ setConfiguration (configuration) {
104
+ const wasWaiting = this.#waitingForConfiguration
105
+ super.setConfiguration(configuration)
106
+ this.#waitingForConfiguration = false
107
+ if (wasWaiting && configuration != null) this.#logInitialConfiguration()
108
+ }
109
+
110
+ /**
111
+ * @param {unknown} error
112
+ */
113
+ setError (error) {
114
+ const wasWaiting = this.#waitingForConfiguration
115
+ super.setError(error)
116
+ this.#waitingForConfiguration = false
117
+ if (!wasWaiting) return
118
+
119
+ try {
120
+ // Avoid inflating telemetry volume for routine initialization timeouts.
121
+ log.errorWithoutTelemetry(
122
+ 'Feature Flags: provider failed to initialize: %s',
123
+ error instanceof Error ? error.message : String(error)
124
+ )
125
+ } catch { /* logging failure must not affect initialization */ }
126
+ }
127
+
128
+ #logInitialConfiguration () {
129
+ try {
130
+ log.debug('Feature Flags: initial configuration available')
131
+ } catch { /* logging failure must not affect initialization */ }
132
+ }
133
+
107
134
  /**
108
135
  * Called when the provider is shut down.
109
136
  * Cleans up resources including channel subscriptions.
@@ -33,6 +33,12 @@ function _handleFlush () {
33
33
  * @param {import('../config')} config - Tracer configuration object
34
34
  */
35
35
  function enable (config) {
36
+ if (config.featureFlags?.DD_FEATURE_FLAGS_EXPOSURES_DATADOG_LOGGING_ENABLED === false) {
37
+ log.debug('Feature Flags: Datadog exposure logging disabled; set %s=true to enable',
38
+ 'DD_FEATURE_FLAGS_EXPOSURES_DATADOG_LOGGING_ENABLED')
39
+ return
40
+ }
41
+
36
42
  if (exposuresWriter) {
37
43
  log.warn('%s already enabled', exposuresWriter.constructor.name)
38
44
  return
@@ -31,14 +31,14 @@ const deliveryStrategies = new WeakMap()
31
31
  let missingRouteWarningLogged = false
32
32
 
33
33
  /**
34
- * Logs the unavailable exposure-delivery warning once.
34
+ * Logs the unavailable event-delivery warning once.
35
35
  *
36
36
  */
37
- function warnExposureDeliveryUnavailable () {
37
+ function warnEventDeliveryUnavailable () {
38
38
  if (missingRouteWarningLogged) return
39
39
  missingRouteWarningLogged = true
40
40
  logger.warn(
41
- 'Feature Flags exposure delivery is disabled because no compatible local EVP route or direct intake ' +
41
+ 'Feature Flags event delivery is disabled because no compatible local EVP route or direct intake ' +
42
42
  'credentials are available.'
43
43
  )
44
44
  }
@@ -169,7 +169,7 @@ function setAgentlessStrategy (config, setWriterEnabledValue) {
169
169
  if (error) {
170
170
  logger.debug('FFE Writer disabled - error getting local receiver info: %s', error.message)
171
171
  }
172
- warnExposureDeliveryUnavailable()
172
+ warnEventDeliveryUnavailable()
173
173
  markUnavailable()
174
174
  })
175
175
  }
@@ -52,7 +52,7 @@ function encodeHrTime (hrTime) {
52
52
  * OtlpTransformer transforms log records to OTLP format.
53
53
  *
54
54
  * This implementation follows the OTLP Logs v1.7.0 Data Model specification:
55
- * https://opentelemetry.io/docs/specs/otlp/#log-data-model
55
+ * https://github.com/open-telemetry/opentelemetry-proto/blob/v1.7.0/opentelemetry/proto/logs/v1/logs.proto
56
56
  *
57
57
  * @class OtlpTransformer
58
58
  * @augments OtlpTransformerBase
@@ -18,7 +18,7 @@ const AGGREGATION_TEMPORALITY_CUMULATIVE = protoAggregationTemporality.values.AG
18
18
  * OtlpTransformer transforms metrics to OTLP format.
19
19
  *
20
20
  * This implementation follows the OTLP Metrics v1.7.0 Data Model specification:
21
- * https://opentelemetry.io/docs/specs/otlp/#metrics-data-model
21
+ * https://github.com/open-telemetry/opentelemetry-proto/blob/v1.7.0/opentelemetry/proto/metrics/v1/metrics.proto
22
22
  *
23
23
  * @class OtlpTransformer
24
24
  * @augments OtlpTransformerBase
@@ -162,8 +162,7 @@ class PeriodicMetricReader {
162
162
  addBatchObservableCallback (callback, observables) {
163
163
  if (typeof callback !== 'function') return
164
164
  const instruments = new Set(observables?.filter(isObservableInstrument))
165
- if (instruments.size === 0) return
166
- if (this.#findBatchCallback(callback, instruments) !== -1) return
165
+ if (instruments.size === 0 || this.#findBatchCallback(callback, instruments) !== -1) return
167
166
  this.#batchCallbacks.push({ callback, instruments })
168
167
  }
169
168
 
@@ -1,4 +1,4 @@
1
- // Vendored from: https://github.com/open-telemetry/opentelemetry-proto/blob/v1.7.0/opentelemetry/proto/logs/v1/common.proto
1
+ // Vendored from: https://github.com/open-telemetry/opentelemetry-proto/blob/v1.7.0/opentelemetry/proto/common/v1/common.proto
2
2
  // Copyright 2019, OpenTelemetry Authors
3
3
  //
4
4
  // Licensed under the Apache License, Version 2.0 (the "License");
@@ -84,7 +84,7 @@ const DD_ERROR_META_KEYS = new Set(['error.message'])
84
84
  * OtlpTraceTransformer transforms DD-formatted spans to OTLP trace JSON format.
85
85
  *
86
86
  * This implementation follows the OTLP trace data model:
87
- * https://opentelemetry.io/docs/specs/otlp/#trace-data-model
87
+ * https://github.com/open-telemetry/opentelemetry-proto/blob/v1.7.0/opentelemetry/proto/trace/v1/trace.proto
88
88
  *
89
89
  * It receives DD-formatted spans (from span_format.js) and produces
90
90
  * an ExportTraceServiceRequest serialized as JSON (http/json protocol only).
@@ -95,6 +95,11 @@ const DD_ERROR_META_KEYS = new Set(['error.message'])
95
95
  class OtlpTraceTransformer extends OtlpTransformerBase {
96
96
  #otelTraceSemanticsEnabled
97
97
 
98
+ // Span meta entries with the same key and value as a resource attribute are already carried by
99
+ // the resource (e.g. DD_TAGS), so they are not repeated as span attributes.
100
+ /** @type {import('@opentelemetry/api').Attributes} */
101
+ #resourceAttributes
102
+
98
103
  /**
99
104
  * Creates a new OtlpTraceTransformer instance.
100
105
  *
@@ -104,6 +109,16 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
104
109
  constructor (resourceAttributes, otelTraceSemanticsEnabled) {
105
110
  super(resourceAttributes, 'http/json', 'traces')
106
111
  this.#otelTraceSemanticsEnabled = otelTraceSemanticsEnabled
112
+ this.#resourceAttributes = resourceAttributes
113
+ }
114
+
115
+ /**
116
+ * @override
117
+ * @param {import('@opentelemetry/api').Attributes} resourceAttributes - Resource attributes
118
+ */
119
+ updateResourceAttributes (resourceAttributes) {
120
+ super.updateResourceAttributes(resourceAttributes)
121
+ this.#resourceAttributes = resourceAttributes
107
122
  }
108
123
 
109
124
  /**
@@ -168,8 +183,8 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
168
183
 
169
184
  return {
170
185
  traceId: span.trace_id.toTraceIdHex(traceIdHigh).padStart(32, '0'),
171
- spanId: this.#idToBytes(span.span_id, 8),
172
- parentSpanId: (parentId && !parentId.equals(ZERO_ID)) ? this.#idToBytes(parentId, 8) : undefined,
186
+ spanId: this.#spanIdToHex(span.span_id),
187
+ parentSpanId: parentId && !parentId.equals(ZERO_ID) ? this.#spanIdToHex(parentId) : undefined,
173
188
  traceState: span.trace_state,
174
189
  name: span.resource,
175
190
  kind: this.#mapSpanKind(span.meta?.['span.kind']),
@@ -216,11 +231,13 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
216
231
  }
217
232
  }
218
233
 
219
- // Add meta string tags, skipping keys that map to dedicated OTLP fields
234
+ // Add meta string tags, skipping keys that map to dedicated OTLP fields and tags the
235
+ // resource already carries with the same value (e.g. global tags from DD_TAGS)
220
236
  if (span.meta) {
221
237
  for (const [key, value] of Object.entries(span.meta)) {
222
238
  if (EXCLUDED_META_KEYS.has(key)) continue
223
239
  if (this.#otelTraceSemanticsEnabled && DD_ERROR_META_KEYS.has(key)) continue
240
+ if (this.#resourceAttributes[key] === value) continue
224
241
  attributes.push({ key, value: { stringValue: value } })
225
242
  }
226
243
  }
@@ -338,26 +355,9 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
338
355
  }
339
356
  }
340
357
 
341
- /**
342
- * Converts a DD Identifier object to a hex-encoded string of the specified byte length.
343
- * Pads with leading zeros if the identifier buffer is shorter than the target.
344
- * Per the OTLP http/json spec, trace-ids and span-ids must be hex-encoded strings.
345
- *
346
- * @param {Identifier} identifier - DD Identifier
347
- * @param {number} targetLength - Target byte length (16 for trace ID, 8 for span ID)
348
- */
349
- #idToBytes (identifier, targetLength) {
350
- const buffer = identifier.toBuffer()
351
- if (buffer.length === targetLength) {
352
- return Buffer.from(buffer).toString('hex')
353
- }
354
- if (buffer.length > targetLength) {
355
- return Buffer.from(buffer.slice(buffer.length - targetLength)).toString('hex')
356
- }
357
- // Pad with leading zeros to reach target length.
358
- const result = Buffer.alloc(targetLength)
359
- Buffer.from(buffer).copy(result, targetLength - buffer.length)
360
- return result.toString('hex')
358
+ /** @param {Identifier} identifier */
359
+ #spanIdToHex (identifier) {
360
+ return identifier.toString(16).slice(-16)
361
361
  }
362
362
 
363
363
  /**
@@ -38,7 +38,7 @@ const id = require('../../id')
38
38
  const DatadogSpanContext = require('../span_context')
39
39
  const log = require('../../log')
40
40
  const tags = require('../../../../../ext/tags')
41
- const { getConfiguredEnvName } = require('../../config/helper')
41
+ const { getValidatedConfiguredEnvName } = require('../../config/helper')
42
42
  const { setAllBaggageItems, getAllBaggageItems, removeAllBaggageItems } = require('../../baggage')
43
43
  const { hasTraceSourcePropagationTag } = require('../../standalone/tracesource')
44
44
  const telemetryMetrics = require('../../telemetry/metrics')
@@ -134,8 +134,7 @@ function isOptionalDatadogTraceStateField (key, injection) {
134
134
  * @returns {DatadogSpanContext | undefined}
135
135
  */
136
136
  function extractGenericContext (traceId, spanId, radix) {
137
- if (!traceId || invalidSegment.test(traceId)) return
138
- if (!spanId) return
137
+ if (!traceId || invalidSegment.test(traceId) || !spanId) return
139
138
 
140
139
  return new DatadogSpanContext({
141
140
  traceId: id(traceId, radix),
@@ -305,13 +304,25 @@ class TextMapPropagator {
305
304
  /** @type {string[] | undefined} Source array that `#baggageTagKeysSet` was built from. */
306
305
  #baggageTagKeysSetSource
307
306
 
308
- /** @type {import('../../config')} */
307
+ /** @type {ReturnType<import('../../config')>} */
309
308
  #config
310
309
 
311
310
  /** @type {typeof extractB3SingleContext | typeof extractB3MultiContext} */
312
311
  #extractB3Context
313
312
 
314
- /** @param {import('../../config')} config */
313
+ /** Whether the `b3` injection style resolves to single-header propagation. */
314
+ #b3InjectIsSingleHeader
315
+
316
+ /** Cached origin used to resolve the v5 `b3` injection style. */
317
+ #b3InjectOrigin
318
+
319
+ /** Cached state of the v5 experimental B3 setting. */
320
+ #b3ExperimentalEnabled
321
+
322
+ /** Cached origin of the shared v5 propagation style. */
323
+ #b3SharedOrigin
324
+
325
+ /** @param {ReturnType<import('../../config')>} config */
315
326
  constructor (config) {
316
327
  this.#config = config
317
328
 
@@ -319,16 +330,44 @@ class TextMapPropagator {
319
330
  // expect single, legacy `DD_TRACE_PROPAGATION_STYLE` callers expect multi.
320
331
  /* istanbul ignore else: v5 fallback */
321
332
  if (DD_MAJOR >= 6) {
333
+ this.#b3InjectIsSingleHeader = true
322
334
  this.#extractB3Context = extractB3SingleContext
323
335
  } else {
324
- const envName = getConfiguredEnvName('DD_TRACE_PROPAGATION_STYLE')
336
+ const envName = getValidatedConfiguredEnvName('DD_TRACE_PROPAGATION_STYLE')
325
337
  // eslint-disable-next-line eslint-rules/eslint-env-aliases
326
338
  this.#extractB3Context = envName === 'OTEL_PROPAGATORS'
327
339
  ? extractB3SingleContext
328
340
  : extractB3MultiContext
341
+
342
+ this.#refreshB3InjectionStyle()
329
343
  }
330
344
  }
331
345
 
346
+ #refreshB3InjectionStyle () {
347
+ const origin = this.#config.getOrigin?.('tracePropagationStyle.inject') ?? 'default'
348
+ const experimentalEnabled = this.#config.experimental?.b3 === true
349
+ const sharedOrigin = origin === 'calculated'
350
+ ? this.#config.getOrigin?.('DD_TRACE_PROPAGATION_STYLE') ?? 'default'
351
+ : undefined
352
+ if (origin === this.#b3InjectOrigin &&
353
+ experimentalEnabled === this.#b3ExperimentalEnabled &&
354
+ sharedOrigin === this.#b3SharedOrigin) return
355
+
356
+ let envName
357
+ if (origin === 'calculated' && !experimentalEnabled && sharedOrigin !== 'remote_config') {
358
+ envName = getValidatedConfiguredEnvName('DD_TRACE_PROPAGATION_STYLE')
359
+ } else if (origin === 'env_var' ||
360
+ origin === 'local_stable_config' ||
361
+ origin === 'fleet_stable_config') {
362
+ envName = getValidatedConfiguredEnvName('DD_TRACE_PROPAGATION_STYLE_INJECT')
363
+ }
364
+ // eslint-disable-next-line eslint-rules/eslint-env-aliases
365
+ this.#b3InjectIsSingleHeader = envName === 'OTEL_PROPAGATORS'
366
+ this.#b3InjectOrigin = origin
367
+ this.#b3ExperimentalEnabled = experimentalEnabled
368
+ this.#b3SharedOrigin = sharedOrigin
369
+ }
370
+
332
371
  /**
333
372
  * Returns a `Set` view of `#config.baggageTagKeys` that is rebuilt only
334
373
  * when the source array reference changes. Avoids an `O(n)` `Set` alloc
@@ -574,9 +613,8 @@ class TextMapPropagator {
574
613
  * @returns {Record<string, string> | undefined}
575
614
  */
576
615
  #injectB3MultipleHeaders (spanContext, carrier) {
577
- // v5 also accepts the legacy `'b3'` spelling for multi; v6+ routes `'b3'` to single-header.
578
616
  const hasB3multi = this.#hasPropagationStyle('inject', 'b3multi') ||
579
- (DD_MAJOR < 6 && this.#hasPropagationStyle('inject', 'b3'))
617
+ (this.#hasPropagationStyle('inject', 'b3') && !this.#usesB3SingleHeaderInjection())
580
618
  if (!hasB3multi) return
581
619
 
582
620
  carrier ??= {}
@@ -603,7 +641,7 @@ class TextMapPropagator {
603
641
  #injectB3SingleHeader (spanContext, carrier) {
604
642
  // v6+ keeps `'b3 single header'` as a back-compat alias for callers that bypass parser normalisation.
605
643
  const hasB3SingleHeader = this.#hasPropagationStyle('inject', 'b3 single header') ||
606
- (DD_MAJOR >= 6 && this.#hasPropagationStyle('inject', 'b3'))
644
+ (this.#hasPropagationStyle('inject', 'b3') && this.#usesB3SingleHeaderInjection())
607
645
  if (!hasB3SingleHeader) return
608
646
 
609
647
  carrier ??= {}
@@ -620,6 +658,11 @@ class TextMapPropagator {
620
658
  return carrier
621
659
  }
622
660
 
661
+ #usesB3SingleHeaderInjection () {
662
+ if (DD_MAJOR < 6) this.#refreshB3InjectionStyle()
663
+ return this.#b3InjectIsSingleHeader
664
+ }
665
+
623
666
  /**
624
667
  * @param {DatadogSpanContext} spanContext
625
668
  * @param {Record<string, string> | undefined} carrier
@@ -982,11 +1025,8 @@ class TextMapPropagator {
982
1025
  const traceparent = { version }
983
1026
  // W3C Trace Context §3.3.1.1: multiple tracestate fields MUST be combined per RFC 7230 §3.2.2.
984
1027
  const tracestate = TraceState.fromString(readTracestate(carrier))
985
- if (invalidSegment.test(traceId)) return
986
- if (invalidSegment.test(spanId)) return
987
-
988
1028
  // Version ff is considered invalid
989
- if (version === 'ff') return
1029
+ if (invalidSegment.test(traceId) || invalidSegment.test(spanId) || version === 'ff') return
990
1030
 
991
1031
  // Version 00 should have no tail, but future versions may
992
1032
  if (tail && version === '00') return
@@ -11,11 +11,10 @@ const UINT64_MASK = 2n ** 64n - 1n
11
11
  /**
12
12
  * @param {unknown} value
13
13
  * @param {number} minLength
14
- * @param {number} maxLength
15
14
  * @returns {value is string}
16
15
  */
17
- function isLowerHex (value, minLength, maxLength) {
18
- if (typeof value !== 'string' || value.length < minLength || value.length > maxLength) return false
16
+ function isLowerHex (value, minLength) {
17
+ if (typeof value !== 'string' || value.length < minLength || value.length > 14) return false
19
18
 
20
19
  for (let index = 0; index < value.length; index++) {
21
20
  const code = value.charCodeAt(index)
@@ -112,11 +111,11 @@ function sanitizeFields (state) {
112
111
  let randomValue = state.get('rv')
113
112
  let threshold = state.get('th')
114
113
 
115
- if (!isLowerHex(randomValue, 14, 14)) {
114
+ if (!isLowerHex(randomValue, 14)) {
116
115
  state.delete('rv')
117
116
  randomValue = undefined
118
117
  }
119
- if (!isLowerHex(threshold, 1, 14)) {
118
+ if (!isLowerHex(threshold, 1)) {
120
119
  state.delete('th')
121
120
  threshold = undefined
122
121
  }