rails_event_viewer 0.3.1 → 0.4.1

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -0
  3. data/README.md +7 -0
  4. data/app/assets/stylesheets/rails_event_viewer/application.css +1 -1
  5. data/app/controllers/rails_event_viewer/application_controller.rb +1 -11
  6. data/app/controllers/rails_event_viewer/events_controller.rb +6 -6
  7. data/app/helpers/rails_event_viewer/application_helper.rb +21 -75
  8. data/app/models/rails_event_viewer/entry.rb +0 -26
  9. data/app/models/rails_event_viewer/page.rb +9 -0
  10. data/app/views/layouts/rails_event_viewer/application.html.erb +3 -4
  11. data/app/views/rails_event_viewer/analytics/overview.html.erb +11 -9
  12. data/app/views/rails_event_viewer/dashboard/index.html.erb +13 -13
  13. data/app/views/rails_event_viewer/event_types/index.html.erb +6 -6
  14. data/app/views/rails_event_viewer/event_types/show.html.erb +14 -14
  15. data/app/views/rails_event_viewer/events/_event_row.html.erb +8 -8
  16. data/app/views/rails_event_viewer/events/_filters_sidebar.html.erb +16 -14
  17. data/app/views/rails_event_viewer/events/index.html.erb +6 -6
  18. data/app/views/rails_event_viewer/events/show.html.erb +22 -22
  19. data/app/views/rails_event_viewer/groups/index.html.erb +7 -7
  20. data/app/views/rails_event_viewer/groups/show.html.erb +11 -11
  21. data/app/views/rails_event_viewer/shared/_navigation.html.erb +8 -14
  22. data/app/views/rails_event_viewer/shared/_pagination.html.erb +10 -10
  23. data/app/views/rails_event_viewer/shared/_stat_card.html.erb +5 -5
  24. data/db/migrate/20261002121337_create_rails_event_viewer_entries.rb +0 -1
  25. data/lib/generators/rails_event_viewer/install/templates/create_rails_event_viewer_entries.rb.erb +0 -1
  26. data/lib/generators/rails_event_viewer/install/templates/initializer.rb.erb +4 -0
  27. data/lib/rails_event_viewer/adapter.rb +0 -8
  28. data/lib/rails_event_viewer/adapters/active_record.rb +10 -44
  29. data/lib/rails_event_viewer/adapters/concerns/in_memory_filtering.rb +0 -7
  30. data/lib/rails_event_viewer/adapters/memory.rb +0 -4
  31. data/lib/rails_event_viewer/adapters/null.rb +0 -8
  32. data/lib/rails_event_viewer/adapters/redis.rb +3 -17
  33. data/lib/rails_event_viewer/flusher.rb +5 -5
  34. data/lib/rails_event_viewer/logging.rb +1 -11
  35. data/lib/rails_event_viewer/version.rb +1 -1
  36. data/lib/rails_event_viewer.rb +6 -5
  37. metadata +7 -2
  38. data/app/assets/stylesheets/rails_event_viewer/tailwind.css +0 -29
@@ -10,10 +10,6 @@ module RailsEventViewer
10
10
  true
11
11
  end
12
12
 
13
- def supports_retention?
14
- true
15
- end
16
-
17
13
  def write_events(events)
18
14
  raise NotImplementedError, "#{self.class} must implement #write_events"
19
15
  end
@@ -59,10 +55,6 @@ module RailsEventViewer
59
55
  end.sort_by { |s| -s[:count] }
60
56
  end
61
57
 
62
- def distinct_group_values(key, source: :context)
63
- raise NotImplementedError, "#{self.class} must implement #distinct_group_values"
64
- end
65
-
66
58
  def group_instances(key, source: :context, limit: 100)
67
59
  raise NotImplementedError, "#{self.class} must implement #group_instances"
68
60
  end
@@ -3,10 +3,6 @@ module RailsEventViewer
3
3
  class ActiveRecord
4
4
  include Adapter
5
5
 
6
- def initialize(**options)
7
- @options = options
8
- end
9
-
10
6
  def table_exists?
11
7
  Entry.table_exists?
12
8
  rescue ::ActiveRecord::NoDatabaseError
@@ -16,7 +12,6 @@ module RailsEventViewer
16
12
  def write_events(events)
17
13
  return if events.empty?
