datadog 2.20.0 → 2.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +212 -1
  3. data/README.md +0 -1
  4. data/ext/LIBDATADOG_DEVELOPMENT.md +3 -0
  5. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +93 -23
  6. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
  7. data/ext/datadog_profiling_native_extension/collectors_stack.c +21 -5
  8. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
  9. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +1 -1
  10. data/ext/datadog_profiling_native_extension/extconf.rb +9 -4
  11. data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
  12. data/ext/datadog_profiling_native_extension/http_transport.c +1 -0
  13. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +12 -0
  14. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +4 -0
  15. data/ext/datadog_profiling_native_extension/profiling.c +2 -0
  16. data/ext/libdatadog_api/datadog_ruby_common.h +1 -1
  17. data/ext/libdatadog_api/ddsketch.c +106 -0
  18. data/ext/libdatadog_api/feature_flags.c +554 -0
  19. data/ext/libdatadog_api/feature_flags.h +5 -0
  20. data/ext/libdatadog_api/init.c +5 -0
  21. data/ext/libdatadog_api/library_config.c +34 -25
  22. data/ext/libdatadog_api/process_discovery.c +24 -18
  23. data/ext/libdatadog_extconf_helpers.rb +1 -1
  24. data/lib/datadog/ai_guard/api_client.rb +82 -0
  25. data/lib/datadog/ai_guard/component.rb +42 -0
  26. data/lib/datadog/ai_guard/configuration/ext.rb +17 -0
  27. data/lib/datadog/ai_guard/configuration/settings.rb +98 -0
  28. data/lib/datadog/ai_guard/configuration.rb +11 -0
  29. data/lib/datadog/ai_guard/evaluation/message.rb +25 -0
  30. data/lib/datadog/ai_guard/evaluation/no_op_result.rb +34 -0
  31. data/lib/datadog/ai_guard/evaluation/request.rb +81 -0
  32. data/lib/datadog/ai_guard/evaluation/result.rb +43 -0
  33. data/lib/datadog/ai_guard/evaluation/tool_call.rb +18 -0
  34. data/lib/datadog/ai_guard/evaluation.rb +72 -0
  35. data/lib/datadog/ai_guard/ext.rb +16 -0
  36. data/lib/datadog/ai_guard.rb +153 -0
  37. data/lib/datadog/appsec/api_security/endpoint_collection/grape_route_serializer.rb +26 -0
  38. data/lib/datadog/appsec/api_security/endpoint_collection/rails_collector.rb +59 -0
  39. data/lib/datadog/appsec/api_security/endpoint_collection/rails_route_serializer.rb +29 -0
  40. data/lib/datadog/appsec/api_security/endpoint_collection/sinatra_route_serializer.rb +26 -0
  41. data/lib/datadog/appsec/api_security/endpoint_collection.rb +10 -0
  42. data/lib/datadog/appsec/api_security/route_extractor.rb +26 -5
  43. data/lib/datadog/appsec/api_security/sampler.rb +7 -4
  44. data/lib/datadog/appsec/assets/blocked.html +8 -0
  45. data/lib/datadog/appsec/assets/blocked.json +1 -1
  46. data/lib/datadog/appsec/assets/blocked.text +3 -1
  47. data/lib/datadog/appsec/assets/waf_rules/README.md +30 -36
  48. data/lib/datadog/appsec/assets/waf_rules/recommended.json +359 -4
  49. data/lib/datadog/appsec/assets/waf_rules/strict.json +43 -2
  50. data/lib/datadog/appsec/assets.rb +1 -1
  51. data/lib/datadog/appsec/autoload.rb +1 -1
  52. data/lib/datadog/appsec/compressed_json.rb +1 -1
  53. data/lib/datadog/appsec/configuration/settings.rb +9 -0
  54. data/lib/datadog/appsec/context.rb +2 -1
  55. data/lib/datadog/appsec/contrib/active_record/instrumentation.rb +3 -1
  56. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +3 -2
  57. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +3 -1
  58. data/lib/datadog/appsec/contrib/graphql/gateway/watcher.rb +3 -1
  59. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +9 -4
  60. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +5 -1
  61. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +7 -2
  62. data/lib/datadog/appsec/contrib/rails/patcher.rb +30 -0
  63. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +3 -1
  64. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +10 -4
  65. data/lib/datadog/appsec/event.rb +12 -14
  66. data/lib/datadog/appsec/metrics/collector.rb +19 -3
  67. data/lib/datadog/appsec/metrics/telemetry_exporter.rb +2 -1
  68. data/lib/datadog/appsec/monitor/gateway/watcher.rb +4 -4
  69. data/lib/datadog/appsec/remote.rb +34 -25
  70. data/lib/datadog/appsec/response.rb +18 -4
  71. data/lib/datadog/appsec/security_engine/engine.rb +3 -3
  72. data/lib/datadog/appsec/security_engine/result.rb +29 -9
  73. data/lib/datadog/appsec/security_engine/runner.rb +19 -9
  74. data/lib/datadog/appsec/security_event.rb +5 -7
  75. data/lib/datadog/core/configuration/agent_settings_resolver.rb +4 -4
  76. data/lib/datadog/core/configuration/components.rb +59 -11
  77. data/lib/datadog/core/configuration/config_helper.rb +100 -0
  78. data/lib/datadog/core/configuration/deprecations.rb +36 -0
  79. data/lib/datadog/core/configuration/ext.rb +0 -1
  80. data/lib/datadog/core/configuration/option.rb +38 -43
  81. data/lib/datadog/core/configuration/option_definition.rb +4 -11
  82. data/lib/datadog/core/configuration/options.rb +9 -10
  83. data/lib/datadog/core/configuration/settings.rb +38 -9
  84. data/lib/datadog/core/configuration/stable_config.rb +10 -0
  85. data/lib/datadog/core/configuration/supported_configurations.rb +373 -0
  86. data/lib/datadog/core/configuration.rb +2 -2
  87. data/lib/datadog/core/ddsketch.rb +19 -0
  88. data/lib/datadog/core/deprecations.rb +2 -2
  89. data/lib/datadog/core/environment/cgroup.rb +52 -25
  90. data/lib/datadog/core/environment/container.rb +140 -46
  91. data/lib/datadog/core/environment/ext.rb +7 -2
  92. data/lib/datadog/core/environment/git.rb +2 -2
  93. data/lib/datadog/core/environment/process.rb +87 -0
  94. data/lib/datadog/core/environment/variable_helpers.rb +3 -3
  95. data/lib/datadog/core/environment/yjit.rb +2 -1
  96. data/lib/datadog/core/error.rb +6 -6
  97. data/lib/datadog/core/feature_flags.rb +61 -0
  98. data/lib/datadog/core/metrics/client.rb +2 -2
  99. data/lib/datadog/core/pin.rb +8 -8
  100. data/lib/datadog/core/process_discovery/tracer_memfd.rb +2 -4
  101. data/lib/datadog/core/process_discovery.rb +48 -23
  102. data/lib/datadog/core/rate_limiter.rb +9 -1
  103. data/lib/datadog/core/remote/client/capabilities.rb +7 -0
  104. data/lib/datadog/core/remote/client.rb +14 -6
  105. data/lib/datadog/core/remote/component.rb +10 -10
  106. data/lib/datadog/core/remote/configuration/content.rb +15 -2
  107. data/lib/datadog/core/remote/configuration/digest.rb +14 -7
  108. data/lib/datadog/core/remote/configuration/repository.rb +1 -1
  109. data/lib/datadog/core/remote/configuration/target.rb +13 -6
  110. data/lib/datadog/core/remote/transport/config.rb +4 -25
  111. data/lib/datadog/core/remote/transport/http/config.rb +10 -50
  112. data/lib/datadog/core/remote/transport/http/negotiation.rb +14 -44
  113. data/lib/datadog/core/remote/transport/http.rb +15 -24
  114. data/lib/datadog/core/remote/transport/negotiation.rb +8 -33
  115. data/lib/datadog/core/remote/worker.rb +25 -37
  116. data/lib/datadog/core/runtime/ext.rb +0 -1
  117. data/lib/datadog/core/runtime/metrics.rb +11 -1
  118. data/lib/datadog/core/semaphore.rb +1 -4
  119. data/lib/datadog/core/tag_builder.rb +0 -4
  120. data/lib/datadog/core/tag_normalizer.rb +84 -0
  121. data/lib/datadog/core/telemetry/component.rb +69 -15
  122. data/lib/datadog/core/telemetry/emitter.rb +6 -6
  123. data/lib/datadog/core/telemetry/event/app_endpoints_loaded.rb +30 -0
  124. data/lib/datadog/core/telemetry/event/app_started.rb +89 -51
  125. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
  126. data/lib/datadog/core/telemetry/event.rb +1 -0
  127. data/lib/datadog/core/telemetry/logger.rb +2 -2
  128. data/lib/datadog/core/telemetry/logging.rb +2 -8
  129. data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
  130. data/lib/datadog/core/telemetry/request.rb +17 -3
  131. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +3 -34
  132. data/lib/datadog/core/telemetry/transport/http.rb +21 -16
  133. data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -11
  134. data/lib/datadog/core/telemetry/worker.rb +88 -32
  135. data/lib/datadog/core/transport/ext.rb +2 -0
  136. data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
  137. data/lib/datadog/core/transport/http/api/instance.rb +4 -21
  138. data/lib/datadog/core/transport/http/builder.rb +9 -5
  139. data/lib/datadog/core/transport/http/client.rb +80 -0
  140. data/lib/datadog/core/transport/http.rb +22 -19
  141. data/lib/datadog/core/transport/response.rb +15 -1
  142. data/lib/datadog/core/transport/transport.rb +90 -0
  143. data/lib/datadog/core/utils/array.rb +29 -0
  144. data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
  145. data/lib/datadog/core/utils/network.rb +22 -1
  146. data/lib/datadog/core/utils/only_once_successful.rb +8 -2
  147. data/lib/datadog/core/utils/safe_dup.rb +2 -2
  148. data/lib/datadog/core/utils/sequence.rb +2 -0
  149. data/lib/datadog/core/utils/time.rb +1 -1
  150. data/lib/datadog/core/utils.rb +2 -0
  151. data/lib/datadog/core/workers/async.rb +10 -1
  152. data/lib/datadog/core/workers/interval_loop.rb +44 -3
  153. data/lib/datadog/core/workers/polling.rb +2 -0
  154. data/lib/datadog/core/workers/queue.rb +100 -1
  155. data/lib/datadog/core.rb +2 -0
  156. data/lib/datadog/data_streams/configuration/settings.rb +49 -0
  157. data/lib/datadog/data_streams/configuration.rb +11 -0
  158. data/lib/datadog/data_streams/ext.rb +11 -0
  159. data/lib/datadog/data_streams/extensions.rb +16 -0
  160. data/lib/datadog/data_streams/pathway_context.rb +169 -0
  161. data/lib/datadog/data_streams/processor.rb +509 -0
  162. data/lib/datadog/data_streams/transport/http/stats.rb +52 -0
  163. data/lib/datadog/data_streams/transport/http.rb +40 -0
  164. data/lib/datadog/data_streams/transport/stats.rb +46 -0
  165. data/lib/datadog/data_streams.rb +100 -0
  166. data/lib/datadog/di/boot.rb +7 -3
  167. data/lib/datadog/di/component.rb +14 -16
  168. data/lib/datadog/di/context.rb +70 -0
  169. data/lib/datadog/di/contrib/active_record.rb +30 -5
  170. data/lib/datadog/di/el/compiler.rb +168 -0
  171. data/lib/datadog/di/el/evaluator.rb +159 -0
  172. data/lib/datadog/di/el/expression.rb +42 -0
  173. data/lib/datadog/di/el.rb +5 -0
  174. data/lib/datadog/di/error.rb +34 -0
  175. data/lib/datadog/di/instrumenter.rb +189 -55
  176. data/lib/datadog/di/logger.rb +2 -2
  177. data/lib/datadog/di/probe.rb +55 -15
  178. data/lib/datadog/di/probe_builder.rb +41 -2
  179. data/lib/datadog/di/probe_file_loader/railtie.rb +1 -1
  180. data/lib/datadog/di/probe_file_loader.rb +1 -1
  181. data/lib/datadog/di/probe_manager.rb +50 -35
  182. data/lib/datadog/di/probe_notification_builder.rb +121 -70
  183. data/lib/datadog/di/probe_notifier_worker.rb +5 -5
  184. data/lib/datadog/di/proc_responder.rb +32 -0
  185. data/lib/datadog/di/remote.rb +89 -84
  186. data/lib/datadog/di/serializer.rb +151 -7
  187. data/lib/datadog/di/transport/diagnostics.rb +8 -36
  188. data/lib/datadog/di/transport/http/diagnostics.rb +1 -33
  189. data/lib/datadog/di/transport/http/input.rb +1 -33
  190. data/lib/datadog/di/transport/http.rb +32 -17
  191. data/lib/datadog/di/transport/input.rb +67 -34
  192. data/lib/datadog/di.rb +61 -5
  193. data/lib/datadog/error_tracking/filters.rb +2 -2
  194. data/lib/datadog/kit/appsec/events/v2.rb +2 -3
  195. data/lib/datadog/open_feature/component.rb +60 -0
  196. data/lib/datadog/open_feature/configuration.rb +27 -0
  197. data/lib/datadog/open_feature/evaluation_engine.rb +70 -0
  198. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  199. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  200. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  201. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  202. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  203. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  204. data/lib/datadog/open_feature/ext.rb +14 -0
  205. data/lib/datadog/open_feature/native_evaluator.rb +38 -0
  206. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  207. data/lib/datadog/open_feature/provider.rb +141 -0
  208. data/lib/datadog/open_feature/remote.rb +67 -0
  209. data/lib/datadog/open_feature/resolution_details.rb +35 -0
  210. data/lib/datadog/open_feature/transport.rb +70 -0
  211. data/lib/datadog/open_feature.rb +19 -0
  212. data/lib/datadog/opentelemetry/api/baggage.rb +1 -1
  213. data/lib/datadog/opentelemetry/configuration/settings.rb +159 -0
  214. data/lib/datadog/opentelemetry/metrics.rb +117 -0
  215. data/lib/datadog/opentelemetry/sdk/configurator.rb +26 -2
  216. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +35 -0
  217. data/lib/datadog/opentelemetry.rb +3 -0
  218. data/lib/datadog/profiling/collectors/code_provenance.rb +41 -7
  219. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +3 -2
  220. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  221. data/lib/datadog/profiling/collectors/info.rb +6 -5
  222. data/lib/datadog/profiling/component.rb +12 -11
  223. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +18 -0
  224. data/lib/datadog/profiling/ext.rb +2 -1
  225. data/lib/datadog/profiling/http_transport.rb +5 -2
  226. data/lib/datadog/profiling/profiler.rb +4 -0
  227. data/lib/datadog/profiling/tag_builder.rb +36 -3
  228. data/lib/datadog/profiling/tasks/exec.rb +2 -2
  229. data/lib/datadog/profiling.rb +1 -2
  230. data/lib/datadog/single_step_instrument.rb +1 -1
  231. data/lib/datadog/tracing/component.rb +6 -17
  232. data/lib/datadog/tracing/configuration/dynamic.rb +2 -2
  233. data/lib/datadog/tracing/configuration/ext.rb +9 -3
  234. data/lib/datadog/tracing/configuration/settings.rb +89 -10
  235. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -4
  236. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
  237. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
  238. data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
  239. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +4 -2
  240. data/lib/datadog/tracing/contrib/component.rb +2 -2
  241. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -1
  242. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +11 -3
  243. data/lib/datadog/tracing/contrib/extensions.rb +10 -2
  244. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +11 -7
  245. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
  246. data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +7 -0
  247. data/lib/datadog/tracing/contrib/graphql/ext.rb +1 -0
  248. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +84 -43
  249. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -3
  250. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -3
  251. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -3
  252. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
  253. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
  254. data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
  255. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  256. data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
  257. data/lib/datadog/tracing/contrib/karafka/patcher.rb +35 -4
  258. data/lib/datadog/tracing/contrib/rack/middlewares.rb +59 -27
  259. data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -0
  260. data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
  261. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +7 -1
  262. data/lib/datadog/tracing/contrib/rails/ext.rb +2 -1
  263. data/lib/datadog/tracing/contrib/rails/integration.rb +1 -1
  264. data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
  265. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
  266. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +3 -1
  267. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -1
  268. data/lib/datadog/tracing/contrib/span_attribute_schema.rb +1 -1
  269. data/lib/datadog/tracing/contrib/status_range_matcher.rb +9 -1
  270. data/lib/datadog/tracing/contrib/utils/quantization/hash.rb +3 -1
  271. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
  272. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
  273. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
  274. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  275. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  276. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +49 -0
  277. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  278. data/lib/datadog/tracing/contrib/waterdrop.rb +41 -0
  279. data/lib/datadog/tracing/contrib.rb +1 -0
  280. data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -1
  281. data/lib/datadog/tracing/distributed/baggage.rb +3 -2
  282. data/lib/datadog/tracing/metadata/ext.rb +9 -1
  283. data/lib/datadog/tracing/remote.rb +1 -9
  284. data/lib/datadog/tracing/sampling/priority_sampler.rb +3 -1
  285. data/lib/datadog/tracing/span.rb +1 -1
  286. data/lib/datadog/tracing/span_event.rb +2 -2
  287. data/lib/datadog/tracing/span_operation.rb +20 -9
  288. data/lib/datadog/tracing/trace_operation.rb +44 -6
  289. data/lib/datadog/tracing/tracer.rb +42 -16
  290. data/lib/datadog/tracing/transport/http/client.rb +12 -26
  291. data/lib/datadog/tracing/transport/http/traces.rb +2 -50
  292. data/lib/datadog/tracing/transport/http.rb +15 -9
  293. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  294. data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
  295. data/lib/datadog/tracing/transport/traces.rb +9 -71
  296. data/lib/datadog/tracing/workers/trace_writer.rb +5 -0
  297. data/lib/datadog/tracing/writer.rb +1 -0
  298. data/lib/datadog/version.rb +2 -2
  299. data/lib/datadog.rb +3 -0
  300. metadata +110 -24
  301. data/ext/libdatadog_api/macos_development.md +0 -26
  302. data/lib/datadog/core/remote/transport/http/api.rb +0 -53
  303. data/lib/datadog/core/remote/transport/http/client.rb +0 -49
  304. data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
  305. data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
  306. data/lib/datadog/core/transport/http/api/spec.rb +0 -36
  307. data/lib/datadog/di/transport/http/api.rb +0 -42
  308. data/lib/datadog/di/transport/http/client.rb +0 -47
  309. data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
  310. data/lib/datadog/tracing/transport/http/api.rb +0 -44
