rails_event_viewer 0.1.3 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: df70eeee67ca7c67116c852f98659be2d9ee3803a1bd4b949d3190f0aed1d40b
4
- data.tar.gz: 0a83f0ce0b0504c7ba9823a1e2c3eaa1e9a4f79297aae5ff35e8f742c01b51ea
3
+ metadata.gz: 26f6b1798d95bf71759a16da1fd092f8d8296a4118378a8c5ce84e12848efe28
4
+ data.tar.gz: b290b0d5803b71aa2f8fda06ee160870672a4073c175c2bb6a0e0856f19b2dbc
5
5
  SHA512:
6
- metadata.gz: 23eaca12170699b79b5fd635204e24a25bfc6a8eb8bbef0605aeaea40fab2701dda65ca4101fb9d64fea1a5c57fc77a9f94d6362a90f91fea587b196b5d38667
7
- data.tar.gz: 8d981f6fe1a2673e91453ddf400be91537bd8b3686217c471e783939f9814ecc0b6240c368689c45219011e6d19ccdfed2e98ea19b61f7dcfcd2e0b5095267a9
6
+ metadata.gz: ef7fc01c18016ff1f9d48de98fcc1f42b248ac1fea588806c6dc3a409b12f75291e4165ee59ff45220b93a024f6b816d7cb2dba4395c2b5c441dd6f323a8a17a
7
+ data.tar.gz: 896dd1e41a71c0c0bd25dbdd0a376ffd7c5c0c68b8d7d6cf445521127560d95ba0d351e1d9f9f4c6c5d73ed00ae6472dbd9096ceddf2d79cd8b1dbf4e3edb37f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0 (2026-10-01)
4
+
5
+ - Pluggable buffers: `config.buffer` accepts any object implementing `RailsEventViewer::Buffer` (`push`, `drain`, `commit`, `revert`, `dead`, `size`, `after_fork`). `push` returns the new size. The in-memory buffer remains the default.
6
+ - A failed batch is reverted and retried with exponential backoff. After three failures it is written one event at a time.
7
+ - Events the adapter keeps rejecting, and events dropped by the buffer cap, are passed to `dead` instead of being lost silently.
8
+ - A failed sync write, or a failed `push` to the buffer in async mode, is now logged and re-raised to `Rails.event`, which reports it to `Rails.error` (or raises, with `raise_on_error`), instead of being swallowed.
9
+ - A banner warns when `sample_rate` is below 1.0, since timelines can have gaps.
10
+ - README now marks the gem as experimental and not yet recommended for production.
11
+
3
12
  ## 0.1.3 (2026-10-01)
4
13
 
5
14
  - The flusher thread starts on the first buffered event, not at boot, so consoles and rake tasks no longer run an idle thread.
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  A Rails engine that captures events emitted with `Rails.event` and gives you a dashboard to browse, search, and chart them.
4
4
 
5
+ > **Experimental.** This gem is in early development and not recommended for production yet. APIs, the database schema, and behavior may change between minor versions.
6
+
5
7
  ## Requirements
6
8
 
7
9
  - Rails 8.1+
@@ -65,6 +67,11 @@ With `async`, events are written by a background thread, never by your web reque
65
67
 
66
68
  Internal Rails events such as `active_record.*` and `action_controller.*` are ignored by default. Remove a pattern from `ignored_events` to capture it.
67
69
 
70
+ ## Sync vs async
71
+
72
+ - With `async = false`, each event is written inside your transaction, and a failed write is reported to `Rails.error`. In development and test, where Rails sets `raise_on_error`, it raises instead.
73
+ - With `async = true`, events wait in an in-memory buffer and are lost if the process is killed. Set `config.buffer` to your own `RailsEventViewer::Buffer`, for example backed by Redis, to keep them.
74
+
68
75
  ## Authentication
69
76
 
70
77
  Without authentication, the dashboard is open in development and test, and returns `403` everywhere else.
@@ -85,27 +92,9 @@ Set `RAILS_ENV` on every server. Without it, Rails falls back to development and
85
92
 
86
93
  ## Storage adapters
87
94
 
