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
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Core
5
+ module Telemetry
6
+ module ConfigurationValue
7
+ module_function
8
+
9
+ def convert(value)
10
+ case value
11
+ when Integer, String, true, false, nil
12
+ value
13
+ when Float
14
+ value.finite? ? value : value.to_s
15
+ when Hash
16
+ value.map { |key, entry_value| "#{key}:#{entry_value}" }.join(",")
17
+ when Array
18
+ value.join(",")
19
+ when Module
20
+ value.name.to_s
21
+ else
22
+ implements_to_s = begin
23
+ value.method(:to_s).owner != Kernel
24
+ rescue NameError
25
+ false
26
+ end
27
+
28
+ if implements_to_s
29
+ value.to_s
30
+ else
31
+ value.class.to_s
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "base"
4
+ require_relative "../configuration_value"
4
5
 
5
6
  module Datadog
6
7
  module Core
@@ -27,10 +28,11 @@ module Datadog
27
28
  def configuration
28
29
  config = Datadog.configuration
29
30
 
31
+ # @type var res: Array[telemetry_configuration]
30
32
  res = @changes.map do |name, value|
31
33
  {
32
34
  name: name,
33
- value: value,
35
+ value: Telemetry::ConfigurationValue.convert(value),
34
36
  origin: @origin,
35
37
  seq_id: Configuration::Option::Precedence::REMOTE_CONFIGURATION.numeric.next,
36
38
  }
@@ -20,7 +20,7 @@ module Datadog
20
20
  def payload
21
21
  {
22
22
  is_first: @is_first,
23
- endpoints: @endpoints
23
+ endpoints: @endpoints,
24
24
  }
25
25
  end
26
26
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "base"
4
+ require_relative "../configuration_value"
4
5
 
5
6
  module Datadog
6
7
  module Core
@@ -65,7 +66,7 @@ module Datadog
65
66
  # actually enabled by env var or by implicit enablement via
66
67
  # remote config.
67
68
  enabled: components.dynamic_instrumentation&.started? || false,
68
- }
69
+ },
69
70
  }
70
71
 
71
72
  if (unsupported_reason = Datadog::Profiling.unsupported_reason)
@@ -214,23 +215,9 @@ module Datadog
214
215
  end
215
216
 
216
217
  def to_telemetry_value(value)
217
- # TODO: Add float if telemetry starts accepting it
218
- case value
219
- when Integer, String, true, false, nil
220
- value
221
- when Hash
222
- value.map { |key, entry_value| "#{key}:#{entry_value}" }.join(",")
223
- when Array
224
- value.join(",")
225
- when Module
226
- value.name.to_s
227
- else
228
- if implements_to_s?(value)
229
- value.to_s
230
- else
231
- value.class.to_s
232
- end
233
- end
218
+ return value.to_s if value.is_a?(Float)
219
+
220
+ Telemetry::ConfigurationValue.convert(value)
234
221
  end
235
222
 
236
223
  def install_signature(settings)
@@ -280,12 +267,6 @@ module Datadog
280
267
  parent_setting = settings.dig(*split_option)
281
268
  parent_setting.send(:resolve_option, option_name.to_sym)
282
269
  end
283
-
284
- def implements_to_s?(value)
285
- value.method(:to_s).owner != Kernel
286
- rescue NameError
287
- false
288
- end
289
270
  end
290
271
  end
291
272
  end
@@ -23,7 +23,7 @@ module Datadog
23
23
  def payload
24
24
  {
25
25
  namespace: @namespace,
26
- series: @metric_series.map(&:to_h)
26
+ series: @metric_series.map(&:to_h),
27
27
  }
28
28
  end
29
29
 
@@ -52,8 +52,8 @@ module Datadog
52
52
  level: @level,
53
53
  stack_trace: @stack_trace,
54
54
  count: @count,
55
- }.compact
56
- ]
55
+ }.compact,
56
+ ],
57
57
  }
58
58
  end
