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
@@ -94,6 +94,19 @@ module Datadog
94
94
  # matches.
95
95
  def add_probe(probe)
96
96
  @lock.synchronize do
97
+ if @installed_probes[probe.id]
98
+ # Either this probe was already installed, or another probe was
99
+ # installed with the same id (previous version perhaps?).
100
+ # Since our state tracking is keyed by probe id, we cannot
101
+ # install this probe since we won't have a way of removing the
102
+ # instrumentation for the probe with the same id which is already
103
+ # installed.
104
+ #
105
+ # The exception raised here will be caught below and logged and
106
+ # reported to telemetry.
107
+ raise Error::AlreadyInstrumented, "Probe with id #{probe.id} is already in installed probes"
108
+ end
109
+
97
110
  # Probe failed to install previously, do not try to install it again.
98
111
  if msg = @failed_probes[probe.id]
99
112
  # TODO test this path
@@ -101,7 +114,7 @@ module Datadog
101
114
  end
102
115
 
103
116
  begin
104
- instrumenter.hook(probe, &method(:probe_executed_callback))
117
+ instrumenter.hook(probe, self)
105
118
 
106
119
  @installed_probes[probe.id] = probe
107
120
  payload = probe_notification_builder.build_installed(probe)
@@ -134,38 +147,30 @@ module Datadog
134
147
  end
135
148
  end
136
149
 
137
- # Removes probes with ids other than in the specified list.
138
- #
139
- # This method is meant to be invoked from remote config processor.
140
- # Remote config contains the list of currently defined probes; any
141
- # probes not in that list have been removed by user and should be
142
- # de-instrumented from the application.
143
- def remove_other_probes(probe_ids)
150
+ # Removes probe with specified id. The probe could be pending or
151
+ # installed. Does nothing if there is no probe with the specified id.
152
+ def remove_probe(probe_id)
144
153
  @lock.synchronize do
145
- @pending_probes.values.each do |probe|
146
- unless probe_ids.include?(probe.id)
147
- @pending_probes.delete(probe.id)
148
- end
149
- end
150
- @installed_probes.values.each do |probe|
151
- unless probe_ids.include?(probe.id)
152
- begin
153
- instrumenter.unhook(probe)
154
- # Only remove the probe from installed list if it was
155
- # successfully de-instrumented. Active probes do incur overhead
156
- # for the running application, and if the error is ephemeral
157
- # we want to try removing the probe again at the next opportunity.
158
- #
159
- # TODO give up after some time?
160
- @installed_probes.delete(probe.id)
161
- rescue => exc
162
- raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
163
- # Silence all exceptions?
164
- # TODO should we propagate here and rescue upstream?
165
- logger.debug { "di: error removing #{probe.type} probe at #{probe.location} (#{probe.id}): #{exc.class}: #{exc}" }
166
- telemetry&.report(exc, description: "Error removing probe")
167
- end
168
- end
154
+ @pending_probes.delete(probe_id)
155
+ end
156
+
157
+ # Do not delete the probe from the registry here in case
158
+ # deinstrumentation fails - though I don't know why deinstrumentation
159
+ # would fail and how we could recover if it does.
160
+ # I plan on tracking the number of outstanding (instrumented) probes
161
+ # in the future, and if deinstrumentation fails I would want to
162
+ # keep that probe as "installed" for the count, so that we can
163
+ # investigate the situation.
164
+ if probe = @installed_probes[probe_id]
165
+ begin
166
+ instrumenter.unhook(probe)
167
+ @installed_probes.delete(probe_id)
168
+ rescue => exc
169
+ raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions
170
+ # Silence all exceptions?
171
+ # TODO should we propagate here and rescue upstream?
172
+ logger.debug { "di: error removing #{probe.type} probe at #{probe.location} (#{probe.id}): #{exc.class}: #{exc}" }
173
+ telemetry&.report(exc, description: "Error removing probe")
169
174
  end
170
175
  end
171
176
  end
@@ -184,7 +189,8 @@ module Datadog
184
189
  begin
185
190
  # TODO is it OK to hook from trace point handler?
186
191
  # TODO the class is now defined, but can hooking still fail?
187
- instrumenter.hook(probe, &method(:probe_executed_callback))
192
+ instrumenter.hook(probe, self)
193
+ @installed_probes[probe.id] = probe
188
194
  @pending_probes.delete(probe.id)
189
195
  break
190
196
  rescue Error::DITargetNotDefined