88
- | Adapter | Use it for |
89
- |---|---|
90
- | `:active_record` | The default. Stores events in your database, with full analytics. |
91
- | `:redis` | High volume, short-lived storage. Keeps the newest `max_events`. |
92
- | `:memory` | Development and tests. Lost on restart. |
93
- | `:null` | Disables storage. |
94
-
95
- Redis needs the `redis` gem:
96
-
97
- ```ruby
98
- config.storage_adapter = :redis
99
- config.adapter_options = {
100
- redis_options: { url: ENV["REDIS_URL"] },
101
- pool_size: 5,
102
- max_events: 10_000
103
- }
104
- ```
105
-
106
- Filtered queries on Redis scan every stored event, so keep `max_events` modest.
95
+ Set `config.storage_adapter` to `:active_record` (default), `:redis`, `:memory`, or `:null`. Redis needs the `redis` gem and takes options through `config.adapter_options`.
107
96
 
108
- For a custom adapter, include `RailsEventViewer::Adapter` and implement the methods it lists.
97
+ Filtered queries on Redis scan every stored event, so keep `max_events` (how many events Redis keeps, set in `adapter_options`) modest. For your own storage, include `RailsEventViewer::Adapter`.
109
98
 
110
99
  ## Rake tasks
111
100
 
@@ -33,6 +33,14 @@
33
33
  </div>
34
34
  <% end %>
35
35
 
36
+ <% if RailsEventViewer.sample_rate.to_f < 1.0 %>
37
+ <div class="mb-4 rounded-md bg-yellow-50 border border-yellow-200 p-4">
38
+ <p class="text-sm font-medium text-yellow-800">
39
+ Sampling is on: only <%= number_to_percentage(RailsEventViewer.sample_rate.to_f * 100, precision: 3, significant: true, strip_insignificant_zeros: true) %> of events are recorded, so timelines can have gaps.
40
+ </p>
41
+ </div>
42
+ <% end %>
43
+
36
44
  <%= yield %>
37
45
  </div>
38
46
  </main>
@@ -40,6 +40,11 @@ RailsEventViewer.configure do |config|
40
40
  # Flush interval in seconds (flushes even if buffer isn't full)
41
41
  # config.flush_interval = 2
42
42
 
43
+ # Buffer used when async is true. The default is in memory, so buffered events
44
+ # are lost if the process is killed. You can write your own buffers (see RailsEventViewer::Buffer)
45
+ # to persist them, for example in Redis.
46
+ # config.buffer = MyRedisBuffer.new
47
+
43
48
  # Sample rate: 1.0 = capture all events, 0.1 = capture 10% of events
44
49
  # Useful for high-volume production environments
45
50
  # config.sample_rate = 1.0