@@ -64,6 +64,9 @@ module Datadog
64
64
  # a common base class but are all of different classes) or for Mongoid
65
65
  # models (that do not have a common base class at all but include a
66
66
  # standard Mongoid module).
67
+ #
68
+ # Important: these serializers are NOT used in log messages.
69
+ # They are only used for variables that are captured in the snapshots.
67
70
  @@flat_registry = []
68
71
  def self.register(condition: nil, &block)
69
72
  @@flat_registry << {condition: condition, proc: block}
@@ -79,6 +82,18 @@ module Datadog
79
82
  attr_reader :redactor
80
83
  attr_reader :telemetry
81
84
 
85
+ def combine_args(args, kwargs, target_self)
86
+ counter = 0
87
+ combined = args.each_with_object({}) do |value, c|
88
+ counter += 1
89
+ # Conversion to symbol is needed here to put args ahead of
90
+ # kwargs when they are merged below.
91
+ c[:"arg#{counter}"] = value
92
+ end.update(kwargs)
93
+ combined[:self] = target_self
94
+ combined
95
+ end
96
+
82
97
  # Serializes positional and keyword arguments to a method,
83
98
  # as obtained by a method probe.
84
99
  #
@@ -93,13 +108,7 @@ module Datadog
93
108
  def serialize_args(args, kwargs, target_self,
94
109
  depth: settings.dynamic_instrumentation.max_capture_depth,
95
110
  attribute_count: settings.dynamic_instrumentation.max_capture_attribute_count)
