request-log-analyzer 1.0.4 → 1.1.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.
data/DESIGN CHANGED
@@ -12,3 +12,13 @@ This allows you to easily add extra reports, filters and outputs.
12
12
 
13
13
  3) Gather output from pipeline.
14
14
  Controller.report
15
+
16
+ At the moment the supported sources are file and STDIN.
17
+ In the future we want to be able to have a generated request database as source.
18
+ This will make interactive downdrilling possible.
19
+
20
+
21
+ For the report generation output we now use the File and the STDOUT class, as they both support <<.
22
+ In the future we want to have a OutputFile, OutputSTDOUT and OutputHTML class, so that reports can generate
23
+ tables, lines and comments and push them into the output class.
24
+
data/README.rdoc CHANGED
@@ -10,6 +10,8 @@ Merb to produce a performance report. Its purpose is to find what actions are be
10
10
  * Fast
11
11
  * MIT licensed
12
12
 
13
+ Request log analyzer was designed and built by Willem van Bergen and Bart ten Brinke.
14
+
13
15
  == Installation
14
16
 
15
17
  Install request-log-analyzer as a Ruby gem:
@@ -36,3 +38,4 @@ For more details and available command line options, see the project's wiki:http
36
38
 
37
39
  * Project wiki at GitHub: http://wiki.github.com/wvanbergen/request-log-analyzer
38
40
  * wvanbergen's blog posts: http://techblog.floorplanner.com/tag/request-log-analyzer
41
+ * bart ten brinke's blog posts: http://movesonrails.com
@@ -49,6 +49,7 @@ begin
49
49
  command_line.option(:before)
50
50
 
51
51
  command_line.switch(:boring, :b)
52
+ command_line.option(:output, :alias => :o, :default => 'FixedWidth')
52
53
  command_line.option(:report_width, :default => terminal_width - 1)
53
54
 
54
55
  command_line.switch(:debug)
@@ -73,6 +74,7 @@ rescue CommandLine::Error => e
73
74
  puts " --database <filename>, -d: Creates an SQLite3 database of all the parsed request information."
74
75
  puts " --debug Print debug information while parsing."
75
76
  puts " --file <filename> Output to file."
77
+ puts " --output <format> Output format. Supports 'HTML' and 'FixedWidth' (default)"
76
78
  puts
77
79
  puts "Examples:"
78
80
  puts " request-log-analyzer development.log"
@@ -116,7 +118,7 @@ else
116
118
  puts "Request log analyzer, by Willem van Bergen and Bart ten Brinke - Version 1.0\n\n"
117
119
 
118
120
  # Run the request_log_analyzer!
119
- RequestLogAnalyzer::Controller.build(arguments, terminal_width).run!
121
+ RequestLogAnalyzer::Controller.build(arguments).run!
120
122
 
121
123
  puts
122
124
  puts "Thanks for using request-log-analyzer"
@@ -7,230 +7,224 @@
7
7
  #
8
8
  # You can redistribute it and/or modify it under the terms
9
9
  # of Ruby's license.
10
- #
11
10
 
