datadog 2.39.0 → 2.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +269 -44
  3. data/README.md +3 -0
  4. data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +12 -24
  5. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +256 -193
  6. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -5
  7. data/ext/datadog_profiling_native_extension/collectors_gc_profiling_helper.c +0 -2
  8. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +19 -20
  9. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.h +5 -1
  10. data/ext/datadog_profiling_native_extension/collectors_stack.c +267 -171
  11. data/ext/datadog_profiling_native_extension/collectors_stack.h +17 -3
  12. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +307 -217
  13. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +10 -5
  14. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +5 -4
  15. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +19 -11
  16. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +7 -10
  17. data/ext/datadog_profiling_native_extension/extconf.rb +67 -99
  18. data/ext/datadog_profiling_native_extension/heap_recorder.c +353 -257
  19. data/ext/datadog_profiling_native_extension/heap_recorder.h +50 -31
  20. data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +3 -27
  21. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +853 -496
  22. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +42 -15
  23. data/ext/datadog_profiling_native_extension/profiling.c +30 -23
  24. data/ext/datadog_profiling_native_extension/ruby_helpers.c +4 -80
  25. data/ext/datadog_profiling_native_extension/ruby_helpers.h +4 -7
  26. data/ext/datadog_profiling_native_extension/setup_signal_handler.c +0 -2
  27. data/ext/datadog_profiling_native_extension/stack_recorder.c +116 -89
  28. data/ext/datadog_profiling_native_extension/stack_recorder.h +12 -5
  29. data/ext/datadog_profiling_native_extension/time_helpers.c +2 -4
  30. data/ext/datadog_profiling_native_extension/time_helpers.h +15 -15
  31. data/ext/libdatadog_api/datadog_ruby_common.c +19 -11
  32. data/ext/libdatadog_api/datadog_ruby_common.h +7 -10
  33. data/ext/libdatadog_api/di.c +43 -3
  34. data/ext/libdatadog_api/extconf.rb +6 -1
  35. data/ext/libdatadog_api/feature_flags.c +8 -0
  36. data/ext/libdatadog_api/init.c +9 -1
  37. data/ext/libdatadog_api/otel_thread_context.c +274 -0
  38. data/ext/libdatadog_api/otel_thread_context.h +5 -0
  39. data/ext/libdatadog_api/process_discovery.c +15 -1
  40. data/ext/libdatadog_api/trace_exporter.c +2078 -0
  41. data/ext/libdatadog_api/trace_exporter.h +5 -0
  42. data/ext/libdatadog_extconf_helpers.rb +14 -9
  43. data/lib/datadog/ai_guard/component.rb +17 -8
  44. data/lib/datadog/ai_guard/configuration/ext.rb +2 -0
  45. data/lib/datadog/ai_guard/configuration.rb +13 -0
  46. data/lib/datadog/ai_guard/contrib/ruby_llm/chat_instrumentation.rb +48 -55
  47. data/lib/datadog/ai_guard/contrib/ruby_llm/integration.rb +1 -1
  48. data/lib/datadog/ai_guard/contrib/ruby_llm/message_adapter.rb +76 -0
  49. data/lib/datadog/ai_guard/contrib/ruby_llm/patcher.rb +1 -0
  50. data/lib/datadog/ai_guard/contrib/ruby_llm/redaction_change_set.rb +135 -0
  51. data/lib/datadog/ai_guard/evaluation/client.rb +50 -0
  52. data/lib/datadog/ai_guard/evaluation/content_part.rb +12 -0
  53. data/lib/datadog/ai_guard/evaluation/message.rb +25 -7
  54. data/lib/datadog/ai_guard/evaluation/message_builder.rb +31 -0
  55. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +6 -6
  56. data/lib/datadog/ai_guard/evaluation/outcome.rb +24 -0
  57. data/lib/datadog/ai_guard/evaluation/request.rb +8 -64
  58. data/lib/datadog/ai_guard/evaluation/response.rb +33 -0
  59. data/lib/datadog/ai_guard/evaluation/result.rb +11 -18
  60. data/lib/datadog/ai_guard/evaluation/tool_call.rb +15 -2
  61. data/lib/datadog/ai_guard/evaluation.rb +36 -14
  62. data/lib/datadog/ai_guard/ext.rb +4 -1
  63. data/lib/datadog/ai_guard/{api_client.rb → http_client.rb} +7 -5
  64. data/lib/datadog/ai_guard/metrics/telemetry.rb +43 -0
  65. data/lib/datadog/ai_guard/redaction/replacements.rb +97 -0
  66. data/lib/datadog/ai_guard/redaction/result.rb +29 -0
  67. data/lib/datadog/ai_guard/redaction.rb +80 -0
  68. data/lib/datadog/ai_guard.rb +58 -57
  69. data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +21 -42
  70. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +1 -1
  71. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +1 -1
  72. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +1 -1
  73. data/lib/datadog/appsec/assets/blocked.html +1 -108
  74. data/lib/datadog/appsec/configuration.rb +11 -2
  75. data/lib/datadog/appsec/context.rb +1 -1
  76. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +1 -1
  77. data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +2 -2
  78. data/lib/datadog/appsec/contrib/devise/configuration.rb +1 -1
  79. data/lib/datadog/appsec/contrib/devise/patcher.rb +1 -1
  80. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +2 -2
  81. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +2 -2
  82. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +1 -1
  83. data/lib/datadog/appsec/contrib/rack/ext.rb +2 -2
  84. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +2 -2
  85. data/lib/datadog/appsec/contrib/rack/patcher.rb +1 -1
  86. data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +6 -3
  87. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +30 -3
  88. data/lib/datadog/appsec/contrib/rails/patches/render_to_body_patch.rb +8 -7
  89. data/lib/datadog/appsec/contrib/rails/request_middleware.rb +2 -1
  90. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +2 -2
  91. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +15 -3
  92. data/lib/datadog/appsec/contrib/sinatra/patches/json_patch.rb +8 -7
  93. data/lib/datadog/appsec/contrib/sinatra/request_middleware.rb +2 -1
  94. data/lib/datadog/appsec/processor/rule_loader.rb +12 -12
  95. data/lib/datadog/appsec/response.rb +1 -1
  96. data/lib/datadog/appsec/route_normalizer/route_pattern.rb +3 -3
  97. data/lib/datadog/appsec/security_engine/engine.rb +5 -4
  98. data/lib/datadog/core/configuration/components.rb +50 -19
  99. data/lib/datadog/core/configuration/components_state.rb +8 -1
  100. data/lib/datadog/core/configuration/option_definition.rb +1 -1
  101. data/lib/datadog/core/configuration/options.rb +1 -1
  102. data/lib/datadog/core/configuration/settings.rb +38 -43
  103. data/lib/datadog/core/configuration/supported_configurations.rb +18 -2
  104. data/lib/datadog/core/crashtracking/component.rb +5 -1
  105. data/lib/datadog/core/diagnostics/environment_logger.rb +36 -1
  106. data/lib/datadog/core/environment/gc.rb +1 -1
  107. data/lib/datadog/core/environment/vm_cache.rb +1 -1
  108. data/lib/datadog/core/environment/yjit.rb +2 -2
  109. data/lib/datadog/core/metrics/helpers.rb +1 -1
  110. data/lib/datadog/core/metrics/options.rb +2 -2
  111. data/lib/datadog/core/process_discovery.rb +6 -2
  112. data/lib/datadog/core/remote/client/capabilities.rb +32 -21
  113. data/lib/datadog/core/remote/client.rb +45 -4
  114. data/lib/datadog/core/remote/component.rb +30 -7
  115. data/lib/datadog/core/remote/configuration/repository.rb +2 -2
  116. data/lib/datadog/core/remote/dispatcher.rb +47 -6
  117. data/lib/datadog/core/remote/worker.rb +8 -0
  118. data/lib/datadog/core/telemetry/configuration_value.rb +38 -0
  119. data/lib/datadog/core/telemetry/event/app_client_configuration_change.rb +3 -1
  120. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +1 -1
  121. data/lib/datadog/core/telemetry/event/app_started.rb +5 -24
  122. data/lib/datadog/core/telemetry/event/generate_metrics.rb +1 -1
  123. data/lib/datadog/core/telemetry/event/log.rb +2 -2
  124. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +1 -1
  125. data/lib/datadog/core/telemetry/metric.rb +2 -2
  126. data/lib/datadog/core/telemetry/request.rb +2 -2
  127. data/lib/datadog/core/transport/http.rb +1 -1
  128. data/lib/datadog/core/transport/transport.rb +1 -1
  129. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +170 -18
  130. data/lib/datadog/core/utils/sequence.rb +3 -6
  131. data/lib/datadog/core/utils/time.rb +16 -54
  132. data/lib/datadog/core/workers/runtime_metrics.rb +1 -1
  133. data/lib/datadog/data_streams/pathway_context.rb +20 -22
  134. data/lib/datadog/data_streams/processor.rb +45 -14
  135. data/lib/datadog/di/boot.rb +1 -0
  136. data/lib/datadog/di/capture_expression_evaluator/result.rb +30 -0
  137. data/lib/datadog/di/capture_expression_evaluator.rb +39 -9
  138. data/lib/datadog/di/component.rb +108 -3
  139. data/lib/datadog/di/configuration.rb +53 -2
  140. data/lib/datadog/di/context.rb +7 -2
  141. data/lib/datadog/di/contrib/active_record.rb +8 -2
  142. data/lib/datadog/di/el/compiler.rb +88 -12
  143. data/lib/datadog/di/el/evaluator.rb +82 -2
  144. data/lib/datadog/di/el/expression.rb +13 -2
  145. data/lib/datadog/di/evaluation_failure.rb +28 -0
  146. data/lib/datadog/di/guardrails_telemetry/reason.rb +87 -0
  147. data/lib/datadog/di/guardrails_telemetry.rb +203 -0
  148. data/lib/datadog/di/instrumenter.rb +106 -15
  149. data/lib/datadog/di/logger.rb +32 -10
  150. data/lib/datadog/di/probe.rb +9 -1
  151. data/lib/datadog/di/probe_builder.rb +7 -6
  152. data/lib/datadog/di/probe_manager.rb +56 -8
  153. data/lib/datadog/di/probe_notification_builder.rb +31 -28
  154. data/lib/datadog/di/probe_notifier_worker.rb +56 -6
  155. data/lib/datadog/di/proc_responder.rb +1 -1
  156. data/lib/datadog/di/remote.rb +49 -5
  157. data/lib/datadog/di/serializer.rb +172 -55
  158. data/lib/datadog/di/snapshot_event.rb +38 -0
  159. data/lib/datadog/di/transport/http.rb +13 -3
  160. data/lib/datadog/di/transport/input.rb +32 -7
  161. data/lib/datadog/error_tracking/component.rb +1 -1
  162. data/lib/datadog/kit/appsec/events/v2.rb +2 -2
  163. data/lib/datadog/open_feature/activation.rb +268 -0
  164. data/lib/datadog/open_feature/agentless/configuration_source.rb +246 -0
  165. data/lib/datadog/open_feature/agentless/parser.rb +43 -0
  166. data/lib/datadog/open_feature/agentless/response.rb +19 -0
  167. data/lib/datadog/open_feature/agentless/transport.rb +103 -0
  168. data/lib/datadog/open_feature/component.rb +85 -13
  169. data/lib/datadog/open_feature/configuration/agentless_endpoint.rb +90 -0
  170. data/lib/datadog/open_feature/configuration/source/resolution.rb +22 -0
  171. data/lib/datadog/open_feature/configuration/source.rb +43 -0
  172. data/lib/datadog/open_feature/configuration.rb +124 -0
  173. data/lib/datadog/open_feature/evaluation_engine.rb +29 -3
  174. data/lib/datadog/open_feature/exposures/batch_builder.rb +1 -1
  175. data/lib/datadog/open_feature/exposures/event.rb +15 -8
  176. data/lib/datadog/open_feature/ext.rb +19 -0
  177. data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +236 -80
  178. data/lib/datadog/open_feature/flag_evaluation/writer.rb +179 -68
  179. data/lib/datadog/open_feature/hooks/evaluation_telemetry_opt_out_hook.rb +25 -0
  180. data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +24 -21
  181. data/lib/datadog/open_feature/native_evaluator.rb +20 -6
  182. data/lib/datadog/open_feature/noop_evaluator.rb +5 -0
  183. data/lib/datadog/open_feature/provider.rb +315 -10
  184. data/lib/datadog/open_feature/remote.rb +5 -5
  185. data/lib/datadog/open_feature/transport.rb +5 -0
  186. data/lib/datadog/open_feature.rb +31 -1
  187. data/lib/datadog/opentelemetry/sdk/propagator.rb +1 -1
  188. data/lib/datadog/opentelemetry/signal_configuration.rb +13 -9
  189. data/lib/datadog/opentelemetry/trace.rb +3 -0
  190. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +10 -2
  191. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +5 -7
  192. data/lib/datadog/profiling/collectors/info.rb +40 -13
  193. data/lib/datadog/profiling/collectors/thread_context.rb +6 -17
  194. data/lib/datadog/profiling/component.rb +33 -39
  195. data/lib/datadog/profiling/exporter.rb +10 -10
  196. data/lib/datadog/profiling/tasks/exec.rb +1 -1
  197. data/lib/datadog/profiling.rb +4 -0
  198. data/lib/datadog/symbol_database/extractor.rb +89 -107
  199. data/lib/datadog/tracing/component.rb +44 -2
  200. data/lib/datadog/tracing/configuration/dynamic/option.rb +4 -0
  201. data/lib/datadog/tracing/configuration/dynamic.rb +5 -3
  202. data/lib/datadog/tracing/configuration/ext.rb +4 -0
  203. data/lib/datadog/tracing/configuration/settings.rb +36 -0
  204. data/lib/datadog/tracing/context.rb +24 -2
  205. data/lib/datadog/tracing/context_provider.rb +17 -5
  206. data/lib/datadog/tracing/contrib/action_cable/events.rb +1 -1
  207. data/lib/datadog/tracing/contrib/action_mailer/events.rb +1 -1
  208. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +2 -2
  209. data/lib/datadog/tracing/contrib/action_view/events.rb +1 -1
  210. data/lib/datadog/tracing/contrib/active_model_serializers/events.rb +1 -1
  211. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +1 -1
  212. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -0
  213. data/lib/datadog/tracing/contrib/active_record/events.rb +1 -1
  214. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +2 -2
  215. data/lib/datadog/tracing/contrib/aws/ext.rb +1 -1
  216. data/lib/datadog/tracing/contrib/aws/services.rb +1 -1
  217. data/lib/datadog/tracing/contrib/elasticsearch/quantize.rb +1 -1
  218. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +10 -5
  219. data/lib/datadog/tracing/contrib/excon/middleware.rb +10 -3
  220. data/lib/datadog/tracing/contrib/ext.rb +3 -3
  221. data/lib/datadog/tracing/contrib/faraday/middleware.rb +10 -4
  222. data/lib/datadog/tracing/contrib/grape/endpoint.rb +32 -5
  223. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +1 -1
  224. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +1 -1
  225. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +1 -1
  226. data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +2 -1
  227. data/lib/datadog/tracing/contrib/http/instrumentation.rb +16 -1
  228. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +8 -2
  229. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +10 -4
  230. data/lib/datadog/tracing/contrib/kafka/events/connection/request.rb +4 -0
  231. data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +7 -2
  232. data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +7 -2
  233. data/lib/datadog/tracing/contrib/kafka/events.rb +1 -1
  234. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +1 -1
  235. data/lib/datadog/tracing/contrib/mongodb/parsers.rb +1 -1
  236. data/lib/datadog/tracing/contrib/opensearch/ext.rb +1 -1
  237. data/lib/datadog/tracing/contrib/opensearch/quantize.rb +1 -1
  238. data/lib/datadog/tracing/contrib/patchable.rb +1 -1
  239. data/lib/datadog/tracing/contrib/patcher.rb +1 -1
  240. data/lib/datadog/tracing/contrib/que/tracer.rb +1 -1
  241. data/lib/datadog/tracing/contrib/racecar/events.rb +1 -1
  242. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +1 -1
  243. data/lib/datadog/tracing/contrib/rack/header_tagging.rb +1 -1
  244. data/lib/datadog/tracing/contrib/rack/patcher.rb +1 -1
  245. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +1 -1
  246. data/lib/datadog/tracing/contrib/redis/configuration/resolver.rb +1 -1
  247. data/lib/datadog/tracing/contrib/redis/quantize.rb +36 -2
  248. data/lib/datadog/tracing/contrib/redis/trace_middleware.rb +9 -3
  249. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +10 -3
  250. data/lib/datadog/tracing/contrib/sequel/database.rb +1 -5
  251. data/lib/datadog/tracing/contrib/sequel/jdbc_connection_string.rb +140 -0
  252. data/lib/datadog/tracing/contrib/sequel/utils.rb +50 -63
  253. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +1 -1
  254. data/lib/datadog/tracing/contrib/sinatra/configuration/settings.rb +1 -1
  255. data/lib/datadog/tracing/contrib/sneakers/tracer.rb +1 -1
  256. data/lib/datadog/tracing/contrib/utils/quantization/hash_formatter.rb +1 -1
  257. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +108 -3
  258. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +1 -1
  259. data/lib/datadog/tracing/correlation.rb +2 -2
  260. data/lib/datadog/tracing/diagnostics/environment_logger.rb +3 -2
  261. data/lib/datadog/tracing/distributed/baggage.rb +1 -3
  262. data/lib/datadog/tracing/distributed/datadog.rb +3 -3
  263. data/lib/datadog/tracing/distributed/propagation.rb +3 -0
  264. data/lib/datadog/tracing/distributed/propagation_policy.rb +5 -0
  265. data/lib/datadog/tracing/distributed/trace_context.rb +14 -271
  266. data/lib/datadog/tracing/distributed/trace_state/datadog.rb +233 -0
  267. data/lib/datadog/tracing/distributed/trace_state/ext.rb +44 -0
  268. data/lib/datadog/tracing/distributed/trace_state/open_telemetry.rb +156 -0
  269. data/lib/datadog/tracing/distributed/trace_state.rb +121 -0
  270. data/lib/datadog/tracing/metadata/ext.rb +3 -0
  271. data/lib/datadog/tracing/metadata/tagging.rb +1 -1
  272. data/lib/datadog/tracing/otel_thread_context.rb +97 -0
  273. data/lib/datadog/tracing/remote.rb +236 -37
  274. data/lib/datadog/tracing/sampling/rule_sampler.rb +2 -0
  275. data/lib/datadog/tracing/span.rb +1 -1
  276. data/lib/datadog/tracing/span_operation.rb +1 -1
  277. data/lib/datadog/tracing/sync_writer.rb +8 -3
  278. data/lib/datadog/tracing/trace_digest.rb +22 -4
  279. data/lib/datadog/tracing/trace_operation.rb +22 -10
  280. data/lib/datadog/tracing/tracer.rb +28 -7
  281. data/lib/datadog/tracing/transport/http/telemetry.rb +94 -0
  282. data/lib/datadog/tracing/transport/http/traces.rb +2 -0
  283. data/lib/datadog/tracing/transport/io/traces.rb +1 -1
  284. data/lib/datadog/tracing/transport/native/response.rb +71 -0
  285. data/lib/datadog/tracing/transport/native/telemetry.rb +17 -0
  286. data/lib/datadog/tracing/transport/native.rb +395 -0
  287. data/lib/datadog/tracing/transport/span_events_negotiation.rb +41 -0
  288. data/lib/datadog/tracing/transport/telemetry.rb +62 -0
  289. data/lib/datadog/tracing/transport/trace_formatter.rb +14 -4
  290. data/lib/datadog/tracing/transport/traces.rb +85 -38
  291. data/lib/datadog/tracing/writer.rb +17 -4
  292. data/lib/datadog/version.rb +1 -1
  293. metadata +51 -13
  294. data/lib/datadog/ai_guard/evaluation/content_builder.rb +0 -31
  295. data/lib/datadog/appsec/utils/hash_coercion.rb +0 -23
  296. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +0 -69
