Class: RailsErrorDashboard::Services::BreadcrumbCollector
- Inherits:
-
Object
- Object
- RailsErrorDashboard::Services::BreadcrumbCollector
- Defined in:
- lib/rails_error_dashboard/services/breadcrumb_collector.rb
Overview
Pure service: Collect breadcrumbs (request activity trail) for error context
Uses a thread-local ring buffer to capture events during a request lifecycle. Thread.current isolation means no mutex/lock is needed.
SAFETY RULES (HOST_APP_SAFETY.md):
-
Every public method wrapped in rescue => e; nil
-
Never raise, never block, never allocate large objects
-
Messages truncated to 500 chars, metadata values to 200 chars
-
Ring buffer has fixed max size (no unbounded growth)
Defined Under Namespace
Classes: RingBuffer
Constant Summary collapse
- THREAD_KEY =
:red_breadcrumbs- MAX_MESSAGE_LENGTH =
500- MAX_METADATA_VALUE_LENGTH =
200- MAX_METADATA_KEYS =
10
Class Method Summary collapse
-
.add(category, message, duration_ms: nil, metadata: nil) ⇒ Object
Add a breadcrumb to the current buffer.
-
.clear_buffer ⇒ Object
Clear the ring buffer (end of request — MUST be called in ensure block).
-
.current_breadcrumbs ⇒ Array<Hash>
Non-destructive read of current breadcrumbs (does NOT clear the buffer) Used by DiagnosticDumpGenerator for on-demand snapshots.
-
.current_buffer ⇒ RingBuffer?
Get the current buffer (for inspection).
-
.filter_sensitive(breadcrumbs) ⇒ Array<Hash>
Filter sensitive data from breadcrumbs before storage Reuses existing SensitiveDataFilter — no new filter logic.
-
.harvest ⇒ Array<Hash>
Harvest breadcrumbs from the current buffer and clear it.
-
.init_buffer ⇒ Object
Initialize a new ring buffer for the current thread (start of request).
Class Method Details
.add(category, message, duration_ms: nil, metadata: nil) ⇒ Object
Add a breadcrumb to the current buffer
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 |
# File 'lib/rails_error_dashboard/services/breadcrumb_collector.rb', line 76 def self.add(category, , duration_ms: nil, metadata: nil) buffer = Thread.current[THREAD_KEY] return unless buffer # Check category filter allowed = RailsErrorDashboard.configuration. if allowed cat_sym = category.to_s.to_sym return unless allowed.include?(cat_sym) end # Build breadcrumb entry with compact keys entry = { t: (Time.now.to_f * 1000).to_i, c: category.to_s, m: () } entry[:d] = duration_ms if duration_ms entry[:meta] = () if .is_a?(Hash) buffer.add(entry) rescue => e RailsErrorDashboard::Logger.debug("[RailsErrorDashboard] BreadcrumbCollector.add failed: #{e.}") nil end |
.clear_buffer ⇒ Object
Clear the ring buffer (end of request — MUST be called in ensure block)
64 65 66 67 68 69 |
# File 'lib/rails_error_dashboard/services/breadcrumb_collector.rb', line 64 def self.clear_buffer Thread.current[THREAD_KEY] = nil rescue => e RailsErrorDashboard::Logger.debug("[RailsErrorDashboard] BreadcrumbCollector.clear_buffer failed: #{e.}") nil end |
.current_breadcrumbs ⇒ Array<Hash>
Non-destructive read of current breadcrumbs (does NOT clear the buffer) Used by DiagnosticDumpGenerator for on-demand snapshots.
120 121 122 123 124 125 126 127 |
# File 'lib/rails_error_dashboard/services/breadcrumb_collector.rb', line 120 def self. buffer = Thread.current[THREAD_KEY] return [] unless buffer buffer.to_a rescue => e RailsErrorDashboard::Logger.debug("[RailsErrorDashboard] BreadcrumbCollector.current_breadcrumbs failed: #{e.}") [] end |
.current_buffer ⇒ RingBuffer?
Get the current buffer (for inspection)
131 132 133 134 135 136 |
# File 'lib/rails_error_dashboard/services/breadcrumb_collector.rb', line 131 def self.current_buffer Thread.current[THREAD_KEY] rescue => e RailsErrorDashboard::Logger.debug("[RailsErrorDashboard] BreadcrumbCollector.current_buffer failed: #{e.}") nil end |
.filter_sensitive(breadcrumbs) ⇒ Array<Hash>
Filter sensitive data from breadcrumbs before storage Reuses existing SensitiveDataFilter — no new filter logic
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 |
# File 'lib/rails_error_dashboard/services/breadcrumb_collector.rb', line 142 def self.filter_sensitive() return [] unless .is_a?(Array) return unless RailsErrorDashboard.configuration.filter_sensitive_data filter = SensitiveDataFilter.parameter_filter return unless filter .map do |crumb| filtered = crumb.dup # Filter message (SQL queries, key=value patterns) if filtered[:m] filtered[:m] = SensitiveDataFilter.send(:filter_message, filter, filtered[:m]) end # Filter metadata values if filtered[:meta].is_a?(Hash) filtered[:meta] = filter.filter(filtered[:meta]) end filtered end rescue => e RailsErrorDashboard::Logger.debug("[RailsErrorDashboard] BreadcrumbCollector.filter_sensitive failed: #{e.}") .is_a?(Array) ? : [] end |
.harvest ⇒ Array<Hash>
Harvest breadcrumbs from the current buffer and clear it
105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/rails_error_dashboard/services/breadcrumb_collector.rb', line 105 def self.harvest buffer = Thread.current[THREAD_KEY] return [] unless buffer result = buffer.to_a buffer.clear result rescue => e RailsErrorDashboard::Logger.debug("[RailsErrorDashboard] BreadcrumbCollector.harvest failed: #{e.}") [] end |
.init_buffer ⇒ Object
Initialize a new ring buffer for the current thread (start of request)
55 56 57 58 59 60 61 |
# File 'lib/rails_error_dashboard/services/breadcrumb_collector.rb', line 55 def self.init_buffer size = RailsErrorDashboard.configuration. || 40 Thread.current[THREAD_KEY] = RingBuffer.new(size) rescue => e RailsErrorDashboard::Logger.debug("[RailsErrorDashboard] BreadcrumbCollector.init_buffer failed: #{e.}") nil end |