@@ -229,7 +235,8 @@ module Datadog
229
235
  # This method is responsible for queueing probe status to be sent to the
230
236
  # backend (once per the probe's lifetime) and a snapshot corresponding
231
237
  # to the current invocation.
232
- def probe_executed_callback(probe:, **opts)
238
+ def probe_executed_callback(context)
239
+ probe = context.probe
233
240
  logger.trace { "di: executed #{probe.type} probe at #{probe.location} (#{probe.id})" }
234
241
  unless probe.emitting_notified?
235
242
  payload = probe_notification_builder.build_emitting(probe)
@@ -237,10 +244,18 @@ module Datadog
237
244
  probe.emitting_notified = true
238
245
  end
239
246
 
240
- payload = probe_notification_builder.build_executed(probe, **opts)
247
+ payload = probe_notification_builder.build_executed(context)
241
248
  probe_notifier_worker.add_snapshot(payload)
242
249
  end
243
250
 
251
+ def probe_condition_evaluation_failed_callback(context, expr, exc)
252
+ probe = context.probe
253
+ if probe.condition_evaluation_failed_rate_limiter&.allow?
254
+ payload = probe_notification_builder.build_condition_evaluation_failed(context, expr, exc)
255
+ probe_notifier_worker.add_snapshot(payload)
256
+ end
257
+ end
258
+
244
259
  # Class/module definition trace point (:end type).
245
260
  # Used to install hooks when the target classes/modules aren't yet
246
261
  # defined when the hook request is received.
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Lint/AssignmentInCondition
4
+
3
5
  module Datadog
4
6
  module DI
5
7
  # Builds probe status notification and snapshot payloads.
@@ -40,32 +42,17 @@ module Datadog
40
42
 
41
43
  # Duration is in seconds.
42
44
  # path is the actual path of the instrumented file.
43
- def build_executed(probe,
44
- path: nil, rv: nil, duration: nil, caller_locations: nil,
45
- serialized_locals: nil, args: nil, kwargs: nil, target_self: nil,
46
- serialized_entry_args: nil)
47
- build_snapshot(probe, rv: rv, serialized_locals: serialized_locals,
48
- # Actual path of the instrumented file.
49
- path: path,
50
- duration: duration,
51
- # TODO check how many stack frames we should be keeping/sending,
52
- # this should be all frames for enriched probes and no frames for
53
- # non-enriched probes?
54
- caller_locations: caller_locations,
55
- args: args, kwargs: kwargs,
56
- target_self: target_self,
57
- serialized_entry_args: serialized_entry_args)
58
- end
59
-
60
- def build_snapshot(probe, rv: nil, serialized_locals: nil, path: nil,
61
- # In Ruby everything is a method, therefore we should always have
62
- # a target self. However, if we are not capturing a snapshot,
63
- # there is no need to pass in the target self.
64
- target_self: nil,
65
- duration: nil, caller_locations: nil,
66
- args: nil, kwargs: nil,
67
- serialized_entry_args: nil)
68
- if probe.capture_snapshot? && !target_self
45
+ def build_executed(context)
46
+ build_snapshot(context)
47
+ end
48
+
49
+ NANOSECONDS = 1_000_000_000
50
+ MILLISECONDS = 1000
51
+
52
+ def build_snapshot(context)
53
+ probe = context.probe
54
+
55
+ if probe.capture_snapshot? && !context.target_self
69
56
  raise ArgumentError, "Asked to build snapshot with snapshot capture but target_self is nil"
70
57
  end
71
58
 
@@ -74,22 +61,14 @@ module Datadog
74
61
  captures = if probe.capture_snapshot?
75
62
  if probe.method?
