datadog 2.39.0 → 2.47.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 (296) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +269 -44
  3. data/README.md +3 -0
  4. data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +12 -24
  5. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +256 -193
  6. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -5
  7. data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +0 -2
  8. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +19 -20
  9. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.h +5 -1
  10. data/ext/datadog_profiling_native_extension/collectors_stack.c +267 -171
  11. data/ext/datadog_profiling_native_extension/collectors_stack.h +17 -3
  12. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +307 -217
  13. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +10 -5
  14. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +5 -4
  15. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +19 -11
  16. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +7 -10
  17. data/ext/datadog_profiling_native_extension/extconf.rb +67 -99
  18. data/ext/datadog_profiling_native_extension/heap_recorder.c +353 -257
  19. data/ext/datadog_profiling_native_extension/heap_recorder.h +50 -31
  20. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +3 -27
  21. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +853 -496
  22. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +42 -15
  23. data/ext/datadog_profiling_native_extension/profiling.c +30 -23
  24. data/ext/datadog_profiling_native_extension/ruby_helpers.c +4 -80
  25. data/ext/datadog_profiling_native_extension/ruby_helpers.h +4 -7
  26. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +0 -2
  27. data/ext/datadog_profiling_native_extension/stack_recorder.c +116 -89
  28. data/ext/datadog_profiling_native_extension/stack_recorder.h +12 -5
  29. data/ext/datadog_profiling_native_extension/time_helpers.c +2 -4
  30. data/ext/datadog_profiling_native_extension/time_helpers.h +15 -15
  31. data/ext/libdatadog_api/datadog_ruby_common.c +19 -11
  32. data/ext/libdatadog_api/datadog_ruby_common.h +7 -10
  33. data/ext/libdatadog_api/di.c +43 -3
  34. data/ext/libdatadog_api/extconf.rb +6 -1
  35. data/ext/libdatadog_api/feature_flags.c +8 -0
  36. data/ext/libdatadog_api/init.c +9 -1
  37. data/ext/libdatadog_api/otel_thread_context.c +274 -0
  38. data/ext/libdatadog_api/otel_thread_context.h +5 -0
  39. data/ext/libdatadog_api/process_discovery.c +15 -1
  40. data/ext/libdatadog_api/trace_exporter.c +2078 -0
  41. data/ext/libdatadog_api/trace_exporter.h +5 -0
  42. data/ext/libdatadog_extconf_helpers.rb +14 -9
  43. data/lib/datadog/ai_guard/component.rb +17 -8
  44. data/lib/datadog/ai_guard/configuration/ext.rb +2 -0
  45. data/lib/datadog/ai_guard/configuration.rb +13 -0
  46. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +48 -55
  47. data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +1 -1
  48. data/lib/datadog/ai_guard/contrib/ruby_llm/message_adapter.rb +76 -0
  49. data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +1 -0
  50. data/lib/datadog/ai_guard/contrib/ruby_llm/redaction_change_set.rb +135 -0
  51. data/lib/datadog/ai_guard/evaluation/client.rb +50 -0
  52. data/lib/datadog/ai_guard/evaluation/content_part.rb +12 -0
  53. data/lib/datadog/ai_guard/evaluation/message.rb +25 -7
  54. data/lib/datadog/ai_guard/evaluation/message_builder.rb +31 -0
  55. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +6 -6
  56. data/lib/datadog/ai_guard/evaluation/outcome.rb +24 -0
  57. data/lib/datadog/ai_guard/evaluation/request.rb +8 -64
  58. data/lib/datadog/ai_guard/evaluation/response.rb +33 -0
  59. data/lib/datadog/ai_guard/evaluation/result.rb +11 -18
  60. data/lib/datadog/ai_guard/evaluation/tool_call.rb +15 -2
  61. data/lib/datadog/ai_guard/evaluation.rb +36 -14
  62. data/lib/datadog/ai_guard/ext.rb +4 -1
  63. data/lib/datadog/ai_guard/{api_client.rb → http_client.rb} +7 -5
  64. data/lib/datadog/ai_guard/metrics/telemetry.rb +43 -0
  65. data/lib/datadog/ai_guard/redaction/replacements.rb +97 -0
  66. data/lib/datadog/ai_guard/redaction/result.rb +29 -0
  67. data/lib/datadog/ai_guard/redaction.rb +80 -0
  68. data/lib/datadog/ai_guard.rb +58 -57
  69. data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +21 -42
  70. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +1 -1
  71. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +1 -1
  72. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +1 -1
  73. data/lib/datadog/appsec/assets/blocked.html +1 -108
  74. data/lib/datadog/appsec/configuration.rb +11 -2
  75. data/lib/datadog/appsec/context.rb +1 -1
  76. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +1 -1
  77. data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +2 -2
  78. data/lib/datadog/appsec/contrib/devise/configuration.rb +1 -1
  79. data/lib/datadog/appsec/contrib/devise/patcher.rb +1 -1
  80. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +2 -2
  81. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +2 -2
  82. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +1 -1
  83. data/lib/datadog/appsec/contrib/rack/ext.rb +2 -2
  84. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +2 -2
  85. data/lib/datadog/appsec/contrib/rack/patcher.rb +1 -1
  86. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +6 -3
  87. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +30 -3
  88. data/lib/datadog/appsec/contrib/rails/patches/render_to_body_patch.rb +8 -7
  89. data/lib/datadog/appsec/contrib/rails/request_middleware.rb +2 -1
  90. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +2 -2
  91. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +15 -3
  92. data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +8 -7
  93. data/lib/datadog/appsec/contrib/sinatra/request_middleware.rb +2 -1
  94. data/lib/datadog/appsec/processor/rule_loader.rb +12 -12
  95. data/lib/datadog/appsec/response.rb +1 -1
  96. data/lib/datadog/appsec/route_normalizer/route_pattern.rb +3 -3
  97. data/lib/datadog/appsec/security_engine/engine.rb +5 -4
  98. data/lib/datadog/core/configuration/components.rb +50 -19
  99. data/lib/datadog/core/configuration/components_state.rb +8 -1
  100. data/lib/datadog/core/configuration/option_definition.rb +1 -1
  101. data/lib/datadog/core/configuration/options.rb +1 -1
  102. data/lib/datadog/core/configuration/settings.rb +38 -43
  103. data/lib/datadog/core/configuration/supported_configurations.rb +18 -2
  104. data/lib/datadog/core/crashtracking/component.rb +5 -1
  105. data/lib/datadog/core/diagnostics/environment_logger.rb +36 -1
  106. data/lib/datadog/core/environment/gc.rb +1 -1
  107. data/lib/datadog/core/environment/vm_cache.rb +1 -1
  108. data/lib/datadog/core/environment/yjit.rb +2 -2
  109. data/lib/datadog/core/metrics/helpers.rb +1 -1
  110. data/lib/datadog/core/metrics/options.rb +2 -2
  111. data/lib/datadog/core/process_discovery.rb +6 -2
  112. data/lib/datadog/core/remote/client/capabilities.rb +32 -21
  113. data/lib/datadog/core/remote/client.rb +45 -4
  114. data/lib/datadog/core/remote/component.rb +30 -7
  115. data/lib/datadog/core/remote/configuration/repository.rb +2 -2
  116. data/lib/datadog/core/remote/dispatcher.rb +47 -6
  117. data/lib/datadog/core/remote/worker.rb +8 -0
  118. data/lib/datadog/core/telemetry/configuration_value.rb +38 -0
  119. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +3 -1
  120. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +1 -1
  121. data/lib/datadog/core/telemetry/event/app_started.rb +5 -24
  122. data/lib/datadog/core/telemetry/event/generate_metrics.rb +1 -1
  123. data/lib/datadog/core/telemetry/event/log.rb +2 -2
  124. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +1 -1
  125. data/lib/datadog/core/telemetry/metric.rb +2 -2
  126. data/lib/datadog/core/telemetry/request.rb +2 -2
  127. data/lib/datadog/core/transport/http.rb +1 -1
  128. data/lib/datadog/core/transport/transport.rb +1 -1
  129. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +170 -18
  130. data/lib/datadog/core/utils/sequence.rb +3 -6
  131. data/lib/datadog/core/utils/time.rb +16 -54
  132. data/lib/datadog/core/workers/runtime_metrics.rb +1 -1
  133. data/lib/datadog/data_streams/pathway_context.rb +20 -22
  134. data/lib/datadog/data_streams/processor.rb +45 -14
  135. data/lib/datadog/di/boot.rb +1 -0
  136. data/lib/datadog/di/capture_expression_evaluator/result.rb +30 -0
  137. data/lib/datadog/di/capture_expression_evaluator.rb +39 -9
  138. data/lib/datadog/di/component.rb +108 -3
  139. data/lib/datadog/di/configuration.rb +53 -2
  140. data/lib/datadog/di/context.rb +7 -2
  141. data/lib/datadog/di/contrib/active_record.rb +8 -2
  142. data/lib/datadog/di/el/compiler.rb +88 -12
  143. data/lib/datadog/di/el/evaluator.rb +82 -2
  144. data/lib/datadog/di/el/expression.rb +13 -2
  145. data/lib/datadog/di/evaluation_failure.rb +28 -0
  146. data/lib/datadog/di/guardrails_telemetry/reason.rb +87 -0
  147. data/lib/datadog/di/guardrails_telemetry.rb +203 -0
  148. data/lib/datadog/di/instrumenter.rb +106 -15
  149. data/lib/datadog/di/logger.rb +32 -10
  150. data/lib/datadog/di/probe.rb +9 -1
  151. data/lib/datadog/di/probe_builder.rb +7 -6
  152. data/lib/datadog/di/probe_manager.rb +56 -8
  153. data/lib/datadog/di/probe_notification_builder.rb +31 -28
  154. data/lib/datadog/di/probe_notifier_worker.rb +56 -6
  155. data/lib/datadog/di/proc_responder.rb +1 -1
  156. data/lib/datadog/di/remote.rb +49 -5
  157. data/lib/datadog/di/serializer.rb +172 -55
  158. data/lib/datadog/di/snapshot_event.rb +38 -0
  159. data/lib/datadog/di/transport/http.rb +13 -3
  160. data/lib/datadog/di/transport/input.rb +32 -7
  161. data/lib/datadog/error_tracking/component.rb +1 -1
  162. data/lib/datadog/kit/appsec/events/v2.rb +2 -2
  163. data/lib/datadog/open_feature/activation.rb +268 -0
  164. data/lib/datadog/open_feature/agentless/configuration_source.rb +246 -0
  165. data/lib/datadog/open_feature/agentless/parser.rb +43 -0
  166. data/lib/datadog/open_feature/agentless/response.rb +19 -0
  167. data/lib/datadog/open_feature/agentless/transport.rb +103 -0
  168. data/lib/datadog/open_feature/component.rb +85 -13
  169. data/lib/datadog/open_feature/configuration/agentless_endpoint.rb +90 -0
  170. data/lib/datadog/open_feature/configuration/source/resolution.rb +22 -0
  171. data/lib/datadog/open_feature/configuration/source.rb +43 -0
  172. data/lib/datadog/open_feature/configuration.rb +124 -0
  173. data/lib/datadog/open_feature/evaluation_engine.rb +29 -3
  174. data/lib/datadog/open_feature/exposures/batch_builder.rb +1 -1
  175. data/lib/datadog/open_feature/exposures/event.rb +15 -8
  176. data/lib/datadog/open_feature/ext.rb +19 -0
  177. data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +236 -80
  178. data/lib/datadog/open_feature/flag_evaluation/writer.rb +179 -68
  179. data/lib/datadog/open_feature/hooks/evaluation_telemetry_opt_out_hook.rb +25 -0
  180. data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +24 -21
  181. data/lib/datadog/open_feature/native_evaluator.rb +20 -6
  182. data/lib/datadog/open_feature/noop_evaluator.rb +5 -0
  183. data/lib/datadog/open_feature/provider.rb +315 -10
  184. data/lib/datadog/open_feature/remote.rb +5 -5
  185. data/lib/datadog/open_feature/transport.rb +5 -0
  186. data/lib/datadog/open_feature.rb +31 -1
  187. data/lib/datadog/opentelemetry/sdk/propagator.rb +1 -1
  188. data/lib/datadog/opentelemetry/signal_configuration.rb +13 -9
  189. data/lib/datadog/opentelemetry/trace.rb +3 -0
  190. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +10 -2
  191. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +5 -7
  192. data/lib/datadog/profiling/collectors/info.rb +40 -13
  193. data/lib/datadog/profiling/collectors/thread_context.rb +6 -17
  194. data/lib/datadog/profiling/component.rb +33 -39
  195. data/lib/datadog/profiling/exporter.rb +10 -10
  196. data/lib/datadog/profiling/tasks/exec.rb +1 -1
  197. data/lib/datadog/profiling.rb +4 -0
  198. data/lib/datadog/symbol_database/extractor.rb +89 -107
  199. data/lib/datadog/tracing/component.rb +44 -2
  200. data/lib/datadog/tracing/configuration/dynamic/option.rb +4 -0
  201. data/lib/datadog/tracing/configuration/dynamic.rb +5 -3
  202. data/lib/datadog/tracing/configuration/ext.rb +4 -0
  203. data/lib/datadog/tracing/configuration/settings.rb +36 -0
  204. data/lib/datadog/tracing/context.rb +24 -2
  205. data/lib/datadog/tracing/context_provider.rb +17 -5
  206. data/lib/datadog/tracing/contrib/action_cable/events.rb +1 -1
  207. data/lib/datadog/tracing/contrib/action_mailer/events.rb +1 -1
  208. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +2 -2
  209. data/lib/datadog/tracing/contrib/action_view/events.rb +1 -1
  210. data/lib/datadog/tracing/contrib/active_model_serializers/events.rb +1 -1
  211. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +1 -1
  212. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -0
  213. data/lib/datadog/tracing/contrib/active_record/events.rb +1 -1
  214. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +2 -2
  215. data/lib/datadog/tracing/contrib/aws/ext.rb +1 -1
  216. data/lib/datadog/tracing/contrib/aws/services.rb +1 -1
  217. data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +1 -1
  218. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +10 -5
  219. data/lib/datadog/tracing/contrib/excon/middleware.rb +10 -3
  220. data/lib/datadog/tracing/contrib/ext.rb +3 -3
  221. data/lib/datadog/tracing/contrib/faraday/middleware.rb +10 -4
  222. data/lib/datadog/tracing/contrib/grape/endpoint.rb +32 -5
  223. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +1 -1
  224. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +1 -1
  225. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +1 -1
  226. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +2 -1
  227. data/lib/datadog/tracing/contrib/http/instrumentation.rb +16 -1
  228. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +8 -2
  229. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +10 -4
  230. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +4 -0
  231. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +7 -2
  232. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +7 -2
  233. data/lib/datadog/tracing/contrib/kafka/events.rb +1 -1
  234. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +1 -1
  235. data/lib/datadog/tracing/contrib/mongodb/parsers.rb +1 -1
  236. data/lib/datadog/tracing/contrib/opensearch/ext.rb +1 -1
  237. data/lib/datadog/tracing/contrib/opensearch/quantize.rb +1 -1
  238. data/lib/datadog/tracing/contrib/patchable.rb +1 -1
  239. data/lib/datadog/tracing/contrib/patcher.rb +1 -1
  240. data/lib/datadog/tracing/contrib/que/tracer.rb +1 -1
  241. data/lib/datadog/tracing/contrib/racecar/events.rb +1 -1
  242. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +1 -1
  243. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +1 -1
  244. data/lib/datadog/tracing/contrib/rack/patcher.rb +1 -1
  245. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +1 -1
  246. data/lib/datadog/tracing/contrib/redis/configuration/resolver.rb +1 -1
  247. data/lib/datadog/tracing/contrib/redis/quantize.rb +36 -2
  248. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +9 -3
  249. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +10 -3
  250. data/lib/datadog/tracing/contrib/sequel/database.rb +1 -5
  251. data/lib/datadog/tracing/contrib/sequel/jdbc_connection_string.rb +140 -0
  252. data/lib/datadog/tracing/contrib/sequel/utils.rb +50 -63
  253. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +1 -1
  254. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +1 -1
  255. data/lib/datadog/tracing/contrib/sneakers/tracer.rb +1 -1
  256. data/lib/datadog/tracing/contrib/utils/quantization/hash_formatter.rb +1 -1
  257. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +108 -3
  258. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +1 -1
  259. data/lib/datadog/tracing/correlation.rb +2 -2
  260. data/lib/datadog/tracing/diagnostics/environment_logger.rb +3 -2
  261. data/lib/datadog/tracing/distributed/baggage.rb +1 -3
  262. data/lib/datadog/tracing/distributed/datadog.rb +3 -3
  263. data/lib/datadog/tracing/distributed/propagation.rb +3 -0
  264. data/lib/datadog/tracing/distributed/propagation_policy.rb +5 -0
  265. data/lib/datadog/tracing/distributed/trace_context.rb +14 -271
  266. data/lib/datadog/tracing/distributed/trace_state/datadog.rb +233 -0
  267. data/lib/datadog/tracing/distributed/trace_state/ext.rb +44 -0
  268. data/lib/datadog/tracing/distributed/trace_state/open_telemetry.rb +156 -0
  269. data/lib/datadog/tracing/distributed/trace_state.rb +121 -0
  270. data/lib/datadog/tracing/metadata/ext.rb +3 -0
  271. data/lib/datadog/tracing/metadata/tagging.rb +1 -1
  272. data/lib/datadog/tracing/otel_thread_context.rb +97 -0
  273. data/lib/datadog/tracing/remote.rb +236 -37
  274. data/lib/datadog/tracing/sampling/rule_sampler.rb +2 -0
  275. data/lib/datadog/tracing/span.rb +1 -1
  276. data/lib/datadog/tracing/span_operation.rb +1 -1
  277. data/lib/datadog/tracing/sync_writer.rb +8 -3
  278. data/lib/datadog/tracing/trace_digest.rb +22 -4
  279. data/lib/datadog/tracing/trace_operation.rb +22 -10
  280. data/lib/datadog/tracing/tracer.rb +28 -7
  281. data/lib/datadog/tracing/transport/http/telemetry.rb +94 -0
  282. data/lib/datadog/tracing/transport/http/traces.rb +2 -0
  283. data/lib/datadog/tracing/transport/io/traces.rb +1 -1
  284. data/lib/datadog/tracing/transport/native/response.rb +71 -0
  285. data/lib/datadog/tracing/transport/native/telemetry.rb +17 -0
  286. data/lib/datadog/tracing/transport/native.rb +395 -0
  287. data/lib/datadog/tracing/transport/span_events_negotiation.rb +41 -0
  288. data/lib/datadog/tracing/transport/telemetry.rb +62 -0
  289. data/lib/datadog/tracing/transport/trace_formatter.rb +14 -4
  290. data/lib/datadog/tracing/transport/traces.rb +85 -38
  291. data/lib/datadog/tracing/writer.rb +17 -4
  292. data/lib/datadog/version.rb +1 -1
  293. metadata +51 -13
  294. data/lib/datadog/ai_guard/evaluation/content_builder.rb +0 -31
  295. data/lib/datadog/appsec/utils/hash_coercion.rb +0 -23
  296. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +0 -69