@@ -0,0 +1,79 @@
1
+ module RailsEventViewer
2
+ class BatchWriter
3
+ include Logging
4
+
5
+ MAX_ATTEMPTS = 3
6
+ LOGGED_EVENTS = 10
7
+
8
+ attr_reader :failed_attempts
9
+
10
+ def initialize(buffer)
11
+ @buffer = buffer
12
+ reset
13
+ end
14
+
15
+ def reset
16
+ @failed_attempts = 0
17
+ @strikes = Hash.new(0)
18
+ end
19
+
20
+ def write(events)
21
+ RailsEventViewer.adapter.write_events(events)
22
+ @buffer.commit(events)
23
+ forget(events)
24
+ @failed_attempts = 0
25
+ true
26
+ rescue => e
27
+ log_error("[RailsEventViewer] Failed to write #{events.size} events: #{e.message}")
28
+ @failed_attempts += 1
29
+ return write_individually(events) if @failed_attempts >= MAX_ATTEMPTS
30
+
31
+ @buffer.revert(events)
32
+ false
33
+ end
34
+
35
+ def forget(events)
36
+ events.each { |event| @strikes.delete(event) } unless @strikes.empty?
37
+ end
38
+
39
+ private
40
+
41
+ def write_individually(events)
42
+ written = []
43
+ rejected = []
44
+ events.each do |event|
45
+ (write_one(event) ? written : rejected) << event
46
+ break if written.empty? && rejected.size >= MAX_ATTEMPTS
47
+ end
48
+
49
+ if written.empty?
50
+ @buffer.revert(events)
51
+ return false
52
+ end
53
+
54
+ rejected.each { |event| @strikes[event] += 1 }
55
+ dead, retry_later = rejected.partition { |event| @strikes[event] >= MAX_ATTEMPTS }
56
+ forget(written + dead)
57
+
58
+ @buffer.commit(written)
59
+ @buffer.revert(retry_later) if retry_later.any?
60
+ give_up(dead) if dead.any?
61
+ @failed_attempts = retry_later.any? ? MAX_ATTEMPTS - 1 : 0
62
+ true
63
+ end
64
+
65
+ def give_up(events)
66
+ names = events.first(LOGGED_EVENTS).map { |event| event[:name] }.join(", ")
67
+ log_error("[RailsEventViewer] Gave up on #{events.size} events the adapter rejected #{MAX_ATTEMPTS} times (#{@last_rejection}): #{names}")
68
+ @buffer.dead(events)
69
+ end
70
+
71
+ def write_one(event)
72
+ RailsEventViewer.adapter.write_events([event])
73
+ true
74
+ rescue => e
75
+ @last_rejection = e.message
76
+ false
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,28 @@
1
+ module RailsEventViewer
2
+ module Buffer
3
+ def push(entry)
4
+ raise NotImplementedError, "#{self.class} must implement #push"
5
+ end
6
+
7
+ def drain(limit)
8
+ raise NotImplementedError, "#{self.class} must implement #drain"
9
+ end
10
+
11
+ def commit(entries)
12
+ end
13
+
14
+ def revert(entries)
15
+ end
16
+
17
+ def dead(entries)
18
+ commit(entries)
19
+ end
20
+
21
+ def size
22
+ raise NotImplementedError, "#{self.class} must implement #size"
23
+ end
24
+
25
+ def after_fork
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,36 @@
1
+ module RailsEventViewer
2
+ module Buffers
3
+ class Memory
4
+ include Buffer
5
+
6
+ def initialize
7
+ @queue = Queue.new
8
+ end
9
+
10
+ def push(entry)
11
+ @queue << entry
12
+ @queue.size
13
+ end
14
+
15
+ def drain(limit)
16
+ entries = []
17
+ limit.times { entries << @queue.pop(true) }
18
+ entries
19
+ rescue ThreadError
20
+ entries
21
+ end
22
+
23
+ def revert(entries)
24
+ entries.each { |entry| @queue << entry }
25
+ end
26
+
27
+ def size
28
+ @queue.size
29
+ end
30
+
31
+ def after_fork
32
+ @queue = Queue.new
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,150 @@
1
+ module RailsEventViewer
2
+ class Flusher
3
+ include Logging
4
+
5
+ SHUTDOWN_TIMEOUT_SECONDS = 5
6
+ MAX_BUFFER_MULTIPLIER = 10
7
+
8
+ def initialize(buffer)
9
+ @buffer = buffer
10
+ @writer = BatchWriter.new(buffer)
11
+ @flush_requests = Queue.new
12
+ @mutex = Mutex.new
13
+ @thread = nil
14
+ @shutdown_requested = false
15
+ @dropped_count = 0
16
+ @pid = Process.pid
17
+ end
18
+
19
+ def push(entry)
20
+ handle_fork_if_needed
21
+
22
+ size = @buffer.push(entry)
23
+ return flush! if shutdown_requested?
24
+
25
+ ensure_thread!
26
+ drop_oldest_events if size > max_buffer_size
27
+ request_flush if size >= RailsEventViewer.buffer_size
28
+ end
29
+
30
+ def flush!
31
+ events = @buffer.drain([@buffer.size, max_buffer_size].min)
32
+ return true if events.empty?
33
+
34
+ written = @writer.write(events)
35
+ report_dropped_events if written
36
+ written
37
+ rescue => e
38
+ log_error("[RailsEventViewer] Failed to flush: #{e.message}")
39
+ false
40
+ end
41
+
42
+ def stop!
43
+ @mutex.synchronize { @shutdown_requested = true }
44
+
45
+ if @thread&.alive?
46
+ @flush_requests << true
47
+ @thread.join(SHUTDOWN_TIMEOUT_SECONDS)
48
+ end
49
+
50
+ @mutex.synchronize { @thread = nil }
51
+
52
+ loop do
53
+ remaining = @buffer.size
54
+ break if remaining.zero?
55
+
56
+ flush!
57
+ break if @buffer.size >= remaining
58
+ end
59
+ end
60
+
61
+ def running?
62
+ @thread&.alive? || false
63
+ end
64
+
65
+ def size
66
+ @buffer.size
67
+ end
68
+
69
+ private
70
+
71
+ def handle_fork_if_needed
72
+ @mutex.synchronize do
73
+ if @pid != Process.pid
74
+ @pid = Process.pid
75
+ @buffer.after_fork
76
+ @writer.reset
77
+ @flush_requests = Queue.new
78
+ @thread = nil
79
+ @shutdown_requested = false
80
+ @dropped_count = 0
81
+ end
82
+ end
83
+ end
84
+
85
+ def max_buffer_size
86
+ RailsEventViewer.buffer_size * MAX_BUFFER_MULTIPLIER
87
+ end
88
+
89
+ def drop_oldest_events
90
+ while @buffer.size > max_buffer_size
91
+ dropped = @buffer.drain(1)
92
+ break if dropped.empty?
93
+
94
+ @buffer.dead(dropped)
95
+ @writer.forget(dropped)
96
+ @mutex.synchronize { @dropped_count += 1 }
97
+ end
98
+ end
99
+
100
+ def report_dropped_events
101
+ dropped = @mutex.synchronize { @dropped_count.tap { @dropped_count = 0 } }
102
+ return if dropped.zero?
103
+
104
+ effective_logger&.warn("[RailsEventViewer] Dropped #{dropped} events because the buffer was full")
105
+ end
106
+
107
+ def request_flush
108
+ @flush_requests << true if @flush_requests.empty? && @writer.failed_attempts.zero?
109
+ end
110
+
111
+ def ensure_thread!
112
+ @mutex.synchronize do
113
+ return if @thread&.alive?
114
+ return if @shutdown_requested
115
+
116
+ @thread = Thread.new { run_loop }
117
+ end
118
+ end
119
+
120
+ def run_loop
121
+ effective_logger&.debug("[RailsEventViewer] Flusher thread started (PID: #{Process.pid})")
122
+
123
+ until shutdown_requested?
124
+ begin
125
+ wait_for_flush
126
+ loop do
127
+ break unless flush!
128
+ break if @buffer.size < RailsEventViewer.buffer_size || shutdown_requested?
129
+ end
130
+ rescue => e
131
+ log_error("[RailsEventViewer] Flusher error: #{e.message}")
132
+ end
133
+ end
134
+
135
+ effective_logger&.debug("[RailsEventViewer] Flusher thread stopped (PID: #{Process.pid})")
136
+ end
137
+
138
+ def wait_for_flush
139
+ backoff = 2**[@writer.failed_attempts, BatchWriter::MAX_ATTEMPTS].min
140
+ @flush_requests.clear unless @writer.failed_attempts.zero?
141
+ return if shutdown_requested?
142
+
143
+ @flush_requests.pop(timeout: RailsEventViewer.flush_interval * backoff)
144
+ end
145
+
146
+ def shutdown_requested?
147
+ @mutex.synchronize { @shutdown_requested }
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,19 @@
1
+ module RailsEventViewer
2
+ module Logging
3
+ private
4
+
5
+ def log_error(message)
6
+ logger = effective_logger
7
+
8
+ if logger
9
+ logger.error(message)
10
+ else
11
+ warn message
12
+ end
13
+ end
14
+
15
+ def effective_logger
16
+ RailsEventViewer.logger || (defined?(Rails) && Rails.logger)
17
+ end
18
+ end
19
+ end
@@ -2,174 +2,35 @@
2
2
 