59
59
 
@@ -57,7 +57,7 @@ module Datadog
57
57
  end
58
58
 
59
59
  def reset?
60
- defined?(@reset) && !!@reset
60
+ !!(defined?(@reset) && @reset)
61
61
  end
62
62
  end
63
63
  end
@@ -37,7 +37,7 @@ module Datadog
37
37
  points: values,
38
38
  type: type,
39
39
  tags: tags,
40
- common: common
40
+ common: common,
41
41
  }
42
42
  end
43
43
 
@@ -179,7 +179,7 @@ module Datadog
179
179
  metric: name,
180
180
  points: values,
181
181
  tags: tags,
182
- common: common
182
+ common: common,
183
183
  }
184
184
  end
185
185
  end
@@ -50,7 +50,7 @@ module Datadog
50
50
  runtime_version: Core::Environment::Ext::ENGINE_VERSION,
51
51
  service_name: config.service,
52
52
  service_version: config.version,
53
- tracer_version: tracer_version
53
+ tracer_version: tracer_version,
54
54
  }
55
55
 
56
56
  tag_process_tags!(app, config)
@@ -64,7 +64,7 @@ module Datadog
64
64
  hostname: Core::Environment::Platform.hostname,
65
65
  kernel_name: Core::Environment::Platform.kernel_name,
66
66
  kernel_release: Core::Environment::Platform.kernel_release,
67
- kernel_version: Core::Environment::Platform.kernel_version
67
+ kernel_version: Core::Environment::Platform.kernel_version,
68
68
  }
69
69
  end
70
70
 
@@ -61,7 +61,7 @@ module Datadog
61
61
  Core::Transport::Ext::HTTP::HEADER_META_LANG_INTERPRETER_VENDOR =>
62
62
  Core::Environment::Ext::LANG_ENGINE,
63
63
  Core::Transport::Ext::HTTP::HEADER_META_TRACER_VERSION =>
64
- Datadog::Core::Environment::Ext::GEM_DATADOG_VERSION
64
+ Datadog::Core::Environment::Ext::GEM_DATADOG_VERSION,
65
65
  }.tap do |headers|
66
66
  # Add application container info
67
67
  headers.merge!(Core::Environment::Container.to_headers)
@@ -69,7 +69,7 @@ module Datadog
69
69
 
70
70
  @current_api_id = api_id
71
71
  client_class = self.class.http_client_class || Core::Transport::HTTP::Client
72
- @client = client_class.new(current_api, logger: logger) # steep:ignore
72
+ @client = client_class.new(current_api, logger: logger)
73
73
  end
74
74
 
75
75
  def downgrade?(response)
@@ -6,8 +6,30 @@ module Datadog
6
6
  # Monkey patches `Kernel#fork` and similar functions, adding an `at_fork` callback mechanism which
7
7
  # is used to restart observability after the VM forks (e.g. in multiprocess Ruby apps).
8
8
  module AtForkMonkeyPatch
9
- AT_FORK_CHILD_BLOCKS = [] # rubocop:disable Style/MutableConstant -- Used to store blocks to run, mutable by design.
10
- private_constant :AT_FORK_CHILD_BLOCKS
9
+ class Callback
10
+ attr_reader :block, :group
11
+
12
+ def initialize(block, group = nil)
13
+ @block = block
14
+ @group = group
15
+ freeze
16
+ end
17
+ end
18
+ private_constant :Callback
19
+
20
+ EMPTY_AT_FORK_BLOCKS = {
21
+ before: [].freeze,
22
+ parent: [].freeze,
23
+ child: [].freeze,
24
+ }.freeze
25
+ private_constant :EMPTY_AT_FORK_BLOCKS
26
+
27
+ @at_fork_blocks = EMPTY_AT_FORK_BLOCKS
28
+
29
+ # Serializes copy-on-write registry updates. Fork dispatch only reads the
30
+ # latest immutable registry reference and never acquires this mutex.
31
+ AT_FORK_REGISTRY_MUTEX = Mutex.new
32
+ private_constant :AT_FORK_REGISTRY_MUTEX
11
33
 
