rails_event_viewer 0.1.0 → 0.1.2
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 +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +59 -374
- data/app/assets/javascripts/rails_event_viewer/application.js +28 -0
- data/app/assets/javascripts/rails_event_viewer/chart.umd.min.js +19 -0
- data/app/assets/javascripts/rails_event_viewer/chartjs-adapter-date-fns.bundle.min.js +7 -0
- data/app/assets/javascripts/rails_event_viewer/chartkick.min.js +2 -0
- data/app/assets/stylesheets/rails_event_viewer/application.css +1 -32
- data/app/assets/stylesheets/rails_event_viewer/tailwind.css +19 -44
- data/app/controllers/rails_event_viewer/application_controller.rb +3 -9
- data/app/controllers/rails_event_viewer/event_types_controller.rb +2 -2
- data/app/controllers/rails_event_viewer/events_controller.rb +2 -2
- data/app/controllers/rails_event_viewer/groups_controller.rb +2 -2
- data/app/helpers/rails_event_viewer/application_helper.rb +4 -0
- data/app/models/rails_event_viewer/page.rb +35 -0
- data/app/views/layouts/rails_event_viewer/application.html.erb +5 -9
- data/app/views/rails_event_viewer/analytics/overview.html.erb +3 -3
- data/app/views/rails_event_viewer/dashboard/index.html.erb +3 -3
- data/app/views/rails_event_viewer/event_types/show.html.erb +3 -3
- data/app/views/rails_event_viewer/events/_event_row.html.erb +1 -1
- data/app/views/rails_event_viewer/events/index.html.erb +2 -2
- data/app/views/rails_event_viewer/events/show.html.erb +1 -1
- data/app/views/rails_event_viewer/groups/show.html.erb +1 -1
- data/app/views/rails_event_viewer/shared/_pagination.html.erb +13 -13
- data/config/routes.rb +2 -2
- data/lib/rails_event_viewer/engine.rb +7 -2
- data/lib/rails_event_viewer/version.rb +1 -1
- data/lib/rails_event_viewer.rb +0 -2
- metadata +7 -17
- data/lib/rails_event_viewer/pagy_countable.rb +0 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ef30fa1ce21c1dc5c6eaa4634707801f2d32b67ab561231865cc67c0eb2c557f
|
|
4
|
+
data.tar.gz: 9a423c1745dfb3a088137fbe4c912c069a1e65ca68afc0e4d1dac2961819baac
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3b25d996bb14c0bada11737d07a98f93fbd10b8f4781ea3163b79185e31a2b6fe30a99b74e69f63e1d6e768a1fa137124bab9f3a47da07f5940466de1933e2ff
|
|
7
|
+
data.tar.gz: 828ddff25e0930d10bc34c75ee58e789f361f6dc14c3bf24c56b463e8b1327b454cf15ce7536f7d32969716f4c6db82bd9640c3bfcada4a8c9aed072132bc158
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2 (2026-09-30)
|
|
4
|
+
|
|
5
|
+
- Removed the `pagy` dependency. Pagination is handled by a small built-in class, so host apps on any Pagy version, or none, can install the gem.
|
|
6
|
+
- Page links now keep the active filters, search query, and `per_page`. Previously every page link dropped them, so page 2 of a filtered list showed unfiltered results.
|
|
7
|
+
- A page number past the end now shows the last page instead of an empty one.
|
|
8
|
+
|
|
9
|
+
## 0.1.1 (2026-09-30)
|
|
10
|
+
|
|
11
|
+
- The dashboard now works under a strict Content Security Policy. Tailwind CSS is compiled into the gem, and Chart.js, its date adapter, and Chartkick are served from the host app with CSP nonces instead of from CDNs.
|
|
12
|
+
- Replaced inline `onclick` handlers and inline `style` attributes with data attributes and a small bundled script, so clickable rows, the payload Copy button, chart sizing, and progress bars work without `unsafe-inline`.
|
|
13
|
+
- The dashboard no longer loads the host app's importmap JavaScript.
|
|
14
|
+
- Fixed event type and group pages for names and values containing dots, such as `order.placed` or `alice@example.com`. Rails was reading the part after the dot as a format, so these pages rendered empty.
|
|
15
|
+
- Ruby 3.3 or newer is required.
|
|
16
|
+
|
|
3
17
|
## 0.1.0 (2026-09-29)
|
|
4
18
|
|
|
5
19
|
Initial release.
|
data/README.md
CHANGED
|
@@ -1,451 +1,136 @@
|
|
|
1
1
|
# RailsEventViewer
|
|
2
2
|
|
|
3
|
-
A Rails engine
|
|
4
|
-
Provides a web UI dashboard for browsing, filtering, and visualizing domain events in your Rails application.
|
|
5
|
-
|
|
6
|
-
## Features
|
|
7
|
-
|
|
8
|
-
- Web-based dashboard for viewing events
|
|
9
|
-
- Flexible storage adapters (ActiveRecord, Redis, Memory, or custom)
|
|
10
|
-
- High-performance buffered ingestion with configurable batch writes
|
|
11
|
-
- Fluent query interface for programmatic access
|
|
12
|
-
- Event filtering by name, tags, and time range
|
|
13
|
-
- Full-text search across event names and payloads
|
|
14
|
-
- Analytics with event counts over time
|
|
15
|
-
- Configurable retention and automatic cleanup
|
|
16
|
-
- HTTP Basic Auth or custom authentication
|
|
3
|
+
A Rails engine that captures events emitted with `Rails.event` and gives you a dashboard to browse, search, and chart them.
|
|
17
4
|
|
|
18
5
|
## Requirements
|
|
19
6
|
|
|
20
7
|
- Rails 8.1+
|
|
21
|
-
- Ruby 3.
|
|
8
|
+
- Ruby 3.3+
|
|
22
9
|
|
|
23
10
|
## Installation
|
|
24
11
|
|
|
25
|
-
Add this line to your application's Gemfile:
|
|
26
|
-
|
|
27
12
|
```ruby
|
|
28
13
|
gem "rails_event_viewer"
|
|
29
14
|
```
|
|
30
15
|
|
|
31
|
-
And then execute:
|
|
32
|
-
|
|
33
16
|
```bash
|
|
34
17
|
bundle install
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Run the install generator:
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
18
|
rails generate rails_event_viewer:install
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
This will:
|
|
44
|
-
- Create a migration for the events table (if using ActiveRecord adapter)
|
|
45
|
-
- Create an initializer with configuration options
|
|
46
|
-
- Mount the engine at `/events`
|
|
47
|
-
|
|
48
|
-
Run the migration:
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
rails db:migrate
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Configuration
|
|
55
|
-
|
|
56
|
-
Configure RailsEventViewer in `config/initializers/rails_event_viewer.rb`:
|
|
57
|
-
|
|
58
|
-
```ruby
|
|
59
|
-
RailsEventViewer.configure do |config|
|
|
60
|
-
# Storage adapter: :active_record, :redis, :memory, :null, or a custom class
|
|
61
|
-
config.storage_adapter = :active_record
|
|
62
|
-
|
|
63
|
-
# Adapter-specific options
|
|
64
|
-
config.adapter_options = {}
|
|
65
|
-
|
|
66
|
-
# Performance settings
|
|
67
|
-
config.async = true # Enable buffered writes
|
|
68
|
-
config.buffer_size = 100 # Flush after N events
|
|
69
|
-
config.flush_interval = 2 # Flush every N seconds
|
|
70
|
-
config.sample_rate = 1.0 # 1.0 = capture all, 0.1 = capture 10%
|
|
71
|
-
|
|
72
|
-
# Retention
|
|
73
|
-
config.retention_period = 7.days
|
|
74
|
-
|
|
75
|
-
# Filtering
|
|
76
|
-
config.captured_events = [] # Empty = capture all
|
|
77
|
-
config.ignored_events = [] # Events to never capture
|
|
78
|
-
|
|
79
|
-
# UI settings
|
|
80
|
-
config.per_page = 25
|
|
81
|
-
|
|
82
|
-
# Authentication (choose one)
|
|
83
|
-
config.http_basic_auth_enabled = true
|
|
84
|
-
config.http_basic_auth_user = ENV["RAILS_EVENT_VIEWER_USER"]
|
|
85
|
-
config.http_basic_auth_password = ENV["RAILS_EVENT_VIEWER_PASSWORD"]
|
|
86
|
-
|
|
87
|
-
# Or use custom authentication
|
|
88
|
-
config.authentication = ->(controller) {
|
|
89
|
-
controller.authenticate_user!
|
|
90
|
-
controller.current_user.admin?
|
|
91
|
-
}
|
|
92
|
-
end
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## Storage Adapters
|
|
96
|
-
|
|
97
|
-
### ActiveRecord (default)
|
|
98
|
-
|
|
99
|
-
Stores events in your database. Best for most applications with full analytics support.
|
|
100
|
-
|
|
101
|
-
```ruby
|
|
102
|
-
config.storage_adapter = :active_record
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
The install generator creates the migration for you. If you skipped the generator, copy the migration from the engine instead. Do not do both, or you will get two migrations for the same table.
|
|
106
|
-
|
|
107
|
-
```bash
|
|
108
|
-
rails rails_event_viewer:install:migrations
|
|
109
19
|
rails db:migrate
|
|
110
20
|
```
|
|
111
21
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
Stores events in Redis sorted sets. Good for high-volume, ephemeral storage where you don't need long-term persistence.
|
|
115
|
-
|
|
116
|
-
First, add the redis gem to your Gemfile:
|
|
117
|
-
|
|
118
|
-
```ruby
|
|
119
|
-
gem "redis"
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
Then configure the adapter:
|
|
123
|
-
|
|
124
|
-
```ruby
|
|
125
|
-
config.storage_adapter = :redis
|
|
126
|
-
config.adapter_options = {
|
|
127
|
-
redis_options: { url: ENV.fetch("REDIS_URL", "redis://localhost:6379") },
|
|
128
|
-
pool_size: 5,
|
|
129
|
-
key_prefix: "rails_event_viewer",
|
|
130
|
-
max_events: 10_000
|
|
131
|
-
}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
Options:
|
|
135
|
-
- `redis_options` - Hash passed to `Redis.new` whenever the pool opens a new connection (default: `{}`)
|
|
136
|
-
- `pool_size` - Number of pooled Redis connections, should match your app's thread count (default: `5`)
|
|
137
|
-
- `pool_timeout` - Seconds to wait for a free connection before raising (default: `5`)
|
|
138
|
-
- `key_prefix` - Prefix for Redis keys (default: `"rails_event_viewer"`). The adapter wraps it in braces, so keys look like `{rails_event_viewer}:events`. The braces are a Redis Cluster hash tag that keeps every key on one node, which the adapter's multi-key commands require. Do not add braces yourself.
|
|
139
|
-
- `max_events` - Maximum events to retain, older events are automatically trimmed (default: `10_000`)
|
|
140
|
-
|
|
141
|
-
The adapter uses a connection pool rather than a single shared connection, since one Redis connection is not safe to use concurrently across multiple threads. Each pooled connection is built independently from `redis_options`. Pass `pool:` with your own `ConnectionPool` for full control.
|
|
142
|
-
|
|
143
|
-
Note: Redis adapter performs analytics by fetching events into memory, which may be slower than ActiveRecord for large datasets.
|
|
144
|
-
|
|
145
|
-
Filtered queries and analytics scan every stored event. Keep `max_events` modest, around 10,000, if you filter often.
|
|
146
|
-
|
|
147
|
-
### Memory
|
|
148
|
-
|
|
149
|
-
In-memory storage for development and testing.
|
|
150
|
-
|
|
151
|
-
```ruby
|
|
152
|
-
config.storage_adapter = :memory
|
|
153
|
-
config.adapter_options = { max_events: 1000 }
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
Options:
|
|
157
|
-
- `max_events` - Maximum events to retain (default: `1000`)
|
|
158
|
-
|
|
159
|
-
Note: Events are lost when the server process restarts. Not recommended for production use.
|
|
160
|
-
|
|
161
|
-
### Null
|
|
162
|
-
|
|
163
|
-
Discards all events. Useful for disabling event capture in specific environments.
|
|
164
|
-
|
|
165
|
-
```ruby
|
|
166
|
-
config.storage_adapter = :null
|
|
167
|
-
```
|
|
22
|
+
The generator creates the migration and an initializer, and mounts the dashboard at `/events`.
|
|
168
23
|
|
|
169
|
-
|
|
24
|
+
## Usage
|
|
170
25
|
|
|
171
|
-
|
|
26
|
+
Emit events with the standard Rails API. RailsEventViewer subscribes automatically.
|
|
172
27
|
|
|
173
28
|
```ruby
|
|
174
|
-
|
|
175
|
-
adapter = ENV.fetch("EVENT_STORAGE", "active_record").to_sym
|
|
176
|
-
config.storage_adapter = adapter
|
|
177
|
-
|
|
178
|
-
case adapter
|
|
179
|
-
when :redis
|
|
180
|
-
config.adapter_options = {
|
|
181
|
-
redis_options: { url: ENV.fetch("REDIS_URL", "redis://localhost:6379") },
|
|
182
|
-
max_events: 10_000
|
|
183
|
-
}
|
|
184
|
-
when :memory
|
|
185
|
-
config.adapter_options = { max_events: 1000 }
|
|
186
|
-
end
|
|
187
|
-
end
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
Then switch adapters when starting your server:
|
|
191
|
-
|
|
192
|
-
```bash
|
|
193
|
-
# Use ActiveRecord (default)
|
|
194
|
-
bin/rails server
|
|
195
|
-
|
|
196
|
-
# Use Redis
|
|
197
|
-
EVENT_STORAGE=redis bin/rails server
|
|
198
|
-
|
|
199
|
-
# Use Memory
|
|
200
|
-
EVENT_STORAGE=memory bin/rails server
|
|
29
|
+
Rails.event.notify("order.placed", order_id: order.id, total: order.total)
|
|
201
30
|
```
|
|
202
31
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
Create your own adapter by including the `RailsEventViewer::Adapter` module:
|
|
32
|
+
Query them from code:
|
|
206
33
|
|
|
207
34
|
```ruby
|
|
208
|
-
class MyCustomAdapter
|
|
209
|
-
include RailsEventViewer::Adapter
|
|
210
|
-
|
|
211
|
-
def write_events(events)
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
def fetch_events(relation)
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
def count_events(relation)
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
def distinct_event_names
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
def find_event(id)
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
def delete_before(timestamp)
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
def distinct_group_values(key, source: :context)
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
def group_instances(key, source: :context, limit: 100)
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
def events_over_time(since:, interval:)
|
|
236
|
-
# Return { time => count }
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
def counts_by_name(limit:)
|
|
240
|
-
# Return { name => count }, highest first
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
def count_since(since)
|
|
244
|
-
# Return count of events since a time
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
config.storage_adapter = MyCustomAdapter
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
## Emitting Events
|
|
252
|
-
|
|
253
|
-
RailsEventViewer automatically subscribes to `Rails.event`. Emit events using the standard Rails API:
|
|
254
|
-
|
|
255
|
-
```ruby
|
|
256
|
-
Rails.event.notify("user.created", user_id: user.id, email: user.email)
|
|
257
|
-
|
|
258
|
-
Rails.event.notify(
|
|
259
|
-
"order.placed",
|
|
260
|
-
order_id: order.id,
|
|
261
|
-
total: order.total,
|
|
262
|
-
tags: { environment: Rails.env, priority: "high" }
|
|
263
|
-
)
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
## Programmatic Access
|
|
267
|
-
|
|
268
|
-
Use the fluent query interface to access events:
|
|
269
|
-
|
|
270
|
-
```ruby
|
|
271
|
-
# Get all events
|
|
272
|
-
RailsEventViewer.events.each { |e| puts e[:name] }
|
|
273
|
-
|
|
274
|
-
# Filter by name
|
|
275
|
-
RailsEventViewer.events.with_name("user.created").to_a
|
|
276
|
-
|
|
277
|
-
# Filter by tag
|
|
278
|
-
RailsEventViewer.events.with_tag(:environment, "production").to_a
|
|
279
|
-
|
|
280
|
-
# Time range
|
|
281
|
-
RailsEventViewer.events.since(1.hour.ago).until(30.minutes.ago).to_a
|
|
282
|
-
|
|
283
|
-
# Search
|
|
284
|
-
RailsEventViewer.events.search("user@example.com").to_a
|
|
285
|
-
|
|
286
|
-
# Combine filters
|
|
287
35
|
RailsEventViewer.events
|
|
288
36
|
.with_name("order.placed")
|
|
289
37
|
.with_tag(:priority, "high")
|
|
290
38
|
.since(1.day.ago)
|
|
291
39
|
.limit(50)
|
|
292
|
-
.
|
|
293
|
-
|
|
294
|
-
# Count
|
|
295
|
-
RailsEventViewer.events.with_name("user.created").count
|
|
296
|
-
|
|
297
|
-
# Pagination
|
|
298
|
-
RailsEventViewer.events.offset(25).limit(25).to_a
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
## Rake Tasks
|
|
302
|
-
|
|
303
|
-
```bash
|
|
304
|
-
# Show statistics
|
|
305
|
-
bin/rails rails_event_viewer:stats
|
|
306
|
-
|
|
307
|
-
# Clean up old events (based on retention_period)
|
|
308
|
-
bin/rails rails_event_viewer:cleanup
|
|
309
|
-
|
|
310
|
-
# Flush buffered events
|
|
311
|
-
bin/rails rails_event_viewer:flush
|
|
312
|
-
|
|
313
|
-
# Clear all events (interactive confirmation required)
|
|
314
|
-
bin/rails rails_event_viewer:clear
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
For automatic cleanup, add to your scheduler:
|
|
318
|
-
|
|
319
|
-
```yaml
|
|
320
|
-
# config/recurring.yml (Solid Queue)
|
|
321
|
-
production:
|
|
322
|
-
rails_event_viewer_cleanup:
|
|
323
|
-
command: "RailsEventViewer.adapter.delete_before(RailsEventViewer.retention_period.ago)"
|
|
324
|
-
schedule: every day at 3am
|
|
40
|
+
.to_a
|
|
325
41
|
```
|
|
326
42
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
```ruby
|
|
330
|
-
# config/schedule.rb (whenever gem)
|
|
331
|
-
every 1.day, at: "3:00 am" do
|
|
332
|
-
rake "rails_event_viewer:cleanup"
|
|
333
|
-
end
|
|
334
|
-
```
|
|
43
|
+
Other filters: `with_context`, `search`, `until`, `offset`, and `count`.
|
|
335
44
|
|
|
336
|
-
##
|
|
45
|
+
## Configuration
|
|
337
46
|
|
|
338
|
-
|
|
47
|
+
`config/initializers/rails_event_viewer.rb`:
|
|
339
48
|
|
|
340
49
|
```ruby
|
|
341
|
-
|
|
342
|
-
config.
|
|
50
|
+
RailsEventViewer.configure do |config|
|
|
51
|
+
config.storage_adapter = :active_record # :active_record, :redis, :memory, :null, or a class
|
|
343
52
|
|
|
344
|
-
#
|
|
345
|
-
config.
|
|
53
|
+
config.async = true # buffer writes in a background thread
|
|
54
|
+
config.buffer_size = 100 # flush after this many events
|
|
55
|
+
config.flush_interval = 2 # or after this many seconds
|
|
56
|
+
config.sample_rate = 1.0 # 0.1 keeps 10% of events
|
|
346
57
|
|
|
347
|
-
#
|
|
348
|
-
config.ignored_events
|
|
58
|
+
config.captured_events = [] # empty captures everything
|
|
59
|
+
config.ignored_events += [/^debug\./] # strings or regexes
|
|
60
|
+
config.retention_period = 7.days # used by the cleanup task
|
|
61
|
+
end
|
|
349
62
|
```
|
|
350
63
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
For high-volume applications, reduce storage by sampling if needed:
|
|
354
|
-
|
|
355
|
-
```ruby
|
|
356
|
-
# Capture only 10% of events
|
|
357
|
-
config.sample_rate = 0.1
|
|
358
|
-
```
|
|
64
|
+
Internal Rails events such as `active_record.*` and `action_controller.*` are ignored by default. Remove a pattern from `ignored_events` to capture it.
|
|
359
65
|
|
|
360
66
|
## Authentication
|
|
361
67
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
Set `RAILS_ENV` on every deployed server. If it is missing, Rails falls back to development and the dashboard is open.
|
|
365
|
-
|
|
366
|
-
### HTTP Basic Auth
|
|
68
|
+
Without authentication, the dashboard is open in development and test, and returns `403` everywhere else.
|
|
367
69
|
|
|
368
70
|
```ruby
|
|
369
71
|
config.http_basic_auth_enabled = true
|
|
370
72
|
config.http_basic_auth_user = ENV["RAILS_EVENT_VIEWER_USER"]
|
|
371
73
|
config.http_basic_auth_password = ENV["RAILS_EVENT_VIEWER_PASSWORD"]
|
|
372
|
-
```
|
|
373
74
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
```ruby
|
|
377
|
-
# Devise example
|
|
75
|
+
# or
|
|
378
76
|
config.authentication = ->(controller) {
|
|
379
77
|
controller.authenticate_user!
|
|
380
78
|
controller.current_user.admin?
|
|
381
79
|
}
|
|
382
80
|
```
|
|
383
81
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
The engine is mounted at the path specified in your routes (default `/events`):
|
|
387
|
-
|
|
388
|
-
```ruby
|
|
389
|
-
# config/routes.rb
|
|
390
|
-
mount RailsEventViewer::Engine, at: "/events"
|
|
391
|
-
```
|
|
82
|
+
Set `RAILS_ENV` on every server. Without it, Rails falls back to development and the dashboard is open.
|
|
392
83
|
|
|
393
|
-
|
|
394
|
-
- `/events` - Dashboard
|
|
395
|
-
- `/events/events` - Event list with filtering
|
|
396
|
-
- `/events/events/:id` - Single event detail
|
|
397
|
-
- `/events/analytics/overview` - Analytics dashboard
|
|
398
|
-
- `/events/groups` - Event groups
|
|
84
|
+
## Storage adapters
|
|
399
85
|
|
|
400
|
-
|
|
86
|
+
| Adapter | Use it for |
|
|
87
|
+
|---|---|
|
|
88
|
+
| `:active_record` | The default. Stores events in your database, with full analytics. |
|
|
89
|
+
| `:redis` | High volume, short-lived storage. Keeps the newest `max_events`. |
|
|
90
|
+
| `:memory` | Development and tests. Lost on restart. |
|
|
91
|
+
| `:null` | Disables storage. |
|
|
401
92
|
|
|
402
|
-
|
|
403
|
-
which is built on top of `ActiveSupport::Notifications`. This means internal Rails instrumentation
|
|
404
|
-
events (`sql.active_record`, `process_action.action_controller`, etc.) are received by the subscriber.
|
|
93
|
+
Redis needs the `redis` gem:
|
|
405
94
|
|
|
406
|
-
|
|
407
|
-
|
|
95
|
+
```ruby
|
|
96
|
+
config.storage_adapter = :redis
|
|
97
|
+
config.adapter_options = {
|
|
98
|
+
redis_options: { url: ENV["REDIS_URL"] },
|
|
99
|
+
pool_size: 5,
|
|
100
|
+
max_events: 10_000
|
|
101
|
+
}
|
|
102
|
+
```
|
|
408
103
|
|
|
409
|
-
|
|
410
|
-
- Application events emitted via `Rails.event.notify("my.event", ...)`
|
|
104
|
+
Filtered queries on Redis scan every stored event, so keep `max_events` modest.
|
|
411
105
|
|
|
412
|
-
|
|
413
|
-
- `active_record.*`, `action_controller.*`, `action_view.*`, `active_job.*`
|
|
414
|
-
- `action_mailer.*`, `active_storage.*`, `action_cable.*`, `rails.*`
|
|
415
|
-
- `turbo.*`, `cache_*`, `*.sql`, `solid_*`
|
|
106
|
+
For a custom adapter, include `RailsEventViewer::Adapter` and implement the methods it lists.
|
|
416
107
|
|
|
417
|
-
|
|
108
|
+
## Rake tasks
|
|
418
109
|
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
110
|
+
```bash
|
|
111
|
+
bin/rails rails_event_viewer:stats # counts and top events
|
|
112
|
+
bin/rails rails_event_viewer:cleanup # delete events older than retention_period
|
|
113
|
+
bin/rails rails_event_viewer:flush # write buffered events now
|
|
114
|
+
bin/rails rails_event_viewer:clear # delete everything, asks first
|
|
423
115
|
```
|
|
424
116
|
|
|
425
|
-
|
|
117
|
+
Schedule `cleanup` daily, for example with Solid Queue in `config/recurring.yml`:
|
|
426
118
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
bin/rails server
|
|
119
|
+
```yaml
|
|
120
|
+
production:
|
|
121
|
+
rails_event_viewer_cleanup:
|
|
122
|
+
command: "RailsEventViewer.adapter.delete_before(RailsEventViewer.retention_period.ago)"
|
|
123
|
+
schedule: every day at 3am
|
|
433
124
|
```
|
|
434
125
|
|
|
435
|
-
|
|
126
|
+
## Development
|
|
436
127
|
|
|
437
128
|
```bash
|
|
129
|
+
bundle install
|
|
438
130
|
bundle exec rake test
|
|
131
|
+
cd test/dummy && bin/rails db:migrate && bin/rails server
|
|
439
132
|
```
|
|
440
133
|
|
|
441
|
-
## Contributing
|
|
442
|
-
|
|
443
|
-
1. Fork it
|
|
444
|
-
2. Create your feature branch (`git checkout -b feature/my-new-feature`)
|
|
445
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
446
|
-
4. Push to the branch (`git push origin feature/my-new-feature`)
|
|
447
|
-
5. Create a Pull Request
|
|
448
|
-
|
|
449
134
|
## License
|
|
450
135
|
|
|
451
|
-
|
|
136
|
+
MIT.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
2
|
+
document.querySelectorAll("[data-event-viewer-width]").forEach((bar) => {
|
|
3
|
+
bar.style.width = `${bar.dataset.eventViewerWidth}%`
|
|
4
|
+
})
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
document.addEventListener("click", (event) => {
|
|
8
|
+
const copyButton = event.target.closest("[data-event-viewer-copy]")
|
|
9
|
+
if (copyButton) {
|
|
10
|
+
if (!navigator.clipboard || copyButton.dataset.eventViewerCopied) return
|
|
11
|
+
|
|
12
|
+
navigator.clipboard.writeText(copyButton.dataset.eventViewerCopy).then(() => {
|
|
13
|
+
const label = copyButton.textContent
|
|
14
|
+
copyButton.dataset.eventViewerCopied = "true"
|
|
15
|
+
copyButton.textContent = "Copied!"
|
|
16
|
+
setTimeout(() => {
|
|
17
|
+
copyButton.textContent = label
|
|
18
|
+
delete copyButton.dataset.eventViewerCopied
|
|
19
|
+
}, 1500)
|
|
20
|
+
})
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const row = event.target.closest("[data-event-viewer-href]")
|
|
25
|
+
if (row && !event.target.closest("a, button")) {
|
|
26
|
+
window.location = row.dataset.eventViewerHref
|
|
27
|
+
}
|
|
28
|
+
})
|