log_bench 0.1.5 → 0.1.7

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: eb8aba2b715d494f771f7bf0b1dda90719ae1a72e36823e86832f6ccbd7966b3
4
- data.tar.gz: e7f5a7f4322778e74a4de3f33603c5de448e2e021c78e1506cf97507226ffa80
3
+ metadata.gz: 6190e59783385726fb1b48af6daf05113619df53ab0f614927e40850fdb69d71
4
+ data.tar.gz: fb9dad6de4675da021d49f9b2c6bf41e387b8bbbd7ae2637fa3345f7beb19682
5
5
  SHA512:
6
- metadata.gz: 966d732d29bdbeaf30f1428b1f26126733120d2922c4a711a74e629fe78eae088702a51a6d20c311f097e98484bb93334a5eb8387b412b31449934e205013604
7
- data.tar.gz: 628a28c650da6ed06b837397e3f79c09fc115ad36c2082ebb8dc9207a87e27034b04696a8d3462b2bb6be278a611ae08909ee4ecac3782509dce908dea5dda45
6
+ metadata.gz: '02058887b5e6192622b8397d163b53d9be4cf1b73b192a216284e40941b5833e7e485d144fe00c21c76face673e5c67036467ed28eaae95842109b81cce2b7b5'
7
+ data.tar.gz: 5722c117f9eb0962007d0d21d8c38a3039df687b792cf9899531acfa438c3ac279637e2256bc6a04d5847634d52e386e18e7b1119c6c2278626ca09432660f3f
data/README.md CHANGED
@@ -46,6 +46,11 @@ Rails.application.configure do
46
46
  # LogBench: Configure structured logging
47
47
  config.lograge.enabled = true
48
48
  config.lograge.formatter = Lograge::Formatters::Json.new
49
+ config.log_bench.show_init_message = :full # or :min or :none
50
+ config.lograge.custom_options = lambda do |event|
51
+ params = event.payload[:params]&.except("controller", "action")
52
+ { params: params } if params.present?
53
+ end
49
54
  config.logger ||= ActiveSupport::Logger.new(config.default_log_file)
50
55
  config.logger.formatter = LogBench::JsonFormatter.new
51
56
  end
data/exe/log_bench CHANGED
@@ -66,13 +66,12 @@ rescue Interrupt
66
66
  exit 0
67
67
  rescue => e
68
68
  Curses.close_screen if defined?(Curses)
69
- puts "❌ Error: #{e.message}"
69
+ puts "❌ Error: #{e.message} \n#{e.backtrace.join("\n")}"
70
70
  puts
71
71
  puts "Common issues:"
72
72
  puts " - Log file doesn't exist or is empty"
73
73
  puts " - Lograge not configured (see README.md for setup)"
74
74
  puts " - Log format not supported (LogBench requires lograge JSON format)"
75
- puts " Error backtrace: \n #{e.backtrace.join("\n")}"
76
75
  puts
77
76
  puts "For help: log_bench --help"
78
77
  exit 1
@@ -21,6 +21,7 @@ module LogBench
21
21
  self.log_file_path = find_log_file(log_file_path)
22
22
  self.state = State.new
23
23
  validate_log_file!
24
+ validate_configuration!
24
25
  end
25
26
 
26
27
  def run
@@ -37,6 +38,8 @@ module LogBench
37
38
 
38
39
  private
39
40
 
41
+ attr_accessor :log_file_path, :state, :screen, :monitor, :input_handler, :renderer
42
+
40
43
  def find_log_file(path)
41
44
  candidates = [path] + DEFAULT_LOG_PATHS
42
45
  candidates.find { |candidate| File.exist?(candidate) } || path
@@ -50,13 +53,24 @@ module LogBench
50
53
  end
51
54
  end
52
55
 
56
+ def log_file_name
57
+ File.basename(log_file_path)
58
+ end
59
+
60
+ def validate_configuration!
61
+ ConfigurationValidator.validate_rails_config!
62
+ rescue ConfigurationValidator::ConfigurationError => e
63
+ puts e.message
64
+ exit 1
65
+ end
66
+
53
67
  def setup_screen