@@ -16,8 +16,42 @@ module Datadog
16
16
  #
17
17
  # @api private
18
18
  class Compiler
19
+ # Compiles +ast+ into eval'able Ruby source.
20
+ #
21
+ # Returns the compiled source and companion compiled Regexp
22
+ # objects.
23
+ #
24
+ # @param ast [untyped] expression AST from the probe definition.
25
+ # @return [Array(String, Array<Regexp>)] the compiled Ruby source and
26
+ # the precompiled Regexps, indexed in the order the compiled code
27
+ # references them.
19
28
  def compile(ast)
20
- compile_partial(ast)
29
+ regexps = []
30
+ code = compile_partial(ast, regexps)
31
+ [code, regexps]
32
+ end
33
+
34
+ # Returns the identifier that +ast+ directly references at its top
35
+ # level, so the message path can redact direct references the same
36
+ # way snapshots redact by name.
37
+ #
38
+ # @param ast [untyped] top-level expression AST from a probe definition.
39
+ # @return [String, nil] the variable name for a `ref`/`iref`, the field
40
+ # name for a `getmember`, or the string key for an `index`; nil when
41
+ # the top-level expression is not a direct reference.
42
+ def redaction_identifier(ast)
43
+ return nil unless Hash === ast && ast.length == 1
44
+
45
+ entry = ast.first
46
+ return nil if entry.nil?
47
+
48
+ op, target = entry
49
+ case op
50
+ when "ref"
51
+ target if String === target
52
+ when "getmember", "index"
53
+ target[1] if Array === target && target.length == 2 && String === target[1]
54
+ end
21
55
  end
