datadog 2.20.0 → 2.26.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 (310) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +212 -1
  3. data/README.md +0 -1
  4. data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
  5. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +93 -23
  6. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
  7. data/ext/datadog_profiling_native_extension/collectors_stack.c +21 -5
  8. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
  9. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +1 -1
  10. data/ext/datadog_profiling_native_extension/extconf.rb +9 -4
  11. data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
  12. data/ext/datadog_profiling_native_extension/http_transport.c +1 -0
  13. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +12 -0
  14. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +4 -0
  15. data/ext/datadog_profiling_native_extension/profiling.c +2 -0
  16. data/ext/libdatadog_api/datadog_ruby_common.h +1 -1
  17. data/ext/libdatadog_api/ddsketch.c +106 -0
  18. data/ext/libdatadog_api/feature_flags.c +554 -0
  19. data/ext/libdatadog_api/feature_flags.h +5 -0
  20. data/ext/libdatadog_api/init.c +5 -0
  21. data/ext/libdatadog_api/library_config.c +34 -25
  22. data/ext/libdatadog_api/process_discovery.c +24 -18
  23. data/ext/libdatadog_extconf_helpers.rb +1 -1
  24. data/lib/datadog/ai_guard/api_client.rb +82 -0
  25. data/lib/datadog/ai_guard/component.rb +42 -0
  26. data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
  27. data/lib/datadog/ai_guard/configuration/settings.rb +98 -0
  28. data/lib/datadog/ai_guard/configuration.rb +11 -0
  29. data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
  30. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +34 -0
  31. data/lib/datadog/ai_guard/evaluation/request.rb +81 -0
  32. data/lib/datadog/ai_guard/evaluation/result.rb +43 -0
  33. data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
  34. data/lib/datadog/ai_guard/evaluation.rb +72 -0
  35. data/lib/datadog/ai_guard/ext.rb +16 -0
  36. data/lib/datadog/ai_guard.rb +153 -0
  37. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
  38. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +59 -0
  39. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +29 -0
  40. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
  41. data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
  42. data/lib/datadog/appsec/api_security/route_extractor.rb +26 -5
  43. data/lib/datadog/appsec/api_security/sampler.rb +7 -4
  44. data/lib/datadog/appsec/assets/blocked.html +8 -0
  45. data/lib/datadog/appsec/assets/blocked.json +1 -1
  46. data/lib/datadog/appsec/assets/blocked.text +3 -1
  47. data/lib/datadog/appsec/assets/waf_rules/README.md +30 -36
  48. data/lib/datadog/appsec/assets/waf_rules/recommended.json +359 -4
  49. data/lib/datadog/appsec/assets/waf_rules/strict.json +43 -2
  50. data/lib/datadog/appsec/assets.rb +1 -1
  51. data/lib/datadog/appsec/autoload.rb +1 -1
  52. data/lib/datadog/appsec/compressed_json.rb +1 -1
  53. data/lib/datadog/appsec/configuration/settings.rb +9 -0
  54. data/lib/datadog/appsec/context.rb +2 -1
  55. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +3 -1
  56. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +3 -2
  57. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +3 -1
  58. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +3 -1
  59. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -4
  60. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +5 -1
  61. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +7 -2
  62. data/lib/datadog/appsec/contrib/rails/patcher.rb +30 -0
  63. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +3 -1
  64. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +10 -4
  65. data/lib/datadog/appsec/event.rb +12 -14
  66. data/lib/datadog/appsec/metrics/collector.rb +19 -3
  67. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +2 -1
  68. data/lib/datadog/appsec/monitor/gateway/watcher.rb +4 -4
  69. data/lib/datadog/appsec/remote.rb +34 -25
  70. data/lib/datadog/appsec/response.rb +18 -4
  71. data/lib/datadog/appsec/security_engine/engine.rb +3 -3
  72. data/lib/datadog/appsec/security_engine/result.rb +29 -9
  73. data/lib/datadog/appsec/security_engine/runner.rb +19 -9
  74. data/lib/datadog/appsec/security_event.rb +5 -7
  75. data/lib/datadog/core/configuration/agent_settings_resolver.rb +4 -4
  76. data/lib/datadog/core/configuration/components.rb +59 -11
  77. data/lib/datadog/core/configuration/config_helper.rb +100 -0
  78. data/lib/datadog/core/configuration/deprecations.rb +36 -0
  79. data/lib/datadog/core/configuration/ext.rb +0 -1
  80. data/lib/datadog/core/configuration/option.rb +38 -43
  81. data/lib/datadog/core/configuration/option_definition.rb +4 -11
  82. data/lib/datadog/core/configuration/options.rb +9 -10
  83. data/lib/datadog/core/configuration/settings.rb +38 -9
  84. data/lib/datadog/core/configuration/stable_config.rb +10 -0
  85. data/lib/datadog/core/configuration/supported_configurations.rb +373 -0
  86. data/lib/datadog/core/configuration.rb +2 -2
  87. data/lib/datadog/core/ddsketch.rb +19 -0
  88. data/lib/datadog/core/deprecations.rb +2 -2
  89. data/lib/datadog/core/environment/cgroup.rb +52 -25
  90. data/lib/datadog/core/environment/container.rb +140 -46
  91. data/lib/datadog/core/environment/ext.rb +7 -2
  92. data/lib/datadog/core/environment/git.rb +2 -2
  93. data/lib/datadog/core/environment/process.rb +87 -0
  94. data/lib/datadog/core/environment/variable_helpers.rb +3 -3
  95. data/lib/datadog/core/environment/yjit.rb +2 -1
  96. data/lib/datadog/core/error.rb +6 -6
  97. data/lib/datadog/core/feature_flags.rb +61 -0
  98. data/lib/datadog/core/metrics/client.rb +2 -2
  99. data/lib/datadog/core/pin.rb +8 -8
  100. data/lib/datadog/core/process_discovery/tracer_memfd.rb +2 -4
  101. data/lib/datadog/core/process_discovery.rb +48 -23
  102. data/lib/datadog/core/rate_limiter.rb +9 -1
  103. data/lib/datadog/core/remote/client/capabilities.rb +7 -0
  104. data/lib/datadog/core/remote/client.rb +14 -6
  105. data/lib/datadog/core/remote/component.rb +10 -10
  106. data/lib/datadog/core/remote/configuration/content.rb +15 -2
  107. data/lib/datadog/core/remote/configuration/digest.rb +14 -7
  108. data/lib/datadog/core/remote/configuration/repository.rb +1 -1
  109. data/lib/datadog/core/remote/configuration/target.rb +13 -6
  110. data/lib/datadog/core/remote/transport/config.rb +4 -25
  111. data/lib/datadog/core/remote/transport/http/config.rb +10 -50
  112. data/lib/datadog/core/remote/transport/http/negotiation.rb +14 -44
  113. data/lib/datadog/core/remote/transport/http.rb +15 -24
  114. data/lib/datadog/core/remote/transport/negotiation.rb +8 -33
  115. data/lib/datadog/core/remote/worker.rb +25 -37
  116. data/lib/datadog/core/runtime/ext.rb +0 -1
  117. data/lib/datadog/core/runtime/metrics.rb +11 -1
  118. data/lib/datadog/core/semaphore.rb +1 -4
  119. data/lib/datadog/core/tag_builder.rb +0 -4
  120. data/lib/datadog/core/tag_normalizer.rb +84 -0
  121. data/lib/datadog/core/telemetry/component.rb +69 -15
  122. data/lib/datadog/core/telemetry/emitter.rb +6 -6
  123. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
  124. data/lib/datadog/core/telemetry/event/app_started.rb +89 -51
  125. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
  126. data/lib/datadog/core/telemetry/event.rb +1 -0
  127. data/lib/datadog/core/telemetry/logger.rb +2 -2
  128. data/lib/datadog/core/telemetry/logging.rb +2 -8
  129. data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
  130. data/lib/datadog/core/telemetry/request.rb +17 -3
  131. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +3 -34
  132. data/lib/datadog/core/telemetry/transport/http.rb +21 -16
  133. data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -11
  134. data/lib/datadog/core/telemetry/worker.rb +88 -32
  135. data/lib/datadog/core/transport/ext.rb +2 -0
  136. data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
  137. data/lib/datadog/core/transport/http/api/instance.rb +4 -21
  138. data/lib/datadog/core/transport/http/builder.rb +9 -5
  139. data/lib/datadog/core/transport/http/client.rb +80 -0
  140. data/lib/datadog/core/transport/http.rb +22 -19
  141. data/lib/datadog/core/transport/response.rb +15 -1
  142. data/lib/datadog/core/transport/transport.rb +90 -0
  143. data/lib/datadog/core/utils/array.rb +29 -0
  144. data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
  145. data/lib/datadog/core/utils/network.rb +22 -1
  146. data/lib/datadog/core/utils/only_once_successful.rb +8 -2
  147. data/lib/datadog/core/utils/safe_dup.rb +2 -2
  148. data/lib/datadog/core/utils/sequence.rb +2 -0
  149. data/lib/datadog/core/utils/time.rb +1 -1
  150. data/lib/datadog/core/utils.rb +2 -0
  151. data/lib/datadog/core/workers/async.rb +10 -1
  152. data/lib/datadog/core/workers/interval_loop.rb +44 -3
  153. data/lib/datadog/core/workers/polling.rb +2 -0
  154. data/lib/datadog/core/workers/queue.rb +100 -1
  155. data/lib/datadog/core.rb +2 -0
  156. data/lib/datadog/data_streams/configuration/settings.rb +49 -0
  157. data/lib/datadog/data_streams/configuration.rb +11 -0
  158. data/lib/datadog/data_streams/ext.rb +11 -0
  159. data/lib/datadog/data_streams/extensions.rb +16 -0
  160. data/lib/datadog/data_streams/pathway_context.rb +169 -0
  161. data/lib/datadog/data_streams/processor.rb +509 -0
  162. data/lib/datadog/data_streams/transport/http/stats.rb +52 -0
  163. data/lib/datadog/data_streams/transport/http.rb +40 -0
  164. data/lib/datadog/data_streams/transport/stats.rb +46 -0
  165. data/lib/datadog/data_streams.rb +100 -0
  166. data/lib/datadog/di/boot.rb +7 -3
  167. data/lib/datadog/di/component.rb +14 -16
  168. data/lib/datadog/di/context.rb +70 -0
  169. data/lib/datadog/di/contrib/active_record.rb +30 -5
  170. data/lib/datadog/di/el/compiler.rb +168 -0
  171. data/lib/datadog/di/el/evaluator.rb +159 -0
  172. data/lib/datadog/di/el/expression.rb +42 -0
  173. data/lib/datadog/di/el.rb +5 -0
  174. data/lib/datadog/di/error.rb +34 -0
  175. data/lib/datadog/di/instrumenter.rb +189 -55
  176. data/lib/datadog/di/logger.rb +2 -2
  177. data/lib/datadog/di/probe.rb +55 -15
  178. data/lib/datadog/di/probe_builder.rb +41 -2
  179. data/lib/datadog/di/probe_file_loader/railtie.rb +1 -1
  180. data/lib/datadog/di/probe_file_loader.rb +1 -1
  181. data/lib/datadog/di/probe_manager.rb +50 -35
  182. data/lib/datadog/di/probe_notification_builder.rb +121 -70
  183. data/lib/datadog/di/probe_notifier_worker.rb +5 -5
  184. data/lib/datadog/di/proc_responder.rb +32 -0
  185. data/lib/datadog/di/remote.rb +89 -84
  186. data/lib/datadog/di/serializer.rb +151 -7
  187. data/lib/datadog/di/transport/diagnostics.rb +8 -36
  188. data/lib/datadog/di/transport/http/diagnostics.rb +1 -33
  189. data/lib/datadog/di/transport/http/input.rb +1 -33
  190. data/lib/datadog/di/transport/http.rb +32 -17
  191. data/lib/datadog/di/transport/input.rb +67 -34
  192. data/lib/datadog/di.rb +61 -5
  193. data/lib/datadog/error_tracking/filters.rb +2 -2
  194. data/lib/datadog/kit/appsec/events/v2.rb +2 -3
  195. data/lib/datadog/open_feature/component.rb +60 -0
  196. data/lib/datadog/open_feature/configuration.rb +27 -0
  197. data/lib/datadog/open_feature/evaluation_engine.rb +70 -0
  198. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  199. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  200. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  201. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  202. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  203. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  204. data/lib/datadog/open_feature/ext.rb +14 -0
  205. data/lib/datadog/open_feature/native_evaluator.rb +38 -0
  206. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  207. data/lib/datadog/open_feature/provider.rb +141 -0
  208. data/lib/datadog/open_feature/remote.rb +67 -0
  209. data/lib/datadog/open_feature/resolution_details.rb +35 -0
  210. data/lib/datadog/open_feature/transport.rb +70 -0
  211. data/lib/datadog/open_feature.rb +19 -0
  212. data/lib/datadog/opentelemetry/api/baggage.rb +1 -1
  213. data/lib/datadog/opentelemetry/configuration/settings.rb +159 -0
  214. data/lib/datadog/opentelemetry/metrics.rb +117 -0
  215. data/lib/datadog/opentelemetry/sdk/configurator.rb +26 -2
  216. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +35 -0
  217. data/lib/datadog/opentelemetry.rb +3 -0
  218. data/lib/datadog/profiling/collectors/code_provenance.rb +41 -7
  219. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +3 -2
  220. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  221. data/lib/datadog/profiling/collectors/info.rb +6 -5
  222. data/lib/datadog/profiling/component.rb +12 -11
  223. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +18 -0
  224. data/lib/datadog/profiling/ext.rb +2 -1
  225. data/lib/datadog/profiling/http_transport.rb +5 -2
  226. data/lib/datadog/profiling/profiler.rb +4 -0
  227. data/lib/datadog/profiling/tag_builder.rb +36 -3
  228. data/lib/datadog/profiling/tasks/exec.rb +2 -2
  229. data/lib/datadog/profiling.rb +1 -2
  230. data/lib/datadog/single_step_instrument.rb +1 -1
  231. data/lib/datadog/tracing/component.rb +6 -17
  232. data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
  233. data/lib/datadog/tracing/configuration/ext.rb +9 -3
  234. data/lib/datadog/tracing/configuration/settings.rb +89 -10
  235. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -4
  236. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
  237. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
  238. data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
  239. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +4 -2
  240. data/lib/datadog/tracing/contrib/component.rb +2 -2
  241. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -1
  242. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +11 -3
  243. data/lib/datadog/tracing/contrib/extensions.rb +10 -2
  244. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +11 -7
  245. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
  246. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +7 -0
  247. data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
  248. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +84 -43
  249. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -3
  250. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -3
  251. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -3
  252. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
  253. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
  254. data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
  255. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  256. data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
  257. data/lib/datadog/tracing/contrib/karafka/patcher.rb +35 -4
  258. data/lib/datadog/tracing/contrib/rack/middlewares.rb +59 -27
  259. data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -0
  260. data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
  261. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +7 -1
  262. data/lib/datadog/tracing/contrib/rails/ext.rb +2 -1
  263. data/lib/datadog/tracing/contrib/rails/integration.rb +1 -1
  264. data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
  265. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
  266. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +3 -1
  267. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -1
  268. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +1 -1
  269. data/lib/datadog/tracing/contrib/status_range_matcher.rb +9 -1
  270. data/lib/datadog/tracing/contrib/utils/quantization/hash.rb +3 -1
  271. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
  272. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
  273. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
  274. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  275. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  276. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +49 -0
  277. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  278. data/lib/datadog/tracing/contrib/waterdrop.rb +41 -0
  279. data/lib/datadog/tracing/contrib.rb +1 -0
  280. data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -1
  281. data/lib/datadog/tracing/distributed/baggage.rb +3 -2
  282. data/lib/datadog/tracing/metadata/ext.rb +9 -1
  283. data/lib/datadog/tracing/remote.rb +1 -9
  284. data/lib/datadog/tracing/sampling/priority_sampler.rb +3 -1
  285. data/lib/datadog/tracing/span.rb +1 -1
  286. data/lib/datadog/tracing/span_event.rb +2 -2
  287. data/lib/datadog/tracing/span_operation.rb +20 -9
  288. data/lib/datadog/tracing/trace_operation.rb +44 -6
  289. data/lib/datadog/tracing/tracer.rb +42 -16
  290. data/lib/datadog/tracing/transport/http/client.rb +12 -26
  291. data/lib/datadog/tracing/transport/http/traces.rb +2 -50
  292. data/lib/datadog/tracing/transport/http.rb +15 -9
  293. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  294. data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
  295. data/lib/datadog/tracing/transport/traces.rb +9 -71
  296. data/lib/datadog/tracing/workers/trace_writer.rb +5 -0
  297. data/lib/datadog/tracing/writer.rb +1 -0
  298. data/lib/datadog/version.rb +2 -2
  299. data/lib/datadog.rb +3 -0
  300. metadata +110 -24
  301. data/ext/libdatadog_api/macos_development.md +0 -26
  302. data/lib/datadog/core/remote/transport/http/api.rb +0 -53
  303. data/lib/datadog/core/remote/transport/http/client.rb +0 -49
  304. data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
  305. data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
  306. data/lib/datadog/core/transport/http/api/spec.rb +0 -36
  307. data/lib/datadog/di/transport/http/api.rb +0 -42
  308. data/lib/datadog/di/transport/http/client.rb +0 -47
  309. data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
  310. data/lib/datadog/tracing/transport/http/api.rb +0 -44