@@ -43,7 +43,7 @@ module Datadog
43
43
  # These are expected failures — no logging needed.
44
44
  #
45
45
  # 2. **Method-level rescues** (`rescue => e` with logging):
46
- # Catch failures in extract_method_scope, find_source_file, etc. Log at debug
46
+ # Catch failures in build_class_method_scopes, find_source_file, etc. Log at debug
47
47
  # for post-hoc diagnosis, return nil or empty array. One bad method/module
48
48
  # doesn't kill the entire class extraction.
49
49
  #
@@ -133,16 +133,16 @@ module Datadog
133
133
  return nil unless Module === mod
134
134
  mod_name = safe_mod_name(mod)
135
135
  return nil unless mod_name
136
-
137
- return nil unless user_code_module?(mod)
136
+ return nil unless user_code_module_name?(mod)
138
137
 
139
138
  source_file = find_source_file(mod)
140
139
  return nil unless source_file
140
+ return nil unless user_code_path?(source_file)
141
141
 
142
142
  inner_scope = if Class === mod
143
- extract_class_scope(mod)
143
+ extract_class_scope(mod, source_file)
144
144
  else
145
- extract_module_scope(mod)
145
+ extract_module_scope(mod, source_file)
146
146
  end
147
147
 
148
148
  wrap_in_file_scope(source_file, [inner_scope])