22
56
 
23
57
  private
@@ -37,9 +71,13 @@ module Datadog
37
71
  len isEmpty isUndefined
38
72
  ].freeze # steep:ignore IncompatibleAssignment
39
73
 
74
+ # `matches` is also a two-argument method but is special-cased in
75
+ # #compile_partial so that regular expressions can be precompiled
76
+ # once, so it is not listed here.
77
+ #
40
78
  # Steep: https://github.com/soutaro/steep/issues/363
41
79
  TWO_ARG_METHODS = %w[
42
- startsWith endsWith contains matches
80
+ startsWith endsWith contains
43
81
  getmember index instanceof
44
82
  ].freeze # steep:ignore IncompatibleAssignment
45
83
 
@@ -49,13 +87,18 @@ module Datadog
49
87
  "or" => "||",
50
88
  }.freeze
51
89
 
52
- def compile_partial(ast)
90
+ # @param ast [untyped] AST node to compile.
91
+ # @param regexps [Array<Regexp>] output array that collects the
92
+ # companion precompiled Regexp objects.
93
+ # @return [String] compiled Ruby source for +ast+.
94
+ def compile_partial(ast, regexps)
53
95
  case ast
54
96
  when Hash
55
- if ast.length != 1
97
+ entry = ast.first
98
+ if ast.length != 1 || entry.nil?
56
99
  raise DI::Error::InvalidExpression, "Expected hash of length 1: #{ast}"
