log_bench 0.5.3 → 0.6.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: c3cadcab03882aae96975ccfc7af0ce41c0e45cb8fed84182e012b1401c6b701
4
- data.tar.gz: 0c9137a70a659e69c3abbedbea4e530bc255f866c77dbdb1495f47dd0a1ffe98
3
+ metadata.gz: 0326e5f99c8bd150ed2b2164175fc45a9fef687d0b6760fc0e5b8cbf0f27931e
4
+ data.tar.gz: 17c3cee96ae98b9bcd3ce4c2cb24abc7aca5fb170905aac8b4c783aa882a94d5
5
5
  SHA512:
6
- metadata.gz: cbedbfc86537d8343f4e44d531a7183112dd010432994c30321285948cf2cbf79fd76ecceba8bc73ab2a0f007ad8690bea584f3d2a7681b905ec8f4fd4696051
7
- data.tar.gz: a1d6c1f51b317c5e04ced37994de4024c35548e36b8a0c57a179c5984c2a0dafeb7f49d524d4abdfb0ebb644a84f5e2d5bc680bfc2d93e46e4ce2d69f33f3e63
6
+ metadata.gz: 719717da54e9424bfa6e2e4c0f9dce92475b8c73974b14891352a4736119cb67dd04a19a44833d28c4a95c12e5cc82fab662f6092c25e20aedf8d522484b44af
7
+ data.tar.gz: dd6b1f058207710197d87f1b10da3a321fb68da0ed0d7237225f3c13adb5bc0aa4c4e32ef98c3ac6fde2f5993a242be0e533d3f798aee88bb5488eba98edef50
@@ -68,7 +68,9 @@ module LogBench
68
68
 
69
69
  def load_initial_data
70
70
  self.log_file = Log::File.new(log_file_path)
71
- state.requests = log_file.requests
71
+ initial_requests = log_file.requests
72
+ state.requests = initial_requests
73
+ state.set_initial_query_count(initial_requests)
72
74
  log_file.mark_as_read!
73
75
  end
74
76
 
@@ -43,6 +43,7 @@ module LogBench
43
43
  def add_new_requests(new_requests)
44
44
  return if new_requests.empty?
45
45
 
46
+ state.track_new_requests(new_requests)
46
47
  state.requests.concat(new_requests)
47
48
  keep_recent_requests
48
49
  end
@@ -50,30 +50,76 @@ module LogBench
50
50
  end
51
51
 
52
52
  def draw_stats
53
- filtered_requests = state.filtered_requests
54
- total_requests = state.requests.size
55
-
56
53
  if state.main_filter.present?
57
- # Filter active - show "X found (Y total)"
58
- stats_text = "#{filtered_requests.size} found (#{total_requests} total)"
59
- header_win.setpos(1, screen.width - stats_text.length - 2)
60
- header_win.attron(color_pair(3)) { header_win.addstr(filtered_requests.size.to_s) }
61
- header_win.addstr(" found (")
62
- header_win.attron(color_pair(3)) { header_win.addstr(total_requests.to_s) }
63
- header_win.addstr(" total)")
54
+ draw_filtered_stats
64
55
  else
65
- # No filter active - show simple count
66
- stats_text = "Requests: #{total_requests}"
67
- header_win.setpos(1, screen.width - stats_text.length - 2)
68
- header_win.addstr("Requests: ")
69
- header_win.attron(color_pair(3)) { header_win.addstr(total_requests.to_s) }
56
+ draw_stats_panel
70
57
  end
71
58
  end
72
59
 
