datadog 2.46.0 → 2.48.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 (286) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +56 -2
  3. data/LICENSE-3rdparty.csv +1 -0
  4. data/ext/datadog_profiling_native_extension/collectors_stack.c +143 -86
  5. data/ext/datadog_profiling_native_extension/collectors_stack.h +9 -1
  6. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +6 -1
  7. data/ext/datadog_profiling_native_extension/extconf.rb +7 -13
  8. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +3 -3
  9. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +67 -14
  10. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +8 -1
  11. data/ext/datadog_profiling_native_extension/profiling.c +1 -1
  12. data/ext/libdatadog_api/extconf.rb +9 -7
  13. data/ext/libdatadog_api/trace_exporter.c +34 -7
  14. data/ext/libdatadog_extconf_helpers.rb +2 -2
  15. data/lib/datadog/ai_guard/component.rb +1 -1
  16. data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +1 -9
  17. data/lib/datadog/ai_guard/contrib/rails/patcher.rb +1 -1
  18. data/lib/datadog/ai_guard/contrib/ruby_llm/message_adapter.rb +1 -1
  19. data/lib/datadog/ai_guard/contrib/ruby_llm/redaction_change_set.rb +3 -3
  20. data/lib/datadog/ai_guard/evaluation/client.rb +3 -3
  21. data/lib/datadog/ai_guard/evaluation.rb +2 -2
  22. data/lib/datadog/ai_guard/http_client.rb +3 -2
  23. data/lib/datadog/ai_guard/redaction.rb +1 -1
  24. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +1 -1
  25. data/lib/datadog/appsec/assets/waf_rules/README.md +3 -3
  26. data/lib/datadog/appsec/assets/waf_rules/recommended.json +5366 -1517
  27. data/lib/datadog/appsec/assets/waf_rules/strict.json +2 -2
  28. data/lib/datadog/appsec/autoload.rb +1 -1
  29. data/lib/datadog/appsec/component.rb +1 -1
  30. data/lib/datadog/appsec/compressed_json.rb +1 -1
  31. data/lib/datadog/appsec/configuration.rb +3 -3
  32. data/lib/datadog/appsec/context.rb +1 -1
  33. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +11 -1
  34. data/lib/datadog/appsec/contrib/active_record/patcher.rb +12 -4
  35. data/lib/datadog/appsec/contrib/aws_lambda/gateway/watcher.rb +2 -2
  36. data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +1 -1
  37. data/lib/datadog/appsec/contrib/devise/configuration.rb +1 -1
  38. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +1 -1
  39. data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +1 -1
  40. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +1 -1
  41. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +1 -1
  42. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +1 -1
  43. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +7 -7
  44. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +1 -1
  45. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +3 -3
  46. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +5 -5
  47. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +31 -4
  48. data/lib/datadog/appsec/contrib/rails/patcher.rb +2 -2
  49. data/lib/datadog/appsec/contrib/rails/patches/render_to_body_patch.rb +8 -7
  50. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +1 -1
  51. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +17 -5
  52. data/lib/datadog/appsec/contrib/sinatra/patcher.rb +1 -1
  53. data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +8 -7
  54. data/lib/datadog/appsec/metrics/collector.rb +5 -5
  55. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +3 -3
  56. data/lib/datadog/appsec/monitor/gateway/watcher.rb +2 -2
  57. data/lib/datadog/appsec/processor/rule_loader.rb +1 -1
  58. data/lib/datadog/appsec/response.rb +1 -1
  59. data/lib/datadog/appsec/security_engine/engine.rb +8 -7
  60. data/lib/datadog/appsec/security_engine/runner.rb +2 -2
  61. data/lib/datadog/appsec/trace_keeper.rb +1 -1
  62. data/lib/datadog/appsec.rb +1 -1
  63. data/lib/datadog/core/configuration/agent_settings_resolver.rb +14 -13
  64. data/lib/datadog/core/configuration/agentless_settings_resolver.rb +2 -2
  65. data/lib/datadog/core/configuration/components.rb +12 -11
  66. data/lib/datadog/core/configuration/components_state.rb +8 -1
  67. data/lib/datadog/core/configuration/settings.rb +5 -2
  68. data/lib/datadog/core/configuration/supported_configurations.rb +3 -0
  69. data/lib/datadog/core/configuration.rb +2 -2
  70. data/lib/datadog/core/crashtracking/component.rb +1 -1
  71. data/lib/datadog/core/diagnostics/environment_logger.rb +1 -1
  72. data/lib/datadog/core/environment/cgroup.rb +2 -2
  73. data/lib/datadog/core/environment/container.rb +3 -3
  74. data/lib/datadog/core/metrics/client.rb +6 -6
  75. data/lib/datadog/core/process_discovery.rb +1 -1
  76. data/lib/datadog/core/remote/client.rb +44 -3
  77. data/lib/datadog/core/remote/dispatcher.rb +34 -3
  78. data/lib/datadog/core/remote/tie/tracing.rb +4 -3
  79. data/lib/datadog/core/remote/transport/http.rb +2 -2
  80. data/lib/datadog/core/runtime/metrics.rb +14 -14
  81. data/lib/datadog/core/semaphore.rb +3 -1
  82. data/lib/datadog/core/telemetry/event/app_started.rb +9 -9
  83. data/lib/datadog/core/telemetry/logger.rb +1 -1
  84. data/lib/datadog/core/telemetry/logging.rb +1 -1
  85. data/lib/datadog/core/telemetry/request.rb +9 -3
  86. data/lib/datadog/core/telemetry/transport/http.rb +2 -2
  87. data/lib/datadog/core/telemetry/worker.rb +1 -1
  88. data/lib/datadog/core/transport/http/adapters/net.rb +1 -1
  89. data/lib/datadog/core/transport/http/adapters/unix_socket.rb +1 -1
  90. data/lib/datadog/core/transport/http/builder.rb +1 -1
  91. data/lib/datadog/core/transport/http.rb +3 -3
  92. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +2 -2
  93. data/lib/datadog/core/workers/async.rb +1 -1
  94. data/lib/datadog/core/workers/runtime_metrics.rb +2 -2
  95. data/lib/datadog/data_streams/pathway_context.rb +1 -1
  96. data/lib/datadog/data_streams/processor.rb +48 -27
  97. data/lib/datadog/data_streams/transport/http.rb +1 -1
  98. data/lib/datadog/data_streams.rb +16 -0
  99. data/lib/datadog/di/capture_expression_evaluator.rb +48 -11
  100. data/lib/datadog/di/component.rb +73 -2
  101. data/lib/datadog/di/configuration.rb +19 -0
  102. data/lib/datadog/di/contrib/active_record.rb +1 -1
  103. data/lib/datadog/di/el/compiler.rb +4 -4
  104. data/lib/datadog/di/el/evaluator.rb +247 -43
  105. data/lib/datadog/di/el/expression.rb +36 -6
  106. data/lib/datadog/di/error.rb +16 -0
  107. data/lib/datadog/di/guardrails_telemetry/reason.rb +3 -2
  108. data/lib/datadog/di/instrumenter.rb +39 -10
  109. data/lib/datadog/di/probe_notification_builder.rb +58 -10
  110. data/lib/datadog/di/probe_notifier_worker.rb +1 -1
  111. data/lib/datadog/di/remote.rb +46 -2
  112. data/lib/datadog/di/serializer.rb +47 -13
  113. data/lib/datadog/di.rb +27 -0
  114. data/lib/datadog/error_tracking/component.rb +1 -1
  115. data/lib/datadog/error_tracking/configuration.rb +1 -1
  116. data/lib/datadog/kit/appsec/events/v2.rb +6 -6
  117. data/lib/datadog/kit/appsec/events.rb +1 -1
  118. data/lib/datadog/kit/enable_core_dumps.rb +1 -1
  119. data/lib/datadog/kit/identity.rb +1 -1
  120. data/lib/datadog/open_feature/activation.rb +26 -1
  121. data/lib/datadog/open_feature/agentless/configuration_source.rb +94 -15
  122. data/lib/datadog/open_feature/component.rb +22 -2
  123. data/lib/datadog/open_feature/configuration/agentless_endpoint.rb +6 -0
  124. data/lib/datadog/open_feature/configuration.rb +10 -4
  125. data/lib/datadog/open_feature/evaluation_engine.rb +62 -4
  126. data/lib/datadog/open_feature/exposures/buffer.rb +4 -0
  127. data/lib/datadog/open_feature/exposures/reporter.rb +17 -4
  128. data/lib/datadog/open_feature/exposures/worker.rb +37 -6
  129. data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +5 -5
  130. data/lib/datadog/open_feature/flag_evaluation/writer.rb +1 -1
  131. data/lib/datadog/open_feature/hooks/debug_logging_hook.rb +56 -0
  132. data/lib/datadog/open_feature/hooks/evaluation_telemetry_opt_out_hook.rb +25 -0
  133. data/lib/datadog/open_feature/hooks/span_enrichment_hook.rb +2 -2
  134. data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +1 -1
  135. data/lib/datadog/open_feature/native_evaluator.rb +1 -1
  136. data/lib/datadog/open_feature/noop_evaluator.rb +1 -1
  137. data/lib/datadog/open_feature/provider.rb +69 -12
  138. data/lib/datadog/open_feature/remote.rb +8 -1
  139. data/lib/datadog/open_feature/resolution_details.rb +2 -2
  140. data/lib/datadog/open_feature/transport.rb +6 -6
  141. data/lib/datadog/opentelemetry/api/context.rb +3 -3
  142. data/lib/datadog/opentelemetry/logs.rb +5 -5
  143. data/lib/datadog/opentelemetry/metrics.rb +3 -3
  144. data/lib/datadog/opentelemetry/sdk/propagator.rb +3 -3
  145. data/lib/datadog/opentelemetry/sdk/span_processor.rb +2 -2
  146. data/lib/datadog/opentelemetry/sdk/trace/span.rb +1 -1
  147. data/lib/datadog/profiling/collectors/code_provenance.rb +3 -3
  148. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +4 -4
  149. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  150. data/lib/datadog/profiling/collectors/info.rb +37 -6
  151. data/lib/datadog/profiling/collectors/thread_context.rb +1 -1
  152. data/lib/datadog/profiling/component.rb +41 -39
  153. data/lib/datadog/profiling/exporter.rb +4 -6
  154. data/lib/datadog/profiling/http_transport.rb +3 -3
  155. data/lib/datadog/profiling/profiler.rb +2 -2
  156. data/lib/datadog/profiling/scheduler.rb +2 -2
  157. data/lib/datadog/profiling/tag_builder.rb +1 -1
  158. data/lib/datadog/symbol_database/extractor.rb +8 -8
  159. data/lib/datadog/symbol_database/uploader.rb +1 -1
  160. data/lib/datadog/tracing/buffer.rb +3 -3
  161. data/lib/datadog/tracing/client_ip.rb +1 -1
  162. data/lib/datadog/tracing/component.rb +15 -10
  163. data/lib/datadog/tracing/configuration/dynamic/option.rb +2 -2
  164. data/lib/datadog/tracing/configuration/ext.rb +2 -0
  165. data/lib/datadog/tracing/configuration/settings.rb +14 -3
  166. data/lib/datadog/tracing/context.rb +1 -1
  167. data/lib/datadog/tracing/context_provider.rb +8 -0
  168. data/lib/datadog/tracing/contrib/action_cable/instrumentation.rb +2 -2
  169. data/lib/datadog/tracing/contrib/active_job/data_streams.rb +27 -2
  170. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +2 -2
  171. data/lib/datadog/tracing/contrib/active_record/configuration/settings.rb +1 -1
  172. data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -1
  173. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +1 -1
  174. data/lib/datadog/tracing/contrib/active_support/configuration/settings.rb +1 -1
  175. data/lib/datadog/tracing/contrib/active_support/notifications/subscriber.rb +2 -2
  176. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +2 -2
  177. data/lib/datadog/tracing/contrib/aws/configuration/settings.rb +1 -1
  178. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +1 -1
  179. data/lib/datadog/tracing/contrib/dalli/configuration/settings.rb +1 -1
  180. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +1 -1
  181. data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +2 -2
  182. data/lib/datadog/tracing/contrib/elasticsearch/configuration/settings.rb +1 -1
  183. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +3 -3
  184. data/lib/datadog/tracing/contrib/ethon/configuration/settings.rb +1 -1
  185. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +17 -12
  186. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +1 -1
  187. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +1 -1
  188. data/lib/datadog/tracing/contrib/excon/middleware.rb +16 -9
  189. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +1 -1
  190. data/lib/datadog/tracing/contrib/faraday/middleware.rb +14 -8
  191. data/lib/datadog/tracing/contrib/grape/endpoint.rb +4 -4
  192. data/lib/datadog/tracing/contrib/graphql/patcher.rb +1 -1
  193. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +3 -3
  194. data/lib/datadog/tracing/contrib/grpc/configuration/settings.rb +1 -1
  195. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +2 -2
  196. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +1 -1
  197. data/lib/datadog/tracing/contrib/grpc.rb +1 -1
  198. data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +1 -1
  199. data/lib/datadog/tracing/contrib/hanami/plugin.rb +1 -1
  200. data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +1 -1
  201. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +1 -1
  202. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +1 -1
  203. data/lib/datadog/tracing/contrib/http/instrumentation.rb +21 -6
  204. data/lib/datadog/tracing/contrib/http.rb +1 -1
  205. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +1 -1
  206. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +14 -8
  207. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +1 -1
  208. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +14 -8
  209. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +2 -2
  210. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +2 -2
  211. data/lib/datadog/tracing/contrib/karafka/monitor.rb +1 -1
  212. data/lib/datadog/tracing/contrib/karafka/patcher.rb +1 -1
  213. data/lib/datadog/tracing/contrib/karafka.rb +1 -1
  214. data/lib/datadog/tracing/contrib/lograge/patcher.rb +1 -1
  215. data/lib/datadog/tracing/contrib/mongodb/configuration/settings.rb +1 -1
  216. data/lib/datadog/tracing/contrib/mongodb/parsers.rb +1 -1
  217. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +1 -1
  218. data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +1 -1
  219. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +3 -3
  220. data/lib/datadog/tracing/contrib/opensearch/configuration/settings.rb +2 -2
  221. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +3 -3
  222. data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +1 -1
  223. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +4 -4
  224. data/lib/datadog/tracing/contrib/presto/configuration/settings.rb +1 -1
  225. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +4 -4
  226. data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +1 -1
  227. data/lib/datadog/tracing/contrib/que/tracer.rb +1 -1
  228. data/lib/datadog/tracing/contrib/racecar/configuration/settings.rb +1 -1
  229. data/lib/datadog/tracing/contrib/racecar/events/batch.rb +1 -1
  230. data/lib/datadog/tracing/contrib/racecar/events/message.rb +1 -1
  231. data/lib/datadog/tracing/contrib/rack/middlewares.rb +3 -3
  232. data/lib/datadog/tracing/contrib/rack/patcher.rb +1 -1
  233. data/lib/datadog/tracing/contrib/rails/framework.rb +7 -7
  234. data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
  235. data/lib/datadog/tracing/contrib/rails/runner.rb +4 -4
  236. data/lib/datadog/tracing/contrib/redis/configuration/settings.rb +1 -1
  237. data/lib/datadog/tracing/contrib/redis/quantize.rb +1 -1
  238. data/lib/datadog/tracing/contrib/redis/tags.rb +1 -1
  239. data/lib/datadog/tracing/contrib/rest_client/configuration/settings.rb +1 -1
  240. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +17 -10
  241. data/lib/datadog/tracing/contrib/sequel/database.rb +1 -1
  242. data/lib/datadog/tracing/contrib/sequel/dataset.rb +1 -1
  243. data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +1 -1
  244. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +2 -2
  245. data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +2 -2
  246. data/lib/datadog/tracing/contrib/sidekiq.rb +1 -1
  247. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +1 -1
  248. data/lib/datadog/tracing/contrib/status_range_env_parser.rb +1 -1
  249. data/lib/datadog/tracing/contrib/sucker_punch/instrumentation.rb +1 -1
  250. data/lib/datadog/tracing/contrib/trilogy/configuration/settings.rb +1 -1
  251. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +3 -3
  252. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +102 -0
  253. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +2 -2
  254. data/lib/datadog/tracing/contrib/waterdrop.rb +1 -1
  255. data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -1
  256. data/lib/datadog/tracing/distributed/b3_multi.rb +1 -1
  257. data/lib/datadog/tracing/distributed/b3_single.rb +1 -1
  258. data/lib/datadog/tracing/distributed/baggage.rb +3 -3
  259. data/lib/datadog/tracing/distributed/datadog.rb +3 -3
  260. data/lib/datadog/tracing/distributed/propagation.rb +7 -7
  261. data/lib/datadog/tracing/distributed/trace_context.rb +1 -1
  262. data/lib/datadog/tracing/distributed/trace_state/open_telemetry.rb +1 -1
  263. data/lib/datadog/tracing/otel_thread_context.rb +3 -3
  264. data/lib/datadog/tracing/pipeline.rb +1 -1
  265. data/lib/datadog/tracing/remote.rb +50 -19
  266. data/lib/datadog/tracing/sampling/priority_sampler.rb +1 -1
  267. data/lib/datadog/tracing/sampling/rule.rb +1 -1
  268. data/lib/datadog/tracing/sampling/rule_sampler.rb +1 -1
  269. data/lib/datadog/tracing/sampling/span/rule_parser.rb +2 -2
  270. data/lib/datadog/tracing/span_operation.rb +1 -1
  271. data/lib/datadog/tracing/trace_digest.rb +1 -1
  272. data/lib/datadog/tracing/trace_operation.rb +6 -6
  273. data/lib/datadog/tracing/tracer.rb +6 -6
  274. data/lib/datadog/tracing/transport/http/telemetry.rb +1 -1
  275. data/lib/datadog/tracing/transport/http.rb +2 -2
  276. data/lib/datadog/tracing/transport/io.rb +1 -1
  277. data/lib/datadog/tracing/transport/native.rb +3 -3
  278. data/lib/datadog/tracing/transport/statistics.rb +2 -2
  279. data/lib/datadog/tracing/transport/telemetry.rb +3 -1
  280. data/lib/datadog/tracing/transport/trace_formatter.rb +12 -12
  281. data/lib/datadog/tracing/transport/traces.rb +1 -1
  282. data/lib/datadog/tracing/utils.rb +1 -1
  283. data/lib/datadog/tracing/workers.rb +1 -1
  284. data/lib/datadog/version.rb +1 -1
  285. metadata +9 -8
  286. data/lib/datadog/appsec/utils/hash_coercion.rb +0 -23