@@ -13,6 +13,7 @@ module Datadog
13
13
  true-client-ip
14
14
  x-client-ip
15
15
  x-forwarded
16
+ forwarded
16
17
  forwarded-for
17
18
  x-cluster-client-ip
18
19
  fastly-client-ip
@@ -20,6 +21,8 @@ module Datadog
20
21
  cf-connecting-ipv6
21
22
  ].freeze
22
23
 
24
+ CGNAT_IP_RANGE = IPAddr.new('100.64.0.0/10')
25
+
23
26
  class << self
24
27
  # Returns a client IP associated with the request if it was
25
28
  # retrieved successfully.
@@ -73,6 +76,8 @@ module Datadog
73
76
  next unless value
74
77
 
75
78
  ips = value.split(',')
79
+ ips = process_forwarded_header_values(ips) if name == 'forwarded'
80
+
76
81
  ips.each do |ip|
77
82
  parsed_ip = ip_to_ipaddr(ip.strip)
78
83
 
@@ -83,6 +88,22 @@ module Datadog
83
88
  nil
84
89
  end
85
90
 
91
+ def process_forwarded_header_values(values)
92
+ values.each_with_object([]) do |value, acc|
93
+ value.downcase!
94
+
95
+ value.split(';').each do |tuple_str|
96
+ tuple_str.strip!
97
+ next unless tuple_str.start_with?('for=')
98
+
99
+ tuple_str.delete_prefix!('for=')
100
+ tuple_str.delete!('"')
101
+
102
+ acc << tuple_str
103
+ end
104
+ end
105
+ end
106
+
86
107
  # Returns whether the given value is more likely to be an IPv4 than an IPv6 address.