3
3
  module RailsEventViewer
4
4
  class Subscriber
5
+ include Logging
6
+
5
7
  NANOSECONDS_PER_SECOND = 1_000_000_000.0
6
- SHUTDOWN_TIMEOUT_SECONDS = 5
7
- MAX_BUFFER_MULTIPLIER = 10
8
+
9
+ delegate :flush!, :stop!, :running?, to: :@flusher
10
+ delegate :size, to: :@flusher, prefix: :buffer
8
11
 
9
12
  def initialize
10
- @buffer = Queue.new
11
- @flush_requests = Queue.new
12
- @mutex = Mutex.new
13
- @flusher_thread = nil
14
- @shutdown_requested = false
15
- @dropped_count = 0
16
- @pid = Process.pid
13
+ @flusher = Flusher.new(RailsEventViewer.buffer)
17
14
  end
18
15
 
19
16
  def emit(event)
20
- handle_fork_if_needed
21
-
22
17
  return unless should_capture?(event)
23
18
  return if sampled_out?
24
19
 
25
20
  entry = build_entry(event)
26
21
 
27
22
  if RailsEventViewer.async
28
- buffer_event(entry)
23
+ @flusher.push(entry)
29
24
  else
30
- write_immediately([entry])
25
+ RailsEventViewer.adapter.write_events([entry])
31
26
  end
