request-log-analyzer 1.0.4 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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