87
108
  #
88
109
  # This is done by checking if a dot (`'.'`) character appears before a colon (`':'`) in the value.
@@ -112,7 +133,7 @@ module Datadog
112
133
  end
113
134
 
114
135
  def global_ip?(parsed_ip)
115
- parsed_ip && !parsed_ip.private? && !parsed_ip.loopback? && !parsed_ip.link_local?
136
+ parsed_ip && !parsed_ip.private? && !parsed_ip.loopback? && !parsed_ip.link_local? && !CGNAT_IP_RANGE.include?(parsed_ip)
116
137
  end
117
138
  end
118
139
  end
@@ -29,7 +29,11 @@ module Datadog
29
29
  # In https://github.com/DataDog/dd-trace-rb/pull/1398#issuecomment-797378810 we have a discussion of alternatives,
30
30
  # including an alternative implementation that is Ractor-safe once spent.
31
31
  class OnlyOnceSuccessful < OnlyOnce
32
- def initialize(limit = 0)
32
+ def initialize(limit = nil)
33
+ if limit && limit <= 0
34
+ raise ArgumentError, "Limit must be a positive integer if provided: #{limit}"
35
+ end
36
+
33
37
  super()
34
38
 
35
39
  @limit = limit
@@ -63,7 +67,9 @@ module Datadog
63
67
 