96
- counter = 0
97
- combined = args.each_with_object({}) do |value, c|
98
- counter += 1
99
- # Conversion to symbol is needed here to put args ahead of
100
- # kwargs when they are merged below.
101
- c[:"arg#{counter}"] = value
102
- end.update(kwargs).update(self: target_self)
111
+ combined = combine_args(args, kwargs, target_self)
103
112
  serialize_vars(combined, depth: depth, attribute_count: attribute_count)
104
113
  end
105
114
 
@@ -150,6 +159,8 @@ module Datadog
150
159
  end
151
160
 
152
161
  serialized = {type: class_name(cls)}
162
+ # https://github.com/soutaro/steep/issues/1860
163
+ # @type var serialized: untyped
153
164
  case value
154
165
  when NilClass
155
166
  serialized.update(isNull: true)
@@ -261,8 +272,120 @@ module Datadog
261
272
  end
262
273
  end
263
274
 
275
+ # This method is used for serializing arbitrary values into log messages.
276
+ # Because the output is meant to be human-readable, we cannot use
277
+ # the "normal" serialization format which is meant to be machine-readable.
278
+ # Serialize objects with depth of 1 and include the class name.
279
+ #
280
+ # Note that this method does not (currently) utilize the custom
281
+ # serializers that the "normal" serialization logic uses.
282
+ #
283
+ # This serializer differs from the RFC in two ways:
284
+ # 1. We omit the middle of long strings rather than the end,
285
+ # and also the inner entries in arrays/hashes/objects.
286
+ # 2. We use Ruby-ish syntax for hashes and objects.
287
+ #
288
+ # We also use the Ruby-like syntax for symbols, which don't exist
289
+ # in other languages.
290
+ def serialize_value_for_message(value, depth = 1)
291
+ # This method is more verbose than "normal" Ruby code to avoid
292
+ # array allocations.
293
+ case value
294
+ when NilClass
295
+ 'nil'
296
+ when Integer, Float, TrueClass, FalseClass, Time, Date
297
+ value.to_s
298
+ when String
299
+ serialize_string_or_symbol_for_message(value)
300
+ when Symbol
301
+ ':' + serialize_string_or_symbol_for_message(value)
302
+ when Array
303
+ return '...' if depth <= 0
304
+
305
+ max = max_capture_collection_size_for_message
306
+ if value.length > max
307
+ value_ = value[0...max - 1] || []
308
+ value_ << '...'
309
+ value_ << value[-1]
310
+ value = value_
311
+ end
312
+ '[' + value.map do |item|
313
+ serialize_value_for_message(item, depth - 1)
314
+ end.join(', ') + ']'
315
+ when Hash
316
+ return '...' if depth <= 0
317
+
318
+ max = max_capture_collection_size_for_message
319
+ keys = value.keys
320
+ truncated = false
321
+ if value.length > max
322
+ keys_ = keys[0...max - 1] || []
323
+ keys_ << keys[-1]
324
+ keys = keys_
325
+ truncated = true
326
+ end
327
+ serialized = keys.map do |key|
328
+ "#{serialize_value_for_message(key, depth - 1)} => #{serialize_value_for_message(value[key], depth - 1)}"
329
+ end
330
+ if truncated
331
+ serialized[serialized.length] = serialized[serialized.length - 1]
332
+ serialized[serialized.length - 2] = '...'
333
+ end
334
+ "{#{serialized.join(", ")}}"
335
+ else
336
+ return '...' if depth <= 0
337
+
338
+ vars = value.instance_variables
339
+ truncated = false
340
+ max = max_capture_attribute_count_for_message
341
+ if vars.length > max
342
+ vars_ = vars[0...max - 1] || []
343
+ vars_ << vars[-1]
344
+ truncated = true
345
+ vars = vars_
346
+ end
347
+ serialized = vars.map do |var|
348
+ # +var+ here is always the instance variable name which is a
349
+ # symbol, we do not need to run it through our serializer.
350
+ "#{var}=#{serialize_value_for_message(value.send(:instance_variable_get, var), depth - 1)}"
351
+ end
352
+ if truncated
353
+ serialized << serialized.last
354
+ serialized[-2] = '...'
355
+ end
356
+ serialized = if serialized.any?
357
+ ' ' + serialized.join(' ')
358
+ end
359
+ "#<#{class_name(value.class)}#{serialized}>"
360
+ end
361
+ rescue => exc
362
+ telemetry&.report(exc, description: "Error serializing for message")
363
+ # TODO class_name(foo) can also fail, which we don't handle here.
364
+ # Telemetry reporting could potentially also fail?
365
+ "#<#{class_name(value.class)}: serialization error>"
366
+ end
367
+
264
368
  private