76
63
  return_arguments = {
77
- "@return": serializer.serialize_value(rv,
64
+ "@return": serializer.serialize_value(context.return_value,
78
65
  depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
79
66
  attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count),
80
- self: serializer.serialize_value(target_self),
67
+ self: serializer.serialize_value(context.target_self),
81
68
  }
82
69
  {
83
70
  entry: {
84
- # standard:disable all
85
- arguments: if serialized_entry_args
86
- serialized_entry_args
87
- else
88
- (args || kwargs) && serializer.serialize_args(args, kwargs, target_self,
89
- depth: probe.max_capture_depth || settings.dynamic_instrumentation.max_capture_depth,
90
- attribute_count: probe.max_capture_attribute_count || settings.dynamic_instrumentation.max_capture_attribute_count)
91
- end,
92
- # standard:enable all
71
+ arguments: context.serialized_entry_args,
93
72
  },
94
73
  return: {
95
74
  arguments: return_arguments,
@@ -98,20 +77,51 @@ module Datadog
98
77
  }
99
78
  elsif probe.line?
100
79
  {
101
- lines: serialized_locals && {
80
+ lines: (locals = context.serialized_locals) && {
102
81
  probe.line_no => {
103
- locals: serialized_locals,
104
- arguments: {self: serializer.serialize_value(target_self)},
82
+ locals: locals,
83
+ arguments: {self: serializer.serialize_value(context.target_self)},
105
84
  },
106
85
  },
107
86
  }
108
87
  end
109
88
  end
110
89
 
90
+ message = nil
91
+ evaluation_errors = []
92
+ if segments = probe.template_segments
93
+ message, evaluation_errors = evaluate_template(segments, context)
94
+ end
95
+ build_snapshot_base(context,
96
+ evaluation_errors: evaluation_errors, message: message,
97
+ captures: captures)
98
+ end
99
+
100
+ def build_condition_evaluation_failed(context, expression, exception)
101
+ error = {
102
+ message: "#{exception.class}: #{exception}",
103
+ expr: expression.dsl_expr,
104
+ }
105
+ build_snapshot_base(context, evaluation_errors: [error])
106
+ end
107
+
108
+ private
109
+
110
+ def build_snapshot_base(context, evaluation_errors: [], captures: nil, message: nil)
111
+ probe = context.probe
112
+
113
+ timestamp = timestamp_now
114
+ duration = context.duration
115
+
111
116
  location = if probe.line?
112
117
  {
113
- file: path,
114
- lines: [probe.line_no],
118
+ file: context.path,
119
+ # Line numbers are required to be strings by the
120
+ # system tests schema.
121
+ # Backend I think accepts them also as integers, but some
122
+ # other languages send strings and we decided to require
123
+ # strings for everyone.
124
+ lines: [probe.line_no.to_s],
115
125
  }
116
126
  elsif probe.method?
117
127
  {
@@ -120,34 +130,50 @@ module Datadog
120
130
  }
121
131
  end
122
132
 
123
- stack = if caller_locations
133
+ stack = if caller_locations = context.caller_locations
124
134
  format_caller_locations(caller_locations)
125
135
  end
126
136
 
127
- timestamp = timestamp_now
128
- {
137
+ payload = {
129
138
  service: settings.service,
130
- "debugger.snapshot": {
131
- id: SecureRandom.uuid,
132
- timestamp: timestamp,
133
- evaluationErrors: [],
134
- probe: {
135
- id: probe.id,
136
- version: 0,
137
- location: location,
139
+ debugger: {
140
+ type: 'snapshot',
141
+ # Product can have three values: di, ld, er.
142
+ # We do not currently implement exception replay.
143
+ # There is currently no specification, and no consensus, for
144
+ # when product should be di (dynamic instrumentation) and when
145
+ # it should be ld (live debugger). I thought the backend was
146
+ # supposed to provide this in probe specification via remote
147
+ # config, but apparently this is not the case and the expectation
148
+ # is that the library figures out the product via heuristics,
149
+ # except there is currently no consensus on said heuristics.
150
+ # .NET always sends ld, other languages send nothing at the moment.
151
+ # Don't send anything for the time being.
152
+ #product: 'di/ld',
153
+ snapshot: {
154
+ id: SecureRandom.uuid,
155
+ timestamp: timestamp,
156
+ evaluationErrors: evaluation_errors,
157
+ probe: {
158
+ id: probe.id,
159
+ version: 0,
160
+ location: location,
161
+ },
162
+ language: 'ruby',
163
+ # TODO add test coverage for callers being nil
164
+ stack: stack,
165
+ # System tests schema validation requires captures to
166
+ # always be present
167
+ captures: captures || {},
138
168
  },
139
- language: 'ruby',
140
- # TODO add test coverage for callers being nil
141
- stack: stack,
142
- captures: captures,
143
169
  },
144
170
  # In python tracer duration is under debugger.snapshot,
145
171
  # but UI appears to expect it here at top level.
146
- duration: duration ? (duration * 10**9).to_i : 0,
172
+ duration: duration ? (duration * NANOSECONDS).to_i : 0,
147
173
  host: nil,
148
174
  logger: {
149
175
  name: probe.file,
150
- method: probe.method_name || 'no_method',
176
+ method: probe.method_name,
151
177
  thread_name: Thread.current.name,
152
178
  # Dynamic instrumentation currently does not need thread_id for
153
179
  # anything. It can be sent if a customer requests it at which point
@@ -160,13 +186,14 @@ module Datadog
160
186
  "dd.trace_id": active_trace&.id&.to_s,
161
187
  "dd.span_id": active_span&.id&.to_s,
162
188
  ddsource: 'dd_debugger',
163
- message: probe.template && evaluate_template(probe.template,
164
- duration: duration ? duration * 1000 : 0),
189
+ message: message,
165
190
  timestamp: timestamp,
166
191
  }
167
- end
168
192
 
169
- private
193
+ tag_process_tags!(payload, settings)
194
+
195
+ payload
196
+ end
170
197
 
171
198
  def build_status(probe, message:, status:)
172
199
  {
@@ -192,16 +219,38 @@ module Datadog
192
219
  end
193
220
  end
194
221
 
195
- def evaluate_template(template, **vars)
196
- message = template.dup
197
- vars.each do |key, value|
198
- message.gsub!("{@#{key}}", value.to_s)
199
- end
200
- message
222
+ def evaluate_template(template_segments, context)
223
+ evaluation_errors = []
224
+ message = template_segments.map do |segment|
225
+ case segment
226
+ when String
227
+ segment
228
+ when EL::Expression
229
+ serializer.serialize_value_for_message(segment.evaluate(context))
230
+ else
231
+ raise ArgumentError, "Invalid template segment type: #{segment}"
232
+ end
233
+ rescue => exc
234
+ evaluation_errors << {
235
+ message: "#{exc.class}: #{exc}",
236
+ expr: segment.dsl_expr,
237
+ }
238
+ '[evaluation error]'
239
+ end.join
240
+ [message, evaluation_errors]
241
+ end
242
+
243
+ def tag_process_tags!(payload, settings)
244
+ return unless settings.experimental_propagate_process_tags_enabled
245
+
246
+ process_tags = Core::Environment::Process.serialized
247
+ return if process_tags.empty?
248
+
249
+ payload[:process_tags] = process_tags
201
250
  end
202
251
 
203
252
  def timestamp_now
204
- (Core::Utils::Time.now.to_f * 1000).to_i
253
+ (Core::Utils::Time.now.to_f * MILLISECONDS).to_i
205
254
  end
206
255
 
207
256
  def active_trace
@@ -218,3 +267,5 @@ module Datadog
218
267
  end
219
268
  end
220
269
  end
270
+
271
+ # rubocop:enable Lint/AssignmentInCondition
@@ -249,12 +249,12 @@ module Datadog
249
249
  @lock.synchronize do
250
250
  batch = instance_variable_get("@#{event_type}_queue")
251
251
  instance_variable_set("@#{event_type}_queue", [])
252
- @io_in_progress = batch.any? # steep:ignore
252
+ @io_in_progress = batch.any?
253
253
  end
254
- logger.trace { "di: #{self.class.name}: checking #{event_type} queue - #{batch.length} entries" } # steep:ignore
255
- if batch.any? # steep:ignore
254
+ logger.trace { "di: #{self.class.name}: checking #{event_type} queue - #{batch.length} entries" }
255
+ if batch.any?
256
256
  begin
257
- logger.trace { "di: sending #{batch.length} #{event_type} event(s) to agent" } # steep:ignore
257
+ logger.trace { "di: sending #{batch.length} #{event_type} event(s) to agent" }
258
258
  send("do_send_#{event_type}", batch)
259
259
  time = Core::Utils::Time.get_time
260
260
  @lock.synchronize do
@@ -268,7 +268,7 @@ module Datadog
268
268
  # telemetry message would also fail.
269
269
  end
270
270
  end
271
- batch.any? # steep:ignore
271
+ batch.any?
272
272
  rescue ThreadError => exc
273
273
  # Normally the queue should only be consumed in this method,
274
274
  # however if anyone consumes it elsewhere we don't want to block
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module DI
5
+ # An adapter to convert procs to responders.
6
+ #
7
+ # Used in test suite and benchmarks.
8
+ #
9
+ # @api private
10
+ class ProcResponder
11
+ def initialize(executed_proc, failed_proc = nil)
12
+ @executed_proc = executed_proc
13
+ @failed_proc = failed_proc
14
+ end
15
+
16
+ attr_reader :executed_proc
17
+ attr_reader :failed_proc
18
+
19
+ def probe_executed_callback(context)
20
+ executed_proc.call(context)
21
+ end
22
+
23
+ def probe_condition_evaluation_failed_callback(context, exc)
24
+ if failed_proc.nil?
25
+ raise NotImplementedError, "Failed proc not provided"
26
+ end
27
+
28
+ failed_proc.call(context, exc)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -12,8 +12,6 @@ module Datadog
12
12
  #
13
13
  # @api private
14
14
  module Remote
15
- class ReadError < StandardError; end
16
-
17
15
  class << self
18
16
  PRODUCT = 'LIVE_DEBUGGING'
19
17
 
@@ -28,7 +26,7 @@ module Datadog
28
26
  end
29
27
 
30
28
  def receivers(telemetry)
31
- receiver do |repository, _changes|
29
+ receiver do |repository, changes|
32
30
  # DEV: Filter our by product. Given it will be very common
33
31
  # DEV: we can filter this out before we receive the data in this method.
34
32
  # DEV: Apply this refactor to AppSec as well if implemented.
@@ -41,84 +39,23 @@ module Datadog
41
39
  # If the component is nil for some reason, we also don't have a
42
40
  # logger instance to report the issue.
43
41
  if component
44
-
45
- probe_manager = component.probe_manager
46
- probe_notification_builder = component.probe_notification_builder
47
- probe_notifier_worker = component.probe_notifier_worker
48
-
49
- current_probe_ids = {}
50
- repository.contents.each do |content|
51
- case content.path.product
52
- when PRODUCT
53
- begin
54
- probe_spec = parse_content(content)
55
- probe = component.parse_probe_spec_and_notify(probe_spec)
56
- component.logger.debug { "di: received #{probe.type} probe at #{probe.location} (#{probe.id}) via RC" }
57
-
58
- begin
59
- # TODO test exception capture
60
- probe_manager.add_probe(probe)
61
- content.applied
62
- rescue DI::Error::DITargetNotInRegistry => exc
63
- component.telemetry&.report(exc, description: "Line probe is targeting a loaded file that is not in code tracker")
64
-
65
- payload = probe_notification_builder.build_errored(probe, exc)
66
- probe_notifier_worker.add_status(payload)
67
-
68
- # If a probe fails to install, we will mark the content
69
- # as errored. On subsequent remote configuration application
70
- # attemps, probe manager will raise the "previously errored"
71
- # exception and we'll rescue it here, again marking the
72
- # content as errored but with a somewhat different exception
73
- # message.
74
- # TODO assert content state (errored for this example)
75
- content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
76
- rescue => exc
77
- raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
78
-
79
- component.logger.debug { "di: unhandled exception adding #{probe.type} probe at #{probe.location} (#{probe.id}) in DI remote receiver: #{exc.class}: #{exc}" }
80
- component.telemetry&.report(exc, description: "Unhandled exception adding probe in DI remote receiver")
81
-
82
- # TODO test this path
83
- payload = probe_notification_builder.build_errored(probe, exc)
84
- probe_notifier_worker.add_status(payload)
85
-
86
- # If a probe fails to install, we will mark the content
87
- # as errored. On subsequent remote configuration application
88
- # attemps, probe manager will raise the "previously errored"
89
- # exception and we'll rescue it here, again marking the
90
- # content as errored but with a somewhat different exception
91
- # message.
92
- # TODO assert content state (errored for this example)
93
- content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
94
- end
95
-
96
- # Important: even if processing fails for this probe config,
97
- # we need to note it as being current so that we do not
98
- # try to remove instrumentation that is still supposed to be
99
- # active.
100
- current_probe_ids[probe_spec.fetch('id')] = true
101
- rescue => exc
102
- raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
103
-
104
- component.logger.debug { "di: unhandled exception handling a probe in DI remote receiver: #{exc.class}: #{exc}" }
105
- component.telemetry&.report(exc, description: "Unhandled exception handling probe in DI remote receiver")
106
-
107
- # TODO assert content state (errored for this example)
108
- content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
109
- end
42
+ changes.each do |change|
43
+ case change.type
44
+ when :insert
45
+ add_probe(change.content, component)
46
+ when :update
47
+ # We do not implement updates at the moment, remove the
48
+ # probe and reinstall.
49
+ remove_probe(change.content, component)
50
+ add_probe(change.content, component)
51
+ when :delete
52
+ remove_probe(change.previous, component)
53
+ else
54
+ # This really should never happen since we generate the
55
+ # change types in the library.
56
+ component.logger.debug { "di: unrecognized change type: #{change.type}" }
110
57
  end
111
58
  end
112
-
113
- begin
114
- # TODO test exception capture
115
- probe_manager.remove_other_probes(current_probe_ids.keys)
116
- rescue => exc
117
- raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
118
-
119
- component.logger.debug { "di: unhandled exception removing probes in DI remote receiver: #{exc.class}: #{exc}" }
120
- component.telemetry&.report(exc, description: "Unhandled exception removing probes in DI remote receiver")
121
- end
122
59
  end
123
60
  end
124
61
  end
@@ -130,14 +67,82 @@ module Datadog
130
67
 
131
68
  private
132
69
 
133
- def parse_content(content)
134
- data = content.data.read
70
+ def add_probe(content, component)
71
+ probe_spec = parse_content(content)
72
+ probe = component.parse_probe_spec_and_notify(probe_spec)
73
+ component.logger.debug { "di: received #{probe.type} probe at #{probe.location} (#{probe.id}) via RC" }
74
+
75
+ begin
76
+ # TODO test exception capture
77
+ component.probe_manager.add_probe(probe)
78
+ content.applied
79
+ rescue DI::Error::DITargetNotInRegistry => exc
80
+ component.telemetry&.report(exc, description: "Line probe is targeting a loaded file that is not in code tracker")
81
+
82
+ payload = component.probe_notification_builder.build_errored(probe, exc)
83
+ component.probe_notifier_worker.add_status(payload)
84
+
85
+ # If a probe fails to install, we will mark the content
86
+ # as errored. On subsequent remote configuration application
87
+ # attemps, probe manager will raise the "previously errored"
88
+ # exception and we'll rescue it here, again marking the
89
+ # content as errored but with a somewhat different exception
90
+ # message.
91
+ # TODO assert content state (errored for this example)
92
+ content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
93
+ rescue => exc
94
+ raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
95
+
96
+ component.logger.debug { "di: unhandled exception adding #{probe.type} probe at #{probe.location} (#{probe.id}) in DI remote receiver: #{exc.class}: #{exc}" }
97
+ component.telemetry&.report(exc, description: "Unhandled exception adding probe in DI remote receiver")
98
+
99
+ # TODO test this path
100
+ payload = component.probe_notification_builder.build_errored(probe, exc)
101
+ component.probe_notifier_worker.add_status(payload)
102
+
103
+ # If a probe fails to install, we will mark the content
104
+ # as errored. On subsequent remote configuration application
105
+ # attemps, probe manager will raise the "previously errored"
106
+ # exception and we'll rescue it here, again marking the
107
+ # content as errored but with a somewhat different exception
108
+ # message.
109
+ # TODO assert content state (errored for this example)
110
+ content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
111
+ end
112
+
113
+ # Important: even if processing fails for this probe config,
114
+ # we need to note it as being current so that we do not
115
+ # try to remove instrumentation that is still supposed to be
116
+ # active.
117
+ #current_probe_ids[probe_spec.fetch('id')] = true
118
+ rescue => exc
119
+ raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
120
+
121
+ component.logger.debug { "di: unhandled exception handling a probe in DI remote receiver: #{exc.class}: #{exc}" }
122
+ component.telemetry&.report(exc, description: "Unhandled exception handling probe in DI remote receiver")
135
123
 
136
- content.data.rewind
124
+ # TODO assert content state (errored for this example)
125
+ content.errored("Error applying dynamic instrumentation configuration: #{exc.class.name} #{exc.message}")
126
+ end
137
127
 
138
- raise ReadError, 'EOF reached' if data.nil?
128
+ # This method does not mark +previous_content+ as succeeded or errored,
129
+ # because that content is from a previous RC response and has already
130
+ # been marked. Removal of probes happens when an RC entry disappears,
131
+ # as such there is nothing to mark.
132
+ def remove_probe(previous_content, component)
133
+ # TODO test exception capture
134
+ probe_spec = parse_content(previous_content)
135
+ probe_id = probe_spec.fetch('id')
136
+ component.probe_manager.remove_probe(probe_id)
137
+ rescue => exc
138
+ raise if component.settings.dynamic_instrumentation.internal.propagate_all_exceptions
139
+
140
+ component.logger.debug { "di: unhandled exception removing probes in DI remote receiver: #{exc.class}: #{exc}" }
141
+ component.telemetry&.report(exc, description: "Unhandled exception removing probes in DI remote receiver")
142
+ end
139
143
 
140
- JSON.parse(data)
144
+ def parse_content(content)
145
+ JSON.parse(content.data)
141
146
  end
142
147
  end
143
148
  end