@@ -50,7 +50,7 @@ module Datadog
50
50
 
51
51
  if trace.nil? || span.nil?
52
52
  return Datadog.logger.warn(
53
- "Kit::AppSec: Tracing is not enabled. Please enable tracing if you want to track events"
53
+ "Kit::AppSec: Tracing is not enabled. Please enable tracing if you want to track events",
54
54
  )
55
55
  end
56
56
 
@@ -69,7 +69,7 @@ module Datadog
69
69
  record_event_telemetry_metric(LOGIN_SUCCESS_EVENT)
70
70
  ::Datadog::AppSec::Instrumentation.gateway.push("appsec.events.user_lifecycle", LOGIN_SUCCESS_EVENT)
71
71
 
72
- return Kit::Identity.set_user(trace, span, **user_attributes) if user_attributes.key?(:id)
72
+ return Kit::Identity.set_user(trace, span, **user_attributes) if user_attributes.key?(:id) # steep:ignore InsufficientKeywordArguments
73
73
 
74
74
  # NOTE: This is a fallback for the case when we don't have an ID,
75
75
  # but need to trigger WAF.
@@ -102,7 +102,7 @@ module Datadog
102
102
 
103
103
  if trace.nil? || span.nil?
104
104
  return Datadog.logger.warn(
105
- "Kit::AppSec: Tracing is not enabled. Please enable tracing if you want to track events"
105
+ "Kit::AppSec: Tracing is not enabled. Please enable tracing if you want to track events",
106
106
  )