265
369
 
370
+ MAX_MESSAGE_COLLECTION_SIZE = 3
371
+ MAX_MESSAGE_ATTRIBUTE_COUNT = 5
372
+
373
+ def max_capture_collection_size_for_message
374
+ max = settings.dynamic_instrumentation.max_capture_collection_size
375
+ if max > MAX_MESSAGE_COLLECTION_SIZE
376
+ max = MAX_MESSAGE_COLLECTION_SIZE
377
+ end
378
+ max
379
+ end
380
+
381
+ def max_capture_attribute_count_for_message
382
+ max = settings.dynamic_instrumentation.max_capture_attribute_count
383
+ if max > MAX_MESSAGE_ATTRIBUTE_COUNT
384
+ max = MAX_MESSAGE_ATTRIBUTE_COUNT
385
+ end
386
+ max
387
+ end
388
+
266
389
  # Returns the name for the specified class object.
267
390
  #
268
391
  # Ruby can have nameless classes, e.g. Class.new is a class object
@@ -273,6 +396,27 @@ module Datadog
273
396
  # and we don't want to invoke user code.
274
397
  cls.name || "[Unnamed class]"
275
398
  end
399
+
400
+ def serialize_string_or_symbol_for_message(value)
401
+ max = settings.dynamic_instrumentation.max_capture_string_length
402
+ if max > 100
403
+ max = 100
404
+ end
405
+ value = value.to_s
406
+ if (length = value.length) > max
407
+ if max < 5
408
+ value[0...max]
409
+ else
410
+ upper = length - max / 2 + 1
411
+ if max % 2 == 0
412
+ upper += 1
413
+ end
414
+ value[0...max / 2 - 1] + '...' + value[upper...length]
415
+ end
416
+ else
417
+ value
418
+ end
419
+ end
276
420
  end