64
68
  private
65
69
 
70
+ # Use this method only after checking that limit is not nil.
66
71
  def check_limit!
72
+ # @type ivar @limit: Integer
67
73
  if @retries >= @limit
68
74
  @failed = true
69
75
  @ran_once = true
@@ -71,7 +77,7 @@ module Datadog
71
77
  end
72
78
 
73
79
  def limited?
74
- !@limit.nil? && @limit.positive?
80
+ !@limit.nil?
75
81
  end
76
82
 
77
83
  def reset_ran_once_state_for_tests
@@ -5,8 +5,8 @@ module Datadog
5
5
  module Utils
6
6
  # Helper methods for safer dup
7
7
  module SafeDup
8
- # String#+@ was introduced in Ruby 2.3
9
- def self.frozen_or_dup(v)
8
+ # Steep: https://github.com/soutaro/steep/issues/2001
9
+ def self.frozen_or_dup(v) # steep:ignore MethodBodyTypeMismatch
10
10
  # For the case of a String we use the methods +@ and -@.
11
11
  # Those methods are only for String objects
12
12
  # they are faster and chepaer on the memory side.
@@ -12,6 +12,8 @@ module Datadog
12
12
  end
13
13
 
14
14
  def next
15
+ # Steep: https://github.com/soutaro/steep/issues/477
16
+ # @type ivar @next_item: ^(::Integer) -> Integer
15
17
  next_item = @next_item ? @next_item.call(@current) : @current