18
14
 
19
- # Use insert_all for bulk inserts (more efficient)
20
15
  records = events.map do |event|
21
16
  {
22
17
  name: event[:name],
@@ -32,7 +27,9 @@ module RailsEventViewer
32
27
  }
33
28
  end
34
29
 
35
- Entry.insert_all(records)
30
+ Entry.transaction(requires_new: !RailsEventViewer.transactional) do
31
+ Entry.insert_all(records, returning: false)
32
+ end
36
33
  end
37
34
 
38
35
  def fetch_events(relation)
@@ -88,16 +85,6 @@ module RailsEventViewer
88
85
  end
89
86
  end
90
87
 
91
- def distinct_group_values(key, source: :context)
92
- column = source == :tags ? :tags : :context
93
- json_path = JsonQuery.extract_path(column, key)
94
-
95
- Entry
96
- .where(JsonQuery.extract_path_present(column, key))
97
- .distinct
98
- .pluck(Arel.sql(json_path))
99
- end
100
-
101
88
  def group_instances(key, source: :context, limit: 100)
102
89
  column = source == :tags ? :tags : :context
103
90
  json_path = JsonQuery.extract_path(column, key)
@@ -148,39 +135,18 @@ module RailsEventViewer
148
135
  end
149
136
 
150
137
  def build_scope(relation)
151
- scope = Entry.order(occurred_at: :desc)
152
- scope = apply_name_filters(scope, relation)
153
- scope = apply_tag_filters(scope, relation)
154
- scope = apply_context_filters(scope, relation)
138
+ scope = Entry.order(occurred_at: :desc, id: :desc)
139
+ scope = scope.where(name: relation.names) if relation.names.present?
140
+ scope = apply_json_filters(scope, :tags, relation.tags)
141
+ scope = apply_json_filters(scope, :context, relation.contexts)
155
142
  scope = apply_time_filters(scope, relation)
156
143
  scope = apply_search(scope, relation)
157
144
  scope
158
145
  end
159
146
 
160
- def apply_name_filters(scope, relation)
161
- return scope if relation.names.blank?
162
-
163
- if relation.names.size == 1
164
- scope.where(name: relation.names.first)
165
- else
166
- scope.where(name: relation.names)
167
- end
168
- end
169
-
170
- def apply_tag_filters(scope, relation)
171
- return scope if relation.tags.blank?
172
-
173
- relation.tags.each do |key, value|
174
- scope = scope.where(*JsonQuery.contains(:tags, key, value))
175
- end
176
- scope
177
- end
178
-
179
- def apply_context_filters(scope, relation)
180
- return scope if relation.contexts.blank?
181
-
182
- relation.contexts.each do |key, value|
183
- scope = scope.where(*JsonQuery.contains(:context, key, value))
147
+ def apply_json_filters(scope, column, filters)
148
+ filters.each do |key, value|
149
+ scope = scope.where(*JsonQuery.contains(column, key, value))
184
150
  end
185
151
  scope
186
152
  end
@@ -6,13 +6,6 @@ module RailsEventViewer
6
6
  module InMemoryFiltering
7
7
  extend ActiveSupport::Concern
8
8
 
9
- def distinct_group_values(key, source: :context)
10
- in_memory_events
11
- .map { |e| group_value_for(e, key, source) }
12
- .compact
13
- .uniq
14
- end
15
-
16
9
  def group_instances(key, source: :context, limit: 100)
17
10
  in_memory_events
18
11
  .group_by { |e| group_value_for(e, key, source).to_s }
@@ -24,10 +24,6 @@ module RailsEventViewer
24
24
  @max_events = max_events
25
25
  end
26
26
 
27
- def supports_retention?
28
- true
29
- end
30
-
31
27
  def write_events(new_events)
32
28
  return if new_events.empty?
33
29
 
@@ -11,10 +11,6 @@ module RailsEventViewer
11
11
  false
12
12
  end
13
13
 
14
- def supports_retention?
15
- false
16
- end
17
-
18
14
  def write_events(events)
19
15
  nil
20
16
  end
@@ -51,10 +47,6 @@ module RailsEventViewer
51
47
  0
52
48
  end
53
49
 
54
- def distinct_group_values(key, source: :context)
55
- []
56
- end
57
-
58
50
  def group_instances(key, source: :context, limit: 100)
