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
@@ -27,8 +27,18 @@ module Datadog
27
27
  cls = Class.new(Evaluator)
28
28
  cls.class_exec do
29
29
  eval(<<-RUBY, Object.new.send(:binding), __FILE__, __LINE__ + 1) # standard:disable Security/Eval
30
- def evaluate(context)
31
- @context = context
30
+ # Evaluates the compiled expression against +context+ within
31
+ # the cooperative evaluation +evaluation_deadline+.
32
+ #
33
+ # @param context [Context] evaluation context (locals, instance
34
+ # variables, and per-invocation special variables).
35
+ # @param evaluation_deadline [Float] invocation-local evaluation
36
+ # deadline, as CLOCK_THREAD_CPUTIME_ID float seconds.
37
+ # @return [Object] the value the expression evaluates to.
38
+ # @raise [DI::Error::ExpressionEvaluationError] the expression
39
+ # failed to evaluate, including the EvaluationTimeout
40
+ # subclass raised when the deadline is exceeded.
41
+ def evaluate(context, evaluation_deadline)
32
42
  #{compiled_expr}
33
43
  end
34
44
  RUBY
@@ -40,12 +50,32 @@ module Datadog
40
50
  attr_reader :evaluator
41
51
  attr_reader :redaction_identifier
42
52
 
43
- def evaluate(context)
44
- @evaluator.evaluate(context)
53
+ # Evaluates the expression against +context+.
54
+ #
55
+ # @param context [Context] evaluation context (locals, instance
56
+ # variables, and per-invocation special variables).
57
+ # @param evaluation_deadline [Float] cooperative evaluation deadline, as
58
+ # CLOCK_THREAD_CPUTIME_ID float seconds.
59
+ # @return [Object] the value the expression evaluates to.
60
+ # @raise [DI::Error::ExpressionEvaluationError] the expression failed
61
+ # to evaluate; DI::Error::EvaluationTimeout, raised when the
62
+ # evaluation deadline is exceeded, is a subclass.
63
+ def evaluate(context, evaluation_deadline:)
64
+ evaluator.evaluate(context, evaluation_deadline)
45
65
  end
46
66
 
47
- def satisfied?(context)
48
- !!evaluate(context)
67
+ # Returns whether the expression evaluates to a truthy value
68
+ # against +context+.
69
+ #
70
+ # @param context [Context] evaluation context.
71
+ # @param evaluation_deadline [Float] cooperative evaluation deadline, as
72
+ # CLOCK_THREAD_CPUTIME_ID float seconds.
73
+ # @return [Boolean] whether the expression is satisfied.
74
+ # @raise [DI::Error::ExpressionEvaluationError] the expression failed
75
+ # to evaluate; DI::Error::EvaluationTimeout, raised when the
76
+ # evaluation deadline is exceeded, is a subclass.
77
+ def satisfied?(context, evaluation_deadline:)
78
+ !!evaluate(context, evaluation_deadline: evaluation_deadline)
49
79
  end
50
80
  end
51
81
  end
@@ -92,6 +92,22 @@ module Datadog
92
92
  # expressions.
93
93
  class ExpressionEvaluationError < Error
94
94
  end
95
+
96
+ # Raised when expression evaluation exceeds the configured
97
+ # evaluation CPU-time budget, or a `matches` operator exceeds its
98
+ # per-match wall-clock bound.
99
+ class EvaluationTimeout < ExpressionEvaluationError
100
+ # Default message for EvaluationTimeout exceptions.
101
+ DEFAULT_MESSAGE = "expression evaluation timeout"
102
+
103
+ # Builds the exception, defaulting +message+ to DEFAULT_MESSAGE.
104
+ #
105
+ # @param message [String] exception message.
106
+ # @return [void]
107
+ def initialize(message: DEFAULT_MESSAGE)
108
+ super(message)
109
+ end
110
+ end
95
111
  end
96
112
  end
97
113
  end
@@ -16,8 +16,9 @@ module Datadog
16
16
  # process-wide rate limiter.
17
17
  RATE_LIMIT_GLOBAL = "rateLimitGlobal"
18
18
 
19
- # Skip reason emitted when a capture expression evaluation exceeds
20
- # the capture time budget.
19
+ # Skip reason emitted when an evaluation exceeds its time budget:
20
+ # a probe condition, a capture expression (including one skipped at
21
+ # the expired capture time budget), or a message template.
21
22
  EVALUATION_TIMEOUT = "evaluationTimeout"