32
27
  rescue => e
33
28
  handle_error(e, event)
34
- end
35
-
36
- def flush!
37
- return if @buffer.empty?
38
-
39
- events_to_write = drain_buffer
40
-
41
- return if events_to_write.empty?
42
-
43
- write_immediately(events_to_write)
44
- report_dropped_events
45
- end
46
-
47
- def stop!
48
- @mutex.synchronize do
49
- @shutdown_requested = true
50
- end
51
-
52
- if @flusher_thread&.alive?
53
- @flush_requests << true
54
- @flusher_thread.join(SHUTDOWN_TIMEOUT_SECONDS)
55
- end
56
-
57
- @mutex.synchronize do
58
- @flusher_thread = nil
59
- end
60
-
61
- flush! until @buffer.empty?
62
- end
63
-
64
- def running?
65
- @flusher_thread&.alive? || false
66
- end
67
-
68
- def buffer_size
69
- @buffer.size
29
+ raise
70
30
  end
71
31
 
72
32
  private
73
33
 
74
- def handle_fork_if_needed
75
- # Threads don't survive forks, so we need to restart the flusher
76
- @mutex.synchronize do
77
- if @pid != Process.pid
78
- @pid = Process.pid
79
- @buffer = Queue.new
80
- @flush_requests = Queue.new
81
- @flusher_thread = nil
82
- @shutdown_requested = false
83
- @dropped_count = 0
84
- end
85
- end
86
- end
87
-
88
- def drain_buffer
89
- events = []
90
- [@buffer.size, max_buffer_size].min.times { events << @buffer.pop(true) }
91
- events
92
- rescue ThreadError
93
- events
94
- end
95
-
96
- def buffer_event(entry)
97
- @buffer << entry
98
- return flush! if shutdown_requested?
99
-
100
- ensure_flusher_thread!
101
- drop_oldest_events if @buffer.size > max_buffer_size
102
- request_flush if @buffer.size >= RailsEventViewer.buffer_size
103
- end
104
-
105
- def max_buffer_size
106
- RailsEventViewer.buffer_size * MAX_BUFFER_MULTIPLIER
107
- end
108
-
109
- def drop_oldest_events
110
- dropped = 0
111
- while @buffer.size > max_buffer_size
112
- @buffer.pop(true)
113
- dropped += 1
114
- end
115
- rescue ThreadError
116
- nil
117
- ensure
118
- @mutex.synchronize { @dropped_count += dropped } if dropped.positive?
119
- end
120
-
121
- def report_dropped_events
122
- dropped = @mutex.synchronize { @dropped_count.tap { @dropped_count = 0 } }
123
- return if dropped.zero?
124
-
125
- effective_logger&.warn("[RailsEventViewer] Dropped #{dropped} events because the buffer was full")
126
- end
127
-
128
- def request_flush
129
- @flush_requests << true if @flush_requests.empty?
130
- end
131
-
132
- def ensure_flusher_thread!
133
- return unless RailsEventViewer.async
134
-
135
- @mutex.synchronize do
136
- return if @flusher_thread&.alive?
137
- return if @shutdown_requested
138
-
139
- @flusher_thread = Thread.new { run_flusher_loop }
140
- end
141
- end
142
-
143
- def run_flusher_loop
144
- effective_logger&.debug("[RailsEventViewer] Flusher thread started (PID: #{Process.pid})")
145
-
146
- until shutdown_requested?
147
- begin
148
- @flush_requests.pop(timeout: RailsEventViewer.flush_interval)
149
- loop do
150
- flush!
151
- break if @buffer.size < RailsEventViewer.buffer_size || shutdown_requested?
152
- end
153
- rescue => e
154
- log_error("[RailsEventViewer] Flusher error: #{e.message}")
155
- end
156
- end
157
-
158
- effective_logger&.debug("[RailsEventViewer] Flusher thread stopped (PID: #{Process.pid})")
159
- end
160
-
161
- def shutdown_requested?
162
- @mutex.synchronize { @shutdown_requested }
163
- end
164
-
165
- def write_immediately(events)
166
- return if events.blank?
167
-
168
- RailsEventViewer.adapter.write_events(events)
169
- rescue => e
170
- log_error("[RailsEventViewer] Failed to write #{events.size} events: #{e.message}")
171
- end
172
-
173
34
  def should_capture?(event)