54
68
  self.screen = Screen.new
55
69
  screen.setup
56
70
  end
57
71
 
58
72
  def setup_components
59
- self.renderer = Renderer::Main.new(screen, state)
73
+ self.renderer = Renderer::Main.new(screen, state, log_file_name)
60
74
  self.input_handler = InputHandler.new(state, screen, renderer)
61
75
  end
62
76
 
@@ -93,10 +107,6 @@ module LogBench
93
107
  monitor&.stop
94
108
  screen&.cleanup
95
109
  end
96
-
97
- private
98
-
99
- attr_accessor :log_file_path, :state, :screen, :monitor, :input_handler, :renderer
100
110
  end
101
111
  end
102
112
  end
@@ -7,6 +7,7 @@ module LogBench
7
7
  module Renderer
8
8
  class Details
9
9
  include Curses
10
+ EMPTY_LINE = {text: "", color: nil}
10
11
 
11
12
  def initialize(screen, state, scrollbar, ansi_renderer)
12
13
  self.screen = screen
@@ -110,7 +111,7 @@ module LogBench
110
111
  else color_pair(2) | A_BOLD
111
112
  end
112
113
 
113
- lines << {text: "", color: nil} # Empty line
114
+ lines << EMPTY_LINE
114
115
  lines << {
115
116
  text: "Method: #{log[:method]}",
116
117
  color: nil,
@@ -125,6 +126,7 @@ module LogBench
125
126
  add_status_duration_lines(lines, log)
126
127
  add_controller_lines(lines, log)
127
128
  add_request_id_lines(lines, log)
129
+ add_params_lines(lines, log, max_width)
128
130
  add_related_logs_section(lines, log)
129
131
 
130
132
  lines
@@ -138,6 +140,7 @@ module LogBench
138
140
  duration: request.duration,
139
141
  controller: request.controller,
140
142
  action: request.action,
143
+ params: request.params,
141
144
  request_id: request.request_id,
142
145
  related_logs: build_related_logs(request)
143
146
  }
@@ -240,6 +243,80 @@ module LogBench
240
243
  end
241
244
  end
242
245
 
246
+ def add_params_lines(lines, log, max_width)
247
+ return unless log[:params]
248
+
249
+ lines << EMPTY_LINE
250
+ lines << {
251
+ text: "Params:",
252
+ color: nil,
253
+ segments: [
254
+ {text: "Params:", color: color_pair(1) | A_BOLD}
255
+ ]
256
+ }
257
+
258
+ params_text = format_params(log[:params])
259
+ indent = " "
260
+
261
+ # Split the params text into lines that fit within the available width
262
+ line_width = max_width - indent.length
263
+ remaining_text = params_text
264
+
265
+ while remaining_text && remaining_text.length > 0
266
+ line_chunk = remaining_text[0, line_width]
267
+ lines << {text: indent + line_chunk, color: nil}
268
+ remaining_text = remaining_text[line_width..] || ""
269
+ end
270
+ end
271
+
272
+ def format_params(params)
273
+ case params
274
+ when Hash
275
+ # Format as readable key-value pairs
276
+ if params.empty?
277
+ "{}"
278
+ else
279
+ formatted_pairs = params.map do |key, value|
280
+ formatted_value = case value
281
+ when Hash
282
+ format_nested_hash(value)
283
+ when Array
284
+ "[#{value.join(", ")}]"
285
+ else
286
+ value.to_s
287
+ end
288
+ "#{key}: #{formatted_value}"
289
+ end
290
+ "{ #{formatted_pairs.join(", ")} }"
291
+ end
292
+ when String
293
+ params
294
+ else
295
+ params.to_s
296
+ end
297
+ end
298
+
299
+ def format_nested_hash(hash, depth = 1)
300
+ return "{}" if hash.empty?
301
+
302
+ if depth > 2 # Limit nesting depth to avoid overly complex display
303
+ "{...}"
304
+ else
305
+ formatted_pairs = hash.map do |key, value|
306
+ formatted_value = case value
307
+ when Hash
308
+ format_nested_hash(value, depth + 1)
309
+ when Array
310
+ "[#{value.join(", ")}]"
311
+ else
312
+ value.to_s
313
+ end
314
+ "#{key}: #{formatted_value}"
315
+ end
316
+ "{ #{formatted_pairs.join(", ")} }"
317
+ end
318
+ end
319
+
243
320
  def add_request_id_lines(lines, log)