12
- class ProgressBar
13
- VERSION = "0.9"
14
-
15
- def initialize (title, total, out = STDERR)
16
- @title = title
17
- @total = total
18
- @out = out
19
- @terminal_width = 80
20
- @bar_mark = '='
21
- @current = 0
22
- @previous = 0
23
- @finished_p = false
24
- @start_time = Time.now
25
- @previous_time = @start_time
26
- @title_width = 24
27
- @format = "%-#{@title_width}s %3d%% %s %s"
28
- @format_arguments = [:title, :percentage, :bar, :stat]
29
- clear
30
- show
31
- end
32
- attr_reader :title
33
- attr_reader :current
34
- attr_reader :total
35
- attr_accessor :start_time
36
-
37
- private
38
- def fmt_bar
39
- bar_width = do_percentage * @terminal_width / 100
40
- sprintf("[%s%s]",
41
- @bar_mark * bar_width,
42
- " " * (@terminal_width - bar_width))
43
- end
11
+ module CommandLine
12
+ class ProgressBar
13
+ VERSION = "0.9"
14
+
15
+ def initialize (title, total, out = STDERR)
16
+ @title = title
17
+ @total = total
18
+ @out = out
19
+ @terminal_width = 80
20
+ @bar_mark = '='
21
+ @current = 0
22
+ @previous = 0
23
+ @finished_p = false
24
+ @start_time = Time.now
25
+ @previous_time = @start_time
26
+ @title_width = 24
27
+ @format = "%-#{@title_width}s %3d%% %s %s"
28
+ @format_arguments = [:title, :percentage, :bar, :stat]
29
+ clear
30
+ show
31
+ end
32
+ attr_reader :title
33
+ attr_reader :current
34
+ attr_reader :total
35
+ attr_accessor :start_time
36
+
37
+ private
38
+ def fmt_bar
39
+ bar_width = do_percentage * @terminal_width / 100
40
+ sprintf("[%s%s]",
41
+ @bar_mark * bar_width,
42
+ " " * (@terminal_width - bar_width))
43
+ end
44
44
 
45
- def fmt_percentage
46
- do_percentage
47
- end
45
+ def fmt_percentage
46
+ do_percentage
47
+ end
48
48
 
49
- def fmt_stat
50
- if @finished_p then elapsed else eta end
51
- end
49
+ def fmt_stat
50
+ if @finished_p then elapsed else eta end
51
+ end
52
52
 
53
- def fmt_stat_for_file_transfer
54
- if @finished_p then
55
- sprintf("%s %s %s", bytes, transfer_rate, elapsed)
56
- else
57
- sprintf("%s %s %s", bytes, transfer_rate, eta)
53
+ def fmt_stat_for_file_transfer
54
+ if @finished_p then
55
+ sprintf("%s %s %s", bytes, transfer_rate, elapsed)
56
+ else
57
+ sprintf("%s %s %s", bytes, transfer_rate, eta)
58
+ end
58
59
  end
59
- end
60
60
 
61
- def fmt_title
62
- @title[0,(@title_width - 1)] + ":"
63
- end
61
+ def fmt_title
62
+ @title[0,(@title_width - 1)] + ":"
63
+ end
64
64
 
65
- def convert_bytes (bytes)
66
- if bytes < 1024
67
- sprintf("%6dB", bytes)
68
- elsif bytes < 1024 * 1000 # 1000kb
69
- sprintf("%5.1fKB", bytes.to_f / 1024)
70
- elsif bytes < 1024 * 1024 * 1000 # 1000mb
71
- sprintf("%5.1fMB", bytes.to_f / 1024 / 1024)
72
- else
73
- sprintf("%5.1fGB", bytes.to_f / 1024 / 1024 / 1024)
65
+ def convert_bytes (bytes)
66
+ if bytes < 1024
67
+ sprintf("%6dB", bytes)
68
+ elsif bytes < 1024 * 1000 # 1000kb
69
+ sprintf("%5.1fKB", bytes.to_f / 1024)
70
+ elsif bytes < 1024 * 1024 * 1000 # 1000mb
71
+ sprintf("%5.1fMB", bytes.to_f / 1024 / 1024)
72
+ else
73
+ sprintf("%5.1fGB", bytes.to_f / 1024 / 1024 / 1024)
74
+ end
74
75
  end
75
- end
76
76
 
77
- def transfer_rate
78
- bytes_per_second = @current.to_f / (Time.now - @start_time)
79
- sprintf("%s/s", convert_bytes(bytes_per_second))
80
- end
77
+ def transfer_rate
78
+ bytes_per_second = @current.to_f / (Time.now - @start_time)
79
+ sprintf("%s/s", convert_bytes(bytes_per_second))
80
+ end
81
81
 
82
- def bytes
83
- convert_bytes(@current)
84
- end
82
+ def bytes
83
+ convert_bytes(@current)
84
+ end
85
85
 
