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 +4 -4
- data/lib/log_bench/app/main.rb +3 -1
- data/lib/log_bench/app/monitor.rb +1 -0
- data/lib/log_bench/app/renderer/header.rb +62 -16
- data/lib/log_bench/app/state.rb +40 -3
- data/lib/log_bench/app/stats.rb +84 -0
- data/lib/log_bench/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0326e5f99c8bd150ed2b2164175fc45a9fef687d0b6760fc0e5b8cbf0f27931e
|
|
4
|
+
data.tar.gz: 17c3cee96ae98b9bcd3ce4c2cb24abc7aca5fb170905aac8b4c783aa882a94d5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 719717da54e9424bfa6e2e4c0f9dce92475b8c73974b14891352a4736119cb67dd04a19a44833d28c4a95c12e5cc82fab662f6092c25e20aedf8d522484b44af
|
|
7
|
+
data.tar.gz: dd6b1f058207710197d87f1b10da3a321fb68da0ed0d7237225f3c13adb5bc0aa4c4e32ef98c3ac6fde2f5993a242be0e533d3f798aee88bb5488eba98edef50
|
data/lib/log_bench/app/main.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) }
|
data/lib/log_bench/app/state.rb
CHANGED
|
@@ -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
|
data/lib/log_bench/version.rb
CHANGED
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.
|
|
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:
|
|
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.
|
|
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
|