22
23
 
23
24
  # Skip reason named in the +di: debug log when a condition-evaluation
@@ -83,7 +83,7 @@ module Datadog
83
83
  # @param settings [Datadog::Core::Configuration::Settings] tracer settings
84
84
  # @param serializer [Serializer] serializer for captured values
85
85
  # @param logger [DI::Logger] logger for instrumenter diagnostics
86
- # @param guardrails_telemetry [GuardrailsTelemetry] emitter for the canonical rate-limit skip metric
86
+ # @param guardrails_telemetry [GuardrailsTelemetry] emitter for the canonical guardrail skip metrics
87
87
  # @param capture_expression_evaluator [CaptureExpressionEvaluator] evaluator for the probe's capture expressions
88
88
  # @param code_tracker [CodeTracker, nil] code tracker for line probe installation
89
89
  # @param telemetry [Datadog::Core::Telemetry::Component, nil] component instrumenter errors are reported through
@@ -554,7 +554,8 @@ module Datadog
554
554
  target_self: target_self,
555
555
  probe: probe, settings: settings, serializer: serializer,
556
556
  )
557
- continue = condition.satisfied?(context)
557
+ continue = condition.satisfied?(context,
558
+ evaluation_deadline: serializer.evaluation_deadline)
558
559
  rescue Exception => exc # standard:disable Lint/RescueException
559
560
  Datadog::DI.reraise_if_fatal(exc)
560
561
  # Evaluation error exception can be raised for "expected"
@@ -563,6 +564,10 @@ module Datadog
563
564
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions &&
564
565
  !exc.is_a?(DI::Error::ExpressionEvaluationError)
565
566
 
567
+ # An evaluation timeout is an internal budget abort, so it
568
+ # stays contained even under propagate_all_exceptions.
569
+ record_condition_evaluation_timeout(probe, exc)
570
+
566
571
  if context
567
572
  # We want to report evaluation errors for conditions
568
573
  # as probe snapshots. However, if we failed to create
@@ -605,7 +610,7 @@ module Datadog
605
610
  serialized_entry_args = if probe.capture_snapshot?
606
611
  serializer.serialize_args(args, kwargs, target_self,
607
612
  deadline: serializer.serialization_deadline,
608
- **probe.snapshot_serializer_limits(settings),)
613
+ **probe.snapshot_serializer_limits(settings))
609
614
  end
610
615
 
611
616
  entry_capture_expressions = nil
@@ -698,7 +703,7 @@ module Datadog
698
703
  entry_capture_expressions: entry_capture_expressions,
699
704
  entry_capture_evaluation_errors: entry_capture_evaluation_errors,
700
705
  caller_locations: caller_locs,
701
- return_value: rv, duration: duration, exception: exc,)
706
+ return_value: rv, duration: duration, exception: exc)
702
707
 
703
708
  begin
704
709
  responder.probe_executed_callback(context)
@@ -788,12 +793,16 @@ module Datadog
788
793
  public :kwargs_from_splat
789
794
  end
790
795
 
791
- # Trace point callback for a line probe.
796
+ # Trace point callback for a line probe hit. Evaluates the probe's
797
+ # condition, applies the per-probe rate limiter and the global rate
798
+ # limiter, then invokes the responder with the built context.
792
799
  #
793
- # @param probe [Probe] the probe installed on this line
794
- # @param iseq [RubyVM::InstructionSequence, nil] instruction sequence the trace point is targeted at, or nil when untargeted
795
- # @param responder [#probe_executed_callback, #probe_condition_evaluation_failed_callback] callback target invoked with the built Context
796
- # @param tp [TracePoint] the firing trace point
800
+ # @param probe [Probe] the probe the trace point was installed for.
801
+ # @param iseq [RubyVM::InstructionSequence, nil] instruction sequence of
802
+ # the probe's target file, when the trace point is targeted.
803
+ # @param responder [#probe_executed_callback, #probe_condition_evaluation_failed_callback, #probe_disabled_callback]
804
+ # callback target for the probe hit.
805
+ # @param tp [TracePoint] the trace point event that fired.
797
806
  # @return [void]
798
807
  def line_trace_point_callback(probe, iseq, responder, tp)
799
808
  di_start_time = Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID)
@@ -832,7 +841,8 @@ module Datadog
832
841
  if condition = probe.condition
833
842
  begin