@@ -275,10 +275,12 @@ module Datadog
275
275
  false
276
276
  end
277
277
 
278
- # Check if module is from user code (not gems or stdlib)
279
- # @param mod [Module] The module to check
280
- # @return [Boolean] true if user code
281
- def user_code_module?(mod)
278
+ # Return whether +mod+ passes the name-based user-code exclusions
279
+ # (named, outside Datadog, not a Ruby root class). Does not resolve
280
+ # a source file.
281
+ # @param mod [Module]
282
+ # @return [Boolean]
283
+ def user_code_module_name?(mod)
282
284
  mod_name = safe_mod_name(mod)
283
285
  return false unless mod_name
284
286
 
@@ -297,6 +299,15 @@ module Datadog
297
299
  return false if mod.equal?(Object) || mod.equal?(BasicObject) ||
298
300
  mod.equal?(Kernel) || mod.equal?(Module) || mod.equal?(Class)
299
301
 
302
+ true
303
+ end
304
+
305
+ # Check if module is from user code (not gems or stdlib)
306
+ # @param mod [Module] The module to check
307
+ # @return [Boolean] true if user code
308
+ def user_code_module?(mod)
309
+ return false unless user_code_module_name?(mod)
310
+
300
311
  source_file = find_source_file(mod)
301
312
  return false unless source_file