86
- def format_time (t)
87
- t = t.to_i
88
- sec = t % 60
89
- min = (t / 60) % 60
90
- hour = t / 3600
91
- sprintf("%02d:%02d:%02d", hour, min, sec);
92
- end
86
+ def format_time (t)
87
+ t = t.to_i
88
+ sec = t % 60
89
+ min = (t / 60) % 60
90
+ hour = t / 3600
91
+ sprintf("%02d:%02d:%02d", hour, min, sec);
92
+ end
93
93
 
94
- # ETA stands for Estimated Time of Arrival.
95
- def eta
96
- if @current == 0
97
- "ETA: --:--:--"
98
- else
99
- elapsed = Time.now - @start_time
100
- eta = elapsed * @total / @current - elapsed;
101
- sprintf("ETA: %s", format_time(eta))
94
+ # ETA stands for Estimated Time of Arrival.
95
+ def eta
96
+ if @current == 0
97
+ "ETA: --:--:--"
98
+ else
99
+ elapsed = Time.now - @start_time
100
+ eta = elapsed * @total / @current - elapsed;
101
+ sprintf("ETA: %s", format_time(eta))
102
+ end
102
103
  end
103
- end
104
104
 
105
- def elapsed
106
- elapsed = Time.now - @start_time
107
- sprintf("Time: %s", format_time(elapsed))
108
- end
105
+ def elapsed
106
+ elapsed = Time.now - @start_time
107
+ sprintf("Time: %s", format_time(elapsed))
108
+ end
109
109
 
110
- def eol
111
- if @finished_p then "\n" else "\r" end
112
- end
113
-
114
- def do_percentage
115
- if @total.zero?
116
- 100
117
- else
118
- @current * 100 / @total
110
+ def eol
111
+ if @finished_p then "\n" else "\r" end
119
112
  end
120
- end
121
113
 
122
- def get_width
123
- # FIXME: I don't know how portable it is.
124
- default_width = 80
125
- begin
126
- tiocgwinsz = 0x5413
127
- data = [0, 0, 0, 0].pack("SSSS")
128
- if @out.ioctl(tiocgwinsz, data) >= 0 then
129
- rows, cols, xpixels, ypixels = data.unpack("SSSS")
130
- if cols >= 0 then cols else default_width end
114
+ def do_percentage
115
+ if @total.zero?
116
+ 100
131
117
  else
132
- default_width
118
+ @current * 100 / @total
133
119
  end
134
- rescue Exception
135
- default_width
136
120
  end
137
- end
138
121
 
139
- def show
140
- arguments = @format_arguments.map {|method|
141
- method = sprintf("fmt_%s", method)
142
- send(method)
143
- }
144
- line = sprintf(@format, *arguments)
145
-
146
- width = get_width
147
- if line.length == width - 1
148
- @out.print(line + eol)
149
- @out.flush
150
- elsif line.length >= width
151
- @terminal_width = [@terminal_width - (line.length - width + 1), 0].max
152
- if @terminal_width == 0 then @out.print(line + eol) else show end
153
- else # line.length < width - 1
154
- @terminal_width += width - line.length + 1
155
- show
122
+ def get_width
123
+ # FIXME: I don't know how portable it is.
124
+ default_width = 80
125
+ begin
126
+ tiocgwinsz = 0x5413
127
+ data = [0, 0, 0, 0].pack("SSSS")
128
+ if @out.ioctl(tiocgwinsz, data) >= 0 then
129
+ rows, cols, xpixels, ypixels = data.unpack("SSSS")
130
+ if cols >= 0 then cols else default_width end
131
+ else
132
+ default_width
133
+ end
134
+ rescue Exception
135
+ default_width
136
+ end
156
137
  end
157
- @previous_time = Time.now
158
- end
159
138
 