244
321
  if log[:request_id]
245
322
  lines << {
@@ -276,7 +353,7 @@ module LogBench
276
353
  query_stats = calculate_query_stats(related_logs)
277
354
 
278
355
  # Add query summary
279
- lines << {text: "", color: nil} # Empty line
356
+ lines << EMPTY_LINE
280
357
 
281
358
  # Show filter status in summary if filtering is active
282
359
  summary_title = "Query Summary:"
@@ -310,7 +387,7 @@ module LogBench
310
387
  end
311
388
  end
312
389
 
313
- lines << {text: "", color: nil} # Empty line
390
+ lines << EMPTY_LINE
314
391
 
315
392
  # Show filtered logs section
316
393
  if state.detail_filter.present?
@@ -459,7 +536,7 @@ module LogBench
459
536
 
460
537
  # Add extra empty lines after all chunks
461
538
  extra_empty_lines.times do
462
- lines << {text: "", color: nil}
539
+ lines << EMPTY_LINE
463
540
  end
464
541
 
465
542
  text_chunks.length
@@ -13,15 +13,15 @@ module LogBench
13
13
 
14
14
  # Layout constants
15
15
  TITLE_X_OFFSET = 2
16
- FILENAME_X_OFFSET = 15
17
16
 
18
17
  # Color constants
19
18
  HEADER_CYAN = 1
20
19
  SUCCESS_GREEN = 3
21
20
 
22
- def initialize(screen, state)
21
+ def initialize(screen, state, log_file_name)
23
22
  self.screen = screen
24
23
  self.state = state
24
+ self.log_file_name = log_file_name
25
25
  end
26
26
 
27
27
  def draw
@@ -36,7 +36,7 @@ module LogBench
36
36
 
37
37
  private
38
38
 
39
- attr_accessor :screen, :state
39
+ attr_accessor :screen, :state, :log_file_name
40
40
 
41
41
  def draw_title
42
42
  header_win.setpos(1, TITLE_X_OFFSET)
@@ -45,8 +45,8 @@ module LogBench
45
45
  end
46
46
 
47
47
  def draw_file_name
48
- header_win.setpos(1, screen.width / 2 - FILENAME_X_OFFSET)
49
- header_win.attron(color_pair(SUCCESS_GREEN)) { header_win.addstr(DEFAULT_LOG_FILENAME) }
48
+ header_win.setpos(1, (screen.width / 2) - (log_file_name.length / 2))
49
+ header_win.attron(color_pair(SUCCESS_GREEN)) { header_win.addstr(log_file_name) }
50
50
  end
51
51
 
52
52
  def draw_stats
@@ -4,12 +4,12 @@ module LogBench
4
4
  module App
5
5
  module Renderer
6
6
  class Main
7
- def initialize(screen, state)
7
+ def initialize(screen, state, log_file_name)
8
8
  self.screen = screen
9
9
  self.state = state
10
10
  self.scrollbar = Scrollbar.new(screen)
11
11
  self.ansi_renderer = Ansi.new(screen)
12
- self.header = Header.new(screen, state)
12
+ self.header = Header.new(screen, state, log_file_name)
13
13
  self.request_list = RequestList.new(screen, state, scrollbar)
14
14
  self.details = Details.new(screen, state, scrollbar, ansi_renderer)
15
15
  self.update_modal = UpdateModal.new(screen, state)
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LogBench
4
+ # Validates that lograge is properly configured for LogBench
5
+ class ConfigurationValidator
6
+ class ConfigurationError < StandardError; end
7
+ ERROR_CONFIGS = {
8
+ enabled: {
9
+ title: "Lograge is not enabled",
10
+ description: "LogBench requires lograge to be enabled in your Rails application.",
11
+ fix: "config.lograge.enabled = true"
12
+ },
13
+ options: {
14
+ title: "Lograge custom_options missing",
15
+ description: "LogBench needs custom_options to include params fields.",
16
+ fix: <<~FIX.strip
17
+ config.lograge.custom_options = lambda do |event|
18
+ params = event.payload[:params]&.except("controller", "action")
19
+ { params: params } if params.present?
20
+ end
21
+ FIX
22
+ },
23
+ formatter: {
24
+ title: "Wrong lograge formatter",
25
+ description: "LogBench requires LogBench::JsonFormatter for proper log parsing.",
26
+ fix: "config.lograge.formatter = LogBench::JsonFormatter.new"
27
+ }
28
+ }.freeze
29
+
30
+ def self.validate_rails_config!
31
+ new.validate_rails_config!
32
+ end
33
+
34
+ def validate_rails_config!
35
+ return true unless defined?(Rails) && Rails.application
36
+
37
+ validate_lograge_enabled!
38
+ validate_custom_options!
39
+ validate_json_formatter!
40
+
41
+ true
42
+ end
43
+
44
+ private
45
+
46
+ def validate_lograge_enabled!
47
+ unless lograge_config&.enabled
48
+ raise ConfigurationError, build_error_message(:enabled)
49
+ end
50
+ end
51
+
52
+ def validate_custom_options!
53
+ unless lograge_config&.custom_options
54
+ raise ConfigurationError, build_error_message(:options)
55
+ end
56
+ end
57
+
58
+ def validate_json_formatter!
59
+ formatter = lograge_config&.formatter
60
+ unless formatter.is_a?(LogBench::JsonFormatter)
61
+ raise ConfigurationError, build_error_message(:formatter)
62
+ end
63
+ end
64
+
65
+ def lograge_config
66
+ return nil unless Rails.application.config.respond_to?(:lograge)
67
+ Rails.application.config.lograge
68
+ end
69
+
70
+ def build_error_message(error_type)
71
+ config = ERROR_CONFIGS[error_type]
72
+
73
+ <<~ERROR
74
+ ❌ #{config[:title]}
75
+
76
+ #{config[:description]}
77
+
78
+ Add this to config/environments/development.rb:
79
+ #{config[:fix]}
80
+
81
+ For complete setup: https://github.com/silva96/log_bench#configuration
82
+ ERROR
83
+ end
84
+ end
85
+ end
@@ -3,7 +3,7 @@
3
3
  module LogBench
4
4
  module Log
5
5
  class Request < Entry
6
- attr_reader :method, :path, :status, :duration, :controller, :action
6
+ attr_reader :method, :path, :status, :duration, :controller, :action, :params
7
7
  attr_accessor :related_logs
8
8
 
9
9
  def initialize(raw_line)
@@ -65,13 +65,14 @@ module LogBench
65
65
  duration: duration,
66
66
  controller: controller,
67
67
  action: action,
68
+ params: params,
68
69
  related_logs: related_logs.map(&:to_h)
69
70
  )