16
18
  @current += 1
17
19
  next_item
@@ -10,7 +10,7 @@ module Datadog
10
10
  # Current monotonic time
11
11
  #
12
12
  # @param unit [Symbol] unit for the resulting value, same as ::Process#clock_gettime, defaults to :float_second
13
- # @return [Numeric] timestamp in the requested unit, since some unspecified starting point
13
+ # @return [Float|Integer] timestamp in the requested unit, since some unspecified starting point
14
14
  def get_time(unit = :float_second)
15
15
  Process.clock_gettime(Process::CLOCK_MONOTONIC, unit)
16
16
  end
@@ -38,6 +38,8 @@ module Datadog
38
38
 
39
39
  # Ensure `str` is a valid UTF-8, ready to be
40
40
  # sent through the tracer transport.
41
+ # DEV-3.0: This method should unconditionally handle invalid byte sequences
42
+ # DEV-3.0: and return a safe string to display.
41
43
  #
42
44
  # @param [String,#to_s] str object to be converted to a UTF-8 string
43
45
  # @param [Boolean] binary whether to expect binary data in the `str` parameter
@@ -8,6 +8,11 @@ module Datadog
8
8
  module Async
9
9
  # Adds threading behavior to workers
10
10
  # to run tasks asynchronously.
11
+ #
12
+ # This module is included in Polling module, and has no other
13
+ # direct users.
14
+ #
15
+ # @api private
11
16
  module Thread
12
17
  FORK_POLICY_STOP = :stop
13
18
  FORK_POLICY_RESTART = :restart
@@ -24,7 +29,11 @@ module Datadog
24
29
  # Methods that must be prepended
25
30
  module PrependedMethods
26
31
  def perform(*args)
27
- start_async { self.result = super(*args) } unless started?
32
+ unless started?
33
+ start_async do
34
+ self.result = super(*args)
35
+ end
36
+ end
28
37
  end
29
38
  end
30
39
 
@@ -5,6 +5,11 @@ module Datadog
5
5
  module Workers
6
6
  # Adds looping behavior to workers, with a sleep
7
7
  # interval between each loop.
8
+ #
9
+ # This module is included in Polling module, and has no other
10
+ # direct users.
11
+ #
12
+ # @api private
8
13
  module IntervalLoop
9
14
  BACK_OFF_RATIO = 1.2
10
15
  BACK_OFF_MAX = 5
@@ -38,15 +43,39 @@ module Datadog
38
43
 
39
44
  def stop_loop
40
45
  mutex.synchronize do
41
- return false unless run_loop?
42
-
46
+ # Do not call run_loop? from this method to see if the loop
47
+ # is running, because @run_loop is normally initialized by
48
+ # the background thread and if the stop is requested right
49
+ # after the worker starts, the background thread may be created
50
+ # (and scheduled) but hasn't run yet, thus skipping the
51
+ # write to @run_loop here would leave the thread running forever.
43
52
  @run_loop = false
53
+
54
+ # It is possible that we don't need to signal shutdown if
55
+ # @run_loop was not initialized (i.e. we changed it from not
56
+ # defined to false above). But let's be safe and signal the
57
+ # shutdown anyway, I don't see what harm it can cause.
44
58
  shutdown.signal
45
59
  end
46
60
 