59
51
  []
60
52
  end
@@ -26,10 +26,6 @@ module RailsEventViewer
26
26
  @max_events = max_events
27
27
  end
28
28
 
29
- def supports_analytics?
30
- true
31
- end
32
-
33
29
  def write_events(events)
34
30
  return if events.empty?
35
31
 
@@ -57,7 +53,7 @@ module RailsEventViewer
57
53
 
58
54
  def fetch_events(relation)
59
55
  if relation.filtered?
60
- return fetch_all_filtered(relation).drop(relation.offset_value).take(relation.limit_value)
56
+ return filter_events(in_memory_events, relation).drop(relation.offset_value).take(relation.limit_value)
61
57
  end
62
58
 
63
59
  start_idx = relation.offset_value
@@ -68,7 +64,7 @@ module RailsEventViewer
68
64
 
69
65
  def count_events(relation)
70
66
  if relation.filtered?
71
- fetch_all_filtered(relation).size
67
+ filter_events(in_memory_events, relation).size
72
68
  else
73
69
  @redis.with { |conn| conn.zcard(events_key) }
74
70
  end
@@ -171,18 +167,8 @@ module RailsEventViewer
171
167
  data
172
168
  end
173
169
 
174
- def fetch_all_events
175
- serialized_events = @redis.with { |conn| conn.zrevrange(events_key, 0, -1) }
176
- serialized_events.map { |json| deserialize_event(json) }
177
- end
178
-
179
- def fetch_all_filtered(relation)
180
- events = fetch_all_events
181
- filter_events(events, relation)
182
- end
183
-
184
170
  def in_memory_events
185
- fetch_all_events
171
+ @redis.with { |conn| conn.zrevrange(events_key, 0, -1) }.map { |json| deserialize_event(json) }
186
172
  end
187
173
  end
188
174
  end
@@ -56,7 +56,7 @@ module RailsEventViewer
56
56
  if @thread&.alive?
57
57
  @flush_requests << true
58
58
  @thread.join(shutdown_timeout)
59
- effective_logger&.warn("[RailsEventViewer] Flusher thread still busy after #{shutdown_timeout}s writing #{@writing} events") if @thread.alive?
59
+ RailsEventViewer.effective_logger.warn("[RailsEventViewer] Flusher thread still busy after #{shutdown_timeout}s writing #{@writing} events") if @thread.alive?
60
60
  end
61
61
 
62
62
  @mutex.synchronize { @thread = nil }
@@ -72,7 +72,7 @@ module RailsEventViewer
72
72
 
73
73
  report_dropped_events
74
74
  leftover = @buffer.size
75
- effective_logger&.warn("[RailsEventViewer] #{leftover} events still buffered at shutdown") if leftover.positive?
75
+ RailsEventViewer.effective_logger.warn("[RailsEventViewer] #{leftover} events still buffered at shutdown") if leftover.positive?
76
76
  end
77
77
 
78
78
  def running?
@@ -126,7 +126,7 @@ module RailsEventViewer
126
126
  end
127
127
  return if dropped.zero?
128
128
 
129
- effective_logger&.warn("[RailsEventViewer] Dropped #{dropped} events because the buffer was full")
129
+ RailsEventViewer.effective_logger.warn("[RailsEventViewer] Dropped #{dropped} events because the buffer was full")
130
130
  end
131
131
 
132
132
  def request_flush
@@ -143,7 +143,7 @@ module RailsEventViewer
143
143
  end
144
144
 
145
145
  def run_loop
146
- effective_logger&.debug("[RailsEventViewer] Flusher thread started (PID: #{Process.pid})")
146
+ RailsEventViewer.effective_logger.debug("[RailsEventViewer] Flusher thread started (PID: #{Process.pid})")
147
147
 
148
148
  until shutdown_requested?
149
149
  begin
@@ -157,7 +157,7 @@ module RailsEventViewer
157
157
  end
158
158
  end
159
159
 
160
- effective_logger&.debug("[RailsEventViewer] Flusher thread stopped (PID: #{Process.pid})")
160
+ RailsEventViewer.effective_logger.debug("[RailsEventViewer] Flusher thread stopped (PID: #{Process.pid})")
161
161
  end
162
162
 
163
163
  def wait_for_flush