70
71
  end
71
72
 
72
73
  private
73
74
 
74
- attr_writer :method, :path, :status, :duration, :controller, :action
75
+ attr_writer :method, :path, :status, :duration, :controller, :action, :params
75
76
 
76
77
  def extract_from_json(data)
77
78
  return false unless super
@@ -83,8 +84,22 @@ module LogBench
83
84
  self.controller = data["controller"]
84
85
  self.action = data["action"]
85
86
  self.request_id = data["request_id"]
87
+ self.params = parse_params(data["params"])
86
88
  true
87
89
  end
90
+
91
+ def parse_params(params_data)
92
+ return nil unless params_data
93
+
94
+ case params_data
95
+ when String
96
+ JSON.parse(params_data)
97
+ when Hash
98
+ params_data
99
+ end
100
+ rescue JSON::ParserError
101
+ params_data.to_s
102
+ end
88
103
  end
89
104
  end
90
105
  end
@@ -2,8 +2,12 @@ require "rails/railtie"
2
2
 
3
3
  module LogBench
4
4
  class Railtie < Rails::Railtie
5
+ LINE = "=" * 70
6
+
5
7
  railtie_name :log_bench
6
8
 
9
+ config.log_bench = ActiveSupport::OrderedOptions.new
10
+
7
11
  # LogBench uses manual configuration (see README.md)