302
313
 
@@ -331,6 +342,25 @@ module Datadog
331
342
  true
332
343
  end
333
344
 
345
+ # Resolve the UnboundMethod +mod+ itself declares for +method_name+,
346
+ # skipping prepended wrapper modules (e.g. a DI method-logpoint wrapper)
347
+ # that shadow it.
348
+ #
349
+ # @param mod [Module] Module or class expected to declare the method
350
+ # @param method_name [Symbol] Name of the method to resolve
351
+ # @return [UnboundMethod] Method whose owner is +mod+, or the
352
+ # prepend-resolved method when +mod+ owns no method in the super chain
353
+ def declared_instance_method(mod, method_name)
354
+ original_method = mod.instance_method(method_name)
355
+ method = original_method
356
+ until method.owner.equal?(mod)
357
+ super_method = method.super_method
358
+ return original_method unless super_method
359
+ method = super_method
360
+ end
361
+ method
362
+ end
363
+
334
364
  # Find source file for a module.
335
365
  # Prefers user code paths over gem/stdlib paths. ActiveRecord models have
336
366
  # generated methods (autosave callbacks) whose source is in the gem, but
@@ -349,7 +379,7 @@ module Datadog
349
379
 
350
380
  # Try instance methods first
351
381
  mod.instance_methods(false).each do |method_name|
352
- method = mod.instance_method(method_name)
382
+ method = declared_instance_method(mod, method_name)
353
383
  location = method.source_location
354
384
  next unless location
355
385
 
@@ -357,10 +387,15 @@ module Datadog
357
387
  return path if user_code_path?(path)
358
388
 
359
389
  fallback ||= path # steep:ignore
390
+ rescue Exception => e # standard:disable Lint/RescueException
391
+ Datadog::DI.reraise_if_fatal(e)
392
+ @logger.debug { "symdb: error resolving #{safe_mod_name(mod)}##{method_name}: #{e.class}: #{e.message}" }
360
393
  end
361
394
 
362
395
  # Try singleton methods
363
396
  mod.singleton_methods(false).each do |method_name|
397
+ # Method-logpoint probes prepend instance methods only, so singleton
398
+ # methods are never shadowed by a DI wrapper.
364
399
  method = mod.method(method_name)
365
400
  location = method.source_location
366
401
  next unless location
@@ -369,6 +404,9 @@ module Datadog
369
404
  return path if user_code_path?(path)
370
405
 
371
406
  fallback ||= path # steep:ignore
407
+ rescue Exception => e # standard:disable Lint/RescueException
408
+ Datadog::DI.reraise_if_fatal(e)
409
+ @logger.debug { "symdb: error resolving #{safe_mod_name(mod)}.#{method_name}: #{e.class}: #{e.message}" }
372
410
  end
373
411
 
374
412
  # Use const_source_location to find where this class/module is declared.