174
35
  return false unless event.is_a?(Hash)
175
36
  return false if event[:name].blank?
@@ -251,19 +112,5 @@ module RailsEventViewer
251
112
  def handle_error(error, event)
252
113
  log_error("[RailsEventViewer] Failed to capture event '#{event[:name]}': #{error.message}")
253
114
  end
254
-
255
- def log_error(message)
256
- logger = effective_logger
257
-
258
- if logger
259
- logger.error(message)
260
- else
261
- warn message
262
- end
263
- end
264
-
265
- def effective_logger
266
- RailsEventViewer.logger || (defined?(Rails) && Rails.logger)
267
- end
268
115
  end
269
116
  end
@@ -1,3 +1,3 @@
1
1
  module RailsEventViewer
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -3,6 +3,8 @@ require "groupdate"
3
3
 
4
4
  require "rails_event_viewer/version"
5
5
  require "rails_event_viewer/engine"
6
+ require "rails_event_viewer/buffer"
7
+ require "rails_event_viewer/buffers/memory"
6
8
 
7
9
  module RailsEventViewer
8
10
  mattr_accessor :storage_adapter, default: :active_record
@@ -12,6 +14,7 @@ module RailsEventViewer
12
14
  mattr_accessor :buffer_size, default: 100 # Flush after N events
13
15
  mattr_accessor :flush_interval, default: 2 # Flush every N seconds
14
16
  mattr_accessor :sample_rate, default: 1.0 # 1.0 = 100%, 0.1 = 10%
17
+ mattr_accessor :buffer, default: Buffers::Memory.new
15
18
 
16
19
  mattr_accessor :retention_period, default: 7.days
17
20
  mattr_accessor :captured_events, default: [] # Empty = capture all
@@ -115,6 +118,9 @@ end
115
118
 
116
119
  require "rails_event_viewer/adapter"
117
120
  require "rails_event_viewer/events_relation"
121
+ require "rails_event_viewer/logging"
122
+ require "rails_event_viewer/batch_writer"
123
+ require "rails_event_viewer/flusher"
118
124
  require "rails_event_viewer/subscriber"
119
125
  require "rails_event_viewer/json_query"
120
126
  require "rails_event_viewer/time_utils"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_event_viewer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keshav Biswa
@@ -107,9 +107,14 @@ files:
107
107
  - lib/rails_event_viewer/adapters/memory.rb
108
108
  - lib/rails_event_viewer/adapters/null.rb
109
109
  - lib/rails_event_viewer/adapters/redis.rb
110
+ - lib/rails_event_viewer/batch_writer.rb
111
+ - lib/rails_event_viewer/buffer.rb
112
+ - lib/rails_event_viewer/buffers/memory.rb
110
113
  - lib/rails_event_viewer/engine.rb
111
114
  - lib/rails_event_viewer/events_relation.rb
115
+ - lib/rails_event_viewer/flusher.rb
112
116
  - lib/rails_event_viewer/json_query.rb
117
+ - lib/rails_event_viewer/logging.rb
113
118
  - lib/rails_event_viewer/subscriber.rb
114
119
  - lib/rails_event_viewer/time_utils.rb
115
120
  - lib/rails_event_viewer/version.rb