277
421
  end
278
422
  end
@@ -1,7 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
4
+
3
5
  require_relative '../../core/transport/parcel'
4
- require_relative 'http/client'
6
+ require_relative '../../core/transport/request'
7
+ require_relative '../../core/transport/transport'
8
+ require_relative 'http/diagnostics'
5
9
 
6
10
  module Datadog
7
11
  module DI
@@ -14,46 +18,14 @@ module Datadog
14
18
  class Request < Datadog::Core::Transport::Request
15
19
  end
16
20
 
17
- class Transport
18
- attr_reader :client, :apis, :default_api, :current_api_id, :logger
19
-
20
- def initialize(apis, default_api, logger:)
21
- @apis = apis
22
- @logger = logger
23
-
24
- @client = HTTP::Client.new(current_api, logger: logger)
25
- end
26
-
27
- def current_api
28
- @apis[HTTP::API::DIAGNOSTICS]
29
- end
30
-
21
+ class Transport < Core::Transport::Transport
31
22
  def send_diagnostics(payload)
23
+ # TODO use transport encoder functionality?
32
24
  json = JSON.dump(payload)
33
25
  parcel = EncodedParcel.new(json)
34
26
  request = Request.new(parcel)
35
27
 
36
- response = @client.send_diagnostics_payload(request)
37
- unless response.ok?
38
- # TODO Datadog::Core::Transport::InternalErrorResponse
39
- # does not have +code+ method, what is the actual API of
40
- # these response objects?
41
- raise Error::AgentCommunicationError, "send_diagnostics failed: #{begin
42
- response.code
43
- rescue
44
- "???"
45
- end}: #{response.payload}"
46
- end
47
- rescue Error::AgentCommunicationError
48
- raise
49
- # Datadog::Core::Transport does not perform any exception mapping,
50
- # therefore we could have any exception here from failure to parse
51
- # agent URI for example.
52
- # If we ever implement retries for network errors, we should distinguish
53
- # actual network errors from non-network errors that are raised by
54
- # transport code.
55
- rescue => exc
56
- raise Error::AgentCommunicationError, "send_diagnostics failed: #{exc.class}: #{exc}"
28
+ client.send_request(:diagnostics, request)
57
29
  end