834
843
  context = build_trace_point_context(probe, tp)
835
- return unless condition.satisfied?(context)
844
+ return unless condition.satisfied?(context,
845
+ evaluation_deadline: serializer.evaluation_deadline)
836
846
  rescue Exception => exc # standard:disable Lint/RescueException
837
847
  Datadog::DI.reraise_if_fatal(exc)
838
848
  # Evaluation error exception can be raised for "expected"
@@ -841,6 +851,10 @@ module Datadog
841
851
  raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions &&
842
852
  !exc.is_a?(DI::Error::ExpressionEvaluationError)
843
853
 
854
+ # An evaluation timeout is an internal budget abort, so it
855
+ # stays contained even under propagate_all_exceptions.
856
+ record_condition_evaluation_timeout(probe, exc)
857
+
844
858
  if context
845
859
  # We want to report evaluation errors for conditions
846
860
  # as probe snapshots. However, if we failed to create
@@ -900,6 +914,21 @@ module Datadog
900
914
  nil
901
915
  end
902
916
 
917
+ # Records a condition evaluation timeout for +probe+ through the
918
+ # shared evaluation-timeout reporter. Does nothing when +exc+ is not
919
+ # an evaluation timeout.
920
+ #
921
+ # @param probe [Probe] the probe whose condition evaluation timed out.
922
+ # @param exc [Exception] the exception raised by the evaluation.
923
+ # @return [void]
924
+ def record_condition_evaluation_timeout(probe, exc)
925
+ DI.report_evaluation_timeout(
926
+ telemetry: telemetry, guardrails_telemetry: guardrails_telemetry, logger: logger,
927
+ probe: probe, kind: "condition", exc: exc,
928
+ )
929
+ nil
930
+ end
931
+
903
932
  def build_trace_point_context(probe, tp)
904
933
  stack = caller_locations
905
934
  # We have two helper methods being invoked from the trace point
@@ -4,6 +4,7 @@
4
4
 
5
5
  require_relative "evaluation_failure"
6
6
  require_relative "fatal_exceptions"
7
+ require_relative "guardrails_telemetry"
7
8
 
8
9
  module Datadog
9
10
  module DI
@@ -17,12 +18,17 @@ module Datadog
17
18
  # @param serializer [Serializer] serializer for captured values
18
19
  # @param logger [DI::Logger] logger for builder diagnostics
19
20
  # @param capture_expression_evaluator [CaptureExpressionEvaluator] evaluator for the probe's capture expressions
21
+ # @param guardrails_telemetry [GuardrailsTelemetry] emitter for the canonical evaluation-timeout skip metric
22
+ # @param telemetry [Datadog::Core::Telemetry::Component, nil] component evaluation timeouts are counted through
20
23
  # @return [void]
21
- def initialize(settings, serializer, logger, capture_expression_evaluator:)
24
+ def initialize(settings, serializer, logger, capture_expression_evaluator:, guardrails_telemetry:,
25
+ telemetry:)
22
26
  @settings = settings
23
27
  @serializer = serializer
24
28
  @logger = logger
25
29
  @capture_expression_evaluator = capture_expression_evaluator
30
+ @guardrails_telemetry = guardrails_telemetry
31
+ @telemetry = telemetry
26
32
  end
27
33
 
28
34
  attr_reader :settings
@@ -33,35 +39,43 @@ module Datadog
33
39
 
34
40
  attr_reader :capture_expression_evaluator
35
41
 
42
+ # The guardrails metric emitter.
43
+ # @return [GuardrailsTelemetry]
44
+ attr_reader :guardrails_telemetry
45
+
46
+ # The telemetry component evaluation timeouts are counted through.
47
+ # @return [Datadog::Core::Telemetry::Component, nil]
48
+ attr_reader :telemetry
49
+
36
50
  def build_received(probe)
37
51
  build_status(probe,
38
52
  message: "Probe #{probe.id} has been received correctly",
39
- status: "RECEIVED",)
53
+ status: "RECEIVED")
40
54
  end
41
55
 
42
56
  def build_installed(probe)
43
57
  build_status(probe,
44
58
  message: "Probe #{probe.id} has been instrumented correctly",
45
- status: "INSTALLED",)
59
+ status: "INSTALLED")
46
60
  end
47
61
 
48
62
  def build_emitting(probe)
49
63
  build_status(probe,
50
64
  message: "Probe #{probe.id} is emitting",
51
- status: "EMITTING",)
65
+ status: "EMITTING")
52
66
  end