60
+ def draw_filtered_stats
61
+ # When filter is active, show compact "X found (Y total)" on line 1
62
+ filtered_requests = state.filtered_requests
63
+ total_requests = state.requests.size
64
+ stats_text = "#{filtered_requests.size} found (#{total_requests} total)"
65
+ header_win.setpos(1, screen.width - stats_text.length - 2)
66
+ header_win.attron(color_pair(3)) { header_win.addstr(filtered_requests.size.to_s) }
67
+ header_win.addstr(" found (")
68
+ header_win.attron(color_pair(3)) { header_win.addstr(total_requests.to_s) }
69
+ header_win.addstr(" total)")
70
+ end
71
+
72
+ def draw_stats_panel
73
+ # Calculate stats
74
+ total_requests = state.requests.size
75
+ total_queries = state.total_queries
76
+ req_per_sec = state.requests_per_second
77
+ req_per_min = state.requests_per_minute
78
+ queries_per_sec = state.queries_per_second
79
+ queries_per_min = state.queries_per_minute
80
+
81
+ # Calculate width for alignment
82
+ header_text = " Total | sec | min"
83
+ max_width = header_text.length
84
+ start_x = screen.width - max_width - 2
85
+
86
+ # Draw header line (dimmed)
87
+ header_win.setpos(1, start_x)
88
+ header_win.attron(A_DIM) { header_win.addstr(header_text) }
89
+
90
+ # Draw requests line
91
+ header_win.setpos(2, start_x)
92
+ draw_stats_line("Req: ", total_requests, req_per_sec, req_per_min)
93
+
94
+ # Draw queries line
95
+ header_win.setpos(3, start_x)
96
+ draw_stats_line("Query: ", total_queries, queries_per_sec, queries_per_min)
97
+ end
98
+
99
+ def draw_stats_line(label, total, per_sec, per_min)
100
+ # Draw label
101
+ header_win.addstr(label)
102
+
103
+ # Draw total (highlighted, right-aligned in 6 chars)
104
+ total_str = format("%6d", total)
105
+ header_win.attron(color_pair(SUCCESS_GREEN)) { header_win.addstr(total_str) }
106
+ header_win.addstr(" | ")
107
+
108
+ # Draw per second (highlighted, right-aligned in 3 chars)
109
+ per_sec_str = format("%3.0f", per_sec)
110
+ header_win.attron(color_pair(SUCCESS_GREEN)) { header_win.addstr(per_sec_str) }
111
+ header_win.addstr(" | ")
112
+
113
+ # Draw per minute (highlighted, right-aligned in 3 chars)
114
+ per_min_str = format("%3.0f", per_min)
115
+ header_win.attron(color_pair(SUCCESS_GREEN)) { header_win.addstr(per_min_str) }
116
+ end
117
+
73
118
  def draw_help_text
74
119
  header_win.setpos(2, 2)
75
120
  header_win.attron(A_DIM) do
76
- header_win.addstr("a:Auto-scroll(")
121
+ help_line_1 = "a:Auto-scroll("
122
+ header_win.addstr(help_line_1)
77
123
  header_win.attron(color_pair(3)) { header_win.addstr(state.auto_scroll ? "ON" : "OFF") }
78
124
  header_win.addstr(") | f:Filter | c:Clear filter | s:Sort(")
79
125
  header_win.attron(color_pair(3)) { header_win.addstr(state.sort.display_name) }
@@ -7,7 +7,7 @@ module LogBench
7
7
  class State
8
8
  include Singleton
9
9
 
10
- attr_reader :main_filter, :sort, :detail_filter, :cleared_requests
10
+ attr_reader :main_filter, :sort, :detail_filter, :cleared_requests, :start_time, :stats, :total_queries
11
11
  attr_accessor :requests, :orphan_requests, :auto_scroll, :scroll_offset, :selected, :detail_scroll_offset, :detail_selected_entry, :text_selection_mode, :update_available, :update_version
12
12
 
13
13
  def initialize
@@ -32,6 +32,9 @@ module LogBench
32
32
  self.update_version = nil
33
33
  self.cleared_requests = nil
34
34
  self.job_ids_map = {}
35
+ self.start_time = Time.now
36
+ self.stats = Stats.new
37
+ self.total_queries = 0
35
38
  end
36
39
 
37
40
  def running?
@@ -91,17 +94,20 @@ module LogBench
91
94
  def clear_requests
92
95
  if cleared_requests
93
96
  cleared_requests[:requests] += requests
97
+ cleared_requests[:total_queries] += total_queries
94
98
  else
95
99
  self.cleared_requests = {
96
100
  requests: requests,
97
101
  selected: selected,
98
102
  scroll_offset: scroll_offset,
99
103
  detail_scroll_offset: detail_scroll_offset,
100
- detail_selected_entry: detail_selected_entry
104
+ detail_selected_entry: detail_selected_entry,
105
+ total_queries: total_queries
101
106
  }
102
107
  end
103
108
 
104
109
  self.requests = []
110
+ self.total_queries = 0
105
111
  self.selected = 0
106
112
  self.scroll_offset = 0
107
113
  self.detail_scroll_offset = 0
@@ -115,6 +121,7 @@ module LogBench
115
121
  restored_requests = cleared_requests[:requests] + requests
116
122
 
117
123
  self.requests = restored_requests
124
+ self.total_queries = cleared_requests[:total_queries] + total_queries
118
125
  self.selected = cleared_requests[:selected]
119
126
  self.scroll_offset = cleared_requests[:scroll_offset]
120
127
  self.detail_scroll_offset = cleared_requests[:detail_scroll_offset]
@@ -297,10 +304,40 @@ module LogBench
297
304
  job_ids_map[job_id]
298
305
  end
299
306
 