61
+ # Previously, this method would return false (and do nothing)
62
+ # if the worker was not running the loop. However, this was racy -
63
+ # see https://github.com/DataDog/ruby-guild/issues/279.
64
+ # stop_loop now always sets the state to "stop requested" and,
65
+ # correspondingly, always returns true.
66
+ #
67
+ # There is some test code that returns false when mocking this
68
+ # method - most likely this method should be treated as a void one
69
+ # and the caller should assume that the stop was always requested.
47
70
  true
48
71
  end
49
72
 
73
+ # TODO This overwrites Queue's +work_pending?+ method with an
74
+ # implementation that, to me, is at leat questionable semantically:
75
+ # the Queue's idea of pending work is if the buffer is not empty,
76
+ # but this module says that work is pending if the work processing
77
+ # loop is scheduled to run (in other words, as long as the background
78
+ # thread is running, there is always pending work).
50
79
  def work_pending?
51
80
  run_loop?
52
81
  end
@@ -104,7 +133,19 @@ module Datadog
104
133
 
105
134
  def perform_loop
106
135
  mutex.synchronize do
107
- @run_loop = true
136
+ unless defined?(@run_loop)
137
+ # This write must only happen if @run_loop is not defined
138
+ # (i.e., not initialized). In the case when the worker is
139
+ # asked to stop right after it is created, the thread may not
140
+ # have run yet by the time +stop_loop+ is invoked and
141
+ # we need to preserve the stop-requested state from
142
+ # +stop_loop+ to +perform_loop+.
143
+ #
144
+ # If the workers are refactored to use classes and inheritance
145
+ # and their state, such as @run_loop, is initialized in
146
+ # constructors, the write can be made unconditional.
147
+ @run_loop = true
148
+ end
108
149
 
109
150
  shutdown.wait(mutex, loop_wait_time) if loop_wait_before_first_iteration?
110
151
  end
@@ -7,6 +7,8 @@ module Datadog
7
7
  module Core
8
8
  module Workers
9
9
  # Adds polling (async looping) behavior to workers
10
+ #
11
+ # @api private
10
12
  module Polling
11
13
  DEFAULT_SHUTDOWN_TIMEOUT = 1
12
14
 
@@ -5,6 +5,17 @@ module Datadog
5
5
  module Workers
6
6
  # Adds queue behavior to workers, with a buffer
7
7
  # to which items can be queued then dequeued.
8
+ #
9
+ # This module is included in some but not all workers.
10
+ # Notably, Data Streams Processor uses a queue but implements it
11
+ # inline rather than using this module.
12
+ #
13
+ # The workers that do include Queue also include Polling, which
14
+ # in turn includes Async::Thread and IntervalLoop. This means
15
+ # we have e.g. +in_iteration?+ always available in any worker
16
+ # that includes Queue.
17
+ #
18
+ # @api private
8
19
  module Queue
9
20
  def self.included(base)
10
21
  base.prepend(PrependedMethods)
@@ -13,11 +24,16 @@ module Datadog
13
24
  # Methods that must be prepended
14
25
  module PrependedMethods
15
26
  def perform(*args)
16
- super(*dequeue) if work_pending?
27
+ if work_pending?
28
+ work = dequeue
29
+ super(*work)
30
+ end
17
31
  end
18
32
  end
19
33
 
20
34
  def buffer
35
+ # Why is this an unsynchronized Array and not a Core::Buffer
36
+ # instance?
21
37
  @buffer ||= []
22
38
  end
23
39
 
@@ -34,10 +50,93 @@ module Datadog
34
50
  !buffer.empty?
35
51
  end
36
52
 
53
+ # Wait for the worker to finish handling all work that has already
54
+ # been submitted to it.
55
+ #
56
+ # If the worker is not enabled, returns nil.
57
+ # If the worker is enabled, returns whether, at the point of return,
58
+ # there was no pending or in progress work.
59
+ #
60
+ # Flushing can time out because there is a constant stream of work
61
+ # submitted at the same or higher rate than it is processed.
62
+ # Flushing can also fail if the worker thread is not running -
63
+ # this method will not flush from the calling thread.
64
+ def flush(timeout: nil)
65
+ # Default timeout is 5 seconds.
66
+ # Specific workers can override it to be more or less
67
+ timeout ||= 5
68
+
69
+ # Nothing needs to be done if the worker is not enabled.
70
+ return nil unless enabled?
71
+
72
+ unless running?
73
+ unless buffer.empty?
74
+ # If we are asked to flush but the worker is not running,
75
+ # do not flush from the caller thread. If the buffer is not
76
+ # empty, it will not be flushed. Log a warning to this effect.
77
+ #
78
+ # We are not guaranteed to have a logger as an instance method,
79
+ # reference the global for now - all other worker methods
80
+ # also reference the logger globally.
81
+ # TODO inject it into worker instances.
82
+ Datadog.logger.debug { "Asked to flush #{self} when the worker is not running" }
83
+ return false
84
+ end
85
+ end
86
+
87
+ started = Utils::Time.get_time
88
+ loop do
89
+ # The AppStarted event is triggered by the worker itself,
90
+ # from the worker thread. As such the main thread has no way
91
+ # to delay itself until that event is queued and we need some
92
+ # way to wait until that event is sent out to assert on it in
93
+ # the test suite. Check the run once flag which *should*
94
+ # indicate the event has been queued (at which point our queue
95
+ # depth check should wait until it's sent).
96
+ # This is still a hack because the flag can be overridden
97
+ # either way with or without the event being sent out.
98
+ # Note that if the AppStarted sending fails, this check
99
+ # will return false and flushing will be blocked until the
100
+ # 15 second timeout.
101
+ # Note that the first wait interval between telemetry event
102
+ # sending is 10 seconds, the timeout needs to be strictly
103
+ # greater than that.
104
+ return true if idle?
105
+
106
+ return false if Utils::Time.get_time - started > timeout
107
+
108
+ sleep 0.5
109
+ end
110
+ end
111
+
37
112
  protected