53
67
 
54
68
  def build_errored(probe, exc)
55
69
  build_status(probe,
56
70
  message: "Instrumentation for probe #{probe.id} failed: #{exc}",
57
71
  status: "ERROR",
58
- exception: exc,)
72
+ exception: exc)
59
73
  end
60
74
 
61
75
  def build_disabled(probe, duration)
62
76
  build_status(probe,
63
77
  message: "Probe #{probe.id} was disabled because it consumed #{duration} seconds of CPU time in DI processing",
64
- status: "ERROR",)
78
+ status: "ERROR")
65
79
  end
66
80
 
67
81
  # Duration is in seconds.
@@ -144,12 +158,13 @@ module Datadog
144
158
  message = nil
145
159
  evaluation_errors = []
146
160
  if segments = probe.template_segments
147
- message, evaluation_errors = evaluate_template(segments, context)
161
+ message, evaluation_errors = evaluate_template(segments, context,
162
+ evaluation_deadline: serializer.evaluation_deadline)
148
163
  end
149
164
  evaluation_errors.concat(capture_expression_evaluation_errors)
150
165
  build_snapshot_base(context,
151
166
  evaluation_errors: evaluation_errors, message: message,
152
- captures: captures,)
167
+ captures: captures)
153
168
  end
154
169
 
155
170
  def build_condition_evaluation_failed(context, expression, exception)
@@ -401,23 +416,56 @@ module Datadog
401
416
  end
402
417
  end
403
418
 
404
- def evaluate_template(template_segments, context)
419
+ # Renders the probe's message template against +context+.
420
+ #
421
+ # @param template_segments [Array<String, EL::Expression>] compiled
422
+ # template segments to render.
423
+ # @param context [Context] evaluation context for expression segments.
424
+ # @param evaluation_deadline [Float] cooperative evaluation deadline, as
425
+ # CLOCK_THREAD_CPUTIME_ID float seconds.
426
+ # @return [Array(String, Array<EvaluationFailure>)] the rendered message
427
+ # and the evaluation errors.
428
+ def evaluate_template(template_segments, context, evaluation_deadline:)
405
429
  evaluation_errors = []
430
+ timed_out = false
406
431
  message = template_segments.map do |segment|
432
+ if segment.is_a?(EL::Expression) && !timed_out &&
433
+ EL::Evaluator.evaluation_deadline_exceeded?(evaluation_deadline)
434
+ raise DI::Error::EvaluationTimeout
435
+ end
407
436
  case segment
408
437
  when String
409
438
  segment
410
439
  when EL::Expression
411
- serializer.serialize_value_for_message(segment.evaluate(context), name: segment.redaction_identifier)
440
+ if timed_out
441
+ # The shared deadline already crossed and reported its
442
+ # timeout, so the remaining segments render as the
443
+ # placeholder rather than raising and re-reporting per
444
+ # segment.
445
+ "[evaluation error]"
446
+ else
447
+ serializer.serialize_value_for_message(segment.evaluate(context, evaluation_deadline: evaluation_deadline), name: segment.redaction_identifier)
448
+ end
412
449
  else
413
450
  raise ArgumentError, "Invalid template segment type: #{segment}"
414
451
  end
415
452
  rescue Exception => exc # standard:disable Lint/RescueException
416
453
  Datadog::DI.reraise_if_fatal(exc)
454
+ DI.report_evaluation_timeout(
455
+ telemetry: telemetry, guardrails_telemetry: guardrails_telemetry, logger: logger,
456
+ probe: context.probe, kind: "template", exc: exc,
457
+ )
417
458
  evaluation_errors << EvaluationFailure.new(
418
459
  expr: segment.dsl_expr, # steep:ignore NoMethod
419
460
  message: "#{exc.class}: #{exc.message}",
420
461
  )
462
+ # The matches wall-time bound also raises EvaluationTimeout
463
+ # without crossing the shared deadline, and it stops only one
464
+ # segment, so it must not suppress the remaining ones.
465
+ if exc.is_a?(DI::Error::EvaluationTimeout) &&
466
+ EL::Evaluator.evaluation_deadline_exceeded?(evaluation_deadline)
467
+ timed_out = true
468
+ end
421
469
  "[evaluation error]"
422
470
  end.join
423
471
  [message, evaluation_errors]