@@ -460,10 +498,9 @@ module Datadog
460
498
  # Extract MODULE scope (without file_hash — that belongs on the FILE root scope).
461
499
  # Does not include nested classes — nesting is handled by extract_all via FQN splitting.
462
500
  # @param mod [Module] The module
501
+ # @param source_file [String] Source file resolved by the caller
463
502
  # @return [Scope] The module scope
464
- def extract_module_scope(mod)
465
- source_file = find_source_file(mod)
466
-
503
+ def extract_module_scope(mod, source_file)
467
504
  Scope.new(
468
505
  scope_type: "MODULE",
469
506
  name: safe_mod_name(mod),
@@ -476,11 +513,11 @@ module Datadog
476
513
 
477
514
  # Extract CLASS scope
478
515
  # @param klass [Class] The class
516
+ # @param source_file [String] Source file resolved by the caller
479
517
  # @return [Scope] The class scope
480
- def extract_class_scope(klass)
481
- methods = klass.instance_methods(false)
482
- start_line, end_line = calculate_class_line_range(klass, methods)
483
- source_file = find_source_file(klass)
518
+ def extract_class_scope(klass, source_file)
519
+ method_scopes = build_class_method_scopes(klass)
520
+ start_line, end_line = line_range_from_scopes(method_scopes)
484
521
 
485
522
  Scope.new(
486
523
  scope_type: "CLASS",
@@ -489,36 +526,20 @@ module Datadog
489
526
  start_line: start_line,
490
527
  end_line: end_line,
491
528
  language_specifics: build_class_language_specifics(klass),
492
- scopes: extract_method_scopes(klass),
529
+ scopes: method_scopes,
493
530
  symbols: extract_scope_symbols(klass)
494
531
  )
495
532
  end
496
533
 
497
- # Calculate class line range from method locations.
498
- # Start from the earliest method start, end at the latest method end (derived
499
- # from iseq trace_points so methods spanning multiple lines aren't truncated).
500
- # @param klass [Class] The class
501
- # @param methods [Array<Symbol>] Method names
534
+ # Derive a [start_line, end_line] range from method scopes, ignoring
535
+ # UNKNOWN sentinel values. The end is the max of method end_lines so a
536
+ # multi-line method's last line is not truncated to its start_line.
537
+ # @param method_scopes [Array<Scope>]
502
538
  # @return [Array<Integer, Integer>] [start_line, end_line]
503
- def calculate_class_line_range(klass, methods)
504
- starts = []
505
- ends = []
506
- methods.each do |method_name|
507
- method = klass.instance_method(method_name)
508
- location = method.source_location
509
- next unless location && location[0]
510
- starts << location[1]
511
- _ranges, method_end = extract_targetable_lines(method, location[1])
512
- ends << method_end
513
- end
514
-
515
- return [UNKNOWN_MIN_LINE, UNKNOWN_MAX_LINE] if starts.empty?
516
-
517
- [starts.min, ends.max]
518
- rescue Exception => e # standard:disable Lint/RescueException
519
- Datadog::DI.reraise_if_fatal(e)
520
- @logger.debug { "symdb: error calculating line range for #{safe_mod_name(klass)}: #{e.class}: #{e.message}" }
521
- [UNKNOWN_MIN_LINE, UNKNOWN_MAX_LINE]
539
+ def line_range_from_scopes(method_scopes)
540
+ starts = method_scopes.map(&:start_line).reject { |line| line == UNKNOWN_MIN_LINE }
541
+ ends = method_scopes.map(&:end_line).reject { |line| line == UNKNOWN_MAX_LINE }
542
+ [starts.min || UNKNOWN_MIN_LINE, ends.max || UNKNOWN_MAX_LINE]
522
543
  end
523
544
 
524
545
  # Build language specifics for CLASS
@@ -566,66 +587,33 @@ module Datadog
566
587
  {}
567
588
  end
568
589
 
569
- # Extract method scopes from a class
570
- # @param klass [Class] The class
571
- # @return [Array<Scope>] Method scopes
572
- def extract_method_scopes(klass)
573
- scopes = []
574
-
575
- # Get all instance methods (public, protected, private)
576
- all_instance_methods = klass.instance_methods(false) +
590
+ # Build a METHOD scope for each user-code instance method +klass+
591
+ # declares.
592
+ # @param klass [Class]
593
+ # @return [Array<Scope>]
594
+ def build_class_method_scopes(klass)
595
+ method_names = (klass.instance_methods(false) +
577
596
  klass.protected_instance_methods(false) +
578
- klass.private_instance_methods(false)
579
- all_instance_methods.uniq!
597
+ klass.private_instance_methods(false)).uniq
580
598
 
581
- all_instance_methods.each do |method_name|
582
- method_scope = extract_method_scope(klass, method_name, :instance)
583
- scopes << method_scope if method_scope
584
- end
599
+ Core::Utils::EnumerableCompat.filter_map(method_names) do |method_name|
600
+ method = declared_instance_method(klass, method_name)
601
+ location = method.source_location
602
+ next unless location
603
+ next unless user_code_path?(location[0])
585
604
 
586
- scopes
605
+ build_instance_method_scope(klass, method_name, method)
606
+ rescue Exception => e # standard:disable Lint/RescueException
607
+ Datadog::DI.reraise_if_fatal(e)
608
+ @logger.debug { "symdb: error resolving #{safe_mod_name(klass)}##{method_name}: #{e.class}: #{e.message}" }
609
+ nil
610
+ end
587
611
  rescue Exception => e # standard:disable Lint/RescueException
588
612
  Datadog::DI.reraise_if_fatal(e)
589
613
  @logger.debug { "symdb: failed to extract methods from #{safe_mod_name(klass)}: #{e.class}: #{e.message}" }
590
614
  []
591
615
  end
592
616
 
593
- # Extract a single method scope
594
- # @param klass [Class] The class
595
- # @param method_name [Symbol] Method name
596
- # @param method_type [Symbol] :instance or :class
597
- # @return [Scope, nil] Method scope or nil
598
- def extract_method_scope(klass, method_name, method_type)
599
- method = klass.instance_method(method_name)
600
- location = method.source_location
601
-
602
- return nil unless location # Skip methods without source location
603
-
604
- source_file, line = location
605
- return nil unless user_code_path?(source_file) # Skip gem/stdlib methods
606
-
607
- targetable_lines, end_line = extract_targetable_lines(method, line)
608
-
609
- Scope.new(
610
- scope_type: "METHOD",
611
- name: method_name.to_s,
612
- source_file: source_file,
613
- start_line: line,
614
- end_line: end_line,
615
- targetable_lines: targetable_lines,
616
- language_specifics: {
617
- visibility: method_visibility(klass, method_name),
618
- method_type: method_type.to_s,
619
- arity: method.arity
620
- },
621
- symbols: extract_method_parameters(method)
622
- )
623
- rescue Exception => e # standard:disable Lint/RescueException
624
- Datadog::DI.reraise_if_fatal(e)
625
- @logger.debug { "symdb: failed to extract method #{safe_mod_name(klass)}##{method_name}: #{e.class}: #{e.message}" }
626
- nil
627
- end
628
-
629
617
  # Get method visibility
630
618
  # @param klass [Class] The class
631
619
  # @param method_name [Symbol] Method name
@@ -647,7 +635,7 @@ module Datadog
647
635
  # @param start_line [Integer] Fallback end_line if iseq unavailable
648
636
  # @return [Array(Array<Hash>, Integer), Array(nil, Integer)]
649
637
  def extract_targetable_lines(method, start_line)
650
- iseq = RubyVM::InstructionSequence.of(method) # steep:ignore
638
+ iseq = RubyVM::InstructionSequence.of(method)
651
639
  unless iseq
652
640
  @logger.debug { "symdb: no iseq for #{method.name} (C extension or native), skipping targetable lines" }
653
641
  return [nil, start_line]
@@ -801,14 +789,14 @@ module Datadog
801
789
  # objects allocated to read `source_location` are not retained between
802
790
  # passes, so they can be GC'd as soon as the inner loop ends.
803
791
  def collect_method_names_by_file(mod)
804
- result = Hash.new { |h, k| h[k] = [] } # steep:ignore
792
+ result = Hash.new { |h, k| h[k] = [] }
805
793
 
806
794
  # Module#instance_methods(false) already returns both public and protected
807
795
  # methods, so iterating it plus private_instance_methods covers all three
808
796
  # visibilities without an intermediate merged array.
809
797
  [mod.instance_methods(false), mod.private_instance_methods(false)].each do |method_names|
810
798
  method_names.each do |method_name|
811
- method = mod.instance_method(method_name)
799
+ method = declared_instance_method(mod, method_name)
812
800
  loc = method.source_location
813
801
  next unless loc
814
802
  next unless user_code_path?(loc[0])
@@ -852,7 +840,7 @@ module Datadog
852
840
  # live only as long as the tree node holds them; they are released when
853
841
  # convert_tree_to_scope finishes building the file's Scope.
854
842
  method_infos = Core::Utils::EnumerableCompat.filter_map(method_names) do |name|
855
- method = mod.instance_method(name)
843
+ method = declared_instance_method(mod, name)
856
844
  # Pass 1 (build_per_file_index) recorded this method under file_path.
857
845
  # If the method has been redefined in another file between the two
858
846
  # passes (e.g. a class reopened during a Rails reload while extract_all
@@ -905,7 +893,7 @@ module Datadog
905
893
  current[:children][part] ||= {
906
894
  name: fqn, type: resolve_scope_type(fqn),
907
895
  children: {}, methods: [], mod: nil,
908
- source_file: file_path, fqn: fqn
896
+ source_file: file_path, fqn: fqn,
909
897
  }
910
898
  current = current[:children][part]
911
899
  end
@@ -924,7 +912,7 @@ module Datadog
924
912
  type: (Class === mod) ? "CLASS" : "MODULE",
925
913
  children: {}, methods: [],
926
914
  mod: mod, source_file: file_path,
927
- fqn: mod_name
915
+ fqn: mod_name,
928
916
  }