12
34
  def self.supported?
13
35
  Process.respond_to?(:fork)
@@ -31,43 +53,147 @@ module Datadog
31
53
  true
32
54
  end
33
55
 
34
- def self.run_at_fork_blocks(stage)
35
- raise(ArgumentError, "Unsupported stage #{stage}") unless stage == :child
56
+ # Runs the callbacks copied for one fork lifecycle. Before callbacks
57
+ # stop at the first failure; parent and child callbacks all run before
58
+ # the first failure is re-raised.
59
+ def self.run_at_fork_blocks(stage, snapshot:, started: nil)
60
+ callbacks = blocks_for(snapshot, stage)
61
+ if stage == :before
62
+ return callbacks.each do |callback|
63
+ started[callback.group] = true if started && callback.group
64
+ callback.block.call
65
+ end
66
+ end
67
+
68
+ error = nil
69
+ callbacks.each do |callback|
70
+ next if started && callback.group && !started.key?(callback.group)
36
71
 
37
- AT_FORK_CHILD_BLOCKS.each(&:call)
72
+ callback.block.call
73
+ rescue Exception => e # rubocop:disable Lint/RescueException -- finish cleanup, then re-raise the first failure
74
+ error ||= e
75
+ end
76
+ raise error if error
38
77
  end
39
78
 
79
+ def self.run_parent_cleanup(snapshot, started, fork_error)
80
+ run_at_fork_blocks(:parent, snapshot: snapshot, started: started)
81
+ rescue Exception => cleanup_error # rubocop:disable Lint/RescueException -- preserve the original fork failure
82
+ Datadog.logger.warn do
83
+ "Parent at-fork cleanup failed while handling #{fork_error.class}: " \
84
+ "#{cleanup_error.class}: #{cleanup_error.message}"
85
+ end
86
+ end
87
+ private_class_method :run_parent_cleanup
88
+
89
+ # Registers a block to run at the given fork +stage+ (+:before+,
90
+ # +:parent+, or +:child+).
91
+ #
92
+ # Returns the registered block so callers can keep a handle to it and
93
+ # later deregister it via {.remove_at_fork}.
40
94
  def self.at_fork(stage, &block)
41
- raise(ArgumentError, "Unsupported stage #{stage}") unless stage == :child
42
95
  raise(ArgumentError, "Missing block argument") unless block
43
96
 
44
- AT_FORK_CHILD_BLOCKS << block
97
+ AT_FORK_REGISTRY_MUTEX.synchronize do
98
+ current = @at_fork_blocks
99
+ @at_fork_blocks = current.merge(
100
+ stage => (blocks_for(current, stage) + [Callback.new(block)]).freeze
101
+ ).freeze
102
+ end
45
103
 
46
- true
104
+ block
105
+ end
106
+
107
+ # Registers one before/parent/child callback triplet atomically relative
108
+ # to the snapshots taken by fork dispatch.
109
+ def self.at_fork_blocks(before:, parent:, child:)
110
+ blocks = {before: before, parent: parent, child: child}
111
+ group = Object.new.freeze
112
+ AT_FORK_REGISTRY_MUTEX.synchronize do
113
+ current = @at_fork_blocks
114
+ @at_fork_blocks = {
115
+ before: (current.fetch(:before) + [Callback.new(before, group)]).freeze,
116
+ parent: (current.fetch(:parent) + [Callback.new(parent, group)]).freeze,
117
+ child: (current.fetch(:child) + [Callback.new(child, group)]).freeze,
118
+ }.freeze
119
+ end
120
+ blocks
47
121
  end
48
122
 