@@ -220,7 +220,7 @@ module Datadog
220
220
  # @return [DI::Transport::Input::Transport]
221
221
  def snapshot_transport
222
222
  @snapshot_transport ||= DI::Transport::HTTP.input(agent_settings: agent_settings, logger: logger,
223
- telemetry: telemetry, guardrails_telemetry: guardrails_telemetry,)
223
+ telemetry: telemetry, guardrails_telemetry: guardrails_telemetry)
224
224
  end
225
225
 
226
226
  # Sends a batch of snapshot payloads to the agent.
@@ -98,6 +98,9 @@ module Datadog
98
98
  # and DI.activate_tracking is defined.
99
99
  DI.activate_tracking
100
100
  was_started = component.started?
101
+ if repository && !was_started
102
+ remove_withdrawn_probes(repository, component)
103
+ end
101
104
  component.start!
102
105
  # A probe delivered in an earlier poll while the component was stopped
103
106
  # was dropped by #receivers (which ignores changes while !started?) and
@@ -145,7 +148,9 @@ module Datadog
145
148
  # DEV: Apply this refactor to AppSec as well if implemented.
146
149
 
147
150
  component = DI.component
148
- if component&.started?
151
+ if changes.empty?
152
+ remove_withdrawn_probes(repository, component) if component
153
+ elsif component&.started?
149
154
  changes.each do |change|
150
155
  case change.type
151
156
  when :insert
@@ -176,7 +181,7 @@ module Datadog
176
181
 
177
182
  def receiver(products = [PRODUCT], &block)
178
183
  matcher = Core::Remote::Dispatcher::Matcher::Product.new(products)
179
- [Core::Remote::Dispatcher::Receiver.new(matcher, &block)]
184
+ [Core::Remote::Dispatcher::Receiver.new(matcher, initial_reconcile: true, &block)]
180
185
  end
181
186
 
182
187
  private
@@ -266,6 +271,45 @@ module Datadog
266
271
  end
267
272
  end
268
273
 
274
+ # Removes every probe the component tracks whose id is absent from all
275
+ # LIVE_DEBUGGING contents in the repository. Reconciles backend deletions that
276
+ # produced no delete change for {.receivers}. Skips removal while a
277
+ # LIVE_DEBUGGING content fails to parse, because that content can hold any
278
+ # tracked probe.
279
+ def remove_withdrawn_probes(repository, component)
280
+ # @type var present_probe_ids: Array[String]
281
+ present_probe_ids = []
282
+ content_failed_to_parse = false
283
+ repository.contents.each do |content|
284
+ next unless content.path.product == PRODUCT
285
+
286
+ begin
287
+ present_probe_ids << parse_content(content)["id"]
288
+ rescue => exc
289
+ component.logger.debug do
290
+ "di: skipping withdrawn-probe reconcile, a LIVE_DEBUGGING content failed to parse: " \
291
+ "#{exc.class}: #{exc.message}"
292
+ end
293
+ content_failed_to_parse = true
294
+ break
295
+ end
296
+ end
297
+ return if content_failed_to_parse
298
+
299
+ probe_repository = component.probe_manager.probe_repository
300
+ tracked_probe_ids = probe_repository.installed_probes.keys | probe_repository.pending_probes.keys
301
+ (tracked_probe_ids - present_probe_ids).each do |probe_id|
302
+ component.logger.trace { "di: removing probe #{probe_id} withdrawn from remote configuration" }
303
+ component.probe_manager.remove_probe(probe_id)
304
+ rescue Exception => exc # standard:disable Lint/RescueException
305
+ Datadog::DI.reraise_if_fatal(exc)
306
+ raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
307
+
308
+ component.logger.debug { "di: error removing withdrawn probe #{probe_id}: #{exc.class}: #{exc.message}" }
309
+ component.telemetry&.report(exc, description: "Error removing withdrawn probe")
310
+ end
311
+ end
312
+
269
313
  def probe_known?(probe_id, component)
270
314
  repo = component.probe_manager.probe_repository