307
+ def track_new_requests(new_requests)
308
+ stats.track_requests(new_requests)
309
+ # Update total queries counter
310
+ self.total_queries += new_requests.sum(&:query_count)
311
+ end
312
+
313
+ def set_initial_query_count(requests)
314
+ self.total_queries = requests.sum(&:query_count)
315
+ end
316
+
317
+ def elapsed_time
318
+ Time.now - start_time
319
+ end
320
+
321
+ def requests_per_second
322
+ stats.requests_per_second
323
+ end
324
+
325
+ def requests_per_minute
326
+ stats.requests_per_minute
327
+ end
328
+
329
+ def queries_per_second
330
+ stats.queries_per_second
331
+ end
332
+
333
+ def queries_per_minute
334
+ stats.queries_per_minute
335
+ end
336
+
300
337
  private
301
338
 
302
339
  attr_accessor :focused_pane, :running, :job_ids_map
303
- attr_writer :main_filter, :detail_filter, :sort, :cleared_requests
340
+ attr_writer :main_filter, :detail_filter, :sort, :cleared_requests, :start_time, :stats, :total_queries
304
341
  end
305
342
  end
306
343
  end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LogBench
4
+ module App
5
+ class Stats
6
+ def initialize
7
+ self.request_history = []
8
+ self.last_calculation_time = Time.now
9
+ self.cached_requests_per_sec = 0.0
10
+ self.cached_requests_per_min = 0.0
11
+ self.cached_queries_per_sec = 0.0
12
+ self.cached_queries_per_min = 0.0
13
+ end
14
+
15
+ def track_requests(new_requests)
16
+ return if new_requests.empty?
17
+
18
+ now = Time.now
19
+ new_requests.each do |req|
20
+ request_history << {time: now, queries: req.query_count}
21
+ end
22
+ end
23
+
24
+ def requests_per_second
25
+ update_calculations
26
+ cached_requests_per_sec
27
+ end
28
+
29
+ def requests_per_minute
30
+ update_calculations
31
+ cached_requests_per_min
32
+ end
33
+
34
+ def queries_per_second
35
+ update_calculations
36
+ cached_queries_per_sec
37
+ end
38
+
39
+ def queries_per_minute
40
+ update_calculations
41
+ cached_queries_per_min
42
+ end
43
+
44
+ private
45
+
46
+ attr_accessor :request_history, :last_calculation_time
47
+ attr_accessor :cached_requests_per_sec, :cached_requests_per_min
48
+ attr_accessor :cached_queries_per_sec, :cached_queries_per_min
49
+
50
+ # Update rate calculations based on recent activity using rolling buffer
51
+ # We calculate per-second rate from last 1 second, per-minute rate from last 60 seconds
52
+ def update_calculations
53
+ now = Time.now
54
+
55
+ # Only recalculate every 0.5 seconds to avoid excessive computation
56
+ return if now - last_calculation_time < 0.5
57
+
58
+ self.last_calculation_time = now
59
+
60
+ # Remove entries older than 60 seconds (we only need last 60 seconds)
61
+ cutoff_time = now - 60.0
62
+ request_history.reject! { |entry| entry[:time] < cutoff_time }
63
+
64
+ # Count requests and queries in the last 1 second
65
+ one_sec_cutoff = now - 1.0
66
+ last_sec_entries = request_history.select { |entry| entry[:time] > one_sec_cutoff }
67
+ last_sec_count = last_sec_entries.size
68
+ last_sec_queries = last_sec_entries.sum { |entry| entry[:queries] }
69
+
70
+ # Count requests and queries in the last 60 seconds (all remaining entries)
71
+ last_min_count = request_history.size
72
+ last_min_queries = request_history.sum { |entry| entry[:queries] }
73
+
74
+ # Per-second rate: count from last 1 second
75
+ self.cached_requests_per_sec = last_sec_count.to_f
76
+ self.cached_queries_per_sec = last_sec_queries.to_f
77
+
78
+ # Per-minute rate: count from last 60 seconds
79
+ self.cached_requests_per_min = last_min_count.to_f
80
+ self.cached_queries_per_min = last_min_queries.to_f
81
+ end
82
+ end
83
+ end
84
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LogBench
4
- VERSION = "0.5.3"
4
+ VERSION = "0.6.0"
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: log_bench
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamín Silva
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-11-12 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: zeitwerk
@@ -142,6 +142,7 @@ files:
142
142
  - lib/log_bench/app/screen.rb
143
143
  - lib/log_bench/app/sort.rb
144
144
  - lib/log_bench/app/state.rb
145
+ - lib/log_bench/app/stats.rb
145
146
  - lib/log_bench/configuration.rb
146
147
  - lib/log_bench/configuration_validator.rb
147
148
  - lib/log_bench/current.rb
@@ -190,7 +191,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
190
191
  - !ruby/object:Gem::Version
191
192
  version: '0'
192
193
  requirements: []
193
- rubygems_version: 3.6.2
194
+ rubygems_version: 3.6.9
194
195
  specification_version: 4
195
196
  summary: A terminal-based Rails log viewer with real-time monitoring and filtering
196
197
  capabilities