929
917
  current[:children][leaf_name] = leaf
930
918
  end
@@ -989,13 +977,7 @@ module Datadog
989
977
  # Recurse into child scopes (nested modules/classes)
990
978
  child_scopes = node[:children].values.map { |child| convert_node_to_scope(child) }
991
979
 
992
- # Compute line range: start from the earliest method start, end at the latest
993
- # method end. Using max(start_line) would underreport the class's end_line for
994
- # classes whose last method spans multiple lines.
995
- starts = method_scopes.map(&:start_line).reject { |l| l == UNKNOWN_MIN_LINE } # steep:ignore
996
- ends = method_scopes.map(&:end_line).reject { |l| l == UNKNOWN_MAX_LINE } # steep:ignore
997
- start_line = starts.empty? ? UNKNOWN_MIN_LINE : starts.min
998
- end_line = ends.empty? ? UNKNOWN_MAX_LINE : ends.max
980
+ start_line, end_line = line_range_from_scopes(method_scopes)
999
981
 
1000
982
  # Extract symbols (constants, class variables) if we have the actual module object
1001
983
  symbols = node[:mod] ? extract_scope_symbols(node[:mod]) : []
@@ -1043,7 +1025,7 @@ module Datadog
1043
1025
  language_specifics: {
1044
1026
  visibility: klass ? method_visibility(klass, method_name) : "public", # steep:ignore
1045
1027
  method_type: "instance",
1046
- arity: method.arity
1028
+ arity: method.arity,
1047
1029
  },
1048
1030
  symbols: extract_method_parameters(method)
1049
1031
  )
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "otel_thread_context"
3
4
  require_relative "tracer"
4
5
  require_relative "flush"
5
6
  require_relative "sync_writer"
@@ -20,14 +21,14 @@ module Datadog
20
21
  tracer = settings.tracing.instance
21
22
  return tracer unless tracer.nil?
22
23
 
24
+ sampler = build_sampler(settings)
25
+
23
26
  # Apply test mode settings if test mode is activated
24
27
  if settings.tracing.test_mode.enabled
25
28
  trace_flush = build_test_mode_trace_flush(settings)
26
- sampler = build_test_mode_sampler
27
29
  writer = build_test_mode_writer(settings, agent_settings)
28
30
  else
29
31
  trace_flush = build_trace_flush(settings)
30
- sampler = build_sampler(settings)
31
32
  writer = build_writer(settings, agent_settings)
32
33
  end
33
34
 
