Class: DeadBro::Configuration
- Inherits:
-
Object
- Object
- DeadBro::Configuration
- Defined in:
- lib/dead_bro/configuration.rb
Constant Summary collapse
- HEARTBEAT_INTERVAL =
seconds
60- METRICS_BACKEND_SKIP_AFTER_507_SECONDS =
10 minutes
600- DEPLOY_REVISION_ENV_KEYS =
First non-empty ENV value wins for release/revision payloads and deploy grouping on the server. Order is roughly: DeadBro-native → common CI/hosting → observability tooling.
%w[ DEAD_BRO_DEPLOY_ID dead_bro_DEPLOY_ID GIT_REV GIT_COMMIT GIT_COMMIT_SHA GIT_SHA CODEBUILD_RESOLVED_SOURCE_REVISION HEROKU_SLUG_COMMIT RENDER_GIT_COMMIT DD_VERSION APP_REVISION RELEASE_VERSION SOURCE_VERSION ].freeze
- REMOTE_SETTING_KEYS =
%w[ enabled sample_rate memory_tracking_enabled allocation_tracking_enabled allocation_sample_rate explain_analyze_enabled slow_query_threshold_ms max_sql_queries_to_send max_logs_to_send excluded_controllers excluded_jobs exclusive_controllers exclusive_jobs monitor_enabled enable_db_stats enable_process_stats enable_system_stats sample_rates_by_type ].freeze
Instance Attribute Summary collapse
-
#allocation_sample_rate ⇒ Object
Remote-managed settings (overwritten by backend JSON
settingson successful API responses). -
#allocation_tracking_enabled ⇒ Object
Remote-managed settings (overwritten by backend JSON
settingson successful API responses). -
#api_key ⇒ Object
Local-only settings (not overwritten by API
settingspayloads). -
#circuit_breaker_enabled ⇒ Object
Local-only settings (not overwritten by API
settingspayloads). -
#circuit_breaker_failure_threshold ⇒ Object
Local-only settings (not overwritten by API
settingspayloads). -
#circuit_breaker_recovery_timeout ⇒ Object
Local-only settings (not overwritten by API
settingspayloads). -
#circuit_breaker_retry_timeout ⇒ Object
Local-only settings (not overwritten by API
settingspayloads). -
#disk_paths ⇒ Object
Local-only settings (not overwritten by API
settingspayloads). -
#enable_db_stats ⇒ Object
Remote-managed settings (overwritten by backend JSON
settingson successful API responses). -
#enable_process_stats ⇒ Object
Remote-managed settings (overwritten by backend JSON
settingson successful API responses). -
#enable_system_stats ⇒ Object
Remote-managed settings (overwritten by backend JSON
settingson successful API responses). -
#enabled ⇒ Object
Local-only settings (not overwritten by API
settingspayloads). -
#excluded_controllers ⇒ Object
Readers for exclusion lists.
-
#excluded_jobs ⇒ Object
Readers for exclusion lists.
-
#exclusive_controllers ⇒ Object
Readers for exclusion lists.
-
#exclusive_jobs ⇒ Object
Readers for exclusion lists.
-
#explain_analyze_enabled ⇒ Object
Local-only opt-in for EXPLAIN plan capture on slow queries.
-
#interfaces_ignore ⇒ Object
Local-only settings (not overwritten by API
settingspayloads). -
#last_heartbeat_at ⇒ Object
Last successful heartbeat HTTP response time while disabled (in-memory only).
-
#last_heartbeat_attempt_at ⇒ Object
Throttles heartbeat attempts to HEARTBEAT_INTERVAL (set when a heartbeat request is started).
-
#max_logs_to_send ⇒ Object
Remote-managed settings (overwritten by backend JSON
settingson successful API responses). -
#max_sql_queries_to_send ⇒ Object
Remote-managed settings (overwritten by backend JSON
settingson successful API responses). -
#memory_tracking_enabled ⇒ Object
Remote-managed settings (overwritten by backend JSON
settingson successful API responses). -
#monitor_enabled ⇒ Object
Remote-managed settings (overwritten by backend JSON
settingson successful API responses). -
#open_timeout ⇒ Object
Local-only settings (not overwritten by API
settingspayloads). -
#read_timeout ⇒ Object
Local-only settings (not overwritten by API
settingspayloads). -
#ruby_dev ⇒ Object
Local-only settings (not overwritten by API
settingspayloads). -
#sample_rate ⇒ Object
Remote-managed settings (overwritten by backend JSON
settingson successful API responses). -
#sample_rates_by_type ⇒ Object
Per-request-type sample rate overrides, keyed by exact "Controller#action" (or "JobClass#perform") strings — same identity ApmRequestType uses server-side.
-
#settings_received_at ⇒ Object
Tracks when we last received settings from the backend (in-memory only).
-
#skip_until ⇒ Object
After HTTP 507 Insufficient Storage from the API, skip all tracking until this UTC time (in-memory only).
-
#slow_query_threshold_ms ⇒ Object
Remote-managed settings (overwritten by backend JSON
settingson successful API responses).
Instance Method Summary collapse
-
#allocation_tracking_active? ⇒ Boolean
Per-request decision: should this request pay for the heavy allocation tracking (object-space sampling + allocation-source tracing)? Combines the on/off flag with allocation_sample_rate.
-
#apply_remote_settings(hash) ⇒ Object
Apply a settings hash received from the backend response.
-
#deploy_id ⇒ Object
Current release revision sent as
revisionon all API payloads — same semantics as#resolve_deploy_id. -
#deploy_id=(value) ⇒ Object
Overrides ENV-based resolution when set to a non-empty string (or clears override when nil/blank).
- #excluded_controller?(controller_name, action_name = nil) ⇒ Boolean
- #excluded_job?(job_class_name) ⇒ Boolean
- #exclusive_controller?(controller_name, action_name) ⇒ Boolean
- #exclusive_job?(job_class_name) ⇒ Boolean
-
#explain_analyze_active? ⇒ Boolean
EXPLAIN capture requires BOTH the local opt-in and the remote flag — locally off means off no matter what the backend sends.
- #heartbeat_due? ⇒ Boolean
-
#initialize ⇒ Configuration
constructor
A new instance of Configuration.
- #resolve_api_key ⇒ Object
- #resolve_deploy_id ⇒ Object
-
#resolve_sample_rate ⇒ Object
Returns the configured sample_rate only (no ENV fallback).
- #should_sample?(request_type_key = nil) ⇒ Boolean
- #skip_tracking? ⇒ Boolean
Constructor Details
#initialize ⇒ Configuration
Returns a new instance of Configuration.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 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 |
# File 'lib/dead_bro/configuration.rb', line 75 def initialize @api_key = nil @open_timeout = 1.0 @read_timeout = 1.0 @enabled = true @ruby_dev = false @circuit_breaker_enabled = true @circuit_breaker_failure_threshold = 3 @circuit_breaker_recovery_timeout = 60 @circuit_breaker_retry_timeout = 300 @explicit_deploy_revision = nil @disk_paths = ["/"] @interfaces_ignore = %w[lo lo0 docker0] # Remote-managed defaults (used until backend sends real values) @sample_rate = 100 @memory_tracking_enabled = true @allocation_tracking_enabled = false # When allocation tracking is on, the heavy per-request work (object-space # sampling, allocation-source tracing) runs on this % of requests so the # ~2-5ms overhead can be capped without turning the feature fully off. @allocation_sample_rate = 100 @explain_analyze_enabled = false # Remote kill switch for EXPLAIN capture. Defaults to true so a local # opt-in works against older backends that never send the key; any # response that sends explain_analyze_enabled: false turns capture off. @remote_explain_analyze_enabled = true @slow_query_threshold_ms = 500 @max_sql_queries_to_send = 500 @max_logs_to_send = 100 self.excluded_controllers = [] self.excluded_jobs = [] self.exclusive_controllers = [] self.exclusive_jobs = [] self.sample_rates_by_type = {} @monitor_enabled = false @enable_db_stats = false @enable_process_stats = false @enable_system_stats = false @settings_received_at = nil @skip_until = nil @last_heartbeat_at = nil @last_heartbeat_attempt_at = nil @settings_mutex = Mutex.new end |
Instance Attribute Details
#allocation_sample_rate ⇒ Object
Remote-managed settings (overwritten by backend JSON settings on successful API responses)
19 20 21 |
# File 'lib/dead_bro/configuration.rb', line 19 def allocation_sample_rate @allocation_sample_rate end |
#allocation_tracking_enabled ⇒ Object
Remote-managed settings (overwritten by backend JSON settings on successful API responses)
19 20 21 |
# File 'lib/dead_bro/configuration.rb', line 19 def allocation_tracking_enabled @allocation_tracking_enabled end |
#api_key ⇒ Object
Local-only settings (not overwritten by API settings payloads).
Note: enabled may still be updated remotely via apply_remote_settings when the backend
returns it in a response; local configure() values apply until the next remote update.
8 9 10 |
# File 'lib/dead_bro/configuration.rb', line 8 def api_key @api_key end |
#circuit_breaker_enabled ⇒ Object
Local-only settings (not overwritten by API settings payloads).
Note: enabled may still be updated remotely via apply_remote_settings when the backend
returns it in a response; local configure() values apply until the next remote update.
8 9 10 |
# File 'lib/dead_bro/configuration.rb', line 8 def circuit_breaker_enabled @circuit_breaker_enabled end |
#circuit_breaker_failure_threshold ⇒ Object
Local-only settings (not overwritten by API settings payloads).
Note: enabled may still be updated remotely via apply_remote_settings when the backend
returns it in a response; local configure() values apply until the next remote update.
8 9 10 |
# File 'lib/dead_bro/configuration.rb', line 8 def circuit_breaker_failure_threshold @circuit_breaker_failure_threshold end |
#circuit_breaker_recovery_timeout ⇒ Object
Local-only settings (not overwritten by API settings payloads).
Note: enabled may still be updated remotely via apply_remote_settings when the backend
returns it in a response; local configure() values apply until the next remote update.
8 9 10 |
# File 'lib/dead_bro/configuration.rb', line 8 def circuit_breaker_recovery_timeout @circuit_breaker_recovery_timeout end |
#circuit_breaker_retry_timeout ⇒ Object
Local-only settings (not overwritten by API settings payloads).
Note: enabled may still be updated remotely via apply_remote_settings when the backend
returns it in a response; local configure() values apply until the next remote update.
8 9 10 |
# File 'lib/dead_bro/configuration.rb', line 8 def circuit_breaker_retry_timeout @circuit_breaker_retry_timeout end |
#disk_paths ⇒ Object
Local-only settings (not overwritten by API settings payloads).
Note: enabled may still be updated remotely via apply_remote_settings when the backend
returns it in a response; local configure() values apply until the next remote update.
8 9 10 |
# File 'lib/dead_bro/configuration.rb', line 8 def disk_paths @disk_paths end |
#enable_db_stats ⇒ Object
Remote-managed settings (overwritten by backend JSON settings on successful API responses)
19 20 21 |
# File 'lib/dead_bro/configuration.rb', line 19 def enable_db_stats @enable_db_stats end |
#enable_process_stats ⇒ Object
Remote-managed settings (overwritten by backend JSON settings on successful API responses)
19 20 21 |
# File 'lib/dead_bro/configuration.rb', line 19 def enable_process_stats @enable_process_stats end |
#enable_system_stats ⇒ Object
Remote-managed settings (overwritten by backend JSON settings on successful API responses)
19 20 21 |
# File 'lib/dead_bro/configuration.rb', line 19 def enable_system_stats @enable_system_stats end |
#enabled ⇒ Object
Local-only settings (not overwritten by API settings payloads).
Note: enabled may still be updated remotely via apply_remote_settings when the backend
returns it in a response; local configure() values apply until the next remote update.
8 9 10 |
# File 'lib/dead_bro/configuration.rb', line 8 def enabled @enabled end |
#excluded_controllers ⇒ Object
Readers for exclusion lists. Writers are defined below so we can compile and cache the regex form once, instead of rebuilding it per request.
26 27 28 |
# File 'lib/dead_bro/configuration.rb', line 26 def excluded_controllers @excluded_controllers end |
#excluded_jobs ⇒ Object
Readers for exclusion lists. Writers are defined below so we can compile and cache the regex form once, instead of rebuilding it per request.
26 27 28 |
# File 'lib/dead_bro/configuration.rb', line 26 def excluded_jobs @excluded_jobs end |
#exclusive_controllers ⇒ Object
Readers for exclusion lists. Writers are defined below so we can compile and cache the regex form once, instead of rebuilding it per request.
26 27 28 |
# File 'lib/dead_bro/configuration.rb', line 26 def exclusive_controllers @exclusive_controllers end |
#exclusive_jobs ⇒ Object
Readers for exclusion lists. Writers are defined below so we can compile and cache the regex form once, instead of rebuilding it per request.
26 27 28 |
# File 'lib/dead_bro/configuration.rb', line 26 def exclusive_jobs @exclusive_jobs end |
#explain_analyze_enabled ⇒ Object
Local-only opt-in for EXPLAIN plan capture on slow queries. The gem runs DB statements (plan-only EXPLAIN) when this is on, so it must be enabled in the app's own config — remote settings can only turn it OFF, never on (see apply_remote_settings). Effective state: #explain_analyze_active?
16 17 18 |
# File 'lib/dead_bro/configuration.rb', line 16 def explain_analyze_enabled @explain_analyze_enabled end |
#interfaces_ignore ⇒ Object
Local-only settings (not overwritten by API settings payloads).
Note: enabled may still be updated remotely via apply_remote_settings when the backend
returns it in a response; local configure() values apply until the next remote update.
8 9 10 |
# File 'lib/dead_bro/configuration.rb', line 8 def interfaces_ignore @interfaces_ignore end |
#last_heartbeat_at ⇒ Object
Last successful heartbeat HTTP response time while disabled (in-memory only)
40 41 42 |
# File 'lib/dead_bro/configuration.rb', line 40 def last_heartbeat_at @last_heartbeat_at end |
#last_heartbeat_attempt_at ⇒ Object
Throttles heartbeat attempts to HEARTBEAT_INTERVAL (set when a heartbeat request is started)
43 44 45 |
# File 'lib/dead_bro/configuration.rb', line 43 def last_heartbeat_attempt_at @last_heartbeat_attempt_at end |
#max_logs_to_send ⇒ Object
Remote-managed settings (overwritten by backend JSON settings on successful API responses)
19 20 21 |
# File 'lib/dead_bro/configuration.rb', line 19 def max_logs_to_send @max_logs_to_send end |
#max_sql_queries_to_send ⇒ Object
Remote-managed settings (overwritten by backend JSON settings on successful API responses)
19 20 21 |
# File 'lib/dead_bro/configuration.rb', line 19 def max_sql_queries_to_send @max_sql_queries_to_send end |
#memory_tracking_enabled ⇒ Object
Remote-managed settings (overwritten by backend JSON settings on successful API responses)
19 20 21 |
# File 'lib/dead_bro/configuration.rb', line 19 def memory_tracking_enabled @memory_tracking_enabled end |
#monitor_enabled ⇒ Object
Remote-managed settings (overwritten by backend JSON settings on successful API responses)
19 20 21 |
# File 'lib/dead_bro/configuration.rb', line 19 def monitor_enabled @monitor_enabled end |
#open_timeout ⇒ Object
Local-only settings (not overwritten by API settings payloads).
Note: enabled may still be updated remotely via apply_remote_settings when the backend
returns it in a response; local configure() values apply until the next remote update.
8 9 10 |
# File 'lib/dead_bro/configuration.rb', line 8 def open_timeout @open_timeout end |
#read_timeout ⇒ Object
Local-only settings (not overwritten by API settings payloads).
Note: enabled may still be updated remotely via apply_remote_settings when the backend
returns it in a response; local configure() values apply until the next remote update.
8 9 10 |
# File 'lib/dead_bro/configuration.rb', line 8 def read_timeout @read_timeout end |
#ruby_dev ⇒ Object
Local-only settings (not overwritten by API settings payloads).
Note: enabled may still be updated remotely via apply_remote_settings when the backend
returns it in a response; local configure() values apply until the next remote update.
8 9 10 |
# File 'lib/dead_bro/configuration.rb', line 8 def ruby_dev @ruby_dev end |
#sample_rate ⇒ Object
Remote-managed settings (overwritten by backend JSON settings on successful API responses)
19 20 21 |
# File 'lib/dead_bro/configuration.rb', line 19 def sample_rate @sample_rate end |
#sample_rates_by_type ⇒ Object
Per-request-type sample rate overrides, keyed by exact "Controller#action" (or "JobClass#perform") strings — same identity ApmRequestType uses server-side.
30 31 32 |
# File 'lib/dead_bro/configuration.rb', line 30 def sample_rates_by_type @sample_rates_by_type end |
#settings_received_at ⇒ Object
Tracks when we last received settings from the backend (in-memory only)
33 34 35 |
# File 'lib/dead_bro/configuration.rb', line 33 def settings_received_at @settings_received_at end |
#skip_until ⇒ Object
After HTTP 507 Insufficient Storage from the API, skip all tracking until this UTC time (in-memory only). Cleared on the next successful API response.
37 38 39 |
# File 'lib/dead_bro/configuration.rb', line 37 def skip_until @skip_until end |
#slow_query_threshold_ms ⇒ Object
Remote-managed settings (overwritten by backend JSON settings on successful API responses)
19 20 21 |
# File 'lib/dead_bro/configuration.rb', line 19 def slow_query_threshold_ms @slow_query_threshold_ms end |
Instance Method Details
#allocation_tracking_active? ⇒ Boolean
Per-request decision: should this request pay for the heavy allocation tracking (object-space sampling + allocation-source tracing)? Combines the on/off flag with allocation_sample_rate. Decide once at request start and reuse the cached result for the matching stop, so start/stop agree.
292 293 294 295 296 297 298 299 300 |
# File 'lib/dead_bro/configuration.rb', line 292 def allocation_tracking_active? return false unless allocation_tracking_enabled rate = allocation_sample_rate.to_i return true if rate >= 100 return false if rate <= 0 rand(1..100) <= rate end |
#apply_remote_settings(hash) ⇒ Object
Apply a settings hash received from the backend response. Only known keys are applied; unknown keys are silently ignored. Serialized so concurrent HTTP threads do not interleave writes with request-thread reads.
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/dead_bro/configuration.rb', line 165 def apply_remote_settings(hash) return unless hash.is_a?(Hash) @settings_mutex.synchronize do hash.each do |key, value| k = key.to_s next unless REMOTE_SETTING_KEYS.include?(k) case k when "sample_rate", "allocation_sample_rate", "slow_query_threshold_ms", "max_sql_queries_to_send", "max_logs_to_send" send(:"#{k}=", value.to_i) when "explain_analyze_enabled" # EXPLAIN runs statements against the customer DB, so the backend # must never be able to switch it on — only off. The local opt-in # (explain_analyze_enabled) stays untouched; see #explain_analyze_active? @remote_explain_analyze_enabled = !!value when "enabled", "memory_tracking_enabled", "allocation_tracking_enabled", "monitor_enabled", "enable_db_stats", "enable_process_stats", "enable_system_stats" send(:"#{k}=", !!value) when "excluded_controllers", "excluded_jobs", "exclusive_controllers", "exclusive_jobs" send(:"#{k}=", Array(value).map(&:to_s)) when "sample_rates_by_type" send(:"#{k}=", value) end end end end |
#deploy_id ⇒ Object
Current release revision sent as revision on all API payloads — same semantics as #resolve_deploy_id.
123 124 125 |
# File 'lib/dead_bro/configuration.rb', line 123 def deploy_id resolve_deploy_id end |
#deploy_id=(value) ⇒ Object
Overrides ENV-based resolution when set to a non-empty string (or clears override when nil/blank).
128 129 130 131 |
# File 'lib/dead_bro/configuration.rb', line 128 def deploy_id=(value) s = value&.respond_to?(:to_s) ? value.to_s.strip : "" @explicit_deploy_revision = s.empty? ? nil : s end |
#excluded_controller?(controller_name, action_name = nil) ⇒ Boolean
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/dead_bro/configuration.rb', line 231 def excluded_controller?(controller_name, action_name = nil) compiled = @compiled_excluded_controllers return false if compiled.nil? || compiled.empty? if action_name target = "#{controller_name}##{action_name}" compiled.each do |entry| if entry[:has_hash] return true if match_compiled?(target, entry) elsif match_compiled?(controller_name, entry) return true end end return false end compiled.each do |entry| next if entry[:has_hash] return true if match_compiled?(controller_name, entry) end false end |
#excluded_job?(job_class_name) ⇒ Boolean
254 255 256 257 258 |
# File 'lib/dead_bro/configuration.rb', line 254 def excluded_job?(job_class_name) compiled = @compiled_excluded_jobs return false if compiled.nil? || compiled.empty? compiled.any? { |entry| match_compiled?(job_class_name, entry) } end |
#exclusive_controller?(controller_name, action_name) ⇒ Boolean
266 267 268 269 270 271 |
# File 'lib/dead_bro/configuration.rb', line 266 def exclusive_controller?(controller_name, action_name) compiled = @compiled_exclusive_controllers return true if compiled.nil? || compiled.empty? target = "#{controller_name}##{action_name}" compiled.any? { |entry| match_compiled?(target, entry) } end |
#exclusive_job?(job_class_name) ⇒ Boolean
260 261 262 263 264 |
# File 'lib/dead_bro/configuration.rb', line 260 def exclusive_job?(job_class_name) compiled = @compiled_exclusive_jobs return true if compiled.nil? || compiled.empty? compiled.any? { |entry| match_compiled?(job_class_name, entry) } end |
#explain_analyze_active? ⇒ Boolean
EXPLAIN capture requires BOTH the local opt-in and the remote flag — locally off means off no matter what the backend sends.
195 196 197 |
# File 'lib/dead_bro/configuration.rb', line 195 def explain_analyze_active? !!(@explain_analyze_enabled && @remote_explain_analyze_enabled) end |
#heartbeat_due? ⇒ Boolean
199 200 201 202 |
# File 'lib/dead_bro/configuration.rb', line 199 def heartbeat_due? return false if api_key.nil? last_heartbeat_attempt_at.nil? || (Time.now.utc - last_heartbeat_attempt_at) >= HEARTBEAT_INTERVAL end |
#resolve_api_key ⇒ Object
307 308 309 310 311 |
# File 'lib/dead_bro/configuration.rb', line 307 def resolve_api_key return @api_key unless @api_key.nil? ENV["DEAD_BRO_API_KEY"] end |
#resolve_deploy_id ⇒ Object
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/dead_bro/configuration.rb', line 211 def resolve_deploy_id explicit = @explicit_deploy_revision&.to_s&.strip return explicit unless explicit.nil? || explicit.empty? DEPLOY_REVISION_ENV_KEYS.each do |key| v = ENV[key] next unless v.respond_to?(:to_s) stripped = v.to_s.strip next if stripped.empty? return stripped end revision = read_revision_file return revision unless revision.nil? || revision.empty? DeadBro.process_deploy_id end |
#resolve_sample_rate ⇒ Object
Returns the configured sample_rate only (no ENV fallback). Use DeadBro.configure or remote settings.
303 304 305 |
# File 'lib/dead_bro/configuration.rb', line 303 def resolve_sample_rate @sample_rate end |
#should_sample?(request_type_key = nil) ⇒ Boolean
273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/dead_bro/configuration.rb', line 273 def should_sample?(request_type_key = nil) sample_rate = if request_type_key && @sample_rates_by_type.key?(request_type_key.to_s) @sample_rates_by_type[request_type_key.to_s] else resolve_sample_rate end sample_rate = 100 if sample_rate.nil? return true if sample_rate >= 100 return false if sample_rate <= 0 # Generate random number 1-100 and check if it's within sample rate rand(1..100) <= sample_rate end |
#skip_tracking? ⇒ Boolean
204 205 206 207 208 209 |
# File 'lib/dead_bro/configuration.rb', line 204 def skip_tracking? t = skip_until return false unless t Time.now.utc < t end |