123
+ # Deregisters a block previously registered with {.at_fork} for the given
124
+ # +stage+. It is a no-op (does not raise) when +block+ was never
125
+ # registered (or was already removed). Raises +ArgumentError+ for an
126
+ # unknown stage, matching the {.at_fork} contract.
127
+ def self.remove_at_fork(stage, block)
128
+ AT_FORK_REGISTRY_MUTEX.synchronize do
129
+ current = @at_fork_blocks
130
+ callbacks = blocks_for(current, stage)
131
+ @at_fork_blocks = current.merge(
132
+ stage => callbacks.reject { |callback| callback.block.equal?(block) }.freeze
133
+ ).freeze
134
+ end
135
+
136
+ nil
137
+ end
138
+
139
+ # Returns the immutable callback registry for one fork lifecycle.
140
+ # Copy-on-write updates publish a new reference, so registrations made
141
+ # afterwards apply only to the next lifecycle without locking this read.
142
+ def self.snapshot_at_fork_blocks
143
+ @at_fork_blocks
144
+ end
145
+
146
+ def self.replace_at_fork_blocks(snapshot)
147
+ AT_FORK_REGISTRY_MUTEX.synchronize { @at_fork_blocks = snapshot }
148
+ end
149
+ private_class_method :replace_at_fork_blocks
150
+
151
+ def self.blocks_for(snapshot, stage)
152
+ case stage
153
+ when :before, :parent, :child then snapshot.fetch(stage)
154
+ else raise(ArgumentError, "Unsupported stage #{stage}")
155
+ end
156
+ end
157
+ private_class_method :blocks_for
158
+
49
159
  # Adds `at_fork` behavior; see parent module for details.
50
160
  module KernelMonkeyPatch
51
161
  def fork
162
+ snapshot = AtForkMonkeyPatch.snapshot_at_fork_blocks
163
+ started = {}
164
+
52
165
  # If a block is provided, it must be wrapped to trigger callbacks.
53
166
  child_block = if block_given?
54
167
  proc do
55
- AtForkMonkeyPatch.run_at_fork_blocks(:child)
168
+ AtForkMonkeyPatch.run_at_fork_blocks(:child, snapshot: snapshot)
56
169
 
57
170
  # Invoke original block
58
171
  yield
59
172
  end
60
173
  end
61
174
 
62
- # Start fork
63
- # If a block is provided, use the wrapped version.
64
- result = child_block.nil? ? super : super(&child_block)
175
+ begin
176
+ # Run pre-fork callbacks in the parent, just before forking.
177
+ AtForkMonkeyPatch.run_at_fork_blocks(:before, snapshot: snapshot, started: started)
178
+
179
+ # Start fork. If a block is provided, use the wrapped version.
180
+ result = child_block.nil? ? super : super(&child_block)
181
+ rescue Exception => e # rubocop:disable Lint/RescueException -- re-raised unchanged; we only need to run parent cleanup first
182
+ # The fork or a before-fork callback failed and we are still in
183
+ # the parent. Restore any state set up by earlier callbacks.
184
+ AtForkMonkeyPatch.send(:run_parent_cleanup, snapshot, started, e)
185
+ raise
186
+ end
65
187
 
66
188
  # When fork gets called without a block, it returns twice:
67
189
  # If we're in the fork, result = nil: trigger child callbacks.
68
- # If we're in the parent, result = pid: we do nothing.
190
+ # If we're in the parent, result = pid: trigger parent callbacks.
69
191
  # (If it gets called with a block, it only returns on the parent)
70
- AtForkMonkeyPatch.run_at_fork_blocks(:child) if result.nil?
192
+ if result.nil?
193
+ AtForkMonkeyPatch.run_at_fork_blocks(:child, snapshot: snapshot)
194
+ else
195
+ AtForkMonkeyPatch.run_at_fork_blocks(:parent, snapshot: snapshot)
196
+ end
71
197
 
72
198
  result
73
199
  end
@@ -78,9 +204,23 @@ module Datadog
78
204
  # Hook provided by Ruby 3.1+ for observability libraries that want to know about fork, see