8
12
 
9
13
  # Provide helpful rake tasks
@@ -11,33 +15,67 @@ module LogBench
11
15
  load "tasks/log_bench.rake"
12
16
  end
13
17
 
18
+ initializer "log_bench.configure" do |app|
19
+ LogBench.setup do |config|
20
+ config.show_init_message = app.config.log_bench.show_init_message
21
+ config.show_init_message = :full if config.show_init_message.nil?
22
+ end
23
+ end
24
+
14
25
  # Show installation instructions when Rails starts in development
15
26
  initializer "log_bench.show_instructions", after: :load_config_initializers do
16
- if Rails.env.development?
17
- # Check if lograge is properly configured
18
- puts "\n" + "=" * 70
19
- puts "\n" + "=" * 70
20
- if Rails.application.config.respond_to?(:lograge) &&
21
- Rails.application.config.lograge.enabled
22
- puts "✅ LogBench is ready to use!"
23
- puts "=" * 70
24
- puts "View your logs: log_bench log/development.log"
25
- else
26
-
27
- puts "🚀 LogBench is ready to configure!"
28
- puts "=" * 70
29
- puts "To start using LogBench:"
30
- puts " 1. See README.md for configuration instructions"
31
- puts " 2. Configure lograge in config/environments/development.rb"
32
- puts " 3. Restart your Rails server"
33
- puts " 4. Make some requests to generate logs"
34
- puts " 5. View logs: log_bench log/development.log"
35
- puts
27
+ return unless Rails.env.development?
28
+
29
+ # Use configuration validator to check lograge setup
30
+ begin
31
+ ConfigurationValidator.validate_rails_config!
32
+ # Lograge is properly configured
33
+ if LogBench.configuration.show_init_message.eql? :full
34
+ print_full_init_message
35
+ print_help_instructions
36
+ puts LINE
37
+ puts LINE
38
+ elsif LogBench.configuration.show_init_message.eql? :min
39
+ print_min_init_message
36
40
  end
37
- puts "For help: log_bench --help"
38
- puts "=" * 70 + "\n"
39
- puts "=" * 70 + "\n"
41
+ rescue ConfigurationValidator::ConfigurationError => e
42
+ # Lograge needs configuration
43
+ print_configuration_instructions
44
+ puts "⚠️ Configuration issue: #{e.message}"
45
+ print_help_instructions
46
+ puts LINE
47
+ puts LINE
40
48
  end
41
49
  end
50
+
51
+ private
52
+
53
+ def print_configuration_instructions
54
+ puts "🚀 LogBench is ready to configure!"
55
+ puts LINE
56
+ puts "To start using LogBench:"
57
+ puts " 1. See README.md for configuration instructions"
58
+ puts " 2. Configure lograge in config/environments/development.rb"
59
+ puts " 3. Restart your Rails server"
60
+ puts " 4. Make some requests to generate logs"
61
+ puts " 5. View logs: log_bench log/development.log"
62
+ puts ""
63
+ end
64
+
65
+ def print_min_init_message
66
+ puts "✅ LogBench is ready to use!"
67
+ end
68
+
69
+ def print_full_init_message
70
+ puts "\n" + LINE
71
+ puts "\n" + LINE
72
+ puts "✅ LogBench is ready to use!"
73
+ puts LINE
74
+ puts "View your logs: log_bench log/development.log"
75
+ end
76
+
77
+ def print_help_instructions
78
+ puts "For help: log_bench --help"
79
+ end
42
80
  end