107
107
  end
108
108
 
@@ -155,7 +155,7 @@ module Datadog
155
155
 
156
156
  if trace.nil? || span.nil?
157
157
  return Datadog.logger.warn(
158
- "Kit::AppSec: Tracing is not enabled. Please enable tracing if you want to track events"
158
+ "Kit::AppSec: Tracing is not enabled. Please enable tracing if you want to track events",
159
159
  )
160
160
  end
161
161
 
@@ -174,7 +174,7 @@ module Datadog
174
174
  record_event_telemetry_metric(SIGNUP_EVENT)
175
175
 
176
176
  if user_attributes.key?(:id)
177
- Kit::Identity.set_user(trace, span, **user_attributes)
177
+ Kit::Identity.set_user(trace, span, **user_attributes) # steep:ignore InsufficientKeywordArguments
178
178
  else
179
179
  # NOTE: {Datadog::Kit::Identity.set_user} requires an ID,
180
180
  # but WAF can evaluate login alone
@@ -235,7 +235,7 @@ module Datadog
235
235
 
236
236
  if telemetry.nil?
237
237
  return Datadog.logger.debug(
238
- "Kit::AppSec: Telemetry component is unavailable. Skip recording SDK metrics"
238
+ "Kit::AppSec: Telemetry component is unavailable. Skip recording SDK metrics",
239
239
  )