79
205
  # https://github.com/ruby/ruby/pull/5017 and https://bugs.ruby-lang.org/issues/17795
80
206
  def _fork
81
- pid = super
207
+ snapshot = AtForkMonkeyPatch.snapshot_at_fork_blocks
208
+ started = {}
209
+ begin
210
+ AtForkMonkeyPatch.run_at_fork_blocks(:before, snapshot: snapshot, started: started)
211
+ pid = super
212
+ rescue Exception => e # rubocop:disable Lint/RescueException -- re-raised unchanged; we only need to run parent cleanup first
213
+ # The fork or a before-fork callback failed, so no child was
214
+ # created. Restore state set up by any earlier callbacks.
215
+ AtForkMonkeyPatch.send(:run_parent_cleanup, snapshot, started, e)
216
+ raise
217
+ end
82
218
 
83
- AtForkMonkeyPatch.run_at_fork_blocks(:child) if pid == 0
219
+ if pid == 0
220
+ AtForkMonkeyPatch.run_at_fork_blocks(:child, snapshot: snapshot)
221
+ else
222
+ AtForkMonkeyPatch.run_at_fork_blocks(:parent, snapshot: snapshot)
223
+ end
84
224
 
85
225
  pid
86
226
  end
@@ -89,9 +229,21 @@ module Datadog
89
229
  # keeps executing code in the child process, killing off the parent, thus effectively replacing it.
90
230
  # This is not covered by `_fork` and thus we have some extra code for it.
91
231
  def daemon(*args)
92
- result = super
232
+ snapshot = AtForkMonkeyPatch.snapshot_at_fork_blocks
233
+ started = {}
234
+ begin
235
+ AtForkMonkeyPatch.run_at_fork_blocks(:before, snapshot: snapshot, started: started)
236
+ result = super
237
+ rescue Exception => e # rubocop:disable Lint/RescueException -- re-raised unchanged; we only need to run parent cleanup first
238
+ # `daemon` or a before-fork callback failed, so the original
239
+ # process survives. Restore state set up by earlier callbacks.
240
+ AtForkMonkeyPatch.send(:run_parent_cleanup, snapshot, started, e)
241
+ raise
242
+ end
93
243
 
94
- AtForkMonkeyPatch.run_at_fork_blocks(:child)
244
+ # `daemon` kills the parent, so there is no surviving parent to run
245
+ # `:parent` callbacks in; only the child continues executing.
246
+ AtForkMonkeyPatch.run_at_fork_blocks(:child, snapshot: snapshot)
95
247
 
96
248
  result
97
249
  end
@@ -5,18 +5,15 @@ module Datadog
5
5
  module Utils
6
6
  # Generates values from a consistent sequence
7
7
  class Sequence
8
- def initialize(seed = 0, &block)
8
+ def initialize(seed = 0)
9
9
  @seed = seed
10
10
  @current = seed
11
- @next_item = block
12
11
  end
13
12
 
14
13
  def next
15
- # Steep: https://github.com/soutaro/steep/issues/477
16
- # @type ivar @next_item: ^(::Integer) -> Integer
17
- next_item = @next_item ? @next_item.call(@current) : @current
14
+ current = @current
18
15
  @current += 1
19
- next_item
16
+ current
20
17
  end
21
18
 
22
19
  def reset!
@@ -5,7 +5,16 @@ module Datadog
5
5
  module Utils
6
6
  # Common database-related utility functions.
7
7
  module Time
8
- module_function
8
+ # timecop monkey-patches Time.now, Process.clock_gettime, etc:
9
+ # https://github.com/travisjeffery/timecop/blob/v0.9.11/lib/timecop/time_extensions.rb
10
+ # It keeps the original methods as aliases.
11
+ # We want the real not-mocked time here, so we use those aliases to the original method if present.
12
+ # If not present we assume these methods have not been monkey-patched and are the original.
13
+
14
+ original_clock_gettime_name = Process.respond_to?(:clock_gettime_without_mock) ? :clock_gettime_without_mock : :clock_gettime
15
+ define_singleton_method(:original_clock_gettime, &Process.method(original_clock_gettime_name))
16
+
17
+ MONOTONIC_CLOCK_ID = RUBY_PLATFORM.include?("darwin") ? Process::CLOCK_MONOTONIC_RAW : Process::CLOCK_MONOTONIC
9
18
 