@@ -42,6 +43,7 @@ module Datadog
42
43
  default_service: settings.service,
43
44
  enabled: settings.tracing.enabled,
44
45
  logger: logger,
46
+ otel_thread_context: OTelThreadContext.build(settings.tracing),
45
47
  trace_flush: trace_flush,
46
48
  sampler: sampler_delegator,
47
49
  span_sampler: build_span_sampler(settings),
@@ -60,7 +62,26 @@ module Datadog
60
62
  end
61
63
  end
62
64
 
65
+ def bind_transport_telemetry(tracer, telemetry)
66
+ return unless tracer.is_a?(Tracing::Tracer)
67
+
68
+ writer = tracer.writer
69
+ return unless writer.respond_to?(:transport)
70
+
71
+ transport = writer.transport
72
+ if transport.is_a?(Transport::Traces::Transport) ||
73
+ (defined?(Transport::Native::Transport) && transport.is_a?(Transport::Native::Transport))
74
+ transport.telemetry = telemetry
75
+ end
76
+ nil
77
+ end
78
+
79
+ # Builds the sampler for the given settings: the sampler that keeps
80
+ # every trace when test mode is enabled, otherwise the sampler built
81
+ # from the custom sampler, sampling rules and rates.
63
82
  def build_sampler(settings)
83
+ return build_test_mode_sampler if settings.tracing.test_mode.enabled
84
+
64
85
  # A custom sampler is provided
65
86
  if (sampler = settings.tracing.sampler)
66
87
  return sampler
@@ -104,9 +125,30 @@ module Datadog
104
125
  return writer
105
126
  end
106
127
 
128
+ if !options.key?(:transport) && settings.tracing.native_transport && (transport = build_native_transport(agent_settings))
129
+ options = options.merge(transport: transport)
130
+ end
131
+
107
132
  Tracing::Writer.new(agent_settings: agent_settings, **options)
108
133
  end
109
134
 
135
+ def build_native_transport(agent_settings)
136
+ require_relative "transport/native"
137
+
138
+ unless Transport::Native.supported?
139
+ Datadog.logger.warn(
140
+ "Native transport requested but not available: #{Transport::Native::UNSUPPORTED_REASON}. " \
141
+ "Falling back to default HTTP transport."
142
+ )
143
+ return nil
144
+ end
145
+
146
+ Transport::Native::Transport.new(
147
+ agent_settings: agent_settings,
148
+ logger: Datadog.logger
149
+ )
150
+ end
151
+
110
152
  def subscribe_to_writer_events!(writer, sampler_delegator, test_mode)
111
153
  return unless writer.respond_to?(:events) # Check if it's a custom, external writer
112
154
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../../../core/telemetry/configuration_value"
4
+
3
5
  module Datadog
4
6
  module Tracing
5
7
  module Configuration
@@ -56,6 +58,8 @@ module Datadog
56
58
  precedence: Core::Configuration::Option::Precedence::REMOTE_CONFIGURATION
57
59
  )
58
60
  end
61
+
62
+ Core::Telemetry::ConfigurationValue.convert(value)
59
63
  end
60
64
 
61
65
  protected
@@ -24,11 +24,11 @@ module Datadog
24
24
  def call(tracing_header_tags)
25
25
  # Modify the remote configuration value that it matches the
26
26
  # environment variable it configures.
27
- tracing_header_tags&.map! do |hash|
27
+ parsed_header_tags = tracing_header_tags&.map do |hash|
28
28
  "#{hash["header"]}:#{hash["tag_name"]}"
29
29
  end
30
30
 
31
- super
31
+ super(parsed_header_tags)
32
32
  end
33
33
  end
34
34
 
@@ -42,6 +42,7 @@ module Datadog
42
42
  def call(tracing_sampling_rate)
43
43
  super
44
44
  Datadog.send(:components).reconfigure_sampler
45
+ tracing_sampling_rate
45
46
  end
46
47
 
47
48
  protected
@@ -78,8 +79,9 @@ module Datadog
78
79
  tracing_sampling_rules = tracing_sampling_rules.to_json
79
80
  end
80
81
 
81
- super
82
+ telemetry_value = super
82
83
  Datadog.send(:components).reconfigure_sampler
84
+ telemetry_value
83
85
  end
84
86
 
85
87
  protected
@@ -13,9 +13,11 @@ module Datadog
13
13
  ENV_BAGGAGE_TAG_KEYS = "DD_TRACE_BAGGAGE_TAG_KEYS"
14
14
  ENV_TRACE_ID_128_BIT_GENERATION_ENABLED = "DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED"
15
15
  ENV_NATIVE_SPAN_EVENTS = "DD_TRACE_NATIVE_SPAN_EVENTS"
16
+ ENV_OTEL_CONTEXT_ENABLED = "DD_TRACE_OTEL_CTX_ENABLED"
16
17
  ENV_RESOURCE_RENAMING_ENABLED = "DD_TRACE_RESOURCE_RENAMING_ENABLED"
17
18
  ENV_RESOURCE_RENAMING_ALWAYS_SIMPLIFIED_ENDPOINT = "DD_TRACE_RESOURCE_RENAMING_ALWAYS_SIMPLIFIED_ENDPOINT"
18
19
  ENV_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED = "DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED"
20
+ ENV_EXPERIMENTAL_NATIVE_TRANSPORT_ENABLED = "DD_EXPERIMENTAL_NATIVE_TRANSPORT_ENABLED"
19
21
 
20
22
  # @public_api
21
23
  module SpanAttributeSchema
@@ -126,6 +128,8 @@ module Datadog
126
128
  ENV_SERVER_ERROR_STATUSES = "DD_TRACE_HTTP_SERVER_ERROR_STATUSES"
127
129
  ENV_CLIENT_ERROR_STATUSES = "DD_TRACE_HTTP_CLIENT_ERROR_STATUSES"
128
130
  end
131
+
132
+ ENV_HTTP_CLIENT_RESOURCE_NAME_QUANTIZE = "DD_TRACE_HTTP_CLIENT_RESOURCE_NAME_QUANTIZE"
129
133
  end
130
134
  end
131
135
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "../../tracing/configuration/ext"
4
4
  require_relative "../../core/environment/variable_helpers"
5
+ require_relative "../../ruby_version"
5
6
  require_relative "../contrib/status_range_matcher"