240
240
  end
241
241
 
@@ -160,7 +160,7 @@ module Datadog
160
160
  unless trace && span
161
161
  Datadog.logger.debug(
162
162
  "Tracing not enabled. Method ##{method} is a no-op. Please enable tracing if you want ##{method}" \
163
- " to track this events"
163
+ " to track this events",
164
164
  )
165
165
  return
166
166
  end
@@ -31,7 +31,7 @@ module Datadog
31
31
  rescue => e
32
32
  Kernel.warn(
33
33
  "[datadog] Failed to enable core dumps. Cause: #{e.class}: #{e.message} " \
34
- "Location: #{Array(e.backtrace).first}"
34
+ "Location: #{Array(e.backtrace).first}",
35
35
  )
36
36
  return
37
37
  end
@@ -96,7 +96,7 @@ module Datadog
96
96
  unless trace && span
97
97
  Datadog.logger.debug(
98
98
  "Tracing not enabled. Method ##{method} is a no-op. Please enable tracing if you want ##{method}" \
99
- " to track this events"
99
+ " to track this events",
100
100
  )
101
101
  return
102
102
  end
@@ -100,6 +100,7 @@ module Datadog
100
100
  return unless @providers.empty?
101
101
  if @delivery_source == Configuration::Source::REMOTE_CONFIG && @delivery_started
102
102
  # Remote Configuration is process-scoped and may already hold configuration needed by the next provider.
103
+ @logger.debug { "Feature Flags: provider detached; process-scoped Remote Configuration remains active" }
103
104
  [nil, nil]
104
105
  else
105
106
  previous_source = @configuration_source
@@ -149,9 +150,14 @@ module Datadog
149
150
  @activated = true
150
151
  unless resolution.enabled?
151
152
  @failure = "Feature Flags are disabled"
