rails_event_viewer 0.1.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 +7 -0
- data/CHANGELOG.md +14 -0
- data/MIT-LICENSE +20 -0
- data/README.md +451 -0
- data/Rakefile +16 -0
- data/app/assets/stylesheets/rails_event_viewer/application.css +32 -0
- data/app/assets/stylesheets/rails_event_viewer/tailwind.css +54 -0
- data/app/controllers/rails_event_viewer/analytics_controller.rb +78 -0
- data/app/controllers/rails_event_viewer/application_controller.rb +84 -0
- data/app/controllers/rails_event_viewer/dashboard_controller.rb +18 -0
- data/app/controllers/rails_event_viewer/event_types_controller.rb +16 -0
- data/app/controllers/rails_event_viewer/events_controller.rb +72 -0
- data/app/controllers/rails_event_viewer/groups_controller.rb +52 -0
- data/app/helpers/rails_event_viewer/application_helper.rb +163 -0
- data/app/models/rails_event_viewer/application_record.rb +5 -0
- data/app/models/rails_event_viewer/entry.rb +34 -0
- data/app/views/layouts/rails_event_viewer/application.html.erb +45 -0
- data/app/views/rails_event_viewer/analytics/overview.html.erb +134 -0
- data/app/views/rails_event_viewer/dashboard/index.html.erb +113 -0
- data/app/views/rails_event_viewer/event_types/index.html.erb +59 -0
- data/app/views/rails_event_viewer/event_types/show.html.erb +102 -0
- data/app/views/rails_event_viewer/events/_event_row.html.erb +42 -0
- data/app/views/rails_event_viewer/events/_filters_sidebar.html.erb +66 -0
- data/app/views/rails_event_viewer/events/index.html.erb +65 -0
- data/app/views/rails_event_viewer/events/show.html.erb +155 -0
- data/app/views/rails_event_viewer/groups/index.html.erb +115 -0
- data/app/views/rails_event_viewer/groups/show.html.erb +134 -0
- data/app/views/rails_event_viewer/shared/_navigation.html.erb +47 -0
- data/app/views/rails_event_viewer/shared/_pagination.html.erb +60 -0
- data/app/views/rails_event_viewer/shared/_stat_card.html.erb +36 -0
- data/config/routes.rb +18 -0
- data/db/migrate/20241208000001_create_rails_event_viewer_entries.rb +36 -0
- data/lib/generators/rails_event_viewer/install/USAGE +33 -0
- data/lib/generators/rails_event_viewer/install/install_generator.rb +86 -0
- data/lib/generators/rails_event_viewer/install/templates/create_rails_event_viewer_entries.rb.erb +35 -0
- data/lib/generators/rails_event_viewer/install/templates/initializer.rb.erb +90 -0
- data/lib/rails_event_viewer/adapter.rb +93 -0
- data/lib/rails_event_viewer/adapters/active_record.rb +227 -0
- data/lib/rails_event_viewer/adapters/concerns/in_memory_filtering.rb +136 -0
- data/lib/rails_event_viewer/adapters/memory.rb +128 -0
- data/lib/rails_event_viewer/adapters/null.rb +63 -0
- data/lib/rails_event_viewer/adapters/redis.rb +213 -0
- data/lib/rails_event_viewer/engine.rb +24 -0
- data/lib/rails_event_viewer/events_relation.rb +130 -0
- data/lib/rails_event_viewer/json_query.rb +111 -0
- data/lib/rails_event_viewer/pagy_countable.rb +31 -0
- data/lib/rails_event_viewer/subscriber.rb +255 -0
- data/lib/rails_event_viewer/time_utils.rb +24 -0
- data/lib/rails_event_viewer/version.rb +3 -0
- data/lib/rails_event_viewer.rb +122 -0
- data/lib/tasks/rails_event_viewer_tasks.rake +81 -0
- metadata +151 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsEventViewer
|
|
4
|
+
class Subscriber
|
|
5
|
+
NANOSECONDS_PER_SECOND = 1_000_000_000.0
|
|
6
|
+
SHUTDOWN_TIMEOUT_SECONDS = 5
|
|
7
|
+
ACTION_CABLE_CHANNEL = "rails_event_viewer:events"
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@buffer = Queue.new
|
|
11
|
+
@mutex = Mutex.new
|
|
12
|
+
@flusher_thread = nil
|
|
13
|
+
@shutdown_requested = false
|
|
14
|
+
@pid = Process.pid
|
|
15
|
+
|
|
16
|
+
ensure_flusher_thread!
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def emit(event)
|
|
20
|
+
handle_fork_if_needed
|
|
21
|
+
|
|
22
|
+
return unless should_capture?(event)
|
|
23
|
+
return if sampled_out?
|
|
24
|
+
|
|
25
|
+
entry = build_entry(event)
|
|
26
|
+
|
|
27
|
+
if RailsEventViewer.async
|
|
28
|
+
buffer_event(entry)
|
|
29
|
+
else
|
|
30
|
+
write_immediately([entry])
|
|
31
|
+
end
|
|
32
|
+
rescue => e
|
|
33
|
+
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
|
+
broadcast_events(events_to_write)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def stop!
|
|
48
|
+
@mutex.synchronize do
|
|
49
|
+
@shutdown_requested = true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
if @flusher_thread&.alive?
|
|
53
|
+
@flusher_thread.wakeup rescue nil
|
|
54
|
+
@flusher_thread.join(SHUTDOWN_TIMEOUT_SECONDS)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
@mutex.synchronize do
|
|
58
|
+
@flusher_thread = nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
flush!
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def running?
|
|
65
|
+
@flusher_thread&.alive? || false
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def buffer_size
|
|
69
|
+
@buffer.size
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
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
|
+
@flusher_thread = nil
|
|
81
|
+
@shutdown_requested = false
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
ensure_flusher_thread!
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def drain_buffer
|
|
88
|
+
events = []
|
|
89
|
+
loop { events << @buffer.pop(true) }
|
|
90
|
+
rescue ThreadError
|
|
91
|
+
events
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def buffer_event(entry)
|
|
95
|
+
@buffer << entry
|
|
96
|
+
ensure_flusher_thread!
|
|
97
|
+
|
|
98
|
+
flush! if @buffer.size >= RailsEventViewer.buffer_size
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def ensure_flusher_thread!
|
|
102
|
+
return unless RailsEventViewer.async
|
|
103
|
+
|
|
104
|
+
@mutex.synchronize do
|
|
105
|
+
return if @flusher_thread&.alive?
|
|
106
|
+
return if @shutdown_requested
|
|
107
|
+
|
|
108
|
+
@flusher_thread = Thread.new { run_flusher_loop }
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def run_flusher_loop
|
|
113
|
+
log_info("[RailsEventViewer] Flusher thread started (PID: #{Process.pid})")
|
|
114
|
+
|
|
115
|
+
until shutdown_requested?
|
|
116
|
+
begin
|
|
117
|
+
sleep(RailsEventViewer.flush_interval)
|
|
118
|
+
flush! unless @buffer.empty?
|
|
119
|
+
rescue => e
|
|
120
|
+
log_error("[RailsEventViewer] Flusher error: #{e.message}")
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
log_info("[RailsEventViewer] Flusher thread stopped (PID: #{Process.pid})")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def shutdown_requested?
|
|
128
|
+
@mutex.synchronize { @shutdown_requested }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def write_immediately(events)
|
|
132
|
+
return if events.blank?
|
|
133
|
+
|
|
134
|
+
RailsEventViewer.adapter.write_events(events)
|
|
135
|
+
rescue => e
|
|
136
|
+
log_error("[RailsEventViewer] Failed to write #{events.size} events: #{e.message}")
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def broadcast_events(events)
|
|
140
|
+
return unless defined?(ActionCable)
|
|
141
|
+
return unless events.any?
|
|
142
|
+
|
|
143
|
+
ActionCable.server.broadcast(
|
|
144
|
+
ACTION_CABLE_CHANNEL,
|
|
145
|
+
{
|
|
146
|
+
action: "new_events",
|
|
147
|
+
count: events.size,
|
|
148
|
+
events: events.map { |e| { name: e[:name], occurred_at: e[:occurred_at]&.iso8601 } }
|
|
149
|
+
}
|
|
150
|
+
)
|
|
151
|
+
rescue => e
|
|
152
|
+
log_error("[RailsEventViewer] Broadcast error: #{e.message}")
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def should_capture?(event)
|
|
156
|
+
return false unless event.is_a?(Hash)
|
|
157
|
+
return false if event[:name].blank?
|
|
158
|
+
|
|
159
|
+
name = event[:name].to_s
|
|
160
|
+
return false if ignored?(name)
|
|
161
|
+
return true if RailsEventViewer.captured_events.empty?
|
|
162
|
+
|
|
163
|
+
captured?(name)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def ignored?(name)
|
|
167
|
+
RailsEventViewer.ignored_events.any? do |pattern|
|
|
168
|
+
matches_pattern?(name, pattern)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def captured?(name)
|
|
173
|
+
RailsEventViewer.captured_events.any? do |pattern|
|
|
174
|
+
matches_pattern?(name, pattern)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def matches_pattern?(name, pattern)
|
|
179
|
+
case pattern
|
|
180
|
+
when String
|
|
181
|
+
name == pattern
|
|
182
|
+
when Regexp
|
|
183
|
+
name.match?(pattern)
|
|
184
|
+
else
|
|
185
|
+
false
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def sampled_out?
|
|
190
|
+
rate = RailsEventViewer.sample_rate
|
|
191
|
+
return false if rate >= 1.0
|
|
192
|
+
|
|
193
|
+
rand > rate
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def build_entry(event)
|
|
197
|
+
timestamp = event[:timestamp]
|
|
198
|
+
occurred_at = if timestamp
|
|
199
|
+
Time.at(timestamp / NANOSECONDS_PER_SECOND)
|
|
200
|
+
else
|
|
201
|
+
Time.current
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
{
|
|
205
|
+
name: event[:name],
|
|
206
|
+
payload: serialize_payload(event[:payload]),
|
|
207
|
+
tags: event[:tags] || {},
|
|
208
|
+
context: event[:context] || {},
|
|
209
|
+
source_file: event.dig(:source_location, :filepath),
|
|
210
|
+
source_line: event.dig(:source_location, :lineno),
|
|
211
|
+
source_label: event.dig(:source_location, :label),
|
|
212
|
+
occurred_at: occurred_at
|
|
213
|
+
}
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def serialize_payload(payload)
|
|
217
|
+
case payload
|
|
218
|
+
when Hash
|
|
219
|
+
payload
|
|
220
|
+
when nil
|
|
221
|
+
{}
|
|
222
|
+
else
|
|
223
|
+
if payload.respond_to?(:to_h)
|
|
224
|
+
payload.to_h
|
|
225
|
+
elsif payload.respond_to?(:as_json)
|
|
226
|
+
payload.as_json
|
|
227
|
+
else
|
|
228
|
+
{ value: payload.to_s }
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def handle_error(error, event)
|
|
234
|
+
log_error("[RailsEventViewer] Failed to capture event '#{event[:name]}': #{error.message}")
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def log_info(message)
|
|
238
|
+
effective_logger&.info(message)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def log_error(message)
|
|
242
|
+
logger = effective_logger
|
|
243
|
+
|
|
244
|
+
if logger
|
|
245
|
+
logger.error(message)
|
|
246
|
+
else
|
|
247
|
+
warn message
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def effective_logger
|
|
252
|
+
RailsEventViewer.logger || (defined?(Rails) && Rails.logger)
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsEventViewer
|
|
4
|
+
module TimeUtils
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
def truncate_to_interval(time, interval)
|
|
8
|
+
return nil unless time
|
|
9
|
+
|
|
10
|
+
case interval
|
|
11
|
+
when :minute
|
|
12
|
+
time.beginning_of_minute
|
|
13
|
+
when :hour
|
|
14
|
+
time.beginning_of_hour
|
|
15
|
+
when :day
|
|
16
|
+
time.beginning_of_day
|
|
17
|
+
when :week
|
|
18
|
+
time.beginning_of_week
|
|
19
|
+
else
|
|
20
|
+
time.beginning_of_hour
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
require "pagy"
|
|
2
|
+
require "chartkick"
|
|
3
|
+
require "groupdate"
|
|
4
|
+
|
|
5
|
+
require "rails_event_viewer/version"
|
|
6
|
+
require "rails_event_viewer/engine"
|
|
7
|
+
|
|
8
|
+
module RailsEventViewer
|
|
9
|
+
mattr_accessor :storage_adapter, default: :active_record
|
|
10
|
+
mattr_accessor :adapter_options, default: {}
|
|
11
|
+
|
|
12
|
+
mattr_accessor :async, default: true # Use buffered writes
|
|
13
|
+
mattr_accessor :buffer_size, default: 100 # Flush after N events
|
|
14
|
+
mattr_accessor :flush_interval, default: 2 # Flush every N seconds
|
|
15
|
+
mattr_accessor :sample_rate, default: 1.0 # 1.0 = 100%, 0.1 = 10%
|
|
16
|
+
|
|
17
|
+
mattr_accessor :retention_period, default: 7.days
|
|
18
|
+
mattr_accessor :captured_events, default: [] # Empty = capture all
|
|
19
|
+
mattr_accessor :ignored_events, default: [
|
|
20
|
+
/^active_record\./,
|
|
21
|
+
/^action_controller\./,
|
|
22
|
+
/^action_view\./,
|
|
23
|
+
/^active_job\./,
|
|
24
|
+
/^action_mailer\./,
|
|
25
|
+
/^active_storage\./,
|
|
26
|
+
/^action_cable\./,
|
|
27
|
+
/^rails\./,
|
|
28
|
+
/^turbo\./,
|
|
29
|
+
/^cache_/,
|
|
30
|
+
/\.sql$/,
|
|
31
|
+
/^solid_/
|
|
32
|
+
]
|
|
33
|
+
mattr_accessor :per_page, default: 25
|
|
34
|
+
|
|
35
|
+
mattr_accessor :http_basic_auth_enabled, default: false
|
|
36
|
+
mattr_accessor :http_basic_auth_user
|
|
37
|
+
mattr_accessor :http_basic_auth_password
|
|
38
|
+
mattr_accessor :authentication, default: nil
|
|
39
|
+
|
|
40
|
+
mattr_accessor :group_keys, default: [:request_id]
|
|
41
|
+
|
|
42
|
+
mattr_accessor :logger
|
|
43
|
+
|
|
44
|
+
class << self
|
|
45
|
+
def configure
|
|
46
|
+
yield self
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
ADAPTER_LOCK = Mutex.new
|
|
50
|
+
|
|
51
|
+
def adapter
|
|
52
|
+
@adapter || ADAPTER_LOCK.synchronize { @adapter ||= build_adapter }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def reset_adapter!
|
|
56
|
+
ADAPTER_LOCK.synchronize { @adapter = nil }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def events
|
|
60
|
+
EventsRelation.new(adapter: adapter)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def subscriber
|
|
64
|
+
@subscriber ||= Subscriber.new
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def subscribe!
|
|
68
|
+
return unless defined?(Rails.event)
|
|
69
|
+
|
|
70
|
+
Rails.event.subscribe(subscriber)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def shutdown!
|
|
74
|
+
subscriber.stop!
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def build_adapter
|
|
80
|
+
adapter_instance = case storage_adapter
|
|
81
|
+
when :active_record
|
|
82
|
+
require "rails_event_viewer/adapters/active_record"
|
|
83
|
+
Adapters::ActiveRecord.new(**adapter_options)
|
|
84
|
+
when :redis
|
|
85
|
+
require "rails_event_viewer/adapters/redis"
|
|
86
|
+
Adapters::Redis.new(**adapter_options)
|
|
87
|
+
when :memory
|
|
88
|
+
require "rails_event_viewer/adapters/memory"
|
|
89
|
+
Adapters::Memory.new(**adapter_options)
|
|
90
|
+
when :null
|
|
91
|
+
require "rails_event_viewer/adapters/null"
|
|
92
|
+
Adapters::Null.new
|
|
93
|
+
when Class
|
|
94
|
+
storage_adapter.new(**adapter_options)
|
|
95
|
+
else
|
|
96
|
+
raise ArgumentError, "Unknown adapter: #{storage_adapter}. " \
|
|
97
|
+
"Valid options: :active_record, :redis, :memory, :null, or a custom adapter class"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
if storage_adapter == :active_record && !adapter_instance.table_exists?
|
|
101
|
+
effective_logger.warn "[RailsEventViewer] Table 'rails_event_viewer_entries' not found. " \
|
|
102
|
+
"Run `rails rails_event_viewer:install:migrations` and `rails db:migrate`. " \
|
|
103
|
+
"Falling back to NullAdapter (events will not be persisted)."
|
|
104
|
+
require "rails_event_viewer/adapters/null"
|
|
105
|
+
Adapters::Null.new
|
|
106
|
+
else
|
|
107
|
+
adapter_instance
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def effective_logger
|
|
112
|
+
logger || (defined?(Rails) && Rails.logger) || Logger.new($stdout)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
require "rails_event_viewer/adapter"
|
|
118
|
+
require "rails_event_viewer/events_relation"
|
|
119
|
+
require "rails_event_viewer/subscriber"
|
|
120
|
+
require "rails_event_viewer/json_query"
|
|
121
|
+
require "rails_event_viewer/pagy_countable"
|
|
122
|
+
require "rails_event_viewer/time_utils"
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :rails_event_viewer do
|
|
4
|
+
desc "Delete events older than the configured retention period"
|
|
5
|
+
task cleanup: :environment do
|
|
6
|
+
retention_period = RailsEventViewer.retention_period
|
|
7
|
+
cutoff_time = Time.current - retention_period
|
|
8
|
+
|
|
9
|
+
puts "RailsEventViewer: Cleaning up events older than #{cutoff_time}..."
|
|
10
|
+
|
|
11
|
+
deleted_count = RailsEventViewer.adapter.delete_before(cutoff_time)
|
|
12
|
+
|
|
13
|
+
puts "RailsEventViewer: Deleted #{deleted_count || 'unknown number of'} events."
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
desc "Show event statistics"
|
|
17
|
+
task stats: :environment do
|
|
18
|
+
adapter = RailsEventViewer.adapter
|
|
19
|
+
adapter_name = adapter.class.name.split("::").last
|
|
20
|
+
|
|
21
|
+
puts ""
|
|
22
|
+
puts "RailsEventViewer Statistics"
|
|
23
|
+
puts "=" * 40
|
|
24
|
+
puts "Storage Adapter: #{adapter_name}"
|
|
25
|
+
puts ""
|
|
26
|
+
|
|
27
|
+
total = RailsEventViewer.events.count
|
|
28
|
+
puts "Total Events: #{total}"
|
|
29
|
+
|
|
30
|
+
last_hour = adapter.count_since(1.hour.ago) rescue "N/A"
|
|
31
|
+
last_day = adapter.count_since(1.day.ago) rescue "N/A"
|
|
32
|
+
last_week = adapter.count_since(1.week.ago) rescue "N/A"
|
|
33
|
+
|
|
34
|
+
puts ""
|
|
35
|
+
puts "Events by Time Period:"
|
|
36
|
+
puts " Last hour: #{last_hour}"
|
|
37
|
+
puts " Last day: #{last_day}"
|
|
38
|
+
puts " Last week: #{last_week}"
|
|
39
|
+
|
|
40
|
+
puts ""
|
|
41
|
+
puts "Top 10 Event Types:"
|
|
42
|
+
top_events = adapter.counts_by_name(limit: 10) rescue {}
|
|
43
|
+
if top_events.empty?
|
|
44
|
+
puts " (no events)"
|
|
45
|
+
else
|
|
46
|
+
top_events.each do |name, count|
|
|
47
|
+
puts " #{name}: #{count}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
puts ""
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
desc "Clear all events (use with caution!)"
|
|
55
|
+
task clear: :environment do
|
|
56
|
+
adapter = RailsEventViewer.adapter
|
|
57
|
+
|
|
58
|
+
if adapter.respond_to?(:clear!)
|
|
59
|
+
print "Are you sure you want to delete ALL events? [y/N] "
|
|
60
|
+
response = $stdin.gets.chomp.downcase
|
|
61
|
+
|
|
62
|
+
if response == "y"
|
|
63
|
+
adapter.clear!
|
|
64
|
+
puts "All events have been cleared."
|
|
65
|
+
else
|
|
66
|
+
puts "Cancelled."
|
|
67
|
+
end
|
|
68
|
+
else
|
|
69
|
+
puts "Clear operation not supported by #{adapter.class.name}"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
desc "Flush any buffered events to storage"
|
|
74
|
+
task flush: :environment do
|
|
75
|
+
puts "Flushing buffered events..."
|
|
76
|
+
|
|
77
|
+
RailsEventViewer.subscriber.flush!
|
|
78
|
+
|
|
79
|
+
puts "Done."
|
|
80
|
+
end
|
|
81
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rails_event_viewer
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Keshav Biswa
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 8.1.0
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 8.1.0
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: pagy
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '43.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '43.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: chartkick
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '5.0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '5.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: groupdate
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '6.0'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '6.0'
|
|
68
|
+
description: RailsEventViewer provides a web interface to browse, search, and analyze
|
|
69
|
+
structured events emitted via Rails.event. Features include event listing, filtering,
|
|
70
|
+
search, and analytics dashboards.
|
|
71
|
+
email:
|
|
72
|
+
- keshavbiswa21@gmail.com
|
|
73
|
+
executables: []
|
|
74
|
+
extensions: []
|
|
75
|
+
extra_rdoc_files: []
|
|
76
|
+
files:
|
|
77
|
+
- CHANGELOG.md
|
|
78
|
+
- MIT-LICENSE
|
|
79
|
+
- README.md
|
|
80
|
+
- Rakefile
|
|
81
|
+
- app/assets/stylesheets/rails_event_viewer/application.css
|
|
82
|
+
- app/assets/stylesheets/rails_event_viewer/tailwind.css
|
|
83
|
+
- app/controllers/rails_event_viewer/analytics_controller.rb
|
|
84
|
+
- app/controllers/rails_event_viewer/application_controller.rb
|
|
85
|
+
- app/controllers/rails_event_viewer/dashboard_controller.rb
|
|
86
|
+
- app/controllers/rails_event_viewer/event_types_controller.rb
|
|
87
|
+
- app/controllers/rails_event_viewer/events_controller.rb
|
|
88
|
+
- app/controllers/rails_event_viewer/groups_controller.rb
|
|
89
|
+
- app/helpers/rails_event_viewer/application_helper.rb
|
|
90
|
+
- app/models/rails_event_viewer/application_record.rb
|
|
91
|
+
- app/models/rails_event_viewer/entry.rb
|
|
92
|
+
- app/views/layouts/rails_event_viewer/application.html.erb
|
|
93
|
+
- app/views/rails_event_viewer/analytics/overview.html.erb
|
|
94
|
+
- app/views/rails_event_viewer/dashboard/index.html.erb
|
|
95
|
+
- app/views/rails_event_viewer/event_types/index.html.erb
|
|
96
|
+
- app/views/rails_event_viewer/event_types/show.html.erb
|
|
97
|
+
- app/views/rails_event_viewer/events/_event_row.html.erb
|
|
98
|
+
- app/views/rails_event_viewer/events/_filters_sidebar.html.erb
|
|
99
|
+
- app/views/rails_event_viewer/events/index.html.erb
|
|
100
|
+
- app/views/rails_event_viewer/events/show.html.erb
|
|
101
|
+
- app/views/rails_event_viewer/groups/index.html.erb
|
|
102
|
+
- app/views/rails_event_viewer/groups/show.html.erb
|
|
103
|
+
- app/views/rails_event_viewer/shared/_navigation.html.erb
|
|
104
|
+
- app/views/rails_event_viewer/shared/_pagination.html.erb
|
|
105
|
+
- app/views/rails_event_viewer/shared/_stat_card.html.erb
|
|
106
|
+
- config/routes.rb
|
|
107
|
+
- db/migrate/20241208000001_create_rails_event_viewer_entries.rb
|
|
108
|
+
- lib/generators/rails_event_viewer/install/USAGE
|
|
109
|
+
- lib/generators/rails_event_viewer/install/install_generator.rb
|
|
110
|
+
- lib/generators/rails_event_viewer/install/templates/create_rails_event_viewer_entries.rb.erb
|
|
111
|
+
- lib/generators/rails_event_viewer/install/templates/initializer.rb.erb
|
|
112
|
+
- lib/rails_event_viewer.rb
|
|
113
|
+
- lib/rails_event_viewer/adapter.rb
|
|
114
|
+
- lib/rails_event_viewer/adapters/active_record.rb
|
|
115
|
+
- lib/rails_event_viewer/adapters/concerns/in_memory_filtering.rb
|
|
116
|
+
- lib/rails_event_viewer/adapters/memory.rb
|
|
117
|
+
- lib/rails_event_viewer/adapters/null.rb
|
|
118
|
+
- lib/rails_event_viewer/adapters/redis.rb
|
|
119
|
+
- lib/rails_event_viewer/engine.rb
|
|
120
|
+
- lib/rails_event_viewer/events_relation.rb
|
|
121
|
+
- lib/rails_event_viewer/json_query.rb
|
|
122
|
+
- lib/rails_event_viewer/pagy_countable.rb
|
|
123
|
+
- lib/rails_event_viewer/subscriber.rb
|
|
124
|
+
- lib/rails_event_viewer/time_utils.rb
|
|
125
|
+
- lib/rails_event_viewer/version.rb
|
|
126
|
+
- lib/tasks/rails_event_viewer_tasks.rake
|
|
127
|
+
homepage: https://github.com/keshavbiswa/rails_event_viewer
|
|
128
|
+
licenses:
|
|
129
|
+
- MIT
|
|
130
|
+
metadata:
|
|
131
|
+
homepage_uri: https://github.com/keshavbiswa/rails_event_viewer
|
|
132
|
+
source_code_uri: https://github.com/keshavbiswa/rails_event_viewer
|
|
133
|
+
changelog_uri: https://github.com/keshavbiswa/rails_event_viewer/blob/main/CHANGELOG.md
|
|
134
|
+
rdoc_options: []
|
|
135
|
+
require_paths:
|
|
136
|
+
- lib
|
|
137
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
138
|
+
requirements:
|
|
139
|
+
- - ">="
|
|
140
|
+
- !ruby/object:Gem::Version
|
|
141
|
+
version: '3.2'
|
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
|
+
requirements:
|
|
144
|
+
- - ">="
|
|
145
|
+
- !ruby/object:Gem::Version
|
|
146
|
+
version: '0'
|
|
147
|
+
requirements: []
|
|
148
|
+
rubygems_version: 3.6.7
|
|
149
|
+
specification_version: 4
|
|
150
|
+
summary: A Rails engine to view structured events from Rails Event Reporter
|
|
151
|
+
test_files: []
|