57
100
  end
58
- op, target = ast.first
101
+ op, target = entry
59
102
  case op
60
103
  when "ref"
61
104
  unless String === target
@@ -94,20 +137,33 @@ module Datadog
94
137
  end
95
138
  when *SINGLE_ARG_METHODS
96
139
  method_name = op.gsub(/[A-Z]/) { |m| "_#{m.downcase}" }
97
- "#{method_name}(#{compile_partial(target)}, '#{var_name_maybe(target)}')"
140
+ "#{method_name}(#{compile_partial(target, regexps)}, '#{var_name_maybe(target)}')"
141
+ when "matches"
142
+ unless Array === target && target.length == 2
143
+ raise DI::Error::InvalidExpression, "Improper matches syntax"
144
+ end
145
+ first, second = target
146
+ if String === second
147
+ # Match against a literal regular expression (string).
148
+ index = precompile_regexp(second, regexps)
149
+ "matches_compiled(#{compile_partial(first, regexps)}, #{index})"
150
+ else
151
+ # Match against a (complex) expression.
152
+ "matches(#{compile_partial(first, regexps)}, (#{compile_partial(second, regexps)}))"
153
+ end
98
154
  when *TWO_ARG_METHODS
99
155
  method_name = op.gsub(/[A-Z]/) { |m| "_#{m.downcase}" }
100
156
  unless Array === target && target.length == 2