10
19
  # Current monotonic time
11
20
  # On macOS, CLOCK_MONOTONIC only has microsecond precision,
@@ -13,71 +22,24 @@ module Datadog
13
22
  #
14
23
  # @param unit [Symbol] unit for the resulting value, same as ::Process#clock_gettime, defaults to :float_second
15
24
  # @return [Float|Integer] timestamp in the requested unit, since some unspecified starting point
16
- MONOTONIC_CLOCK_ID = RUBY_PLATFORM.include?("darwin") ? Process::CLOCK_MONOTONIC_RAW : Process::CLOCK_MONOTONIC
17
-
18
- def get_time(unit = :float_second)
19
- Process.clock_gettime(MONOTONIC_CLOCK_ID, unit)
25
+ def self.get_time(unit = :float_second)
26
+ original_clock_gettime(MONOTONIC_CLOCK_ID, unit)
20
27
  end
21
28
 
22
29
  # Current wall time.
23
30
  #
24
31
  # @return [Time] current time object
25
- def now
26
- ::Time.now
27
- end
28
-
29
- # Overrides the implementation of `#now
30
- # with the provided callable.
31
- #
32
- # Overriding the method `#now` instead of
33
- # indirectly calling `block` removes
34
- # one level of method call overhead.
35
- #
36
- # @param block [Proc] block that returns a `Time` object representing the current wall time
37
- def now_provider=(block)
38
- class << self
39
- # Avoid method redefinition warning.
40
- # `rescue nil` is added in case customers remove the method
41
- # themselves to squelch the warning.
42
- begin
43
- remove_method(:now)
44
- rescue
45
- nil
46
- end
47
- end
48
- define_singleton_method(:now, &block)
49
- end
50
-
51
- # Overrides the implementation of `#get_time
52
- # with the provided callable.
53
- #
54
- # Overriding the method `#get_time` instead of
55
- # indirectly calling `block` removes
56
- # one level of method call overhead.
57
- #
58
- # @param block [Proc] block that accepts unit and returns timestamp in the requested unit
59
- def get_time_provider=(block)
60
- class << self
61
- # Avoid method redefinition warning
62
- # `rescue nil` is added in case customers remove the method
63
- # themselves to squelch the warning.
64
- begin
65
- remove_method(:get_time)
66
- rescue
67
- nil
68
- end
69
- end
70
- define_singleton_method(:get_time, &block)
71
- end
32
+ original_time_now_name = ::Time.respond_to?(:now_without_mock_time) ? :now_without_mock_time : :now
33
+ define_singleton_method(:now, &::Time.method(original_time_now_name))
72
34
 
73
- def measure(unit = :float_second)
35
+ def self.measure(unit = :float_second)
74
36
  before = get_time(unit)
75
37
  yield
76
38
  after = get_time(unit)
77
39
  after - before
78
40
  end
79
41
 
80
- def as_utc_epoch_ns(time)
42
+ def self.as_utc_epoch_ns(time)
81
43
  # we use #to_r instead of #to_f because Float doesn't have enough precision to represent exact nanoseconds, see
82
44
  # https://rubyapi.org/3.0/o/time#method-i-to_f
83
45
  (time.to_r * 1_000_000_000).to_i
@@ -32,7 +32,7 @@ module Datadog
32
32
  end
33
33
 
34
34
  # Workers::Async::Thread settings
35
- self.fork_policy = options.fetch(:fork_policy, Workers::Async::Thread::FORK_POLICY_STOP)
35
+ self.fork_policy = options.fetch(:fork_policy, Workers::Async::Thread::FORK_POLICY_RESTART)
36
36
 