38
113
 
39
114
  attr_writer \
40
115
  :buffer
116
+
117
+ # Returns whether this worker has no pending work and is not actively
118
+ # working.
119
+ #
120
+ # The reason why "actively working" is considered is that we use
121
+ # flushing to ensure all work is completed before asserting on the
122
+ # outcome in the tests - if work is happening in a background thread,
123
+ # it's too early to assert on its results.
124
+ def idle?
125
+ # We have a +work_pending?+ method in this class that semantically
126
+ # would be appropriate here instead of calling +buffer.empty?+.
127
+ # Unfortunately IntervalLoop replaces our implementation of
128
+ # +work_pending?+ with one that doesn't make sense at least for the
129
+ # Queue. And we can't change the order of module includes because
130
+ # they all override +perform+ and the correct behavior depends on
131
+ # placing IntervalLoop after Queue.
132
+ #
133
+ # The TraceWriter worker then defines +work_pending?+ to be the
134
+ # same as Queue implementation here... Essentially, it demands
135
+ # the behavior that perhaps should be applied to all workers.
136
+ #
137
+ # Until this mess is untangled, call +buffer.empty?+ here.
138
+ buffer.empty? && !in_iteration?
139
+ end
41
140
  end
42
141
  end
43
142
  end
data/lib/datadog/core.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'core/deprecations'
4
+ require_relative 'core/configuration/config_helper'
4
5
  require_relative 'core/extensions'
5
6
 
6
7
  # We must load core extensions to make certain global APIs
@@ -21,6 +22,7 @@ module Datadog
21
22
  end
22
23
  end
23
24
 
25
+ DATADOG_ENV = Core::Configuration::ConfigHelper.new
24
26
  extend Core::Extensions
25
27
 