6
7
  require_relative "../contrib/status_range_env_parser"
7
8
  require_relative "http"
@@ -255,6 +256,14 @@ module Datadog
255
256
  o.type :bool
256
257
  end
257
258
 
259
+ option :otel_thread_context_enabled do |o|
260
+ o.env Tracing::Configuration::Ext::ENV_OTEL_CONTEXT_ENABLED
261
+ o.default do
262
+ RUBY_ENGINE == "ruby" && RUBY_PLATFORM.include?("linux")
263
+ end
264
+ o.type :bool
265
+ end
266
+
258
267
  # Configures an alternative trace transport behavior, where
259
268
  # traces can be sent to the agent and backend before all spans
260
269
  # have finished.
@@ -482,6 +491,22 @@ module Datadog
482
491
  end
483
492
  end
484
493
 
494
+ # Use the native trace transport (Rust via C FFI) instead of
495
+ # the default pure-Ruby HTTP transport.
496
+ #
497
+ # The native transport delegates serialization, stats
498
+ # computation, and HTTP sending to the Rust data pipeline.
499
+ #
500
+ # This option is recommended for internal use only.
501
+ #
502
+ # @default `true` on CRuby 4.0.0 and later, otherwise `false`
503
+ # @return [Boolean]
504
+ option :native_transport do |o|
505
+ o.env Configuration::Ext::ENV_EXPERIMENTAL_NATIVE_TRANSPORT_ENABLED
506
+ o.default { RUBY_ENGINE == "ruby" && RubyVersion.is?(">= 4.0.0") }
507
+ o.type :bool
508
+ end
509
+
485
510
  # A custom writer instance.
486
511
  # The object must respect the {Datadog::Tracing::Writer} interface.
487
512
  #
@@ -543,6 +568,17 @@ module Datadog
543
568
  o.default 512
544
569
  end
545
570
 
571
+ # Whether outbound HTTP client resource names include a quantized request path.
572
+ # URL tags and outgoing requests are unchanged.
573
+ #
574
+ # @default `DD_TRACE_HTTP_CLIENT_RESOURCE_NAME_QUANTIZE` environment variable, otherwise `false`.
575
+ # @return [Boolean]
576
+ option :http_client_resource_name_quantize do |o|
577
+ o.env Tracing::Configuration::Ext::ENV_HTTP_CLIENT_RESOURCE_NAME_QUANTIZE
578
+ o.default false
579
+ o.type :bool
580
+ end
581
+
546
582
  # HTTP error statuses configuration
547
583
  # @public_api
548
584
  settings :http_error_statuses do
@@ -20,9 +20,15 @@ module Datadog
20
20
  attr_reader \
21
21
  :active_trace
22
22
 
23
+ attr_writer \
24
+ :otel_thread_context
25
+
23
26
  def initialize(
24
- trace: nil
27
+ trace: nil,
28
+ otel_thread_context: nil
25
29
  )
30
+ @otel_thread_context = otel_thread_context
31
+ @active_trace = nil
26
32
  activate!(trace)
27
33
  end
28
34
 
@@ -54,14 +60,30 @@ module Datadog
54
60
  # Creates a copy of the context, when forked.
55
61
  def fork_clone
56
62
  forked_trace = @active_trace&.fork_clone
57
- self.class.new(trace: forked_trace)
63
+
64
+ self.class.new(
65
+ trace: forked_trace,
66
+ otel_thread_context: @otel_thread_context
67
+ )
58
68
  end
59
69
 
60
70
  private
61
71
 
62
72
  def set_active_trace!(trace)
73
+ previous_trace = @active_trace
74
+
63
75
  # Don't retain finished traces
64
76
  @active_trace = (trace && !trace.finished?) ? trace : nil
77
+
78
+ return @active_trace if previous_trace == @active_trace
79
+
80
+ if @active_trace
81
+ @otel_thread_context&.update_from_trace_op(@active_trace)
82
+ elsif previous_trace
83
+ @otel_thread_context&.clear
84
+ end
85
+
86
+ @active_trace
65
87
  end
66
88
  end
67
89
  end
@@ -12,12 +12,23 @@ module Datadog
12
12
  # @see https://ruby-doc.org/core-3.1.2/Thread.html#method-i-5B-5D Thread attributes are fiber-local
13
13
  class DefaultContextProvider
14
14
  # Initializes the default context provider with a fiber-bound context.
15
- def initialize
16
- @context = FiberLocalContext.new
15
+ def initialize(otel_thread_context: nil)
16
+ @otel_thread_context = otel_thread_context
17
+ @context = FiberLocalContext.new(otel_thread_context: otel_thread_context)
17
18
  end
18
19
 
19
20
  # Sets the current context.
20
21
  def context=(ctx)
22
+ ctx.otel_thread_context = @otel_thread_context if ctx.respond_to?(:otel_thread_context=)
23
+
24
+ if (otel_thread_context = @otel_thread_context)
25
+ if (trace = ctx&.active_trace)
26
+ otel_thread_context.update_from_trace_op(trace)
27
+ else
28
+ otel_thread_context.clear
29
+ end
30
+ end
31
+
21
32
  @context.local = ctx
22
33
  end
23
34
 
@@ -48,10 +59,11 @@ module Datadog
48
59
  class FiberLocalContext
49
60
  # To support multiple tracers simultaneously, each {Datadog::Tracing::FiberLocalContext}
50
61
  # instance has its own fiber-local variable.
51
- def initialize
62
+ def initialize(otel_thread_context: nil)
63
+ @otel_thread_context = otel_thread_context
52
64
  @key = :"datadog_context_#{FiberLocalContext.next_instance_id}"
53
65
 
54
- self.local = Context.new
66
+ self.local = Context.new(otel_thread_context: otel_thread_context)
55
67
  end
56
68
 
57
69
  # Override the fiber-local context with a new context.
@@ -61,7 +73,7 @@ module Datadog
61
73
 
62
74
  # Return the fiber-local context.
63
75
  def local(storage = Thread.current)
64
- storage[@key] ||= Context.new
76
+ storage[@key] ||= Context.new(otel_thread_context: @otel_thread_context)
65
77
  end
66
78
 
67
79
  # Ensure two instances of {FiberLocalContext} do not conflict.