request-log-analyzer 1.1.3 → 1.1.4
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/README.rdoc +1 -1
- data/bin/request-log-analyzer +1 -0
- data/lib/request_log_analyzer.rb +19 -7
- data/lib/request_log_analyzer/file_format/merb.rb +1 -1
- data/lib/request_log_analyzer/output/fixed_width.rb +6 -2
- data/lib/request_log_analyzer/source.rb +28 -7
- data/lib/request_log_analyzer/source/log_parser.rb +82 -27
- data/lib/request_log_analyzer/tracker/duration.rb +23 -8
- data/spec/unit/tracker/duration_tracker_spec.rb +9 -0
- metadata +3 -3
data/README.rdoc
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
This is a simple command line tool to analyze request log files of both Rails and
|
4
4
|
Merb to produce a performance report. Its purpose is to find what actions are best candidates for optimization.
|
5
5
|
|
6
|
-
* Analyzes Rails log files (all versions)
|
6
|
+
* Analyzes Rails log files (all versions), Merb logs, or any other log format
|
7
7
|
* Can combine multiple files (handy if you are using logrotate)
|
8
8
|
* Uses several metrics, including cumulative request time, average request time, process blockers, database and rendering time, HTTP methods and statuses, Rails action cache statistics, etc.) (Sample output: http://wiki.github.com/wvanbergen/request-log-analyzer/sample-output)
|
9
9
|
* Low memory footprint (server-safe)
|
data/bin/request-log-analyzer
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
2
|
require File.dirname(__FILE__) + '/../lib/request_log_analyzer'
|
3
3
|
require File.dirname(__FILE__) + '/../lib/cli/command_line_arguments'
|
4
|
+
require File.dirname(__FILE__) + '/../lib/cli/progressbar'
|
4
5
|
require File.dirname(__FILE__) + '/../lib/cli/tools'
|
5
6
|
|
6
7
|
# Parse the arguments given via commandline
|
data/lib/request_log_analyzer.rb
CHANGED
@@ -1,15 +1,25 @@
|
|
1
1
|
require 'date'
|
2
|
-
require File.dirname(__FILE__) + '/cli/progressbar'
|
3
2
|
|
3
|
+
# RequestLogAnalyzer is the base namespace in which all functionality of RequestLogAnalyzer is implemented.
|
4
|
+
#
|
5
|
+
# - This module itselfs contains some functions to help with class and source file loading.
|
6
|
+
# - The actual application resides in the RequestLogAnalyzer::Controller class.
|
4
7
|
module RequestLogAnalyzer
|
5
|
-
|
8
|
+
|
9
|
+
# The current version of request-log-analyzer.
|
10
|
+
# This will be diplayed in output reports etc.
|
6
11
|
VERSION = '1.1'
|
7
12
|
|
13
|
+
# Loads constants in the RequestLogAnalyzer namespace using self.load_default_class_file(base, const)
|
14
|
+
# <tt>const</tt>:: The constant that is not yet loaded in the RequestLogAnalyzer namespace. This should be passed as a string or symbol.
|
8
15
|
def self.const_missing(const)
|
9
16
|
load_default_class_file(RequestLogAnalyzer, const)
|
10
17
|
end
|
11
18
|
|
12
|
-
#
|
19
|
+
# Loads constants that reside in the RequestLogAnalyzer tree using the constant name
|
20
|
+
# and its base constant to determine the filename.
|
21
|
+
# <tt>base</tt>:: The base constant to load the constant from. This should be Foo when the constant Foo::Bar is being loaded.
|
22
|
+
# <tt>const</tt>:: The constant to load from the base constant as a string or symbol. This should be 'Bar' or :Bar when the constant Foo::Bar is being loaded.
|
13
23
|
def self.load_default_class_file(base, const)
|
14
24
|
path = to_underscore(base.to_s)
|
15
25
|
basename = to_underscore(const.to_s)
|
@@ -19,14 +29,16 @@ module RequestLogAnalyzer
|
|
19
29
|
end
|
20
30
|
|
21
31
|
# Convert a string/symbol in camelcase (RequestLogAnalyzer::Controller) to underscores (request_log_analyzer/controller)
|
32
|
+
# This function can be used to load the file (using require) in which the given constant is defined.
|
33
|
+
# <tt>str</tt>:: The string to convert in the following format: <tt>ModuleName::ClassName</tt>
|
22
34
|
def self.to_underscore(str)
|
23
35
|
str.to_s.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
|
24
36
|
end
|
25
37
|
|
26
|
-
# Convert a string/symbol in underscores (request_log_analyzer/controller) to camelcase
|
38
|
+
# Convert a string/symbol in underscores (<tt>request_log_analyzer/controller</tt>) to camelcase
|
39
|
+
# (<tt>RequestLogAnalyzer::Controller</tt>). This can be used to find the class that is defined in a given filename.
|
40
|
+
# <tt>str</tt>:: The string to convert in the following format: <tt>module_name/class_name</tt>
|
27
41
|
def self.to_camelcase(str)
|
28
|
-
str.to_s.
|
42
|
+
str.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
29
43
|
end
|
30
44
|
end
|
31
|
-
|
32
|
-
|
@@ -39,8 +39,8 @@ module RequestLogAnalyzer::FileFormat
|
|
39
39
|
|
40
40
|
report do |analyze|
|
41
41
|
analyze.timespan :line_type => :started
|
42
|
+
analyze.frequency :category => REQUEST_CATEGORIZER, :amount => 20, :title => "Top 20 by hits"
|
42
43
|
analyze.hourly_spread :line_type => :started
|
43
|
-
|
44
44
|
analyze.duration :dispatch_time, :category => REQUEST_CATEGORIZER, :title => 'Request dispatch duration'
|
45
45
|
# analyze.duration :action_time, :category => REQUEST_CATEGORIZER, :title => 'Request action duration'
|
46
46
|
# analyze.duration :after_filters_time, :category => REQUEST_CATEGORIZER, :title => 'Request after_filter duration'
|
@@ -162,14 +162,18 @@ module RequestLogAnalyzer::Output
|
|
162
162
|
bar << colorize(characters[:block] * (width.to_f * (row[index].to_f - column[:treshold])).round, :red)
|
163
163
|
row_values.push(bar)
|
164
164
|
else
|
165
|
+
# Create a bar by combining block characters
|
165
166
|
row_values.push(characters[:block] * (width.to_f * row[index].to_f).round)
|
166
167
|
end
|
167
168
|
else
|
169
|
+
# Too few characters for a ratio bar. Display nothing
|
168
170
|
row_values.push('')
|
169
171
|
end
|
170
172
|
else
|
171
|
-
alignment = (columns[index][:align] == :right ? '' : '-')
|
172
|
-
|
173
|
+
alignment = (columns[index][:align] == :right ? '' : '-')
|
174
|
+
cell_value = "%#{alignment}#{width}s" % row[index].to_s[0...width]
|
175
|
+
cell_value = colorize(cell_value, :bold, :brown) if columns[index][:highlight]
|
176
|
+
row_values.push(cell_value)
|
173
177
|
end
|
174
178
|
end
|
175
179
|
puts row_values.join(style[:cell_separator] ? " #{characters[:vertical_line]} " : ' ')
|
@@ -1,12 +1,27 @@
|
|
1
|
+
# The RequestLogAnalyzer::Source module contains all functionality that loads requests from a given source
|
2
|
+
# and feed them to the pipeline for further processing. The requests (see RequestLogAnalyzer::Request) that
|
3
|
+
# will be parsed from a source, will be piped throug filters (see RequestLogAnalyzer::Filter) and are then
|
4
|
+
# fed to an aggregator (see RequestLogAnalyzer::Aggregator). The source instance is thus the beginning of
|
5
|
+
# the RequestLogAnalyzer chain.
|
6
|
+
#
|
7
|
+
# - The base class for all sources is RequestLogAnalyzer::Source::Base. All source classes should inherit from this class.
|
8
|
+
# - Currently, RequestLogAnalyzer::Source::LogParser is the only implemented source.
|
1
9
|
module RequestLogAnalyzer::Source
|
2
10
|
|
11
|
+
# Loads constants that reside in the RequestLogAnalyzer::Source namespace. This function uses
|
12
|
+
# RequestLogAnalyzer::load_default_class_file to load the file in which the constant is declared.
|
13
|
+
# <tt>const</tt>:: The constant to load in the RequestLogAnalyzer::Source namespace.
|
3
14
|
def self.const_missing(const)
|
4
15
|
RequestLogAnalyzer::load_default_class_file(self, const)
|
5
16
|
end
|
6
17
|
|
7
|
-
#
|
18
|
+
# The base Source class. All other sources should inherit from this class.
|
19
|
+
#
|
20
|
+
# A source implememtation should at least implement the each_request method, which should yield
|
21
|
+
# RequestLogAnalyzer::Request instances that will be fed through the pipleine.
|
8
22
|
class Base
|
9
23
|
|
24
|
+
# Make the Spurce instance aware of the current file format
|
10
25
|
include RequestLogAnalyzer::FileFormat::Awareness
|
11
26
|
|
12
27
|
# A hash of options
|
@@ -24,23 +39,29 @@ module RequestLogAnalyzer::Source
|
|
24
39
|
# The number of skipped lines because of warnings
|
25
40
|
attr_reader :skipped_lines
|
26
41
|
|
27
|
-
#
|
28
|
-
|
29
|
-
#
|
30
|
-
# <tt>format</tt> The file format
|
31
|
-
# <tt>options</tt> Are passed to the filters.
|
42
|
+
# Initializer, which will register the file format and save any options given as a hash.
|
43
|
+
# <tt>format</tt>:: The file format instance
|
44
|
+
# <tt>options</tt>:: A hash of options that can be used by a specific Source implementation
|
32
45
|
def initialize(format, options = {})
|
33
46
|
@options = options
|
34
47
|
register_file_format(format)
|
35
48
|
end
|
36
49
|
|
50
|
+
# The prepare method is called before the RequestLogAnalyzer::Source::Base#each_request method is called.
|
51
|
+
# Use this method to implement any initialization that should occur before this source can produce Request
|
52
|
+
# instances.
|
37
53
|
def prepare
|
38
54
|
end
|
39
55
|
|
40
|
-
|
56
|
+
# This function is called to actually produce the requests that will be send into the pipeline.
|
57
|
+
# The implementation should yield instances of RequestLogAnalyzer::Request.
|
58
|
+
# <tt>options</tt>:: A Hash of options that can be used in the implementation.
|
59
|
+
def each_request(options = {}, &block) # :yields: request
|
41
60
|
return true
|
42
61
|
end
|
43
62
|
|
63
|
+
# This function is called after RequestLogAnalyzer::Source::Base#each_request finished. Any code to
|
64
|
+
# wrap up, free resources, etc. can be put in this method.
|
44
65
|
def finalize
|
45
66
|
end
|
46
67
|
|
@@ -7,18 +7,24 @@ module RequestLogAnalyzer::Source
|
|
7
7
|
# De order in which lines occur is used to combine lines to a single request. If these lines
|
8
8
|
# are mixed, requests cannot be combined properly. This can be the case if data is written to
|
9
9
|
# the log file simultaneously by different mongrel processes. This problem is detected by the
|
10
|
-
# parser
|
11
|
-
#
|
10
|
+
# parser. It will emit warnings when this occurs. LogParser supports multiple parse strategies
|
11
|
+
# that deal differently with this problem.
|
12
12
|
class LogParser < Base
|
13
13
|
|
14
|
+
# The default parse strategy that will be used to parse the input.
|
14
15
|
DEFAULT_PARSE_STRATEGY = 'assume-correct'
|
16
|
+
|
17
|
+
# All available parse strategies.
|
15
18
|
PARSE_STRATEGIES = ['cautious', 'assume-correct']
|
16
19
|
|
17
20
|
attr_reader :source_files
|
18
21
|
|
19
|
-
# Initializes the parser instance.
|
22
|
+
# Initializes the log file parser instance.
|
20
23
|
# It will apply the language specific FileFormat module to this instance. It will use the line
|
21
|
-
# definitions in this module to parse any input.
|
24
|
+
# definitions in this module to parse any input that it is given (see parse_io).
|
25
|
+
#
|
26
|
+
# <tt>format</tt>:: The current file format instance
|
27
|
+
# <tt>options</tt>:: A hash of options that are used by the parser
|
22
28
|
def initialize(format, options = {})
|
23
29
|
@line_definitions = {}
|
24
30
|
@options = options
|
@@ -35,7 +41,11 @@ module RequestLogAnalyzer::Source
|
|
35
41
|
self.register_file_format(format)
|
36
42
|
end
|
37
43
|
|
38
|
-
|
44
|
+
# Reads the input, which can either be a file, sequence of files or STDIN to parse
|
45
|
+
# lines specified in the FileFormat. This lines will be combined into Request instances,
|
46
|
+
# that will be yielded. The actual parsing occurs in the parse_io method.
|
47
|
+
# <tt>options</tt>:: A Hash of options that will be pased to parse_io.
|
48
|
+
def each_request(options = {}, &block) # :yields: request
|
39
49
|
|
40
50
|
case @source_files
|
41
51
|
when IO;
|
@@ -50,28 +60,46 @@ module RequestLogAnalyzer::Source
|
|
50
60
|
end
|
51
61
|
end
|
52
62
|
|
53
|
-
# Parses a list of
|
54
|
-
|
63
|
+
# Parses a list of subsequent files of the same format, by calling parse_file for every
|
64
|
+
# file in the array.
|
65
|
+
# <tt>files</tt>:: The Array of files that should be parsed
|
66
|
+
# <tt>options</tt>:: A Hash of options that will be pased to parse_io.
|
67
|
+
def parse_files(files, options = {}, &block) # :yields: request
|
55
68
|
files.each { |file| parse_file(file, options, &block) }
|
56
69
|
end
|
57
70
|
|
58
|
-
# Parses a file.
|
59
|
-
#
|
71
|
+
# Parses a log file. Creates an IO stream for the provided file, and sends it to parse_io for
|
72
|
+
# further handling. This method supports progress updates that can be used to display a progressbar
|
73
|
+
# <tt>file</tt>:: The file that should be parsed.
|
74
|
+
# <tt>options</tt>:: A Hash of options that will be pased to parse_io.
|
60
75
|
def parse_file(file, options = {}, &block)
|
61
76
|
@progress_handler.call(:started, file) if @progress_handler
|
62
77
|
File.open(file, 'r') { |f| parse_io(f, options, &block) }
|
63
78
|
@progress_handler.call(:finished, file) if @progress_handler
|
64
79
|
end
|
65
80
|
|
81
|
+
|
82
|
+
# Parses an IO stream. It will simply call parse_io. This function does not support progress updates
|
83
|
+
# because the length of a stream is not known.
|
84
|
+
# <tt>stream</tt>:: The IO stream that should be parsed.
|
85
|
+
# <tt>options</tt>:: A Hash of options that will be pased to parse_io.
|
66
86
|
def parse_stream(stream, options = {}, &block)
|
67
87
|
parse_io(stream, options, &block)
|
68
88
|
end
|
69
89
|
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
|
90
|
+
# This method loops over each line of the input stream. It will try to parse this line as any of
|
91
|
+
# the lines that are defined by the current file format (see RequestLogAnalyazer::FileFormat).
|
92
|
+
# It will then combine these parsed line into requests using heuristics. These requests (see
|
93
|
+
# RequestLogAnalyzer::Request) will then be yielded for further processing in the pipeline.
|
94
|
+
#
|
95
|
+
# - RequestLogAnalyzer::LineDefinition#matches is called to test if a line matches a line definition of the file format.
|
96
|
+
# - update_current_request is used to combine parsed lines into requests using heuristics.
|
97
|
+
# - The method will yield progress updates if a progress handler is installed using progress=
|
98
|
+
# - The method will yield parse warnings if a warning handler is installed using warning=
|
99
|
+
#
|
100
|
+
# <tt>io</tt>:: The IO instance to use as source
|
101
|
+
# <tt>options</tt>:: A hash of options that can be used by the parser.
|
102
|
+
def parse_io(io, options = {}, &block) # :yields: request
|
75
103
|
|
76
104
|
@current_io = io
|
77
105
|
@current_io.each_line do |line|
|
@@ -95,19 +123,27 @@ module RequestLogAnalyzer::Source
|
|
95
123
|
@current_io = nil
|
96
124
|
end
|
97
125
|
|
98
|
-
# Add a block to this method to install a progress handler while parsing
|
126
|
+
# Add a block to this method to install a progress handler while parsing.
|
127
|
+
# <tt>proc</tt>:: The proc that will be called to handle progress update messages
|
99
128
|
def progress=(proc)
|
100
129
|
@progress_handler = proc
|
101
130
|
end
|
102
131
|
|
103
|
-
# Add a block to this method to install a warning handler while parsing
|
132
|
+
# Add a block to this method to install a warning handler while parsing,
|
133
|
+
# <tt>proc</tt>:: The proc that will be called to handle parse warning messages
|
104
134
|
def warning=(proc)
|
105
135
|
@warning_handler = proc
|
106
136
|
end
|
107
137
|
|
108
|
-
# This method is called by the parser if it encounteres any problems.
|
109
|
-
# It will call the warning handler
|
110
|
-
#
|
138
|
+
# This method is called by the parser if it encounteres any parsing problems.
|
139
|
+
# It will call the installed warning handler if any.
|
140
|
+
#
|
141
|
+
# By default, RequestLogAnalyzer::Controller will install a warning handler
|
142
|
+
# that will pass the warnings to each aggregator so they can do something useful
|
143
|
+
# with it.
|
144
|
+
#
|
145
|
+
# <tt>type</tt>:: The warning type (a Symbol)
|
146
|
+
# <tt>message</tt>:: A message explaining the warning
|
111
147
|
def warn(type, message)
|
112
148
|
@warning_handler.call(type, message, @current_io.lineno) if @warning_handler
|
113
149
|
end
|
@@ -118,13 +154,25 @@ module RequestLogAnalyzer::Source
|
|
118
154
|
# new request when a header line is encountered en will emit the request when a footer line
|
119
155
|
# is encountered.
|
120
156
|
#
|
157
|
+
# Combining the lines is done using heuristics. Problems can occur in this process. The
|
158
|
+
# current parse strategy defines how these cases are handled.
|
159
|
+
#
|
160
|
+
# When using the 'assume-correct' parse strategy (default):
|
161
|
+
# - Every line that is parsed before a header line is ignored as it cannot be included in
|
162
|
+
# any request. It will emit a :no_current_request warning.
|
163
|
+
# - If a header line is found before the previous requests was closed, the previous request
|
164
|
+
# will be yielded and a new request will be started.
|
165
|
+
#
|
166
|
+
# When using the 'cautious' parse strategy:
|
121
167
|
# - Every line that is parsed before a header line is ignored as it cannot be included in
|
122
168
|
# any request. It will emit a :no_current_request warning.
|
123
169
|
# - A header line that is parsed before a request is closed by a footer line, is a sign of
|
124
|
-
# an
|
125
|
-
# discarded
|
170
|
+
# an unproperly ordered file. All data that is gathered for the request until then is
|
171
|
+
# discarded and the next request is ignored as well. An :unclosed_request warning is
|
126
172
|
# emitted.
|
127
|
-
|
173
|
+
#
|
174
|
+
# <tt>request_data</tt>:: A hash of data that was parsed from the last line.
|
175
|
+
def update_current_request(request_data, &block) # :yields: request
|
128
176
|
if header_line?(request_data)
|
129
177
|
unless @current_request.nil?
|
130
178
|
case options[:parse_strategy]
|
@@ -153,21 +201,28 @@ module RequestLogAnalyzer::Source
|
|
153
201
|
end
|
154
202
|
end
|
155
203
|
|
156
|
-
# Handles the parsed request by
|
157
|
-
#
|
158
|
-
|
204
|
+
# Handles the parsed request by sending it into the pipeline.
|
205
|
+
#
|
206
|
+
# - It will call RequestLogAnalyzer::Request#validate on the request instance
|
207
|
+
# - It will send the request into the pipeline, checking whether it was accepted by all the filters.
|
208
|
+
# - It will update the parsed_requests and skipped_requests variables accordingly
|
209
|
+
#
|
210
|
+
# <tt>request</tt>:: The parsed request instance (RequestLogAnalyzer::Request)
|
211
|
+
def handle_request(request, &block) # :yields: request
|
159
212
|
@parsed_requests += 1
|
160
213
|
request.validate
|
161
214
|
accepted = block_given? ? yield(request) : true
|
162
215
|
@skipped_requests += 1 if not accepted
|
163
216
|
end
|
164
217
|
|
165
|
-
# Checks whether a given line hash is a header line.
|
218
|
+
# Checks whether a given line hash is a header line according to the current file format.
|
219
|
+
# <tt>hash</tt>:: A hash of data that was parsed from the line.
|
166
220
|
def header_line?(hash)
|
167
221
|
hash[:line_definition].header
|
168
222
|
end
|
169
223
|
|
170
|
-
# Checks whether a given line hash is a footer line.
|
224
|
+
# Checks whether a given line hash is a footer line according to the current file format.
|
225
|
+
# <tt>hash</tt>:: A hash of data that was parsed from the line.
|
171
226
|
def footer_line?(hash)
|
172
227
|
hash[:line_definition].footer
|
173
228
|
end
|
@@ -49,9 +49,11 @@ module RequestLogAnalyzer::Tracker
|
|
49
49
|
duration = options[:duration].respond_to?(:call) ? options[:duration].call(request) : request[options[:duration]]
|
50
50
|
|
51
51
|
if !duration.nil? && !category.nil?
|
52
|
-
@categories[category] ||= {:hits => 0, :cumulative => 0.0}
|
52
|
+
@categories[category] ||= {:hits => 0, :cumulative => 0.0, :min => duration, :max => duration }
|
53
53
|
@categories[category][:hits] += 1
|
54
54
|
@categories[category][:cumulative] += duration
|
55
|
+
@categories[category][:min] = duration if duration < @categories[category][:min]
|
56
|
+
@categories[category][:max] = duration if duration > @categories[category][:max]
|
55
57
|
end
|
56
58
|
end
|
57
59
|
end
|
@@ -63,7 +65,15 @@ module RequestLogAnalyzer::Tracker
|
|
63
65
|
def cumulative_duration(cat)
|
64
66
|
categories[cat][:cumulative]
|
65
67
|
end
|
66
|
-
|
68
|
+
|
69
|
+
def min_duration(cat)
|
70
|
+
categories[cat][:min]
|
71
|
+
end
|
72
|
+
|
73
|
+
def max_duration(cat)
|
74
|
+
categories[cat][:max]
|
75
|
+
end
|
76
|
+
|
67
77
|
def average_duration(cat)
|
68
78
|
categories[cat][:cumulative] / categories[cat][:hits]
|
69
79
|
end
|
@@ -106,11 +116,16 @@ module RequestLogAnalyzer::Tracker
|
|
106
116
|
output.title(options[:title])
|
107
117
|
|
108
118
|
top_categories = @categories.sort { |a, b| yield(b[1]) <=> yield(a[1]) }.slice(0...amount)
|
109
|
-
output.table({:title => 'Category', :width => :rest},
|
110
|
-
{:title => '
|
119
|
+
output.table({:title => 'Category', :width => :rest},
|
120
|
+
{:title => 'Hits', :align => :right, :highlight => (options[:sort] == :hits), :min_width => 4},
|
121
|
+
{:title => 'Cumulative', :align => :right, :highlight => (options[:sort] == :cumulative), :min_width => 10},
|
122
|
+
{:title => 'Average', :align => :right, :highlight => (options[:sort] == :average), :min_width => 8},
|
123
|
+
{:title => 'Min', :align => :right, :highlight => (options[:sort] == :min)},
|
124
|
+
{:title => 'Max', :align => :right, :highlight => (options[:sort] == :max)}) do |rows|
|
111
125
|
|
112
126
|
top_categories.each do |(cat, info)|
|
113
|
-
rows << [cat, info[:hits], "%0.02fs" % info[:cumulative], "%0.02fs" % (info[:cumulative] / info[:hits])
|
127
|
+
rows << [cat, info[:hits], "%0.02fs" % info[:cumulative], "%0.02fs" % (info[:cumulative] / info[:hits]),
|
128
|
+
"%0.02fs" % info[:min], "%0.02fs" % info[:max]]
|
114
129
|
end
|
115
130
|
end
|
116
131
|
|
@@ -125,11 +140,11 @@ module RequestLogAnalyzer::Tracker
|
|
125
140
|
options[:report].each do |report|
|
126
141
|
case report
|
127
142
|
when :average
|
128
|
-
report_table(output, options[:top], :title => "#{options[:title]} - top #{options[:top]} by average time") { |cat| cat[:cumulative] / cat[:hits] }
|
143
|
+
report_table(output, options[:top], :title => "#{options[:title]} - top #{options[:top]} by average time", :sort => :average) { |cat| cat[:cumulative] / cat[:hits] }
|
129
144
|
when :cumulative
|
130
|
-
report_table(output, options[:top], :title => "#{options[:title]} - top #{options[:top]} by cumulative time") { |cat| cat[:cumulative] }
|
145
|
+
report_table(output, options[:top], :title => "#{options[:title]} - top #{options[:top]} by cumulative time", :sort => :cumulative) { |cat| cat[:cumulative] }
|
131
146
|
when :hits
|
132
|
-
report_table(output, options[:top], :title => "#{options[:title]} - top #{options[:top]} by hits") { |cat| cat[:hits] }
|
147
|
+
report_table(output, options[:top], :title => "#{options[:title]} - top #{options[:top]} by hits", :sort => :hits) { |cat| cat[:hits] }
|
133
148
|
else
|
134
149
|
raise "Unknown duration report specified: #{report}!"
|
135
150
|
end
|
@@ -41,6 +41,15 @@ describe RequestLogAnalyzer::Tracker::Duration, 'static category' do
|
|
41
41
|
@tracker.average_duration('b').should == 0.35
|
42
42
|
end
|
43
43
|
|
44
|
+
it "should set min and max duration correctly" do
|
45
|
+
@tracker.update(request(:category => 'a', :duration => 0.2))
|
46
|
+
@tracker.update(request(:category => 'b', :duration => 0.3))
|
47
|
+
@tracker.update(request(:category => 'b', :duration => 0.4))
|
48
|
+
|
49
|
+
@tracker.min_duration('b').should == 0.3
|
50
|
+
@tracker.max_duration('b').should == 0.4
|
51
|
+
end
|
52
|
+
|
44
53
|
end
|
45
54
|
|
46
55
|
describe RequestLogAnalyzer::Tracker::Duration, 'dynamic category' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: request-log-analyzer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Willem van Bergen
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-
|
13
|
+
date: 2009-02-08 00:00:00 +01:00
|
14
14
|
default_executable: request-log-analyzer
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -143,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
143
|
requirements: []
|
144
144
|
|
145
145
|
rubyforge_project: r-l-a
|
146
|
-
rubygems_version: 1.
|
146
|
+
rubygems_version: 1.3.1
|
147
147
|
signing_key:
|
148
148
|
specification_version: 2
|
149
149
|
summary: A command line tool to analyze Rails logs
|