153
+ @logger.debug do
154
+ reason = @settings.feature_flags.enabled ? "the resolved configuration source is offline" : "feature_flags.enabled is false"
155
+ "Feature Flags: disabled because #{reason}"
156
+ end
152
157
  return
153
158
  end
154
159
 
160
+ @logger.debug { "Feature Flags: configuration source is #{resolution.source}" }
155
161
  component = Component.build(
156
162
  @settings,
157
163
  @agent_settings,
@@ -230,6 +236,20 @@ module Datadog
230
236
  end
231
237
 
232
238
  @configuration_source = configuration_source
239
+
240
+ @logger.debug do
241
+ agentless = @settings.feature_flags.agentless
242
+ sdk_env = @settings.env ? "sdk_env=#{@settings.env.inspect}" : "sdk_env is unset"
243
+ requested_env = if endpoint.managed?
244
+ @settings.env ? "requested_env=#{@settings.env.inspect}" : "requested_env is unset"
245
+ else
246
+ "requested_env is not sent (custom endpoint)"
247
+ end
248
+ "Feature Flags: starting agentless configuration source at #{endpoint.origin} " \
249
+ "(#{sdk_env}, #{requested_env}, " \
250
+ "poll interval #{agentless.poll_interval_seconds}s, request timeout #{agentless.request_timeout_seconds}s)"
251
+ end
252
+
233
253
  started = configuration_source.start
234
254
  @failure = "Feature Flags agentless delivery could not start" unless started
235
255
  started
@@ -241,7 +261,7 @@ module Datadog
241
261
  @failure = "Feature Flags Remote Configuration is unavailable"
242
262
  @logger.warn(
243
263
  "#{@failure}. To enable Remote Configuration, " \
244
- "see https://docs.datadoghq.com/remote_configuration/."
264
+ "see https://docs.datadoghq.com/remote_configuration/.",
245
265
  )
246
266
  return false
247
267
  end
@@ -249,6 +269,7 @@ module Datadog
249
269
  receivers = @remote_receivers ||= Remote.receivers(
250
270
  @telemetry,
251
271
  component_provider: -> { @mutex.synchronize { @component } },
272
+ logger: @logger,
252
273
  )
253
274
  remote.register(
254
275
  capabilities: Remote.capabilities,
@@ -256,6 +277,10 @@ module Datadog
256
277
  receivers: receivers,
257
278
  )
258
279
  remote.start
280
+ @logger.debug do
281
+ sdk_env = @settings.env ? "sdk_env=#{@settings.env.inspect}" : "sdk_env is unset"
282
+ "Feature Flags: started Remote Configuration source (#{sdk_env}) using Datadog Agent"
283
+ end
259
284
  true
260
285
  end
261
286
 
@@ -61,6 +61,7 @@ module Datadog
61
61
  @retry_wait = retry_wait || ->(delay) { wait_for_retry(delay) }
62
62
  @etag = nil
63
63
  @warned = {}
64
+ @failed_polls = 0
64
65
  @lifecycle_mutex = Mutex.new
65
66
  @started = false
66
67
  @stopped = false
@@ -91,6 +92,7 @@ module Datadog
91
92
  end
92
93
 
93
94
  super
95
+ @logger.debug { "Feature Flags: stopped agentless configuration polling" }
94
96
  true
95
97
  end
96
98
 
@@ -110,6 +112,7 @@ module Datadog
110
112
  return if stopped?
111
113
 
112
114
  response = @transport.get(@etag)
115
+ log_poll_attempt(response, attempt)
113
116
  unless retryable?(response)
114
117
  apply_response(response)
115
118
  return
@@ -120,7 +123,9 @@ module Datadog
120
123
  return
121
124
  end
122
125
 
123
- return if wait_before_retry(retry_delay(attempt))
126
+ delay = retry_delay(attempt)
127
+ @logger.debug { "Feature Flags: retrying agentless configuration poll in #{delay.round(1)}s" }
128
+ return if wait_before_retry(delay)
124
129
 
125
130
  attempt += 1
126
131
  end
@@ -131,6 +136,7 @@ module Datadog
131
136
  def after_fork
132
137
  # The child must report its own failures, while the inherited ETag still matches its configuration.
133
138
  @warned = {}
139
+ @failed_polls = 0
134
140
  nil
135
141
  end
136
142
 
@@ -140,6 +146,53 @@ module Datadog
140
146
  @lifecycle_mutex.synchronize { @stopped }
141
147
  end
142
148
 
149
+ def log_poll_attempt(response, attempt)
150
+ @logger.debug do
151
+ status = response.status
152
+ outcome = case status
153
+ when nil
154
+ "failed: #{request_error_class(response)}"
155
+ when 304
156
+ "configuration unchanged (HTTP 304)"
157
+ when 200
158
+ etag = response.etag.to_s.strip
159
+ etag.empty? ? "configuration received (HTTP 200)" : "configuration received (ETag #{etag}; HTTP 200)"
160
+ else
161
+ "#{http_failure_reason(status)} (HTTP #{status})"
162
+ end
163
+ "Feature Flags: agentless configuration poll attempt #{attempt}/#{MAX_ATTEMPTS}: #{outcome}"
164
+ end
165
+ end
166
+
167
+ def http_failure_reason(status)
168
+ case status
169
+ when 400
170
+ "configuration request rejected as invalid"
171
+ when 401
172
+ "configuration request lacks valid authentication credentials"
173
+ when 403
174
+ "access to the configuration endpoint is forbidden"
175
+ when 404
176
+ "configuration endpoint not found"
177
+ when 408
178
+ "configuration request timed out"
179
+ when 429
180
+ "configuration request rate limited"
181
+ when 500
182
+ "configuration endpoint encountered an internal error"
183
+ when 502
184
+ "configuration endpoint received an invalid upstream response"
185
+ when 503
186
+ "configuration endpoint is temporarily unavailable"
187
+ when 504
188
+ "configuration endpoint timed out waiting for an upstream response"
189
+ when 500..599
190
+ "configuration endpoint reported a server error"
191
+ else
192
+ "unexpected response from the configuration endpoint"
193
+ end
194
+ end
195
+
143
196
  def retryable?(response)
144
197
  status = response.status
145
198
  return true unless status
@@ -181,17 +234,18 @@ module Datadog
181
234
  end
182
235
 
183
236
  def apply_response(response)
184
- case response.status
237
+ status = response.status
238
+ case status
185
239
  when 200
240
+ log_recovery
186
241
  apply_configuration(response)
187
242
  when 304
188
- nil
243
+ log_recovery
189
244
  when 401, 403
190
- warn_once(:authentication) do
191
- @logger.warn(
192
- "Feature Flags agentless endpoint returned HTTP #{response.status}; verify endpoint authentication"
193
- )
194
- end
245
+ report_poll_failure(
246
+ :authentication,
247
+ "Feature Flags agentless #{http_failure_reason(status)}; verify endpoint authentication (HTTP #{status})",
248
+ )
195
249
  else
196
250
  warn_failure(response, 1)
197
251
  end
@@ -203,6 +257,9 @@ module Datadog
203
257
  warn_once(:malformed) do
204
258
  @logger.error("Feature Flags agentless endpoint returned malformed UFC payload")
205
259
  end
260
+ @logger.debug do
261
+ "Feature Flags: ignoring malformed agentless configuration payload"
262
+ end
206
263
  return
207
264
  end
208
265
 
@@ -224,16 +281,38 @@ module Datadog
224
281
  else
225
282
  :http_unexpected
226
283
  end
227
- warn_once(category) do
228
- if status
229
- @logger.warn("Feature Flags agentless endpoint returned HTTP #{status} after #{attempts} attempt(s)")
230
- else
231
- error_class = response.error ? response.error.class : StandardError
232
- @logger.warn("Feature Flags agentless request failed after #{attempts} attempt(s): #{error_class}")
233
- end
284
+ refresh = (attempts == MAX_ATTEMPTS) ? "; next poll in #{@poll_interval_seconds}s" : ""
285
+ message = if status
286
+ "Feature Flags agentless #{http_failure_reason(status)} after #{attempts} attempt(s) (HTTP #{status})#{refresh}"
287
+ else
288
+ "Feature Flags agentless request failed after #{attempts} attempt(s): #{request_error_class(response)}#{refresh}"
289
+ end
290
+ report_poll_failure(category, message)
291
+ end
292
+
293
+ # Repeats are debug-only so a persistent outage does not flood warnings.
294
+ def report_poll_failure(category, message)
295
+ @failed_polls += 1
296
+ if @warned[category]
297
+ @logger.debug { message }
298
+ else
299
+ warn_once(category) { @logger.warn(message) }
234
300
  end
235
301
  end
236
302
 
303
+ def log_recovery
304
+ return if @failed_polls.zero?
305
+
306
+ failed_polls = @failed_polls
307
+ @failed_polls = 0
308
+ @logger.debug { "Feature Flags: agentless configuration delivery recovered after #{failed_polls} failed poll(s)" }
309
+ end
310
+
311
+ # Class only: transport error messages can embed endpoint details.
312
+ def request_error_class(response)
313
+ response.error ? response.error.class : StandardError
314
+ end
315
+
237
316
  def warn_once(category)
238
317
  return if @warned[category]
239
318
 
@@ -13,6 +13,7 @@ require_relative "flag_evaluation/writer"
13
13
  require_relative "hooks/flag_eval_metrics_hook"
14
14
  require_relative "hooks/flag_eval_evp_hook"
15
15
  require_relative "hooks/span_enrichment_hook"
16
+ require_relative "hooks/debug_logging_hook"
16
17
 
17
18
  module Datadog
18
19
  module OpenFeature
@@ -70,12 +71,15 @@ module Datadog
70
71
  @flag_eval_metrics_hook = create_flag_eval_metrics_hook
71
72
  @flag_eval_evp_hook = create_flag_eval_evp_hook
72
73
  @span_enrichment_hook = create_span_enrichment_hook
74
+ @debug_logging_hook = nil
73
75
 
74
76
  @configuration_mutex = Mutex.new
75
77
  @reconfiguration_mutex = Mutex.new
76
78
  @configuration_condition = ConditionVariable.new
77
79
  @configuration_received = false
78
80
  @configuration_shutdown = false
81
+
82
+ @engine.warn_raw_data_logging
79
83
  end
80
84
 
81
85
  def reconfigure!(configuration)
@@ -102,9 +106,19 @@ module Datadog
102
106
  @on_configuration_change&.call(event) if event
103
107
  end
104
108
 
109
+ # Checked per evaluation so the SDK skips hook dispatch unless debug logging is on. Built lazily
110
+ # because Remote Configuration can build this component before the OpenFeature SDK loads.
111
+ def debug_logging_hook
112
+ return unless @logger.debug?
113
+
114
+ @engine.warn_raw_data_logging
115
+ @debug_logging_hook ||= create_debug_logging_hook
116
+ end
117
+
105
118
  def wait_for_configuration(&cancelled)
106
- timeout_seconds = @settings.feature_flags.initialization_timeout_ms / 1000.0
107
- deadline = Core::Utils::Time.get_time + timeout_seconds
119
+ timeout_ms = @settings.feature_flags.initialization_timeout_ms
120
+ @logger.debug { "Feature Flags: waiting up to #{timeout_ms}ms for configuration" }
121
+ deadline = Core::Utils::Time.get_time + timeout_ms / 1000.0
108
122
 
109
123
  @configuration_mutex.synchronize do
110
124
  loop do
@@ -176,6 +190,12 @@ module Datadog
176
190
  store = Hooks::SpanEnrichmentHook::SpanEnrichmentStateStore.new
177
191
  Hooks::SpanEnrichmentHook.new(store, logger: @logger)
178
192
  end
193
+
194
+ def create_debug_logging_hook
195
+ return unless Hooks::DebugLoggingHook.available?
196
+
197
+ Hooks::DebugLoggingHook.new(@logger)
198
+ end
179
199
  end
180
200
  end
181
201
  end
@@ -84,6 +84,12 @@ module Datadog
84
84
  def managed?
85
85
  @managed
86
86
  end
87
+
88
+ # Excludes userinfo, path, and query, which may carry credentials.
89
+ def origin
90
+ origin = "#{uri.scheme}://#{uri.host}"
91
+ (uri.port == uri.default_port) ? origin : "#{origin}:#{uri.port}"
92
+ end
87
93
  end
88
94
  end
89
95
  end
@@ -44,7 +44,7 @@ module Datadog
44
44
 
45
45
  Datadog.logger.warn(
46
46
  "DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED is deprecated; use " \
47
- "DD_FEATURE_FLAGS_ENABLED and DD_FEATURE_FLAGS_CONFIGURATION_SOURCE instead"
47
+ "DD_FEATURE_FLAGS_ENABLED and DD_FEATURE_FLAGS_CONFIGURATION_SOURCE instead",
48
48
  )
49
49
  end
50
50
  end
@@ -78,6 +78,12 @@ module Datadog
78
78
  o.default true
79
79
  end
80
80
 
81
+ option :evaluation_telemetry_per_subject_opt_out_enabled do |o|
82
+ o.type :bool
83
+ o.env "DD_FEATURE_FLAGS_EVALUATION_TELEMETRY_PER_SUBJECT_OPT_OUT_ENABLED"
84
+ o.default false
85
+ end
86
+
81
87
  option :configuration_source do |o|
82
88
  o.type :string
83
89
  o.env "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE"
@@ -112,7 +118,7 @@ module Datadog
112
118
  value
113
119
  else
114
120
  Datadog.logger.warn(
115
- "Feature Flags agentless poll interval must be positive; using the default"
121
+ "Feature Flags agentless poll interval must be positive; using the default",
116
122
  )
117
123
  Settings::DEFAULT_POLL_INTERVAL_SECONDS
118
124
  end
@@ -132,7 +138,7 @@ module Datadog
132
138
  else
133
139
  Datadog.logger.warn(
134
140
  "Feature Flags agentless request timeout must be within (0, " \
135
- "#{Settings::MAX_REQUEST_TIMEOUT_SECONDS}]; using the default"
141
+ "#{Settings::MAX_REQUEST_TIMEOUT_SECONDS}]; using the default",
136
142
  )
137
143
  Settings::DEFAULT_REQUEST_TIMEOUT_SECONDS
138
144
  end
@@ -153,7 +159,7 @@ module Datadog
153
159
  else
154
160
  Datadog.logger.warn(
155
161
  "Feature Flags provider initialization timeout must be within (0, " \
156
- "#{Settings::MAX_INITIALIZATION_TIMEOUT_MS}]; using the default"
162
+ "#{Settings::MAX_INITIALIZATION_TIMEOUT_MS}]; using the default",
157
163
  )
158
164
  Settings::DEFAULT_INITIALIZATION_TIMEOUT_MS
159
165
  end
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "json"
4
+
3
5
  require_relative "ext"
4
6
  require_relative "noop_evaluator"
5
7
  require_relative "native_evaluator"
6
8
  require_relative "resolution_details"
9
+ require_relative "../core/utils/only_once"
7
10
 
8
11
  module Datadog
9
12
  module OpenFeature
@@ -14,10 +17,15 @@ module Datadog
14
17
 
15
18
  ALLOWED_TYPES = %i[boolean string number float integer object].freeze
16
19
 
20
+ RAW_DATA_WARNING = "Feature Flags: debug logging is enabled, so feature flag logs include raw evaluation data " \
21
+ "(targeting keys, evaluation context attributes, flag values, and default values) that may contain " \
22
+ "personal or sensitive information. Unset DD_TRACE_DEBUG when you no longer need it."
23
+
17
24
  def initialize(reporter, telemetry:, logger:)
18
25
  @reporter = reporter
19
26
  @telemetry = telemetry
20
27
  @logger = logger
28
+ @raw_data_warning = Core::Utils::OnlyOnce.new
21
29
 
22
30
  @evaluator = NoopEvaluator.new(nil)
23
31
  end
@@ -34,7 +42,7 @@ module Datadog
34
42
  unless ALLOWED_TYPES.include?(expected_type)
35
43
  message = "unknown type #{expected_type.inspect}, allowed types #{ALLOWED_TYPES.join(", ")}"
36
44
  result = ResolutionDetails.build_error(
37
- value: default_value, error_code: Ext::UNKNOWN_TYPE, error_message: message
45
+ value: default_value, error_code: Ext::UNKNOWN_TYPE, error_message: message,
38
46
  )
39
47
 
40
48
  return with_observe_full_evaluation_data(result, observe_full_evaluation_data)
@@ -42,19 +50,21 @@ module Datadog
42
50
 
43
51
  context = evaluation_context&.fields.to_h
44
52
  result = evaluator.get_assignment(
45
- flag_key, default_value: default_value, context: context, expected_type: expected_type
53
+ flag_key, default_value: default_value, context: context, expected_type: expected_type,
46
54
  )
47
55
 
48
56
  result = with_observe_full_evaluation_data(result, observe_full_evaluation_data)
57
+ log_resolution(flag_key, expected_type, result)
49
58
 
50
59
  @reporter.report(result, flag_key: flag_key, context: evaluation_context)
51
60
 
52
61
  result
53
62
  rescue => e
54
63
  @telemetry.report(e, description: "OpenFeature: Failed to fetch flag value")
64
+ log_resolution_failure(flag_key, e)
55
65
 
56
66
  result = ResolutionDetails.build_error(
57
- value: default_value, error_code: Ext::GENERAL, error_message: "#{e.class}: #{e.message}"
67
+ value: default_value, error_code: Ext::GENERAL, error_message: "#{e.class}: #{e.message}",
58
68
  )
59
69
  # `== true` is load-bearing: Steep types this local as `(nil | bool)` in a rescue
60
70
  # body, and coercing at the use site is what narrows it to `bool`.
@@ -66,12 +76,13 @@ module Datadog
66
76
  # in the format expected by `libdatadog` without any modifications
67
77
  def reconfigure!(configuration)
68
78
  if configuration.nil?
69
- @logger.debug("OpenFeature: Removing configuration")
79
+ @logger.debug("Feature Flags: configuration removed")
70
80
 
71
81
  return @evaluator = NoopEvaluator.new(configuration)
72
82
  end
73
83
 
74
84
  @evaluator = NativeEvaluator.new(configuration)
85
+ @logger.debug { "Feature Flags: configuration applied; #{configuration_summary(configuration)}" }
75
86
  rescue => e
76
87
  message = "OpenFeature: Failed to reconfigure, reverting to the previous configuration"
77
88
 
@@ -81,8 +92,55 @@ module Datadog
81
92
  raise ReconfigurationError, "#{e.class}: #{e.message}"
82
93
  end
83
94
 
95
+ # Public so Component can warn before handing out DebugLoggingHook, which logs raw data.
96
+ def warn_raw_data_logging
97
+ return unless @logger.debug?
98
+
99
+ @raw_data_warning.run { @logger.warn(RAW_DATA_WARNING) }
100
+ end
101
+
84
102
  private
85
103
 
104
+ # The SDK can still replace this result, for example on a type mismatch; DebugLoggingHook logs the final one.
105
+ def log_resolution(flag_key, expected_type, result)
106
+ @logger.debug do
107
+ warn_raw_data_logging
108
+ "Feature Flags: provider resolved #{flag_key.inspect}: type=#{expected_type}, " \
109
+ "value=#{result.value.inspect}, variant=#{result.variant.inspect}, reason=#{result.reason.inspect}, " \
110
+ "allocation=#{result.allocation_key.inspect}, serial_id=#{result.serial_id.inspect}, " \
111
+ "error_code=#{result.error_code.inspect}, error_message=#{result.error_message.inspect}"
112
+ end
113
+ end
114
+
115
+ def log_resolution_failure(flag_key, error)
116
+ @logger.debug do
117
+ warn_raw_data_logging
118
+ "Feature Flags: provider failed to resolve #{flag_key.inspect}: #{error.class}: #{error.message}; " \
119
+ "returning the default value"
120
+ end
121
+ end
122
+
123
+ def configuration_summary(configuration)
124
+ payload = begin
125
+ JSON.parse(configuration)
126
+ rescue JSON::NestingError
127
+ # Native parsing accepts deeper JSON than Ruby's default nesting limit.
128
+ return "received payload metadata unavailable: JSON nesting exceeds the diagnostic parser limit"
129
+ end
130
+ return "received payload metadata unavailable: expected a JSON object" unless payload.is_a?(Hash)
131
+
132
+ flags = payload["flags"]
133
+ environment = payload["environment"]
134
+ environment_name = environment["name"] if environment.is_a?(Hash)
135
+ flag_count = flags.is_a?(Hash) ? "flag_count=#{flags.size}" : "flag_count is not specified"
136
+ environment_summary = environment_name ? "environment=#{environment_name.inspect}" : "environment is not specified"
137
+ created_at = payload["createdAt"]
138
+ creation_summary = created_at ? "created_at=#{created_at.inspect}" : "created_at is not specified"
139
+
140
+ "received payload: #{flag_count}, #{environment_summary}, #{creation_summary}; " \
141
+ "individual flags may still fail evaluation"
142
+ end
143
+
86
144
  # Stamps observe_full_evaluation_data onto the result metadata so the hook
87
145
  # reads it from the event, not live config.
88
146
  def with_observe_full_evaluation_data(result, observe_full_evaluation_data)
@@ -21,6 +21,10 @@ module Datadog
21
21
  super
22
22
  end
23
23
 
24
+ def pending_dropped_count
25
+ @dropped
26
+ end
27
+
24
28
  protected
25
29
 
26
30
  def drain!
@@ -19,21 +19,34 @@ module Datadog
19
19
  # might be missing depending on the customer way of using flags evaluation API.
20
20
  # In addition to that the evaluation result must be marked for reporting.
21
21
  def report(result, flag_key:, context:)
22
- return false if context.nil?
23
- return false unless result.log?
22
+ return skip(flag_key, "no evaluation context") if context.nil?
23
+ return skip(flag_key, "the evaluation result is not marked for exposure logging") unless result.log?
24
24
 
25
25
  key = Event.cache_key(result, flag_key: flag_key, context: context)
26
26
  value = Event.cache_value(result, flag_key: flag_key, context: context)
27
- return false if @deduplicator.duplicate?(key, value)
27
+ return skip(flag_key, "duplicate of the last exposure for this subject") if @deduplicator.duplicate?(key, value)
28
28
 
29
29
  event = Event.build(result, flag_key: flag_key, context: context)
30
- @worker.enqueue(event)
30
+ return false unless @worker.enqueue(event)
31
+
32
+ @logger.debug do
33
+ "Feature Flags: queued exposure for #{flag_key.inspect} (allocation=#{result.allocation_key.inspect}, " \
34
+ "variant=#{result.variant.inspect}, targeting_key=#{context.targeting_key.inspect})"
35
+ end
36
+ true
31
37
  rescue => e
32
38
  @logger.debug { "OpenFeature: Failed to report resolution details: #{e.class}: #{e.message}" }
33
39
  @telemetry.report(e, description: "OpenFeature: Failed to report resolution details")
34
40
 
35
41
  false
36
42
  end
43
+
44
+ private
45
+
46
+ def skip(flag_key, reason)
47
+ @logger.debug { "Feature Flags: exposure for #{flag_key.inspect} not recorded: #{reason}" }
48
+ false
49
+ end
37
50
  end
38
51
  end
39
52
  end