58
30
  end
59
31
  end
@@ -1,43 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../../core/transport/http/api/instance'
4
- require_relative '../../../core/transport/http/api/spec'
5
- require_relative 'client'
3
+ require_relative '../../../core/transport/http/api/endpoint'
6
4
 
7
5
  module Datadog
8
6
  module DI
9
7
  module Transport
10
8
  module HTTP
11
9
  module Diagnostics
12
- module Client
13
- def send_diagnostics_payload(request)
14
- send_request(request) do |api, env|
15
- api.send_diagnostics(env)
16
- end
17
- end
18
- end
19
-
20
10
  module API
21
- class Instance < Core::Transport::HTTP::API::Instance
22
- def send_diagnostics(env)
23
- raise Core::Transport::HTTP::API::Instance::EndpointNotSupportedError.new('diagnostics', self) unless spec.is_a?(Diagnostics::API::Spec)
24
-
25
- spec.send_diagnostics(env) do |request_env|
26
- call(request_env)
27
- end
28
- end
29
- end
30
-
31
- class Spec < Core::Transport::HTTP::API::Spec
32
- attr_accessor :diagnostics
33
-
34
- def send_diagnostics(env, &block)
35
- raise Core::Transport::HTTP::API::Spec::EndpointNotDefinedError.new('diagnostics', self) if diagnostics.nil?
36
-
37
- diagnostics.call(env, &block)
38
- end
39
- end
40
-
41
11
  class Endpoint < Datadog::Core::Transport::HTTP::API::Endpoint
42
12
  attr_reader :encoder
43
13
 
@@ -57,8 +27,6 @@ module Datadog
57
27
  end
58
28
  end
59
29
  end
60
-
61
- HTTP::Client.include(Diagnostics::Client)
62
30
  end
63
31
  end
64
32
  end
@@ -1,43 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../../core/transport/http/api/instance'
4
- require_relative '../../../core/transport/http/api/spec'
5
- require_relative 'client'
3
+ require_relative '../../../core/transport/http/api/endpoint'
6
4
 
7
5
  module Datadog
8
6
  module DI
9
7
  module Transport
10
8
  module HTTP
11
9
  module Input
12
- module Client
13
- def send_input_payload(request)
14
- send_request(request) do |api, env|
15
- api.send_input(env)
16
- end
17
- end
18
- end
19
-
20
10
  module API
21
- class Instance < Core::Transport::HTTP::API::Instance
22
- def send_input(env)
23
- raise Core::Transport::HTTP::API::Instance::EndpointNotSupportedError.new('input', self) unless spec.is_a?(Input::API::Spec)
24
-
25
- spec.send_input(env) do |request_env|
26
- call(request_env)
27
- end
28
- end
29
- end
30
-
31
- class Spec < Core::Transport::HTTP::API::Spec
32
- attr_accessor :input
33
-
34
- def send_input(env, &block)
35
- raise Core::Transport::HTTP::API::Spec::EndpointNotDefinedError.new('input', self) if input.nil?
36
-
37
- input.call(env, &block)
38
- end
39
- end
40
-
41
11
  class Endpoint < Datadog::Core::Transport::HTTP::API::Endpoint
42
12
  HEADER_CONTENT_TYPE = 'Content-Type'
43
13
 
@@ -69,8 +39,6 @@ module Datadog
69
39
  end
70
40
  end
71
41
  end
72
-
73
- HTTP::Client.include(Input::Client)
74
42
  end
75
43
  end
76
44
  end
@@ -1,31 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../core/encoding'
4
+ require_relative '../../core/transport/http'
3
5
  require_relative 'diagnostics'
4
6
  require_relative 'input'
5
- require_relative 'http/api'
6
- require_relative '../../core/transport/http'
7
7
 
8
8
  module Datadog
9
9
  module DI
10
10
  module Transport
11
11
  # Namespace for HTTP transport components
12
12
  module HTTP