26
28
  # Add shutdown hook:
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../core/environment/variable_helpers'
4
+ require_relative '../ext'
5
+
6
+ module Datadog
7
+ module DataStreams
8
+ module Configuration
9
+ # Configuration settings for Data Streams Monitoring.
10
+ module Settings
11
+ def self.extended(base)
12
+ base = base.singleton_class unless base.is_a?(Class)
13
+ add_settings!(base)
14
+ end
15
+
16
+ def self.add_settings!(base)
17
+ base.class_eval do
18
+ # Data Streams Monitoring configuration
19
+ # @public_api
20
+ settings :data_streams do
21
+ # Whether Data Streams Monitoring is enabled. When enabled, the library will
22
+ # collect and report data lineage information for messaging systems.
23
+ #
24
+ # @default `DD_DATA_STREAMS_ENABLED` environment variable, otherwise `false`.
25
+ # @return [Boolean]
26
+ option :enabled do |o|
27
+ o.type :bool
28
+ o.env Ext::ENV_ENABLED
29
+ o.default false
30
+ end
31
+
32
+ # The interval (in seconds) at which Data Streams Monitoring stats are flushed.
33
+ #
34
+ # @default 10.0
35
+ # @env '_DD_TRACE_STATS_WRITER_INTERVAL'
36
+ # @return [Float]
37
+ # @!visibility private
38
+ option :interval do |o|
39
+ o.type :float
40
+ o.env '_DD_TRACE_STATS_WRITER_INTERVAL'
41
+ o.default 10.0
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configuration/settings'
4
+
5
+ module Datadog
6
+ module DataStreams
7
+ # Configuration for Data Streams Monitoring
8
+ module Configuration
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module DataStreams
5
+ # Constants for Data Streams Monitoring configuration
6
+ # @public_api
7
+ module Ext
8
+ ENV_ENABLED = 'DD_DATA_STREAMS_ENABLED'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configuration'
4
+ require_relative '../core/configuration'
5
+
6
+ module Datadog
7
+ module DataStreams
8
+ # Extends Datadog with Data Streams Monitoring features
9
+ module Extensions
10
+ # Inject Data Streams settings into global configuration.
11
+ def self.activate!
12
+ Core::Configuration::Settings.extend(Configuration::Settings)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
4
+ require_relative '../core/utils/base64'
5
+
6
+ module Datadog
7
+ module DataStreams
8
+ # Represents a pathway context for data streams monitoring
9
+ class PathwayContext
10
+ # The current pathway hash value (result of FNV-1a hash function)
11
+ attr_accessor :hash
12
+ # When the pathway started
13
+ attr_accessor :pathway_start
14
+ # When the current edge started
15
+ attr_accessor :current_edge_start
16
+ # The hash value of the parent checkpoint
17
+ attr_accessor :parent_hash
18
+ # The direction tag of the previous checkpoint (e.g., 'direction:in', 'direction:out'), or nil if none
19
+ attr_accessor :previous_direction
20
+ # Hash value of the closest checkpoint in opposite direction (used for loop detection)
21
+ attr_accessor :closest_opposite_direction_hash
22
+ # Edge start time of the closest opposite direction checkpoint
23
+ attr_accessor :closest_opposite_direction_edge_start
24
+
25
+ def initialize(hash_value:, pathway_start:, current_edge_start:)
26
+ @hash = hash_value
27
+ @pathway_start = pathway_start
28
+ @current_edge_start = current_edge_start
29
+ @parent_hash = nil
30
+
31
+ @previous_direction = nil
32
+ @closest_opposite_direction_hash = 0
33
+ @closest_opposite_direction_edge_start = current_edge_start
34
+ end
35
+
36
+ def encode_b64
37
+ Core::Utils::Base64.strict_encode64(encode)
38
+ end
39
+
40
+ # Decode pathway context from base64 encoded string
41
+ def self.decode_b64(encoded_ctx)
42
+ return nil unless encoded_ctx && !encoded_ctx.empty?
43
+
44
+ begin
45
+ binary_data = Core::Utils::Base64.strict_decode64(encoded_ctx)
46
+ decode(binary_data)
47
+ rescue ArgumentError => e
48
+ # Invalid base64 encoding - may indicate version mismatch or corruption
49
+ Datadog.logger.debug("Failed to decode DSM pathway context: #{e.message}")
50
+ nil
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def encode
57
+ # Format:
58
+ # - 8 bytes: hash value (little-endian)
59
+ # - VarInt: pathway start time (milliseconds)
60
+ # - VarInt: current edge start time (milliseconds)
61
+ [@hash].pack('Q') <<
62
+ encode_var_int_64(time_to_ms(@pathway_start)) <<
63
+ encode_var_int_64(time_to_ms(@current_edge_start))
64
+ end
65
+
66
+ # Decode pathway context from binary data
67
+ def self.decode(binary_data)
68
+ return nil unless binary_data && binary_data.bytesize >= 8
69
+
70
+ reader = StringIO.new(binary_data)
71
+
72
+ # Extract 8-byte hash (little-endian)
73
+ hash_bytes = reader.read(8)
74
+ return nil unless hash_bytes
75
+
76
+ hash_value = hash_bytes.unpack1('Q') # : Integer
77
+
78
+ # Extract pathway start time (VarInt milliseconds)
79
+ pathway_start_ms = decode_varint(reader)
80
+ return nil unless pathway_start_ms
81
+
82
+ # Extract current edge start time (VarInt milliseconds)
83
+ current_edge_start_ms = decode_varint(reader)
84
+ return nil unless current_edge_start_ms
85
+
86
+ # Convert milliseconds to Time objects
87
+ pathway_start = ms_to_time(pathway_start_ms)
88
+ current_edge_start = ms_to_time(current_edge_start_ms)
89
+
90
+ new(
91
+ hash_value: hash_value,
92
+ pathway_start: pathway_start,
93
+ current_edge_start: current_edge_start
94
+ )
95
+ rescue EOFError
96
+ # Not enough data in binary stream
97
+ nil
98
+ end
99
+ private_class_method :decode
100
+
101
+ # Encode an unsigned 64-bit integer using LEB128 variable-length encoding.
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
106
+ #
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.
109
+ #
110
+ # @param value [Integer] Unsigned integer value to encode
111
+ # @return [String] Binary string of encoded bytes
112
+ def encode_var_int_64(value)
113
+ bytes = []
114
+ while value >= 0x80
115
+ bytes << ((value & 0x7F) | 0x80)
116
+ value >>= 7
117
+ end
118
+ bytes << value
119
+ bytes.pack('C*')
120
+ end
121
+
122
+ # Decode an unsigned LEB128 variable-length integer from IO stream.
123
+ #
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
132
+ #
133
+ # @param io [StringIO] IO stream to read from
134
+ # @return [Integer, nil] Decoded unsigned integer, or nil if malformed
135
+ def self.decode_varint(io)
136
+ value = 0
137
+ shift = 0
138
+
139
+ loop do
140
+ byte = io.readbyte
141
+
142
+ # Add this byte's 7 data bits to our value
143
+ value |= (byte & 0x7F) << shift
144
+
145
+ # If high bit is clear, we're done
146
+ return value unless (byte & 0x80).nonzero?
147
+
148
+ shift += 7
149
+
150
+ # Safety: prevent infinite decoding
151
+ return nil if shift >= 64
152
+ end
153
+ rescue EOFError
154
+ # Stream ended unexpectedly - malformed data
155
+ nil
156
+ end
157
+ private_class_method :decode_varint
158
+
159
+ def self.ms_to_time(milliseconds)
160
+ ::Time.at(milliseconds / 1000.0)
161
+ end
162
+ private_class_method :ms_to_time
163
+
164
+ def time_to_ms(time)
165
+ (time.to_f * 1000).to_i
166
+ end
167
+ end
168
+ end
169
+ end