271
315
  !!(repo.find_installed(probe_id) ||
@@ -96,8 +96,25 @@ module Datadog
96
96
  # hex string, or UTF-8 with replacement characters) before returning
97
97
  # it from the custom serializer.
98
98
  @@flat_registry = []
99
- def self.register(condition: nil, &block)
99
+ # Registers a custom serializer. The block serializes each value
100
+ # for which +condition+ returns a truthy value.
101
+ #
102
+ # @param condition [Proc] receives the value being serialized and
103
+ # returns whether this serializer applies to it
104
+ # @param block [Proc] serializer for the values +condition+ accepts
105
+ # @raise [ArgumentError] if +condition+ is not a Proc or +block+ is absent
106
+ # @return [void]
107
+ def self.register(condition:, &block)
108
+ unless condition.is_a?(Proc)
109
+ raise ArgumentError, "Serializer.register condition must be a Proc, got #{condition.class}"
110
+ end
111
+
112
+ unless block
113
+ raise ArgumentError, "Serializer.register requires a serializer block"
114
+ end
115
+
100
116
  @@flat_registry << {condition: condition, proc: block}
117
+ nil
101
118
  end
102
119
 
103
120
  # Initializes the serializer.
@@ -114,6 +131,10 @@ module Datadog
114
131
  @logger = logger
115
132
  @guardrails_telemetry = guardrails_telemetry
116
133
  @telemetry = telemetry
134
+ # Evaluation budget for one expression evaluation, as thread CPU
135
+ # seconds, converted from max_time_to_evaluate_ms here so that
136
+ # deadline resolution per probe firing traverses no settings.
137
+ @evaluation_budget_seconds = settings.dynamic_instrumentation.max_time_to_evaluate_ms / 1000.0
117
138
  end
118
139
 
119
140
  attr_reader :settings
@@ -159,7 +180,7 @@ module Datadog
159
180
  # @param args [Array] positional arguments passed to the probed method
160
181
  # @param kwargs [Hash{Symbol => Object}] keyword arguments passed to the probed method
161
182
  # @param target_self [any] receiver of the probed method invocation
162
- # @param deadline [Float] absolute capture deadline in monotonic clock seconds
183
+ # @param deadline [Float] absolute capture deadline in thread CPU clock seconds
163
184
  # @return [Hash{Symbol => Hash}] serialized arguments keyed by argument name
164
185
  def serialize_args(args, kwargs, target_self,
165
186
  deadline:,
@@ -169,7 +190,7 @@ module Datadog
169
190
  collection_size: nil)
170
191
  combined = combine_args(args, kwargs, target_self)
171
192
  serialize_vars(combined, depth: depth, attribute_count: attribute_count,
172
- length: length, collection_size: collection_size, deadline: deadline,)
193
+ length: length, collection_size: collection_size, deadline: deadline)
173
194
  end
174
195
 
175
196
  # Serializes variables captured by a line probe.
@@ -178,7 +199,7 @@ module Datadog
178
199
  # of executed code.
179
200
  #
180
201
  # @param vars [Hash{Symbol => Object}] variables to serialize, keyed by variable name
181
- # @param deadline [Float] absolute capture deadline in monotonic clock seconds
202
+ # @param deadline [Float] absolute capture deadline in thread CPU clock seconds
182
203
  # @return [Hash{Symbol => Hash}] serialized variables keyed by variable name
183
204
  def serialize_vars(vars,
184
205
  deadline:,
@@ -188,7 +209,7 @@ module Datadog
188
209
  collection_size: nil)
189
210
  vars.each_with_object({}) do |(k, v), agg|
190
211
  agg[k] = serialize_value(v, name: k, depth: depth, attribute_count: attribute_count,
191
- length: length, collection_size: collection_size, deadline: deadline,)
212
+ length: length, collection_size: collection_size, deadline: deadline)
192
213
  end
193
214
  end
194
215
 
@@ -207,7 +228,7 @@ module Datadog
207
228
  #
208
229
  # @param value [any] value to serialize
209
230
  # @param name [Symbol, String, nil] name the value is bound to, used for redaction
210
- # @param deadline [Float] absolute capture deadline in monotonic clock seconds
231
+ # @param deadline [Float] absolute capture deadline in thread CPU clock seconds
211
232
  # @return [Hash{Symbol => Object}] serialized value; the {type:, notCapturedReason: "timeout"} stub when the deadline is exceeded