160
- def show_if_needed
161
- if @total.zero?
162
- cur_percentage = 100
163
- prev_percentage = 0
164
- else
165
- cur_percentage = (@current * 100 / @total).to_i
166
- prev_percentage = (@previous * 100 / @total).to_i
139
+ def show
140
+ arguments = @format_arguments.map {|method|
141
+ method = sprintf("fmt_%s", method)
142
+ send(method)
143
+ }
144
+ line = sprintf(@format, *arguments)
145
+
146
+ width = get_width
147
+ if line.length == width - 1
148
+ @out.print(line + eol)
149
+ @out.flush
150
+ elsif line.length >= width
151
+ @terminal_width = [@terminal_width - (line.length - width + 1), 0].max
152
+ if @terminal_width == 0 then @out.print(line + eol) else show end
153
+ else # line.length < width - 1
154
+ @terminal_width += width - line.length + 1
155
+ show
156
+ end
157
+ @previous_time = Time.now
167
158
  end
168
159
 
169
- # Use "!=" instead of ">" to support negative changes
170
- if cur_percentage != prev_percentage ||
171
- Time.now - @previous_time >= 1 || @finished_p
172
- show
160
+ def show_if_needed
161
+ if @total.zero?
162
+ cur_percentage = 100
163
+ prev_percentage = 0
164
+ else
165
+ cur_percentage = (@current * 100 / @total).to_i
166
+ prev_percentage = (@previous * 100 / @total).to_i
167
+ end
168
+
169
+ # Use "!=" instead of ">" to support negative changes
170
+ if cur_percentage != prev_percentage ||
171
+ Time.now - @previous_time >= 1 || @finished_p
172
+ show
173
+ end
173
174
  end
174
- end
175
175
 
176
- public
177
- def clear
178
- @out.print "\r"
179
- @out.print(" " * (get_width - 1))
180
- @out.print "\r"
181
- end
176
+ public
177
+ def clear
178
+ @out.print "\r"
179
+ @out.print(" " * (get_width - 1))
180
+ @out.print "\r"
181
+ end
182
182
 
183
- def finish
184
- @current = @total
185
- @finished_p = true
186
- show
187
- end
183
+ def finish
184
+ @current = @total
185
+ @finished_p = true
186
+ show
187
+ end
188
188
 
189
- def finished?
190
- @finished_p
191
- end
189
+ def finished?
190
+ @finished_p
191
+ end
192
192
 
193
- def file_transfer_mode
194
- @format_arguments = [:title, :percentage, :bar, :stat_for_file_transfer]
195
- end
193
+ def file_transfer_mode
194
+ @format_arguments = [:title, :percentage, :bar, :stat_for_file_transfer]
195
+ end
196
196
 
197
- def format= (format)
198
- @format = format
199
- end
197
+ def format= (format)
198
+ @format = format
199
+ end
200
200
 
201
- def format_arguments= (arguments)
202
- @format_arguments = arguments
203
- end
201
+ def format_arguments= (arguments)
202
+ @format_arguments = arguments
203
+ end
204
204
 
205
- def halt
206
- @finished_p = true
207
- show
208
- end
205
+ def halt
206
+ @finished_p = true
207
+ show
208
+ end
209
209
 
210
- def inc (step = 1)
211
- @current += step
212
- @current = @total if @current > @total
213
- show_if_needed
214
- @previous = @current
215
- end
210
+ def inc (step = 1)
211
+ @current += step
212
+ @current = @total if @current > @total
213
+ show_if_needed
214
+ @previous = @current
215
+ end
216
216
 
217
- def set (count)
218
- count = 0 if count < 0
219
- count = @total if count > @total
217
+ def set (count)
218
+ count = 0 if count < 0
219
+ count = @total if count > @total
220
220
 
221
- @current = count
222
- show_if_needed
223
- @previous = @current
224
- end
225
-
226
- def inspect
227
- "#<ProgressBar:#{@current}/#{@total}>"
228
- end
229
- end
221
+ @current = count
222
+ show_if_needed
223
+ @previous = @current
224
+ end
230
225
 
231
- class ReversedProgressBar < ProgressBar
232
- def do_percentage
233
- 100 - super
226
+ def inspect
227
+ "#<ProgressBar:#{@current}/#{@total}>"
228
+ end
234
229
  end
235
230
  end