@@ -3,17 +3,7 @@ module RailsEventViewer
3
3
  private
4
4
 
5
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)
6
+ RailsEventViewer.effective_logger.error(message)
17
7
  end
18
8
  end
19
9
  end
@@ -1,3 +1,3 @@
1
1
  module RailsEventViewer
2
- VERSION = "0.3.1"
2
+ VERSION = "0.4.1"
3
3
  end
@@ -14,6 +14,7 @@ module RailsEventViewer
14
14
  mattr_accessor :buffer_size, default: 100 # Flush after N events
15
15
  mattr_accessor :flush_interval, default: 2 # Flush every N seconds
16
16
  mattr_accessor :sample_rate, default: 1.0 # 1.0 = 100%, 0.1 = 10%
17
+ mattr_accessor :transactional, default: true # false = a failed sync write does not abort the caller's transaction
17
18
  mattr_accessor :buffer, default: Buffers::Memory.new
18
19
 
19
20
  mattr_accessor :retention_period, default: 7.days
@@ -76,6 +77,10 @@ module RailsEventViewer
76
77
  subscriber.stop!
77
78
  end
78
79
 
80
+ def effective_logger
81
+ logger || (defined?(Rails) && Rails.logger) || (@fallback_logger ||= Logger.new($stdout))
82
+ end
83
+
79
84
  private
80
85
 
81
86
  def build_adapter
@@ -101,7 +106,7 @@ module RailsEventViewer
101
106
 
102
107
  if storage_adapter == :active_record && !adapter_instance.table_exists?
103
108
  effective_logger.warn "[RailsEventViewer] Table 'rails_event_viewer_entries' not found. " \
104
- "Run `rails rails_event_viewer:install:migrations` and `rails db:migrate`. " \
109
+ "Run `bin/rails generate rails_event_viewer:install` and `bin/rails db:migrate`. " \
105
110
  "Falling back to NullAdapter (events will not be persisted)."
106
111
  require "rails_event_viewer/adapters/null"
107
112
  Adapters::Null.new
@@ -109,10 +114,6 @@ module RailsEventViewer
109
114
  adapter_instance
110
115
  end
111
116
  end
112
-
113
- def effective_logger
114
- logger || (defined?(Rails) && Rails.logger) || Logger.new($stdout)
115
- end
116
117
  end
117
118
  end
118
119
 
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.3.1
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keshav Biswa
@@ -30,6 +30,9 @@ dependencies:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
32
  version: '5.0'
33
+ - - "<"
34
+ - !ruby/object:Gem::Version
35
+ version: '6'
33
36
  type: :runtime
34
37
  prerelease: false
35
38
  version_requirements: !ruby/object:Gem::Requirement
@@ -37,6 +40,9 @@ dependencies:
37
40
  - - ">="
38
41
  - !ruby/object:Gem::Version
39
42
  version: '5.0'
43
+ - - "<"
44
+ - !ruby/object:Gem::Version
45
+ version: '6'
40
46
  - !ruby/object:Gem::Dependency
41
47
  name: groupdate
42
48
  requirement: !ruby/object:Gem::Requirement
@@ -69,7 +75,6 @@ files:
69
75
  - app/assets/javascripts/rails_event_viewer/chartjs-adapter-date-fns.bundle.min.js
70
76
  - app/assets/javascripts/rails_event_viewer/chartkick.min.js
71
77
  - app/assets/stylesheets/rails_event_viewer/application.css
72
- - app/assets/stylesheets/rails_event_viewer/tailwind.css
73
78
  - app/controllers/rails_event_viewer/analytics_controller.rb
74
79
  - app/controllers/rails_event_viewer/application_controller.rb
75
80
  - app/controllers/rails_event_viewer/dashboard_controller.rb
@@ -1,29 +0,0 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
4
-
5
- .json-tree {
6
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
7
- font-size: 0.875rem;
8
- }
9
-
10
- .json-tree-key {
11
- color: #4b5563;
12
- font-weight: 500;
13
- }
14
-
15
- .json-tree-string {
16
- color: #059669;
17
- }
18
-
19
- .json-tree-number {
20
- color: #2563eb;
21
- }
22
-
23
- .json-tree-boolean {
24
- color: #7c3aed;
25
- }
26
-
27
- .json-tree-null {
28
- color: #9ca3af;
29
- }