Class: Datadog::DI::ProbeManager Private
- Inherits:
-
Object
- Object
- Datadog::DI::ProbeManager
- Defined in:
- lib/datadog/di/probe_manager.rb
Overview
This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.
Stores probes received from remote config (that we can parse, in other words, whose type/attributes we support), requests needed instrumentation for the probes via Instrumenter, and stores pending probes (those which haven’t yet been instrumented successfully due to their targets not existing) and failed probes (where we are certain the target will not ever be loaded, or otherwise become valid).
Instance Attribute Summary collapse
-
#definition_trace_point ⇒ Object
readonly
private
Class/module definition trace point (:end type).
- #instrumenter ⇒ Object readonly private
- #logger ⇒ Object readonly private
- #probe_notification_builder ⇒ Object readonly private
- #probe_notifier_worker ⇒ Object readonly private
- #settings ⇒ Object readonly private
- #telemetry ⇒ Object readonly private
Instance Method Summary collapse
-
#add_probe(probe) ⇒ Object
private
Requests to install the specified probe.
- #clear_hooks ⇒ Object private
-
#close ⇒ Object
private
TODO test that close is called during component teardown and the trace point is cleared.
-
#failed_probes ⇒ Object
private
Probes that failed to instrument for reasons other than the target is not yet loaded are added to this collection, so that we do not try to instrument them every time remote configuration is processed.
-
#initialize(settings, instrumenter, probe_notification_builder, probe_notifier_worker, logger, telemetry: nil) ⇒ ProbeManager
constructor
private
A new instance of ProbeManager.
-
#install_pending_line_probes(path) ⇒ Object
private
Installs pending line probes, if any, for the file of the specified absolute path.
- #installed_probes ⇒ Object private
- #pending_probes ⇒ Object private
-
#probe_executed_callback(probe:, **opts) ⇒ Object
private
Entry point invoked from the instrumentation when the specfied probe is invoked (that is, either its target method is invoked, or execution reached its target file/line).
-
#remove_other_probes(probe_ids) ⇒ Object
private
Removes probes with ids other than in the specified list.
Constructor Details
#initialize(settings, instrumenter, probe_notification_builder, probe_notifier_worker, logger, telemetry: nil) ⇒ ProbeManager
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns a new instance of ProbeManager.
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/datadog/di/probe_manager.rb', line 18 def initialize(settings, instrumenter, probe_notification_builder, probe_notifier_worker, logger, telemetry: nil) @settings = settings @instrumenter = instrumenter @probe_notification_builder = probe_notification_builder @probe_notifier_worker = probe_notifier_worker @logger = logger @telemetry = telemetry @installed_probes = {} @pending_probes = {} @failed_probes = {} @lock = Monitor.new @definition_trace_point = TracePoint.trace(:end) do |tp| install_pending_method_probes(tp.self) rescue => exc raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions logger.warn("Unhandled exception in definition trace point: #{exc.class}: #{exc}") telemetry&.report(exc, description: "Unhandled exception in definition trace point") # TODO test this path end end |
Instance Attribute Details
#definition_trace_point ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Class/module definition trace point (:end type). Used to install hooks when the target classes/modules aren’t yet defined when the hook request is received.
241 242 243 |
# File 'lib/datadog/di/probe_manager.rb', line 241 def definition_trace_point @definition_trace_point end |
#instrumenter ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
62 63 64 |
# File 'lib/datadog/di/probe_manager.rb', line 62 def instrumenter @instrumenter end |
#logger ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
41 42 43 |
# File 'lib/datadog/di/probe_manager.rb', line 41 def logger @logger end |
#probe_notification_builder ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
63 64 65 |
# File 'lib/datadog/di/probe_manager.rb', line 63 def probe_notification_builder @probe_notification_builder end |
#probe_notifier_worker ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
64 65 66 |
# File 'lib/datadog/di/probe_manager.rb', line 64 def probe_notifier_worker @probe_notifier_worker end |
#settings ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
61 62 63 |
# File 'lib/datadog/di/probe_manager.rb', line 61 def settings @settings end |
#telemetry ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
42 43 44 |
# File 'lib/datadog/di/probe_manager.rb', line 42 def telemetry @telemetry end |
Instance Method Details
#add_probe(probe) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Requests to install the specified probe.
If the target of the probe does not exist, assume the relevant code is not loaded yet (rather than that it will never be loaded), and store the probe in a pending probe list. When classes are defined, or files loaded, the probe will be checked against the newly defined classes/loaded files, and will be installed if it matches.
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/datadog/di/probe_manager.rb', line 95 def add_probe(probe) @lock.synchronize do # Probe failed to install previously, do not try to install it again. if msg = @failed_probes[probe.id] # TODO test this path raise Error::ProbePreviouslyFailed, msg end begin instrumenter.hook(probe, &method(:probe_executed_callback)) @installed_probes[probe.id] = probe payload = probe_notification_builder.build_installed(probe) probe_notifier_worker.add_status(payload) # The probe would only be in the pending probes list if it was # previously attempted to be installed and the target was not loaded. # Always remove from pending list here because it makes the # API smaller and shouldn't cause any actual problems. @pending_probes.delete(probe.id) true rescue Error::DITargetNotDefined @pending_probes[probe.id] = probe false end rescue => exc # In "propagate all exceptions" mode we will try to instrument again. raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions logger.warn("Error processing probe configuration: #{exc.class}: #{exc}") telemetry&.report(exc, description: "Error processing probe configuration") # TODO report probe as failed to agent since we won't attempt to # install it again. # TODO add top stack frame to message @failed_probes[probe.id] = "#{exc.class}: #{exc}" raise end end |
#clear_hooks ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
51 52 53 54 55 56 57 58 59 |
# File 'lib/datadog/di/probe_manager.rb', line 51 def clear_hooks @lock.synchronize do @pending_probes.clear @installed_probes.each do |probe_id, probe| instrumenter.unhook(probe) end @installed_probes.clear end end |
#close ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
TODO test that close is called during component teardown and the trace point is cleared
46 47 48 49 |
# File 'lib/datadog/di/probe_manager.rb', line 46 def close definition_trace_point.disable clear_hooks end |
#failed_probes ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Probes that failed to instrument for reasons other than the target is not yet loaded are added to this collection, so that we do not try to instrument them every time remote configuration is processed.
81 82 83 84 85 |
# File 'lib/datadog/di/probe_manager.rb', line 81 def failed_probes @lock.synchronize do @failed_probes end end |
#install_pending_line_probes(path) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Installs pending line probes, if any, for the file of the specified absolute path.
This method is meant to be called from the script_compiled trace point, which is invoked for each required or loaded file (and also for eval’d code, but those invocations are filtered out).
208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/datadog/di/probe_manager.rb', line 208 def install_pending_line_probes(path) @lock.synchronize do @pending_probes.values.each do |probe| if probe.line? if probe.file_matches?(path) add_probe(probe) end end end end end |
#installed_probes ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
66 67 68 69 70 |
# File 'lib/datadog/di/probe_manager.rb', line 66 def installed_probes @lock.synchronize do @installed_probes end end |
#pending_probes ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
72 73 74 75 76 |
# File 'lib/datadog/di/probe_manager.rb', line 72 def pending_probes @lock.synchronize do @pending_probes end end |
#probe_executed_callback(probe:, **opts) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Entry point invoked from the instrumentation when the specfied probe is invoked (that is, either its target method is invoked, or execution reached its target file/line).
This method is responsible for queueing probe status to be sent to the backend (once per the probe’s lifetime) and a snapshot corresponding to the current invocation.
227 228 229 230 231 232 233 234 235 236 |
# File 'lib/datadog/di/probe_manager.rb', line 227 def probe_executed_callback(probe:, **opts) unless probe.emitting_notified? payload = probe_notification_builder.build_emitting(probe) probe_notifier_worker.add_status(payload) probe.emitting_notified = true end payload = probe_notification_builder.build_executed(probe, **opts) probe_notifier_worker.add_snapshot(payload) end |
#remove_other_probes(probe_ids) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Removes probes with ids other than in the specified list.
This method is meant to be invoked from remote config processor. Remote config contains the list of currently defined probes; any probes not in that list have been removed by user and should be de-instrumented from the application.
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/datadog/di/probe_manager.rb', line 141 def remove_other_probes(probe_ids) @lock.synchronize do @pending_probes.values.each do |probe| unless probe_ids.include?(probe.id) @pending_probes.delete(probe.id) end end @installed_probes.values.each do |probe| unless probe_ids.include?(probe.id) begin instrumenter.unhook(probe) # Only remove the probe from installed list if it was # successfully de-instrumented. Active probes do incur overhead # for the running application, and if the error is ephemeral # we want to try removing the probe again at the next opportunity. # # TODO give up after some time? @installed_probes.delete(probe.id) rescue => exc raise if settings.dynamic_instrumentation.internal.propagate_all_exceptions # Silence all exceptions? # TODO should we propagate here and rescue upstream? logger.warn("Error removing probe #{probe.id}: #{exc.class}: #{exc}") telemetry&.report(exc, description: "Error removing probe #{probe.id}") end end end end end |