236
-
@@ -38,7 +38,7 @@ module RequestLogAnalyzer::Aggregator
38
38
 
39
39
  # The report function is called at the end. Implement any result reporting
40
40
  # in this function.
41
- def report(output = STDOUT, report_width = 80, color = false)
41
+ def report(output)
42
42
  end
43
43
 
44
44
  end
@@ -50,13 +50,13 @@ module RequestLogAnalyzer::Aggregator
50
50
  end
51
51
 
52
52
  # Prints a short report of what has been inserted into the database
53
- def report(output = STDOUT, report_width = 80, color = false)
54
- output << "\n"
55
- output << green("━" * report_width, color) + "\n"
53
+ def report(output)
54
+ output.title('Request database created')
55
+
56
56
  output << "A database file has been created with all parsed request information.\n"
57
57
  output << "#{@request_count} requests have been added to the database.\n"
58
58
  output << "To execute queries on this database, run the following command:\n"
59
- output << " $ sqlite3 #{options[:database]}\n"
59
+ output << output.colorize(" $ sqlite3 #{options[:database]}\n", :bold)
60
60
  output << "\n"
61
61
  end
62
62
 
@@ -14,11 +14,9 @@ module RequestLogAnalyzer::Aggregator
14
14
  @warnings << "WARNING #{type.inspect} on line #{lineno}: #{message}\n"
15
15
  end
16
16
 
17
- def report(output=STDOUT, report_width = 80, color = false)
18
- output << "\n"
19
- output << "Warnings during parsing:\n"
20
- output << green("━" * report_width, color) + "\n"
21
- output << @warnings + "\n"
17
+ def report(output)
18
+ output.title("Warnings during parsing")
19
+ output.puts @warnings
22
20
  end
23
21
 
24
22
  end
@@ -67,36 +67,41 @@ module RequestLogAnalyzer::Aggregator
67
67
  @trackers.each { |tracker| tracker.finalize }
68
68
  end
69
69
 
70
- def report(output=STDOUT, report_width = 80, color = false)
71
- report_header(output, report_width, color)
70
+ def report(output)
71
+ report_header(output)
72
72
  if source.parsed_requests > 0
73
- @trackers.each { |tracker| tracker.report(output, report_width, color) }
73
+ @trackers.each { |tracker| tracker.report(output) }
74
74
  else
75
- output << "\n"
76
- output << "There were no requests analyzed.\n"
75
+ output.puts
76
+ output.puts('There were no requests analyzed.')
77
77
  end
78
- report_footer(output, report_width, color)
78
+ report_footer(output)
79
79
  end
80
80
 
81
- def report_header(output=STDOUT, report_width = 80, color = false)
82
- output << "Request summary\n"
83
- output << green("━" * report_width, color) + "\n"
84
- output << "Parsed lines: #{green(source.parsed_lines, color)}\n"
85
- output << "Parsed requests: #{green(source.parsed_requests, color)}\n"
86
- output << "Skipped lines: #{green(source.skipped_lines, color)}\n" if source.skipped_lines > 0
87
- if has_warnings?
88
- output << "Warnings: " + @warnings_encountered.map { |(key, value)| "#{key.inspect}: #{blue(value, color)}" }.join(', ') + "\n"
81
+ def report_header(output)
82
+ output.title("Request summary")
83
+
84
+ output.with_style(:cell_separator => false) do
85
+ output.table({:width => 20}, {:font => :bold}) do |rows|
86
+ rows << ['Parsed lines:', source.parsed_lines]
87
+ rows << ['Parsed request:', source.parsed_requests]
88
+ rows << ['Skipped lines:', source.skipped_lines]
89
+
90
+ rows << ["Warnings:", @warnings_encountered.map { |(key, value)| "#{key.inspect}: #{value}" }.join(', ')] if has_warnings?
91
+ end
89
92
  end
90
93
  output << "\n"
91
94
  end
92
95
 