43
81
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LogBench
4
- VERSION = "0.1.5"
4
+ VERSION = "0.1.7"
5
5
  end
data/lib/log_bench.rb CHANGED
@@ -13,12 +13,25 @@ loader.setup
13
13
  module LogBench
14
14
  class Error < StandardError; end
15
15
 
16
- # Parse log lines and return an array of entries
17
- def self.parse(lines)
18
- lines = [lines] if lines.is_a?(String)
16
+ class Configuration
17
+ attr_accessor :show_init_message
18
+ end
19
+
20
+ class << self
21
+ attr_accessor :configuration
22
+
23
+ # Parse log lines and return an array of entries
24
+ def parse(lines)
25
+ lines = [lines] if lines.is_a?(String)
26
+
27
+ collection = Log::Collection.new(lines)
28
+ collection.requests
29
+ end
19
30
 
20
- collection = Log::Collection.new(lines)
21
- collection.requests
31
+ def setup
32
+ self.configuration ||= Configuration.new
33
+ yield(configuration)
34
+ end
22
35
  end
23
36
  end
24
37
 
@@ -13,49 +13,6 @@ namespace :log_bench do
13
13
  puts "4. Restart Rails server"
14
14
  end
15
15
 
16
- desc "Check LogBench configuration"
17
- task check: :environment do
18
- puts "\n" + "=" * 60
19
- puts "🔍 LogBench Configuration Check"
20
- puts "=" * 60
21
-
22
- if Rails.application.config.respond_to?(:lograge) &&
23
- Rails.application.config.lograge.enabled
24
- puts "✅ Lograge is enabled"
25
-
26
- if Rails.application.config.lograge.formatter.is_a?(Lograge::Formatters::Json)
27
- puts "✅ JSON formatter is configured"
28
- else
29
- puts "⚠️ JSON formatter is not configured"
30
- puts " LogBench requires JSON format"
31
- puts " See README.md for configuration instructions"
32
- end
33
-
34
- # Check if log file exists and has content
35
- log_file = "log/#{Rails.env}.log"
36
- if File.exist?(log_file) && File.size(log_file) > 0
37
- puts "✅ Log file exists: #{log_file}"
38
- else
39
- puts "⚠️ Log file is empty or doesn't exist: #{log_file}"
40
- puts " Make some requests to generate logs"
41
- end
42
-
43
- puts
44
- puts "🎉 LogBench is ready to use!"
45
- puts " Command: log_bench #{log_file}"
46
- else
47
- puts "❌ Lograge is not enabled"
48
- puts
49
- puts "To fix this:"
50
- puts " 1. See README.md for configuration instructions"
51
- puts " 2. Configure lograge in config/environments/development.rb"
52
- puts " 3. Restart your Rails server"
53
- puts " 4. Make some requests"
54
- puts " 5. Run: log_bench log/development.log"
55
- end
56
- puts "=" * 60 + "\n"
57
- end
58
-
59
16
  desc "Show LogBench usage instructions"
60
17
  task :help do
61
18
  puts <<~HELP
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.1.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamín Silva
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-06-09 00:00:00.000000000 Z
10
+ date: 2025-06-17 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: zeitwerk
@@ -139,6 +139,7 @@ files:
139
139
  - lib/log_bench/app/screen.rb
140
140
  - lib/log_bench/app/sort.rb
141
141
  - lib/log_bench/app/state.rb
142
+ - lib/log_bench/configuration_validator.rb
142
143
  - lib/log_bench/json_formatter.rb
143
144
  - lib/log_bench/log/cache_entry.rb
144
145
  - lib/log_bench/log/call_line_entry.rb