212
233
  def serialize_value(value, deadline:,
213
234
  name: nil,
@@ -541,21 +562,34 @@ module Datadog
541
562
  "#<#{class_name(value.class)}: serialization error>"
542
563
  end
543
564
 
544
- # Computes the absolute monotonic deadline (in seconds) for a capture
545
- # point from max_time_to_serialize_ms. Resolve once per capture point and
546
- # share across all values serialized on the built-in paths so the
547
- # budget is not exceeded.
565
+ # Computes the absolute thread CPU time deadline (in seconds) for a
566
+ # capture point from max_time_to_serialize_ms.
548
567
  #
549
- # @return [Float] absolute monotonic clock reading, in seconds, at
568
+ # @return [Float] absolute thread CPU clock reading, in seconds, at
550
569
  # which the capture budget is exhausted
551
570
  def serialization_deadline
552
- Core::Utils::Time.get_time + settings.dynamic_instrumentation.max_time_to_serialize_ms / 1000.0
571
+ Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_second) +
572
+ settings.dynamic_instrumentation.max_time_to_serialize_ms / 1000.0
573
+ end
574
+
575
+ # Computes the absolute thread CPU time deadline (in seconds) for an
576
+ # expression evaluation by adding the evaluation budget, converted
577
+ # from max_time_to_evaluate_ms once at construction, to the current
578
+ # thread CPU clock reading.
579
+ #
580
+ # @return [Float] absolute thread CPU clock reading, in seconds, at
581
+ # which the evaluation budget is exhausted
582
+ def evaluation_deadline
583
+ Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_second) +
584
+ evaluation_budget_seconds
553
585
  end
554
586
 
555
587
  private
556
588
 
589
+ attr_reader :evaluation_budget_seconds
590
+
557
591
  def deadline_exceeded?(deadline)
558
- Core::Utils::Time.get_time >= deadline
592
+ Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_second) >= deadline
559
593
  end
560
594
 
561
595
  MAX_MESSAGE_COLLECTION_SIZE = 3
data/lib/datadog/di.rb CHANGED
@@ -173,6 +173,33 @@ module Datadog
173
173
  Datadog.send(:components).dynamic_instrumentation
174
174
  end
175
175
 
176
+ # Reports an evaluation timeout: increments the evaluation_timeouts
177
+ # telemetry counter, emits the canonical guardrail skip metric for the
178
+ # timeout, and logs at debug with the probe id, the evaluation kind and
179
+ # the exception. Returns whether +exc+ was an evaluation timeout, so
180
+ # callers can run timeout-specific follow-up.
181
+ #
182
+ # @param telemetry [Datadog::Core::Telemetry::Component, nil] component
183
+ # the counter increments through.
184
+ # @param guardrails_telemetry [GuardrailsTelemetry] emitter for the
185
+ # canonical evaluation-timeout skip metric.
186
+ # @param logger [DI::Logger] logger the timeout log writes through.
187
+ # @param probe [Probe] the probe whose evaluation timed out.
188
+ # @param kind [String] the evaluation kind, e.g. "condition",
189
+ # "capture expression" or "template".
190
+ # @param exc [Exception] the exception raised by the evaluation.
191
+ # @return [Boolean] whether the exception was an evaluation timeout.
192
+ def report_evaluation_timeout(telemetry:, guardrails_telemetry:, logger:, probe:, kind:, exc:)
193
+ return false unless exc.is_a?(DI::Error::EvaluationTimeout)
194
+ telemetry&.inc(TELEMETRY_NAMESPACE, "evaluation_timeouts", 1)
195
+ guardrails_telemetry.skipped(
196
+ reason: GuardrailsTelemetry::Reason::EVALUATION_TIMEOUT,
197
+ probe: probe,
198
+ )
199
+ logger.debug { "di: probe #{probe.id}: #{kind} evaluation timed out: #{exc.class}: #{exc.message}" }
200
+ true
201
+ end
202
+
176
203
  # Track how many outstanding instrumentations are in DI.
177
204
  #
178
205
  # It is hard to find the actual instrumentations - there is no
@@ -36,7 +36,7 @@ module Datadog
36
36
  elsif RubyVersion.is?("< 2.7")
37
37
  logger.warn(
38
38
  "error tracking: cannot enable error tracking: Ruby 2.7+ is required, but running
39
- on #{RUBY_VERSION}"
39
+ on #{RUBY_VERSION}",
40
40
  )
41
41
  false
42
42
  else
@@ -35,7 +35,7 @@ module Datadog
35
35
  Datadog.logger.warn(
36
36
  "Invalid handled errors scope: #{value}. " \
37
37
  "Supported values are: #{Ext::VALID_HANDLED_ERRORS.join(" | ")}. " \
38
- "Deactivating the feature."
38
+ "Deactivating the feature.",
39
39
  )
40
40
  end
41
41