93
- def report_footer(output=STDOUT, report_width = 80, color = false)
94
- output << "\n"
95
- if has_serious_warnings?
96
- output << green("" * report_width, color) + "\n"
97
- output << "Multiple warnings were encountered during parsing. Possibly, your logging " + "\n"
98
- output << "is not setup correctly. Visit this website for logging configuration tips:" + "\n"
99
- output << blue("http://github.com/wvanbergen/request-log-analyzer/wikis/configure-logging", color) + "\n"
96
+ def report_footer(output)
97
+ if has_serious_warnings?
98
+
99
+ output.title("Parse warnings")
100
+
101
+ output.puts "Multiple warnings were encountered during parsing. Possibly, your logging "
102
+ output.puts "is not setup correctly. Visit this website for logging configuration tips:"
103
+ output.puts output.link("http://github.com/wvanbergen/request-log-analyzer/wikis/configure-logging")
104
+ output.puts
100
105
  end
101
106
  end
102
107
 
@@ -29,17 +29,19 @@ module RequestLogAnalyzer
29
29
  # Builds a RequestLogAnalyzer::Controller given parsed command line arguments
30
30
  # <tt>arguments<tt> A CommandLine::Arguments hash containing parsed commandline parameters.
31
31
  # <rr>report_with</tt> Width of the report. Defaults to 80.
32
- def self.build(arguments, report_width = 80)
33
-
34
- options = { :report_width => arguments[:report_width].to_i, :output => STDOUT}
32
+ def self.build(arguments)
33
+ options = { }
35
34
 
36
35
  options[:database] = arguments[:database] if arguments[:database]
37
36
  options[:debug] = arguments[:debug]
38
- options[:colorize] = !arguments[:boring]
39
37
 
38
+ output_class = RequestLogAnalyzer::Output::const_get(arguments[:output])
40
39
  if arguments[:file]
41
- options[:output] = File.new(arguments[:file], "w+")
42
- options[:colorize] = false
40
+ output_file = File.new(arguments[:file], "w+")
41
+ options[:output] = output_class.new(output_file, :width => 80, :color => false, :characters => :ascii)
42
+ else
43
+ options[:output] = output_class.new(STDOUT, :width => arguments[:report_width].to_i,
44
+ :color => !arguments[:boring], :characters => (arguments[:boring] ? :ascii : :utf))
43
45
  end
44
46
 
45
47
  # Create the controller with the correct file format
@@ -129,7 +131,7 @@ module RequestLogAnalyzer
129
131
  def handle_progress(message, value = nil)
130
132
  case message
131
133
  when :started
132
- @progress_bar = ProgressBar.new(green(File.basename(value), options[:colorize]), File.size(value))
134
+ @progress_bar = CommandLine::ProgressBar.new(File.basename(value), File.size(value))
133
135
  when :finished
134
136
  @progress_bar.finish
135
137
  @progress_bar = nil
@@ -176,6 +178,8 @@ module RequestLogAnalyzer
176
178
  # 6. Finalize Source
177
179
  def run!
178
180
 
181
+
182
+
179
183
  @filters.each { |filter| filter.prepare }
180
184
  @aggregators.each { |agg| agg.prepare }
181
185
 
@@ -189,12 +193,15 @@ module RequestLogAnalyzer
189
193
  puts "Caught interrupt! Stopped parsing."
190
194
  end
191
195
 
192
- puts "\n"
193
-
194
196
  @aggregators.each { |agg| agg.finalize }
195
- @aggregators.each { |agg| agg.report(@output, options[:report_width], options[:colorize]) }
196
-
197
+
198
+ @output.header
199
+ @aggregators.each { |agg| agg.report(@output) }
200
+ @output.footer
201
+
197
202
  @source.finalize
203
+
204
+
198
205
  end
199
206
 
200
207
  end
@@ -17,9 +17,12 @@ module RequestLogAnalyzer
17
17
  register_file_format(format)
18
18
  end
19
19
 
20
+ # Initialize the filter
20
21
  def prepare
21
22
  end
22
23
 
24
+ # Return the request if the request should be kept.
25
+ # Return nil otherwise.
23
26
  def filter(request)
24
27
  return nil unless request
25
28
  return request