101
157
  raise DI::Error::InvalidExpression, "Improper #{op} syntax"
102
158
  end
103
159
  first, second = target
104
- "#{method_name}(#{compile_partial(first)}, (#{compile_partial(second)}))"
160
+ "#{method_name}(#{compile_partial(first, regexps)}, (#{compile_partial(second, regexps)}))"
105
161
  when *MULTI_ARG_METHODS.keys
106
162
  unless Array === target && target.length >= 1
107
163
  raise DI::Error::InvalidExpression, "Improper #{op} syntax"
108
164
  end
109
165
  compiled_targets = target.map do |item|
110
- "(#{compile_partial(item)})"
166
+ "(#{compile_partial(item, regexps)})"
111
167
  end
112
168
  compiled_op = MULTI_ARG_METHODS[op]
113
169
  "(#{compiled_targets.join(" #{compiled_op} ")})"
@@ -115,18 +171,21 @@ module Datadog
115
171
  unless Array === target && target.length == 3
116
172
  raise DI::Error::InvalidExpression, "Improper #{op} syntax"
117
173
  end
118
- "#{op}(#{target.map { |arg| "(#{compile_partial(arg)})" }.join(", ")})"
174
+ "#{op}(#{target.map { |arg| "(#{compile_partial(arg, regexps)})" }.join(", ")})"
119
175
  when "not"
120
- "!(#{compile_partial(target)})"
176
+ "!(#{compile_partial(target, regexps)})"
121
177
  when *OPERATORS.keys
122
178
  unless Array === target && target.length == 2
123
179
  raise DI::Error::InvalidExpression, "Improper #{op} syntax"
124
180
  end
125
181
  first, second = target
126
182
  operator = OPERATORS.fetch(op)
127
- "(#{compile_partial(first)}) #{operator} (#{compile_partial(second)})"
183
+ "(#{compile_partial(first, regexps)}) #{operator} (#{compile_partial(second, regexps)})"
128
184
  when "any", "all", "filter"
129
- "#{op}(#{compile_partial(target.first)}) { |current_item, current_key, current_value| #{compile_partial(target.last)} }"
185
+ unless Array === target && target.length == 2
186
+ raise DI::Error::InvalidExpression, "Improper #{op} syntax"
187
+ end
188
+ "#{op}(#{compile_partial(target.first, regexps)}) { |current_item, current_key, current_value| #{compile_partial(target.last, regexps)} }"
130
189
  else
131
190
  raise DI::Error::InvalidExpression, "Unknown operation: #{op}"
132
191
  end
@@ -162,6 +221,23 @@ module Datadog
162
221
  def escape(needle)
163
222
  needle.gsub("\\") { "\\\\" }.gsub('"') { "\\\"" }.gsub("#") { "\\#" }
164
223
  end
224
+
225
+ # Compile a literal regular expression +regexp_str+ at
226
+ # instrumentation time. Append it to +regexps+, returning its index for
227
+ # Evaluator#matches_compiled to look up.
228
+ #
229
+ # @param regexp_str [String] regular expression source.
230
+ # @param regexps [Array<Regexp>] output array to append the compiled
231
+ # regexp to.
232
+ # @return [Integer] index into +regexps+.
233
+ # @raise [DI::Error::InvalidExpression] if +needle+ is not a valid regexp.
234
+ def precompile_regexp(regexp_str, regexps)
235
+ index = regexps.length
236
+ regexps << Evaluator.compile_regexp(regexp_str)
237
+ index
238
+ rescue RegexpError => exc
239
+ raise DI::Error::InvalidExpression, "Invalid regular expression in matches: #{exc.class}: #{exc.message}"
240
+ end
165
241
  end
166
242
  end
167
243
  end
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../../ruby_version"
4
+
5
+ require "timeout" if Datadog::RubyVersion.is?("< 3.2")
6
+
3
7
  module Datadog
4
8
  module DI
5
9
  module EL
@@ -7,6 +11,20 @@ module Datadog
7
11
  #
8
12
  # @api private
9
13
  class Evaluator
14
+ # Maximum wall-clock time allowed for evaluating a single `matches`
15
+ # operator.
16
+ MATCHES_TIMEOUT_SECONDS = 0.5
17
+
18
+ # @param regexps [Array<Regexp>] Regexps precompiled from literal
19
+ # `matches` patterns, looked up by index by #matches_compiled.
20
+ # Empty when the expression has no `matches` pattern with
21
+ # a direct regular expression argument.
22
+ def initialize(regexps = [])
23
+ @regexps = regexps
24
+ end
25
+
26
+ attr_reader :regexps
27
+
10
28
  def ref(var)
11
29
  @context.fetch(var)
12
30
  end
@@ -48,10 +66,72 @@ module Datadog
48
66
  end
49
67
  end
50
68
 
69
+ # Build a Regexp from a pattern string. On Ruby 3.2+ the per-match
70
+ # timeout (MATCHES_TIMEOUT_SECONDS) is baked into the compiled
71
+ # Regexp via the in-engine `timeout:` keyword, which interrupts the
72
+ # matcher at every backtrack step and reliably bounds matcher
73
+ # runtime regardless of pattern shape. On older Rubies the Regexp
74
+ # carries no timeout; the bound is applied at match time by
75
+ # #bounded_match? instead.
76
+ #
77
+ # @param needle [String] regexp source.
78
+ # @return [Regexp] compiled regexp, with baked-in timeout on Ruby 3.2+.
79
+ if Datadog::RubyVersion.is?(">= 3.2")
80
+ def self.compile_regexp(needle)
81
+ Regexp.new(needle, timeout: MATCHES_TIMEOUT_SECONDS)
82
+ end
83
+ else
84
+ def self.compile_regexp(needle)
85
+ Regexp.compile(needle)
86
+ end
87
+ end
88
+
89
+ # Match +haystack+ against a regexp whose needle is computed at
90
+ # evaluation time, so the Regexp cannot be precompiled. Literal needles are
91
+ # precompiled by the Compiler and dispatched to #matches_compiled.
92
+ #
93
+ # @param haystack [String] string to match against.
94
+ # @param needle [String] regexp source.
95
+ # @return [Boolean] whether the haystack matches the regexp.
96
+ # @raise [Regexp::TimeoutError] (Ruby 3.2+) regexp engine exceeded MATCHES_TIMEOUT_SECONDS.
97
+ # @raise [Timeout::Error] (Ruby < 3.2) Timeout.timeout fired.
51
98
  def matches(haystack, needle)
52
- re = Regexp.compile(needle)
53
- !!(haystack =~ re)
99
+ bounded_match?(Evaluator.compile_regexp(needle), haystack)
100
+ end
101
+
102
+ # Match +haystack+ against a Regexp precompiled at expression-compile
103
+ # time, looked up by index in +regexps+.
104
+ #
105
+ # @param haystack [String] string to match against.
106
+ # @param index [Integer] position of the precompiled Regexp in +regexps+.
107
+ # @return [Boolean] whether the haystack matches the regexp.
108
+ # @raise [Regexp::TimeoutError] (Ruby 3.2+) regexp engine exceeded MATCHES_TIMEOUT_SECONDS.
109
+ # @raise [Timeout::Error] (Ruby < 3.2) Timeout.timeout fired.
110
+ def matches_compiled(haystack, index)
111
+ bounded_match?(regexps.fetch(index), haystack)
112
+ end
113
+
114
+ # Match +haystack+ against +re+, bounded at MATCHES_TIMEOUT_SECONDS
115
+ # wall-clock.
116
+ #
117
+ # @param re [Regexp] regexp to match against.
118
+ # @param haystack [String] string to match against.
119
+ # @return [Boolean] whether the haystack matches the regexp.
120
+ if Datadog::RubyVersion.is?(">= 3.2")
121
+ def bounded_match?(re, haystack)
122
+ # Uses Regexp#match? rather than =~ so that the
123
+ # thread-local match data ($~, $1, ...) is not mutated as a side
124
+ # effect of DI expression evaluation.
125
+ re.match?(haystack)
126
+ end
127
+ else
128
+ def bounded_match?(re, haystack)
129
+ Timeout.timeout(MATCHES_TIMEOUT_SECONDS) do
130
+ re.match?(haystack)
131
+ end
132
+ end
54
133
  end
134
+ private :bounded_match?
55
135
 
56
136
  def getmember(object, field)
57
137
  object.instance_variable_get("@#{field}")
@@ -7,12 +7,22 @@ module Datadog
7
7
  #
8
8
  # @api private
9
9
  class Expression
10
- def initialize(dsl_expr, compiled_expr)
10
+ # @param dsl_expr [String] human-readable DSL form, kept for debugging.
11
+ # @param compiled_expr [String] Ruby source produced by Compiler#compile.
12
+ # @param regexps [Array<Regexp>] precompiled `matches` regexps (see
13
+ # Compiler#precompile_regexp), the second element returned by
14
+ # Compiler#compile.
15
+ # @param redaction_identifier [String, nil] the identifier this
16
+ # expression directly references at its top level (see
17
+ # Compiler#redaction_identifier); nil when it is not a direct
18
+ # reference.
19
+ def initialize(dsl_expr, compiled_expr, regexps: [], redaction_identifier: nil)
11
20
  unless String === compiled_expr
12
21
  raise ArgumentError, "compiled_expr must be a string"
13
22
  end
14
23
 
15
24
  @dsl_expr = dsl_expr
25
+ @redaction_identifier = redaction_identifier
16
26
 
17
27
  cls = Class.new(Evaluator)
18
28
  cls.class_exec do
@@ -23,11 +33,12 @@ module Datadog
23
33
  end
24
34
  RUBY
25
35
  end
26
- @evaluator = cls.new
36
+ @evaluator = cls.new(regexps)
27
37
  end
28
38
 
29
39
  attr_reader :dsl_expr
30
40
  attr_reader :evaluator
41
+ attr_reader :redaction_identifier
31
42
 
32
43
  def evaluate(context)
33
44
  @evaluator.evaluate(context)
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module DI
5
+ # An expression evaluation failure reported in a snapshot's evaluation
6
+ # errors: the expression that failed and the error description.
7
+ # @api private
8
+ class EvaluationFailure
9
+ # Initializes the failure.
10
+ #
11
+ # @param expr [String] name of the capture expression, or source of the condition or template expression, that failed to evaluate or serialize
12
+ # @param message [String] error description of the failed evaluation
13
+ # @return [void]
14
+ def initialize(expr:, message:)
15
+ @expr = expr
16
+ @message = message
17
+ end
18
+
19
+ # The name of the capture expression, or the source of the condition or
20
+ # template expression, that failed to evaluate or serialize.
21
+ # @return [String]
22
+ attr_reader :expr
23
+
24
+ # @return [String] the error description of the failed evaluation
25
+ attr_reader :message
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module DI
5
+ class GuardrailsTelemetry
6
+ # Canonical guardrails reason codes. Codes
7
+ # shared with the other Datadog tracers keep the exact shared
8
+ # spelling; codes with no shared counterpart use the Ruby tracer's
9
+ # own name for the same decision.
10
+ module Reason
11
+ # Skip reason emitted when a probe firing is rejected by the
12
+ # probe's own rate limiter.
13
+ RATE_LIMIT_PROBE = "rateLimitProbe"
14
+
15
+ # Skip reason emitted when a probe firing is rejected by the
16
+ # process-wide rate limiter.
17
+ RATE_LIMIT_GLOBAL = "rateLimitGlobal"
18
+
19
+ # Skip reason emitted when a capture expression evaluation exceeds
20
+ # the capture time budget.
21
+ EVALUATION_TIMEOUT = "evaluationTimeout"
22
+
23
+ # Skip reason named in the +di: debug log when a condition-evaluation
24
+ # failure notification is rejected by the per-probe limiter.
25
+ EVALUATION_ERROR_THROTTLED = "evaluationErrorThrottled"
26
+
27
+ # Skip reason naming the per-invocation DI time budget guardrail
28
+ # decision.
29
+ BUDGET_EXCEEDED_INVOCATION = "budgetExceededInvocation"
30
+
31
+ # Skip reason naming the process-wide DI time budget guardrail
32
+ # decision.
33
+ BUDGET_EXCEEDED_GLOBAL = "budgetExceededGlobal"
34
+
35
+ # Skip and drop reason emitted when an event is discarded at a
36
+ # full worker queue.
37
+ QUEUE_FULL = "queueFull"
38
+
39
+ # Skip and drop reason naming the queue high-watermark guardrail
40
+ # decision.
41
+ QUEUE_HIGH_WATERMARK = "queueHighWatermark"
42
+
43
+ # Drop reason emitted when a snapshot exceeds the serialized
44
+ # payload limit.
45
+ PAYLOAD_TOO_LARGE = "payloadTooLarge"
46
+
47
+ # Drop reason naming the batch byte-cap guardrail decision.
48
+ BATCH_BYTES_EXCEEDED = "batchBytesExceeded"
49
+
50
+ # Capture-incomplete reason emitted when a capture or
51
+ # serialization runtime exception ends the capture of a value
52
+ # early.
53
+ RUNTIME_ERROR = "runtimeError"
54
+
55
+ # Capture-incomplete reason emitted when a value is not captured
56
+ # because the capture time budget expired while it was being
57
+ # serialized.
58
+ CAPTURE_TIMEOUT = "timeout"
59
+
60
+ # Capture-incomplete reason emitted when a value at the maximum
61
+ # capture depth is not captured.
62
+ DEPTH = "depth"
63
+
64
+ # Capture-incomplete reason emitted when an object with more
65
+ # fields than the capture limit is partially captured.
66
+ FIELD_COUNT = "fieldCount"
67
+
68
+ # Capture-incomplete reason emitted when a collection with more
69
+ # items than the capture limit is partially captured.
70
+ COLLECTION_SIZE = "collectionSize"
71
+
72
+ # Capture-incomplete reason emitted when a captured string is
73
+ # trimmed to the capture limit.
74
+ STRING_LENGTH = "stringLength"
75
+
76
+ # Capture-incomplete reason for an early-capture cause with no
77
+ # more specific code.
78
+ OTHER = "other"
79
+
80
+ # The skip reasons the events.skipped metric tags with the firing
81
+ # probe's id; every other skip reason names a process-wide
82
+ # decision and carries no probe id.
83
+ PROBE_SCOPED_REASONS = [RATE_LIMIT_PROBE, EVALUATION_TIMEOUT].freeze
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "fatal_exceptions"
4
+ require_relative "guardrails_telemetry/reason"
5
+
6
+ module Datadog
7
+ module DI
8
+ # Telemetry namespace for DI metrics, shared with the other tracers.
9
+ # The telemetry wire name is
10
+ # +dd.instrumentation_telemetry_data.debugger.*+.
11
+ TELEMETRY_NAMESPACE = "debugger"
12
+
13
+ # Telemetry reporter for the DI guardrails: the reason codes and the
14
+ # metric emitters.
15
+ class GuardrailsTelemetry
16
+ # Skip count metric name, emitted under +DI::TELEMETRY_NAMESPACE+.
17
+ EVENTS_SKIPPED = "events.skipped"
18
+ # Drop count metric name, emitted under +DI::TELEMETRY_NAMESPACE+.
19
+ EVENTS_DROPPED = "events.dropped"
20
+ # Incomplete-capture count metric name, emitted under +DI::TELEMETRY_NAMESPACE+.
21
+ CAPTURE_INCOMPLETE = "capture.incomplete"
22
+
23
+ # Queue byte-count metric, emitted under +DI::TELEMETRY_NAMESPACE+.
24
+ QUEUE_DROPPED_BYTES = "guardrails.queue.dropped_bytes"
25
+
26
+ # Tag value for probes that capture a full snapshot.
27
+ EVENT_TYPE_SNAPSHOT = "snapshot"
28
+ # Tag value for probes that capture evaluated log output.
29
+ EVENT_TYPE_LOG = "log"
30
+
31
+ # Initializes the reporter.
32
+ #
33
+ # @param settings [Datadog::Core::Configuration::Settings] tracer settings the propagation escape hatch is read from
34
+ # @param logger [DI::Logger] logger for contained emission failures
35
+ # @param telemetry [Datadog::Core::Telemetry::Component, nil] component the guardrails metrics are emitted through
36
+ def initialize(settings:, logger:, telemetry:)
37
+ @settings = settings
38
+ @logger = logger
39
+ @telemetry = telemetry
40
+ @skipped_tags_memo = {}
41
+ @dropped_tags_memo = {}
42
+ @capture_incomplete_tags_memo = {}
43
+ end
44
+
45
+ # The tracer settings.
46
+ # @return [Datadog::Core::Configuration::Settings]
47
+ attr_reader :settings
48
+ # The logger.
49
+ # @return [DI::Logger]
50
+ attr_reader :logger
51
+ # The component the guardrails metrics are emitted through.
52
+ # @return [Datadog::Core::Telemetry::Component, nil]
53
+ attr_reader :telemetry
54
+ # The memo of frozen tag hashes for emitted skip metrics without
55
+ # +probe_id+, keyed by reason and event type.
56
+ # @return [Hash{String => Hash{String => Hash{Symbol => String}}}]
57
+ attr_reader :skipped_tags_memo
58
+ # The memo of frozen tag hashes for emitted drop metrics, keyed by
59
+ # reason and event type.
60
+ # @return [Hash{String => Hash{String => Hash{Symbol => String}}}]
61
+ attr_reader :dropped_tags_memo
62
+ # The memo of frozen tag hashes for emitted incomplete-capture
63
+ # metrics, keyed by reason and event type.
64
+ # @return [Hash{String => Hash{String => Hash{Symbol => String}}}]
65
+ attr_reader :capture_incomplete_tags_memo
66
+
67
+ # Returns the canonical event_type tag for a probe, derived from
68
+ # whether the probe captures a full snapshot.
69
+ #
70
+ # @param probe [Probe] the probe whose firing is being tagged
71
+ # @return [String] EVENT_TYPE_SNAPSHOT or EVENT_TYPE_LOG
72
+ def self.probe_event_type_tag(probe)
73
+ probe.capture_snapshot? ? EVENT_TYPE_SNAPSHOT : EVENT_TYPE_LOG
74
+ end
75
+
76
+ # Emits the canonical +debugger.events.skipped+ count metric for a
77
+ # probe firing a guardrail rejected.
78
+ #
79
+ # @param reason [String] a GuardrailsTelemetry::Reason constant naming the skip cause
80
+ # @param probe [Probe] the probe whose firing was rejected
81
+ # @return [void]
82
+ def skipped(reason:, probe:)
83
+ probe_id = probe.id if Reason::PROBE_SCOPED_REASONS.include?(reason)
84
+ inc_metric(
85
+ DI::TELEMETRY_NAMESPACE, EVENTS_SKIPPED, 1,
86
+ tags: skipped_tags(reason, self.class.probe_event_type_tag(probe), probe_id),
87
+ )
88
+ nil
89
+ end
90
+
91
+ # Emits the canonical +debugger.events.dropped+ count metric, and,
92
+ # when +bytes+ is provided, the +guardrails.queue.dropped_bytes+ count
93
+ # metric.
94
+ #
95
+ # @param reason [String] a GuardrailsTelemetry::Reason constant naming the drop cause
96
+ # @param event_type [String] the canonical event_type tag value
97
+ # @param bytes [Integer, nil] encoded bytes discarded, when the drop site knows the count
98
+ # @return [void]
99
+ def dropped(reason:, event_type:, bytes: nil)
100
+ tags = dropped_tags(reason, event_type)
101
+ inc_metric(DI::TELEMETRY_NAMESPACE, EVENTS_DROPPED, 1, tags: tags)
102
+ inc_metric(DI::TELEMETRY_NAMESPACE, QUEUE_DROPPED_BYTES, bytes, tags: tags) if bytes
103
+ nil
104
+ end
105
+
106
+ # Emits the canonical +debugger.capture.incomplete+ count metric for
107
+ # a capture limit enforced on a snapshot that is still sent.
108
+ #
109
+ # @param reason [String] a GuardrailsTelemetry::Reason constant naming the enforced capture limit
110
+ # @param event_type [String] the canonical event_type tag value
111
+ # @return [void]
112
+ def capture_incomplete(reason:, event_type:)
113
+ inc_metric(
114
+ DI::TELEMETRY_NAMESPACE, CAPTURE_INCOMPLETE, 1,
115
+ tags: capture_incomplete_tags(reason, event_type),
116
+ )
117
+ nil
118
+ end
119
+
120
+ private
121
+
122
+ # Emits a guardrails count metric.
123
+ #
124
+ # @param namespace [String] telemetry namespace the metric is emitted under
125
+ # @param metric_name [String] guardrails metric name
126
+ # @param value [Integer] increment count, or the discarded byte count
127
+ # @param tags [Hash{Symbol => String}] frozen metric tags for the emission
128
+ # @return [void]
129
+ def inc_metric(namespace, metric_name, value, tags:)
130
+ telemetry&.inc(namespace, metric_name, value, tags: tags)
131
+ rescue Exception => exc # standard:disable Lint/RescueException
132
+ Datadog::DI.reraise_if_fatal(exc)
133
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
134
+ report_emission_failure(namespace, metric_name, exc)
135
+ nil
136
+ end
137
+
138
+ def report_emission_failure(namespace, metric_name, exc)
139
+ logger.debug { "di: error emitting #{namespace}.#{metric_name} metric: #{exc.class}: #{exc.message}" }
140
+ begin
141
+ telemetry&.report(exc, description: "Error emitting #{namespace}.#{metric_name} metric")
142
+ rescue Exception => nested_exc # standard:disable Lint/RescueException
143
+ Datadog::DI.reraise_if_fatal(nested_exc)
144
+ logger.debug do
145
+ "di: error reporting #{namespace}.#{metric_name} metric emission failure: " \
146
+ "#{nested_exc.class}: #{nested_exc.message}"
147
+ end
148
+ end
149
+ nil
150
+ end
151
+
152
+ # Returns the frozen tags for a skip emission. Emissions without
153
+ # +probe_id+ are memoized; probe-scoped emissions allocate one
154
+ # frozen hash per emission: remote configuration adds and removes
155
+ # probes frequently, and a per-probe-id memo would grow without
156
+ # bound over the process lifetime.
157
+ #
158
+ # @param reason [String] the skip reason
159
+ # @param event_type [String] the canonical event_type tag value
160
+ # @param probe_id [String, nil] the rejected probe's id
161
+ # @return [Hash{Symbol => String}] the frozen skip-metric tags
162
+ def skipped_tags(reason, event_type, probe_id)
163
+ return {reason: reason, event_type: event_type, probe_id: probe_id}.freeze if probe_id
164
+
165
+ memoized_tags(skipped_tags_memo, reason, event_type)
166
+ end
167
+
168
+ # Returns the frozen tags for a drop emission, memoized per
169
+ # (reason, event_type) pair.
170
+ #
171
+ # @param reason [String] the drop reason
172
+ # @param event_type [String] the canonical event_type tag value
173
+ # @return [Hash{Symbol => String}] the frozen drop-metric tags
174
+ def dropped_tags(reason, event_type)
175
+ memoized_tags(dropped_tags_memo, reason, event_type)
176
+ end
177
+
178
+ # Returns the frozen tags for an incomplete-capture emission,
179
+ # memoized per (reason, event_type) pair.
180
+ #
181
+ # @param reason [String] the enforced capture limit
182
+ # @param event_type [String] the canonical event_type tag value
183
+ # @return [Hash{Symbol => String}] the frozen incomplete-capture metric tags
184
+ def capture_incomplete_tags(reason, event_type)
185
+ memoized_tags(capture_incomplete_tags_memo, reason, event_type)
186
+ end
187
+
188
+ # Returns the frozen tags memoized per (reason, event_type) pair,
189
+ # so a repeated emission allocates no tag hash. The memo is
190
+ # lock-free: a racy first write can only discard a duplicate
191
+ # frozen hash.
192
+ #
193
+ # @param memo [Hash{String => Hash{String => Hash{Symbol => String}}}] the emission-kind memo to index
194
+ # @param reason [String] the emission reason
195
+ # @param event_type [String] the canonical event_type tag value
196
+ # @return [Hash{Symbol => String}] the frozen metric tags
197
+ def memoized_tags(memo, reason, event_type)
198
+ reason_memos = memo[reason] ||= {}
199
+ reason_memos[event_type] ||= {reason: reason, event_type: event_type}.freeze
200
+ end
201
+ end
202
+ end
203
+ end