37
37
  # Workers::IntervalLoop settings
38
38
  self.loop_base_interval = options.fetch(:interval, DEFAULT_FLUSH_INTERVAL)
@@ -98,40 +98,38 @@ module Datadog
98
98
  end
99
99
  private_class_method :decode
100
100
 
101
- # Encode an unsigned 64-bit integer using LEB128 variable-length encoding.
101
+ # Encode a signed 64-bit integer using ZigZag + LEB128 variable-length encoding.
102
102
  #
103
- # This implements unsigned LEB128 (Little Endian Base 128) encoding as specified
104
- # in DWARF5 standard section 7.6:
105
- # https://dwarfstd.org/doc/DWARF5.pdf#page=301
103
+ # dd-trace-go's DSM pathway encoder (internal/datastreams/propagator.go, via
104
+ # sketches-go's ddsketch/encoding package) ZigZag-maps the signed value before
105
+ # applying plain unsigned LEB128 (DWARF5 section 7.6:
106
+ # https://dwarfstd.org/doc/DWARF5.pdf#page=301). This implementation previously
107
+ # skipped the ZigZag step, which silently decoded to the wrong timestamp on any
108
+ # dd-trace-go consumer -- see https://github.com/DataDog/dd-trace-go/issues/5163.
106
109
  #
107
- # Each byte uses 7 bits for data and 1 bit to indicate continuation.
108
- # The high bit is set if more bytes follow, clear for the final byte.
110
+ # ZigZag maps signed values to unsigned ones so small-magnitude negative numbers
111
+ # still encode to few bytes: 0, -1, 1, -2, 2, ... -> 0, 1, 2, 3, 4, ...
109
112
  #
110
- # @param value [Integer] Unsigned integer value to encode
113
+ # @param value [Integer] Signed integer value to encode
111
114
  # @return [String] Binary string of encoded bytes
112
115
  def encode_var_int_64(value)
116
+ zigzag = (value << 1) ^ (value >> 63)
113
117
  bytes = []
114
- while value >= 0x80
115
- bytes << ((value & 0x7F) | 0x80)
116
- value >>= 7
118
+ while zigzag >= 0x80
119
+ bytes << ((zigzag & 0x7F) | 0x80)
120
+ zigzag >>= 7
117
121
  end
118
- bytes << value
122
+ bytes << zigzag
119
123
  bytes.pack("C*")
120
124
  end
121
125
 
122
- # Decode an unsigned LEB128 variable-length integer from IO stream.
126
+ # Decode a signed 64-bit integer encoded with ZigZag + LEB128 from an IO stream.
123
127
  #
124
- # This implements unsigned LEB128 (Little Endian Base 128) decoding as specified
125
- # in DWARF5 standard section 7.6:
126
- # https://dwarfstd.org/doc/DWARF5.pdf#page=301
127
- #
128
- # VarInt format: Each byte uses 7 bits for data, 1 bit for continuation
129
- # - High bit set = more bytes follow
130
- # - High bit clear = final byte
131
- # - Data bits accumulated in little-endian order
128
+ # See #encode_var_int_64 for why the ZigZag un-map is required to match
129
+ # dd-trace-go's wire format.
132
130
  #
133
131
  # @param io [StringIO] IO stream to read from
134
- # @return [Integer, nil] Decoded unsigned integer, or nil if malformed
132
+ # @return [Integer, nil] Decoded signed integer, or nil if malformed
135
133
  def self.decode_varint(io)
136
134
  value = 0
137
135
  shift = 0
@@ -143,7 +141,7 @@ module Datadog
143
141
  value |= (byte & 0x7F) << shift
144
142
 
145
143
  # If high bit is clear, we're done
146
- return value unless (byte & 0x80).nonzero?
144
+ return (value >> 1) ^ -(value & 1) unless (byte & 0x80).nonzero?
147
145
 
148
146
  shift += 7
149
147