log_bench 0.2.10 → 0.3.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/README.md +2 -0
- data/lib/log_bench/app/clipboard.rb +3 -0
- data/lib/log_bench/app/copy_handler.rb +2 -1
- data/lib/log_bench/app/input_handler.rb +15 -6
- data/lib/log_bench/app/renderer/details.rb +5 -0
- data/lib/log_bench/app/renderer/header.rb +3 -1
- data/lib/log_bench/app/renderer/main.rb +4 -0
- data/lib/log_bench/app/screen.rb +29 -10
- data/lib/log_bench/app/state.rb +46 -2
- data/lib/log_bench/log/entry.rb +8 -70
- data/lib/log_bench/log/file.rb +22 -39
- data/lib/log_bench/log/parser.rb +43 -10
- data/lib/log_bench/log/query_entry.rb +6 -33
- data/lib/log_bench/log/request.rb +9 -24
- data/lib/log_bench/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be28717377a43d247a6df582b248c5775fd1a0cf99410ff05b4257ac424955c8
|
4
|
+
data.tar.gz: 7c15c4cffc32521a4c8c2afbcee8f3bd0df74ed7799adf5136fcaf2bf25561d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e09e1a5ba15a01948ba8cd459c9cecb335bac3ffefcbf921ce464f30bd6f6a2ae68db80b2529d2e41db8f141c5f58961aed9af6b926b5d35999c7370c7bae370
|
7
|
+
data.tar.gz: 98513f5775754f387dfe7baf89af16861841f734412f4ccd30d9adea40fbad67b92899ca240af7efed048aa6bdf917da20dd25c71a21ad72840f9c540a4cfacb
|
data/README.md
CHANGED
@@ -118,6 +118,8 @@ log_bench log/development.log
|
|
118
118
|
- **Auto-scroll**: `a` to toggle auto-scroll mode
|
119
119
|
- **Copy**: `y` to copy the selected item to clipboard (request details or SQL query)
|
120
120
|
- **Text selection**: `t` to toggle text selection mode (enables mouse text selection)
|
121
|
+
- **Clear requests**: `Ctrl+L` to clear all requests from memory (preserves current position)
|
122
|
+
- **Undo clear**: `Ctrl+R` to restore previously cleared requests + any new requests (restores exact position)
|
121
123
|
- **Quit**: `q` to exit
|
122
124
|
|
123
125
|
### Filtering
|
@@ -12,6 +12,9 @@ module LogBench
|
|
12
12
|
if system("which pbcopy > /dev/null 2>&1")
|
13
13
|
# macOS
|
14
14
|
IO.popen("pbcopy", "w") { |io| io.write(text) }
|
15
|
+
elsif system("which wl-copy > /dev/null 2>&1")
|
16
|
+
# Linux with wl-copy (wayland)
|
17
|
+
IO.popen("wl-copy", "w") { |io| io.write(text) }
|
15
18
|
elsif system("which xclip > /dev/null 2>&1")
|
16
19
|
# Linux with xclip
|
17
20
|
IO.popen("xclip -selection clipboard", "w") { |io| io.write(text) }
|
@@ -146,7 +146,8 @@ module LogBench
|
|
146
146
|
def sql_query?(text)
|
147
147
|
# Check for common SQL keywords that indicate this is a SQL query
|
148
148
|
sql_keywords = %w[SELECT INSERT UPDATE DELETE TRANSACTION BEGIN COMMIT ROLLBACK SAVEPOINT]
|
149
|
-
|
149
|
+
pattern = /\b(#{sql_keywords.join("|")})\b/i
|
150
|
+
text.match?(pattern)
|
150
151
|
end
|
151
152
|
end
|
152
153
|
end
|
@@ -12,6 +12,8 @@ module LogBench
|
|
12
12
|
CTRL_D = 4 # Half page down
|
13
13
|
CTRL_U = 21 # Half page up
|
14
14
|
CTRL_C = 3 # Quit
|
15
|
+
CTRL_L = 12 # Clear requests
|
16
|
+
CTRL_R = 18 # Undo clear requests (restore)
|
15
17
|
ESC = 27 # Escape
|
16
18
|
|
17
19
|
# UI constants
|
@@ -34,7 +36,9 @@ module LogBench
|
|
34
36
|
return
|
35
37
|
end
|
36
38
|
|
37
|
-
if ch ==
|
39
|
+
if ch == KEY_RESIZE
|
40
|
+
handle_resize
|
41
|
+
elsif ch == KEY_MOUSE
|
38
42
|
mouse_handler.handle_mouse_input
|
39
43
|
elsif filter_mode_active?
|
40
44
|
handle_filter_input(ch)
|
@@ -43,6 +47,11 @@ module LogBench
|
|
43
47
|
end
|
44
48
|
end
|
45
49
|
|
50
|
+
def handle_resize
|
51
|
+
screen.handle_resize
|
52
|
+
renderer&.invalidate_caches
|
53
|
+
end
|
54
|
+
|
46
55
|
private
|
47
56
|
|
48
57
|
attr_accessor :state, :screen, :renderer, :mouse_handler, :copy_handler
|
@@ -127,11 +136,7 @@ module LogBench
|
|
127
136
|
when "f", "F", "/"
|
128
137
|
state.enter_filter_mode
|
129
138
|
when "c", "C"
|
130
|
-
|
131
|
-
state.clear_filter
|
132
|
-
else
|
133
|
-
state.clear_detail_filter
|
134
|
-
end
|
139
|
+
state.clear_filter
|
135
140
|
when "s", "S"
|
136
141
|
state.cycle_sort_mode
|
137
142
|
when "q", "Q", CTRL_C
|
@@ -141,6 +146,10 @@ module LogBench
|
|
141
146
|
screen.turn_text_selection_mode(state.text_selection_mode?)
|
142
147
|
when "y", "Y"
|
143
148
|
copy_handler.copy_to_clipboard
|
149
|
+
when CTRL_L
|
150
|
+
state.clear_requests
|
151
|
+
when CTRL_R
|
152
|
+
state.undo_clear_requests
|
144
153
|
when ESC
|
145
154
|
handle_escape
|
146
155
|
end
|
@@ -84,7 +84,9 @@ module LogBench
|
|
84
84
|
|
85
85
|
header_win.setpos(3, 2)
|
86
86
|
header_win.attron(A_DIM) do
|
87
|
-
header_win.addstr("←→/hl:
|
87
|
+
header_win.addstr("←→/hl:Pane | ↑↓/jk:Navigate | g/G:Top/End | y:Copy highlighted | Ctrl+L:Clear | Ctrl+R:Restore(")
|
88
|
+
header_win.attron(color_pair(3)) { header_win.addstr(state.can_undo_clear? ? "READY" : "N/A") }
|
89
|
+
header_win.addstr(")")
|
88
90
|
end
|
89
91
|
end
|
90
92
|
|
@@ -34,6 +34,10 @@ module LogBench
|
|
34
34
|
details.get_cached_detail_lines(request)
|
35
35
|
end
|
36
36
|
|
37
|
+
def invalidate_caches
|
38
|
+
details.invalidate_cache
|
39
|
+
end
|
40
|
+
|
37
41
|
private
|
38
42
|
|
39
43
|
attr_accessor :screen, :state, :header, :scrollbar, :request_list, :ansi_renderer, :details, :update_modal
|
data/lib/log_bench/app/screen.rb
CHANGED
@@ -33,6 +33,7 @@ module LogBench
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def cleanup
|
36
|
+
cleanup_windows
|
36
37
|
close_screen
|
37
38
|
end
|
38
39
|
|
@@ -59,6 +60,17 @@ module LogBench
|
|
59
60
|
enabled ? mousemask(0) : mousemask(BUTTON1_CLICKED)
|
60
61
|
end
|
61
62
|
|
63
|
+
def handle_resize
|
64
|
+
# Update terminal dimensions
|
65
|
+
resizeterm(0, 0)
|
66
|
+
clear
|
67
|
+
refresh
|
68
|
+
|
69
|
+
# Recreate windows with new dimensions
|
70
|
+
cleanup_windows
|
71
|
+
setup_windows
|
72
|
+
end
|
73
|
+
|
62
74
|
private
|
63
75
|
|
64
76
|
attr_writer :header_win, :log_win, :panel_width, :detail_win
|
@@ -70,25 +82,32 @@ module LogBench
|
|
70
82
|
|
71
83
|
def setup_colors
|
72
84
|
start_color
|
85
|
+
use_default_colors
|
73
86
|
cbreak
|
74
87
|
noecho
|
75
88
|
curs_set(0)
|
76
89
|
stdscr.keypad(true)
|
77
90
|
stdscr.timeout = INPUT_TIMEOUT_MS
|
78
91
|
|
79
|
-
# Define color pairs
|
80
|
-
init_pair(HEADER_CYAN, COLOR_CYAN,
|
81
|
-
init_pair(DEFAULT_WHITE, COLOR_WHITE,
|
82
|
-
init_pair(SUCCESS_GREEN, COLOR_GREEN,
|
83
|
-
init_pair(WARNING_YELLOW, COLOR_YELLOW,
|
84
|
-
init_pair(INFO_BLUE, COLOR_BLUE,
|
85
|
-
init_pair(ERROR_RED, COLOR_RED,
|
86
|
-
init_pair(BRIGHT_WHITE, COLOR_WHITE,
|
87
|
-
init_pair(BLACK, COLOR_BLACK,
|
88
|
-
init_pair(MAGENTA, COLOR_MAGENTA,
|
92
|
+
# Define color pairs with transparent background (-1)
|
93
|
+
init_pair(HEADER_CYAN, COLOR_CYAN, -1) # Header/Cyan
|
94
|
+
init_pair(DEFAULT_WHITE, COLOR_WHITE, -1) # Default/White
|
95
|
+
init_pair(SUCCESS_GREEN, COLOR_GREEN, -1) # GET/Success/Green
|
96
|
+
init_pair(WARNING_YELLOW, COLOR_YELLOW, -1) # POST/Warning/Yellow
|
97
|
+
init_pair(INFO_BLUE, COLOR_BLUE, -1) # PUT/Blue
|
98
|
+
init_pair(ERROR_RED, COLOR_RED, -1) # DELETE/Error/Red
|
99
|
+
init_pair(BRIGHT_WHITE, COLOR_WHITE, -1) # Bold/Bright white
|
100
|
+
init_pair(BLACK, COLOR_BLACK, -1) # Black
|
101
|
+
init_pair(MAGENTA, COLOR_MAGENTA, -1) # Magenta
|
89
102
|
init_pair(SELECTION_HIGHLIGHT, COLOR_BLACK, COLOR_CYAN) # Selection highlighting
|
90
103
|
end
|
91
104
|
|
105
|
+
def cleanup_windows
|
106
|
+
header_win&.close
|
107
|
+
log_win&.close
|
108
|
+
detail_win&.close
|
109
|
+
end
|
110
|
+
|
92
111
|
def setup_windows
|
93
112
|
self.panel_width = width / 2 - 2
|
94
113
|
|
data/lib/log_bench/app/state.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module LogBench
|
4
4
|
module App
|
5
5
|
class State
|
6
|
-
attr_reader :main_filter, :sort, :detail_filter
|
6
|
+
attr_reader :main_filter, :sort, :detail_filter, :cleared_requests
|
7
7
|
attr_accessor :requests, :auto_scroll, :scroll_offset, :selected, :detail_scroll_offset, :detail_selected_entry, :text_selection_mode, :update_available, :update_version
|
8
8
|
|
9
9
|
def initialize
|
@@ -21,6 +21,7 @@ module LogBench
|
|
21
21
|
self.sort = Sort.new
|
22
22
|
self.update_available = false
|
23
23
|
self.update_version = nil
|
24
|
+
self.cleared_requests = nil
|
24
25
|
end
|
25
26
|
|
26
27
|
def running?
|
@@ -58,6 +59,14 @@ module LogBench
|
|
58
59
|
end
|
59
60
|
|
60
61
|
def clear_filter
|
62
|
+
if left_pane_focused?
|
63
|
+
clear_requests_filter
|
64
|
+
else
|
65
|
+
clear_detail_filter
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def clear_requests_filter
|
61
70
|
main_filter.clear
|
62
71
|
self.selected = 0
|
63
72
|
self.scroll_offset = 0
|
@@ -69,6 +78,41 @@ module LogBench
|
|
69
78
|
self.detail_selected_entry = 0
|
70
79
|
end
|
71
80
|
|
81
|
+
def clear_requests
|
82
|
+
# Store current state for undo functionality
|
83
|
+
self.cleared_requests = {
|
84
|
+
requests: requests.dup,
|
85
|
+
selected: selected,
|
86
|
+
scroll_offset: scroll_offset,
|
87
|
+
detail_scroll_offset: detail_scroll_offset,
|
88
|
+
detail_selected_entry: detail_selected_entry
|
89
|
+
}
|
90
|
+
|
91
|
+
self.requests = []
|
92
|
+
self.selected = 0
|
93
|
+
self.scroll_offset = 0
|
94
|
+
self.detail_scroll_offset = 0
|
95
|
+
self.detail_selected_entry = 0
|
96
|
+
end
|
97
|
+
|
98
|
+
def undo_clear_requests
|
99
|
+
return unless cleared_requests
|
100
|
+
|
101
|
+
# Append any new requests that came in after the clear to the restored requests
|
102
|
+
restored_requests = cleared_requests[:requests] + requests
|
103
|
+
|
104
|
+
self.requests = restored_requests
|
105
|
+
self.selected = cleared_requests[:selected]
|
106
|
+
self.scroll_offset = cleared_requests[:scroll_offset]
|
107
|
+
self.detail_scroll_offset = cleared_requests[:detail_scroll_offset]
|
108
|
+
self.detail_selected_entry = cleared_requests[:detail_selected_entry]
|
109
|
+
self.cleared_requests = nil
|
110
|
+
end
|
111
|
+
|
112
|
+
def can_undo_clear?
|
113
|
+
!cleared_requests.nil?
|
114
|
+
end
|
115
|
+
|
72
116
|
def cycle_sort_mode
|
73
117
|
sort.cycle
|
74
118
|
end
|
@@ -235,7 +279,7 @@ module LogBench
|
|
235
279
|
private
|
236
280
|
|
237
281
|
attr_accessor :focused_pane, :running
|
238
|
-
attr_writer :main_filter, :detail_filter, :sort
|
282
|
+
attr_writer :main_filter, :detail_filter, :sort, :cleared_requests
|
239
283
|
end
|
240
284
|
end
|
241
285
|
end
|
data/lib/log_bench/log/entry.rb
CHANGED
@@ -5,22 +5,12 @@ module LogBench
|
|
5
5
|
class Entry
|
6
6
|
attr_reader :type, :raw_line, :request_id, :timestamp, :content, :timing
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
self.
|
10
|
-
self.timestamp =
|
11
|
-
self.
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
def self.build(raw_line)
|
16
|
-
new(raw_line) if parseable?(raw_line)
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.parseable?(line)
|
20
|
-
data = JSON.parse(line.strip)
|
21
|
-
data.is_a?(Hash)
|
22
|
-
rescue JSON::ParserError
|
23
|
-
false
|
8
|
+
def initialize(json_data)
|
9
|
+
self.json_data = json_data
|
10
|
+
self.timestamp = parse_timestamp(json_data["timestamp"])
|
11
|
+
self.request_id = json_data["request_id"]
|
12
|
+
self.content = json_data["message"] || ""
|
13
|
+
self.type = :other
|
24
14
|
end
|
25
15
|
|
26
16
|
def http_request?
|
@@ -33,60 +23,8 @@ module LogBench
|
|
33
23
|
|
34
24
|
private
|
35
25
|
|
36
|
-
attr_writer :type, :
|
37
|
-
|
38
|
-
def parse!
|
39
|
-
parse_json
|
40
|
-
end
|
41
|
-
|
42
|
-
def parse_json
|
43
|
-
data = JSON.parse(raw_line)
|
44
|
-
return false unless data.is_a?(Hash)
|
45
|
-
|
46
|
-
# extract_from_json returns false if log should be discarded
|
47
|
-
extract_from_json(data)
|
48
|
-
rescue JSON::ParserError
|
49
|
-
false
|
50
|
-
end
|
51
|
-
|
52
|
-
def extract_from_json(data)
|
53
|
-
# Discard logs without request_id - they can't be correlated
|
54
|
-
return false unless data["request_id"]
|
55
|
-
|
56
|
-
self.timestamp = parse_timestamp(data["timestamp"])
|
57
|
-
self.request_id = data["request_id"]
|
58
|
-
self.content = data["message"] || ""
|
59
|
-
self.type = determine_json_type(data)
|
60
|
-
true
|
61
|
-
end
|
62
|
-
|
63
|
-
def determine_json_type(data)
|
64
|
-
return :http_request if lograge_request?(data)
|
65
|
-
return :cache if cache_message?(data)
|
66
|
-
return :sql if sql_message?(data)
|
67
|
-
return :sql_call_line if call_stack_message?(data)
|
68
|
-
|
69
|
-
:other
|
70
|
-
end
|
71
|
-
|
72
|
-
def lograge_request?(data)
|
73
|
-
data["method"] && data["path"] && data["status"]
|
74
|
-
end
|
75
|
-
|
76
|
-
def sql_message?(data)
|
77
|
-
message = data["message"] || ""
|
78
|
-
%w[SELECT INSERT UPDATE DELETE TRANSACTION BEGIN COMMIT ROLLBACK SAVEPOINT].any? { |op| message.include?(op) }
|
79
|
-
end
|
80
|
-
|
81
|
-
def cache_message?(data)
|
82
|
-
message = data["message"] || ""
|
83
|
-
message.include?("CACHE")
|
84
|
-
end
|
85
|
-
|
86
|
-
def call_stack_message?(data)
|
87
|
-
message = data["message"] || ""
|
88
|
-
message.include?("↳")
|
89
|
-
end
|
26
|
+
attr_writer :type, :timestamp, :request_id, :content, :timing
|
27
|
+
attr_accessor :json_data
|
90
28
|
|
91
29
|
def parse_timestamp(timestamp_str)
|
92
30
|
return Time.now unless timestamp_str
|
data/lib/log_bench/log/file.rb
CHANGED
@@ -3,7 +3,8 @@
|
|
3
3
|
module LogBench
|
4
4
|
module Log
|
5
5
|
class File
|
6
|
-
|
6
|
+
INACTIVE_SLEEP_TIME = 0.5
|
7
|
+
ACTIVE_SLEEP_TIME = 0.01
|
7
8
|
|
8
9
|
def initialize(path)
|
9
10
|
self.path = find_log_file(path)
|
@@ -15,42 +16,36 @@ module LogBench
|
|
15
16
|
collection.requests
|
16
17
|
end
|
17
18
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
19
|
+
def watch
|
20
|
+
loop do
|
21
|
+
new_lines = read_new_lines
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
if new_lines.empty?
|
24
|
+
sleep INACTIVE_SLEEP_TIME
|
25
|
+
next
|
26
|
+
end
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
-
end
|
28
|
+
new_collection = Collection.new(new_lines)
|
29
|
+
yield new_collection unless new_collection.empty?
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
self.collection = nil
|
33
|
-
self.last_position = 0
|
31
|
+
sleep ACTIVE_SLEEP_TIME
|
32
|
+
end
|
34
33
|
end
|
35
34
|
|
36
|
-
def
|
37
|
-
|
38
|
-
recent_lines = all_lines.last(max_lines)
|
39
|
-
Collection.new(recent_lines)
|
35
|
+
def mark_as_read!
|
36
|
+
self.last_position = size
|
40
37
|
end
|
41
38
|
|
42
|
-
|
43
|
-
return enum_for(:watch) unless block_given?
|
39
|
+
private
|
44
40
|
|
45
|
-
|
46
|
-
new_lines = read_new_lines
|
47
|
-
next if new_lines.empty?
|
41
|
+
attr_accessor :path, :last_position
|
48
42
|
|
49
|
-
|
50
|
-
|
43
|
+
def collection
|
44
|
+
@collection ||= Collection.new(lines)
|
45
|
+
end
|
51
46
|
|
52
|
-
|
53
|
-
|
47
|
+
def lines
|
48
|
+
@lines ||= read_lines
|
54
49
|
end
|
55
50
|
|
56
51
|
def size
|
@@ -61,18 +56,6 @@ module LogBench
|
|
61
56
|
::File.exist?(path)
|
62
57
|
end
|
63
58
|
|
64
|
-
def mtime
|
65
|
-
::File.mtime(path)
|
66
|
-
end
|
67
|
-
|
68
|
-
def mark_as_read!
|
69
|
-
self.last_position = size
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
attr_writer :path, :last_position
|
75
|
-
|
76
59
|
def read_lines
|
77
60
|
return [] unless exist?
|
78
61
|
|
data/lib/log_bench/log/parser.rb
CHANGED
@@ -4,10 +4,13 @@ module LogBench
|
|
4
4
|
module Log
|
5
5
|
class Parser
|
6
6
|
def self.parse_line(raw_line)
|
7
|
-
|
7
|
+
clean_line = raw_line.encode("UTF-8", invalid: :replace, undef: :replace, replace: "").strip
|
8
|
+
data = JSON.parse(clean_line)
|
9
|
+
return unless data.is_a?(Hash)
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
+
build_specific_entry(data)
|
12
|
+
rescue JSON::ParserError
|
13
|
+
nil
|
11
14
|
end
|
12
15
|
|
13
16
|
def self.parse_lines(lines)
|
@@ -19,16 +22,18 @@ module LogBench
|
|
19
22
|
build_requests_from_groups(grouped)
|
20
23
|
end
|
21
24
|
|
22
|
-
def self.build_specific_entry(
|
23
|
-
case
|
25
|
+
def self.build_specific_entry(data)
|
26
|
+
case determine_json_type(data)
|
24
27
|
when :http_request
|
25
|
-
Request.
|
26
|
-
when :sql
|
27
|
-
QueryEntry.
|
28
|
+
Request.new(data)
|
29
|
+
when :sql
|
30
|
+
QueryEntry.new(data, cached: false)
|
31
|
+
when :cache
|
32
|
+
QueryEntry.new(data, cached: true)
|
28
33
|
when :sql_call_line
|
29
|
-
CallLineEntry.
|
34
|
+
CallLineEntry.new(data)
|
30
35
|
else
|
31
|
-
|
36
|
+
Entry.new(data)
|
32
37
|
end
|
33
38
|
end
|
34
39
|
|
@@ -57,6 +62,34 @@ module LogBench
|
|
57
62
|
def self.find_related_logs(entries)
|
58
63
|
entries.reject { |entry| entry.is_a?(Request) }
|
59
64
|
end
|
65
|
+
|
66
|
+
def self.determine_json_type(data)
|
67
|
+
return :http_request if lograge_request?(data)
|
68
|
+
return :cache if cache_message?(data)
|
69
|
+
return :sql if sql_message?(data)
|
70
|
+
return :sql_call_line if call_stack_message?(data)
|
71
|
+
|
72
|
+
:other
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.lograge_request?(data)
|
76
|
+
data["method"] && data["path"] && data["status"]
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.sql_message?(data)
|
80
|
+
message = data["message"] || ""
|
81
|
+
%w[SELECT INSERT UPDATE DELETE TRANSACTION BEGIN COMMIT ROLLBACK SAVEPOINT].any? { |op| message.include?(op) }
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.cache_message?(data)
|
85
|
+
message = data["message"] || ""
|
86
|
+
message.include?("CACHE")
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.call_stack_message?(data)
|
90
|
+
message = data["message"] || ""
|
91
|
+
message.include?("↳")
|
92
|
+
end
|
60
93
|
end
|
61
94
|
end
|
62
95
|
end
|
@@ -14,21 +14,11 @@ module LogBench
|
|
14
14
|
SAVEPOINT = "SAVEPOINT"
|
15
15
|
SQL_OPERATIONS = [SELECT, INSERT, UPDATE, DELETE, TRANSACTION, BEGIN_TRANSACTION, COMMIT, ROLLBACK, SAVEPOINT].freeze
|
16
16
|
|
17
|
-
def initialize(
|
18
|
-
super(
|
17
|
+
def initialize(json_data, cached: false)
|
18
|
+
super(json_data)
|
19
19
|
self.type = cached ? :cache : :sql
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
def self.build(raw_line)
|
24
|
-
return unless parseable?(raw_line)
|
25
|
-
|
26
|
-
entry = Entry.new(raw_line)
|
27
|
-
return unless [:sql, :cache].include?(entry.type)
|
28
|
-
|
29
|
-
# Create QueryEntry for both SQL and CACHE entries
|
30
|
-
cached = entry.type == :cache
|
31
|
-
new(raw_line, cached: cached)
|
20
|
+
self.timing = extract_timing
|
21
|
+
self.operation = extract_operation
|
32
22
|
end
|
33
23
|
|
34
24
|
def duration_ms
|
@@ -72,7 +62,7 @@ module LogBench
|
|
72
62
|
end
|
73
63
|
|
74
64
|
def cached?
|
75
|
-
|
65
|
+
type == :cache
|
76
66
|
end
|
77
67
|
|
78
68
|
def hit?
|
@@ -81,24 +71,7 @@ module LogBench
|
|
81
71
|
|
82
72
|
private
|
83
73
|
|
84
|
-
attr_accessor :operation
|
85
|
-
|
86
|
-
def extract_from_json(data)
|
87
|
-
# Call parent method which checks for request_id
|
88
|
-
return false unless super
|
89
|
-
|
90
|
-
message = data["message"] || ""
|
91
|
-
return false unless sql_message?(data) || cache_message?(data)
|
92
|
-
|
93
|
-
self.content = message.strip
|
94
|
-
extract_timing_and_operation
|
95
|
-
true
|
96
|
-
end
|
97
|
-
|
98
|
-
def extract_timing_and_operation
|
99
|
-
self.timing = extract_timing
|
100
|
-
self.operation = extract_operation
|
101
|
-
end
|
74
|
+
attr_accessor :operation, :cached
|
102
75
|
|
103
76
|
def extract_timing
|
104
77
|
match = clean_content.match(/\(([0-9.]+ms)\)/)
|
@@ -5,18 +5,17 @@ module LogBench
|
|
5
5
|
class Request < Entry
|
6
6
|
attr_reader :method, :path, :status, :duration, :controller, :action, :params, :related_logs
|
7
7
|
|
8
|
-
def initialize(
|
8
|
+
def initialize(json_data)
|
9
9
|
super
|
10
|
+
self.type = :http_request
|
10
11
|
self.related_logs = []
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
new(raw_line)
|
12
|
+
self.method = json_data["method"]
|
13
|
+
self.path = json_data["path"]
|
14
|
+
self.status = json_data["status"]
|
15
|
+
self.duration = json_data["duration"]
|
16
|
+
self.controller = json_data["controller"]
|
17
|
+
self.action = json_data["action"]
|
18
|
+
self.params = parse_params(json_data["params"])
|
20
19
|
end
|
21
20
|
|
22
21
|
def add_related_log(log_entry)
|
@@ -79,20 +78,6 @@ module LogBench
|
|
79
78
|
@cached_query_count = nil
|
80
79
|
end
|
81
80
|
|
82
|
-
def extract_from_json(data)
|
83
|
-
return false unless super
|
84
|
-
|
85
|
-
self.method = data["method"]
|
86
|
-
self.path = data["path"]
|
87
|
-
self.status = data["status"]
|
88
|
-
self.duration = data["duration"]
|
89
|
-
self.controller = data["controller"]
|
90
|
-
self.action = data["action"]
|
91
|
-
self.request_id = data["request_id"]
|
92
|
-
self.params = parse_params(data["params"])
|
93
|
-
true
|
94
|
-
end
|
95
|
-
|
96
81
|
def parse_params(params_data)
|
97
82
|
return nil unless params_data
|
98
83
|
|
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.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Benjamín Silva
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-08
|
10
|
+
date: 2025-09-08 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: zeitwerk
|