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_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_buffer ⇒ RingBuffer?
Get the current buffer (for inspection)
119 120 121 122 123 124 |
# File 'lib/rails_error_dashboard/services/breadcrumb_collector.rb', line 119 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
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/rails_error_dashboard/services/breadcrumb_collector.rb', line 130 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 |