13
- module_function
13
+ DIAGNOSTICS = Diagnostics::API::Endpoint.new(
14
+ '/debugger/v1/diagnostics',
15
+ Core::Encoding::JSONEncoder,
16
+ )
17
+
18
+ INPUT = Input::API::Endpoint.new(
19
+ '/debugger/v2/input',
20
+ Core::Encoding::JSONEncoder,
21
+ )
22
+
23
+ LEGACY_INPUT = Input::API::Endpoint.new(
24
+ # We used to use /debugger/v1/input, but now input
25
+ # payloads should be going to the diagnostics endpoint
26
+ # which I gather performs data redaction.
27
+ '/debugger/v1/diagnostics',
28
+ Core::Encoding::JSONEncoder,
29
+ )
14
30
 
15
31
  # Builds a new Transport::HTTP::Client with default settings
16
32
  # Pass a block to override any settings.
17
- def diagnostics(
33
+ def self.diagnostics(
18
34
  agent_settings:,
19
35
  logger:,
20
- api_version: nil,
21
36
  headers: nil
22
37
  )
23
- Core::Transport::HTTP.build(api_instance_class: Diagnostics::API::Instance,
38
+ Core::Transport::HTTP.build(
24
39
  logger: logger,
25
- agent_settings: agent_settings, api_version: api_version, headers: headers) do |transport|
26
- apis = API.defaults
27
-
28
- transport.api API::DIAGNOSTICS, apis[API::DIAGNOSTICS]
40
+ agent_settings: agent_settings,
41
+ headers: headers,
42
+ ) do |transport|
43
+ transport.api 'diagnostics', DIAGNOSTICS
29
44
 
30
45
  # Call block to apply any customization, if provided
31
46
  yield(transport) if block_given?
@@ -34,18 +49,18 @@ module Datadog
34
49
 
35
50
  # Builds a new Transport::HTTP::Client with default settings
36
51
  # Pass a block to override any settings.
37
- def input(
52
+ def self.input(
38
53
  agent_settings:,
39
54
  logger:,
40
- api_version: nil,
41
55
  headers: nil
42
56
  )
43
- Core::Transport::HTTP.build(api_instance_class: Input::API::Instance,
57
+ Core::Transport::HTTP.build(
44
58
  logger: logger,
45
- agent_settings: agent_settings, api_version: api_version, headers: headers) do |transport|
46
- apis = API.defaults
47
-
48
- transport.api API::INPUT, apis[API::INPUT]
59
+ agent_settings: agent_settings,
60
+ headers: headers,
61
+ ) do |transport|
62
+ transport.api 'input', INPUT, fallback: 'legacy_input', default: true
63
+ transport.api 'legacy_input', LEGACY_INPUT
49
64
 
50
65
  # Call block to apply any customization, if provided
51
66
  yield(transport) if block_given?
@@ -1,7 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../core/chunker'
4
+ require_relative '../../core/encoding'
5
+ require_relative '../../core/tag_builder'
3
6
  require_relative '../../core/transport/parcel'
4
- require_relative 'http/client'
7
+ require_relative '../../core/transport/request'
8
+ require_relative '../../core/transport/transport'
9
+ require_relative '../error'
10
+ require_relative 'http/input'
5
11
 
6
12
  module Datadog
7
13
  module DI
@@ -21,47 +27,74 @@ module Datadog
21
27
  end
22
28
  end
23
29
 
24
- class Transport
25
- attr_reader :client, :apis, :default_api, :current_api_id, :logger
30
+ class Transport < Core::Transport::Transport
31
+ # The limit on an individual snapshot payload, aka "log line",
32
+ # is 1 MB.
33
+ #
34
+ # TODO There is an RFC for snapshot pruning that should be
35
+ # implemented to reduce the size of snapshots to be below this
36
+ # limit, so that we can send a portion of the captured data
37
+ # rather than dropping the snapshot entirely.
38
+ MAX_SERIALIZED_SNAPSHOT_SIZE = 1024 * 1024
26
39
 
27
- def initialize(apis, default_api, logger:)
28
- @apis = apis
29
- @logger = logger
40
+ # The maximum chunk (batch) size that intake permits is 5 MB.
41
+ #
42
+ # Two bytes are for the [ and ] of JSON array syntax.
43
+ MAX_CHUNK_SIZE = 5 * 1024 * 1024 - 2
30
44
 
31
- @client = HTTP::Client.new(current_api, logger: logger)
32
- end
33
-
34
- def current_api
35
- @apis[HTTP::API::INPUT]
36
- end
45
+ # Try to send smaller payloads to avoid large network requests.
46
+ # If a payload is larger than default chunk size but is under the
47
+ # max chunk size, it will still get sent out.
48
+ DEFAULT_CHUNK_SIZE = 2 * 1024 * 1024
37
49
 
38
50
  def send_input(payload, tags)
39
- json = JSON.dump(payload)
40
- parcel = EncodedParcel.new(json)
51
+ # Tags are the same for all chunks, serialize them one time.
41
52
  serialized_tags = Core::TagBuilder.serialize_tags(tags)
53
+
54
+ encoder = Core::Encoding::JSONEncoder
55
+ encoded_snapshots = Core::Utils::Array.filter_map(payload) do |snapshot|
56
+ encoded = encoder.encode(snapshot)
57
+ if encoded.length > MAX_SERIALIZED_SNAPSHOT_SIZE
58
+ # Drop the snapshot.
59
+ # TODO report via telemetry metric?
60
+ logger.debug { "di: dropping too big snapshot" }
61
+ nil
62
+ else
63
+ encoded
64
+ end
65
+ end
66
+
67
+ Datadog::Core::Chunker.chunk_by_size(
68
+ encoded_snapshots, DEFAULT_CHUNK_SIZE,
69
+ ).each do |chunk|
70
+ # We drop snapshots that are too big earlier.
71
+ # The limit on chunked payload length here is greater
72
+ # than the limit on snapshot size, therefore no chunks
73
+ # can exceed limits here.
74
+ chunked_payload = encoder.join(chunk)
75
+
76
+ # We need to rescue exceptions for each chunk so that
77
+ # subsequent chunks are attempted to be sent.
78
+ begin
79
+ send_input_chunk(chunked_payload, serialized_tags)
80
+ rescue => exc
81
+ logger.debug { "di: failed to send snapshot chunk: #{exc.class}: #{exc} (at #{exc.backtrace.first})" }
82
+ end
83
+ end
84
+
85
+ payload
86
+ end
87
+
88
+ def send_input_chunk(chunked_payload, serialized_tags)
89
+ parcel = EncodedParcel.new(chunked_payload)
42
90
  request = Request.new(parcel, serialized_tags)
43
91
 
44
- response = @client.send_input_payload(request)
45
- unless response.ok?
46
- # TODO Datadog::Core::Transport::InternalErrorResponse
47
- # does not have +code+ method, what is the actual API of
48
- # these response objects?
49
- raise Error::AgentCommunicationError, "send_input failed: #{begin
50
- response.code
51
- rescue
52
- "???"
53
- end}: #{response.payload}"
92
+ client.send_request(:input, request).tap do |response|
93
+ if downgrade?(response)
94
+ downgrade!
95
+ return send_input_chunk(chunked_payload, serialized_tags)
96
+ end
54
97
  end
55
- rescue Error::AgentCommunicationError
56
- raise
57
- # Datadog::Core::Transport does not perform any exception mapping,
58
- # therefore we could have any exception here from failure to parse
59
- # agent URI for example.
60
- # If we ever implement retries for network errors, we should distinguish
61
- # actual network errors from non-network errors that are raised by
62
- # transport code.
63
- rescue => exc
64
- raise Error::AgentCommunicationError, "send_input failed: #{exc.class}: #{exc}"
65
98
  end
66
99
  end
67
100
  end
data/lib/datadog/di.rb CHANGED
@@ -9,16 +9,13 @@ module Datadog
9
9
  #
10
10
  # @api private
11
11
  module DI
12
+ INSTRUMENTED_COUNTERS_LOCK = Mutex.new
13
+
12
14
  class << self
13
15
  def enabled?
14
16
  Datadog.configuration.dynamic_instrumentation.enabled
15
17
  end
16
- end
17
-
18
- # Expose DI to global shared objects
19
- Extensions.activate!
20
18
 
21
- class << self
22
19
  # This method is called from DI Remote handler to issue DI operations
23
20
  # to the probe manager (add or remove probes).
24
21
  #
@@ -31,6 +28,65 @@ module Datadog
31
28
  def component
32
29
  Datadog.send(:components).dynamic_instrumentation
33
30
  end
31
+
32
+ # Track how many outstanding instrumentations are in DI.
33
+ #
34
+ # It is hard to find the actual instrumentations - there is no
35
+ # method provided by Ruby to list all trace points, and we would
36
+ # need to manually track our instrumentation modules for method probes.
37
+ # Plus, tracking the modules could create active references to
38
+ # instrumentation, which is not desired.
39
+ #
40
+ # A simpler solution is to maintain a counter which is increased
41
+ # whenever a probe is installed and decreased when a probe is removed.
42
+ #
43
+ # This counter does not include pending probes - being not installed,
44
+ # those pose no concerns to customer applications.
45
+ def instrumented_count(kind = nil)
46
+ INSTRUMENTED_COUNTERS_LOCK.synchronize do
47
+ if defined?(@instrumented_count)
48
+ if kind
49
+ validate_kind!(kind)
50
+ @instrumented_count[kind] || 0
51
+ else
52
+ @instrumented_count.inject(0) do |sum, (_kind, count)|
53
+ sum + count
54
+ end
55
+ end
56
+ else
57
+ 0
58
+ end
59
+ end
60
+ end
61
+
62
+ def instrumented_count_inc(kind)
63
+ validate_kind!(kind)
64
+ INSTRUMENTED_COUNTERS_LOCK.synchronize do
65
+ @instrumented_count = Hash.new(0) unless defined?(@instrumented_count)
66
+ @instrumented_count[kind] += 1
67
+ end
68
+ end
69
+
70
+ def instrumented_count_dec(kind)
71
+ validate_kind!(kind)
72
+ INSTRUMENTED_COUNTERS_LOCK.synchronize do
73
+ @instrumented_count = Hash.new(0) unless defined?(@instrumented_count)
74
+ if @instrumented_count[kind] <= 0
75
+ Datadog.logger.debug { "di: attempting to decrease instrumented count below zero for #{kind}" }
76
+ return
77
+ end
78
+ @instrumented_count[kind] -= 1
79
+ end
80
+ end
81
+
82
+ private def validate_kind!(kind)
83
+ unless %i[line method].include?(kind)
84
+ raise ArgumentError, "Invalid kind: #{kind}"
85
+ end
86
+ end
34
87
  end
88
+
89
+ # Expose DI to global shared objects
90
+ Extensions.activate!
35
91
  end
36
92
  end
@@ -18,10 +18,10 @@ module Datadog
18
18
  regex_match = regex.match(file_path)
19
19
  return unless regex_match
20
20
 
21
- gem_name = regex_match[1]
21
+ gem_name = regex_match[1] #: String
22
22
 
23
23
  begin
24
- Gem::Specification.find_by_name(gem_name) # steep:ignore
24
+ Gem::Specification.find_by_name(gem_name)
25
25
  rescue Gem::MissingSpecError
26
26
  nil
27
27
  end