request-log-analyzer 1.3.7 → 1.4.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/LICENSE +3 -3
- data/README.rdoc +1 -1
- data/bin/request-log-analyzer +17 -14
- data/lib/cli/command_line_arguments.rb +51 -51
- data/lib/cli/database_console.rb +3 -3
- data/lib/cli/database_console_init.rb +2 -2
- data/lib/cli/progressbar.rb +10 -10
- data/lib/cli/tools.rb +3 -3
- data/lib/request_log_analyzer.rb +4 -4
- data/lib/request_log_analyzer/aggregator.rb +10 -10
- data/lib/request_log_analyzer/aggregator/database_inserter.rb +9 -9
- data/lib/request_log_analyzer/aggregator/echo.rb +14 -9
- data/lib/request_log_analyzer/aggregator/summarizer.rb +26 -26
- data/lib/request_log_analyzer/controller.rb +153 -69
- data/lib/request_log_analyzer/database.rb +13 -13
- data/lib/request_log_analyzer/database/base.rb +17 -17
- data/lib/request_log_analyzer/database/connection.rb +3 -3
- data/lib/request_log_analyzer/database/request.rb +2 -2
- data/lib/request_log_analyzer/database/source.rb +1 -1
- data/lib/request_log_analyzer/file_format.rb +15 -15
- data/lib/request_log_analyzer/file_format/amazon_s3.rb +16 -16
- data/lib/request_log_analyzer/file_format/apache.rb +20 -19
- data/lib/request_log_analyzer/file_format/merb.rb +12 -12
- data/lib/request_log_analyzer/file_format/rack.rb +4 -4
- data/lib/request_log_analyzer/file_format/rails.rb +146 -70
- data/lib/request_log_analyzer/file_format/rails_development.rb +4 -49
- data/lib/request_log_analyzer/filter.rb +6 -6
- data/lib/request_log_analyzer/filter/anonymize.rb +6 -6
- data/lib/request_log_analyzer/filter/field.rb +9 -9
- data/lib/request_log_analyzer/filter/timespan.rb +12 -10
- data/lib/request_log_analyzer/line_definition.rb +15 -14
- data/lib/request_log_analyzer/log_processor.rb +22 -22
- data/lib/request_log_analyzer/mailer.rb +15 -9
- data/lib/request_log_analyzer/output.rb +53 -12
- data/lib/request_log_analyzer/output/fixed_width.rb +40 -41
- data/lib/request_log_analyzer/output/html.rb +20 -20
- data/lib/request_log_analyzer/request.rb +35 -36
- data/lib/request_log_analyzer/source.rb +7 -7
- data/lib/request_log_analyzer/source/database_loader.rb +7 -7
- data/lib/request_log_analyzer/source/log_parser.rb +48 -43
- data/lib/request_log_analyzer/tracker.rb +128 -14
- data/lib/request_log_analyzer/tracker/duration.rb +39 -132
- data/lib/request_log_analyzer/tracker/frequency.rb +31 -32
- data/lib/request_log_analyzer/tracker/hourly_spread.rb +20 -19
- data/lib/request_log_analyzer/tracker/timespan.rb +17 -17
- data/lib/request_log_analyzer/tracker/traffic.rb +36 -116
- data/request-log-analyzer.gemspec +19 -15
- data/spec/fixtures/rails_22.log +1 -1
- data/spec/integration/command_line_usage_spec.rb +1 -1
- data/spec/lib/helpers.rb +7 -7
- data/spec/lib/macros.rb +3 -3
- data/spec/lib/matchers.rb +41 -27
- data/spec/lib/mocks.rb +15 -14
- data/spec/lib/testing_format.rb +9 -9
- data/spec/spec_helper.rb +6 -6
- data/spec/unit/aggregator/database_inserter_spec.rb +13 -13
- data/spec/unit/aggregator/summarizer_spec.rb +4 -4
- data/spec/unit/controller/controller_spec.rb +2 -2
- data/spec/unit/controller/log_processor_spec.rb +1 -1
- data/spec/unit/database/base_class_spec.rb +19 -19
- data/spec/unit/database/connection_spec.rb +3 -3
- data/spec/unit/database/database_spec.rb +25 -25
- data/spec/unit/file_format/amazon_s3_format_spec.rb +5 -5
- data/spec/unit/file_format/apache_format_spec.rb +13 -13
- data/spec/unit/file_format/file_format_api_spec.rb +13 -13
- data/spec/unit/file_format/line_definition_spec.rb +24 -17
- data/spec/unit/file_format/merb_format_spec.rb +41 -45
- data/spec/unit/file_format/rails_format_spec.rb +157 -117
- data/spec/unit/filter/anonymize_filter_spec.rb +2 -2
- data/spec/unit/filter/field_filter_spec.rb +13 -13
- data/spec/unit/filter/filter_spec.rb +1 -1
- data/spec/unit/filter/timespan_filter_spec.rb +15 -15
- data/spec/unit/mailer_spec.rb +30 -0
- data/spec/unit/{source/request_spec.rb → request_spec.rb} +30 -30
- data/spec/unit/source/log_parser_spec.rb +27 -27
- data/spec/unit/tracker/duration_tracker_spec.rb +115 -78
- data/spec/unit/tracker/frequency_tracker_spec.rb +74 -63
- data/spec/unit/tracker/hourly_spread_spec.rb +28 -20
- data/spec/unit/tracker/timespan_tracker_spec.rb +25 -13
- data/spec/unit/tracker/tracker_api_spec.rb +13 -13
- data/spec/unit/tracker/traffic_tracker_spec.rb +81 -79
- data/tasks/github-gem.rake +125 -75
- data/tasks/request_log_analyzer.rake +2 -2
- metadata +8 -6
@@ -3,12 +3,11 @@ module RequestLogAnalyzer::Tracker
|
|
3
3
|
# Analyze the duration of a specific attribute
|
4
4
|
#
|
5
5
|
# === Options
|
6
|
-
# * <tt>:amount</tt> The amount of lines in the report
|
7
6
|
# * <tt>:category</tt> Proc that handles request categorization for given fileformat (REQUEST_CATEGORIZER)
|
8
7
|
# * <tt>:duration</tt> The field containing the duration in the request hash.
|
9
8
|
# * <tt>:if</tt> Proc that has to return !nil for a request to be passed to the tracker.
|
10
9
|
# * <tt>:line_type</tt> The line type that contains the duration field (determined by the category proc).
|
11
|
-
# * <tt>:title</tt> Title do be displayed above the report
|
10
|
+
# * <tt>:title</tt> Title do be displayed above the report
|
12
11
|
# * <tt>:unless</tt> Handle request if this proc is false for the handled request.
|
13
12
|
#
|
14
13
|
# The items in the update request hash are set during the creation of the Duration tracker.
|
@@ -21,141 +20,62 @@ module RequestLogAnalyzer::Tracker
|
|
21
20
|
# EmployeeController#index.html [GET] | 5802 | 1477.32s | 0.25s
|
22
21
|
# .............
|
23
22
|
class Duration < Base
|
24
|
-
|
23
|
+
|
24
|
+
include RequestLogAnalyzer::Tracker::StatisticsTracking
|
25
|
+
|
25
26
|
attr_reader :categories
|
26
27
|
|
27
28
|
# Check if duration and catagory option have been received,
|
28
29
|
def prepare
|
29
30
|
raise "No duration field set up for category tracker #{self.inspect}" unless options[:duration]
|
30
|
-
raise "No categorizer set up for duration tracker #{self.inspect}"
|
31
|
+
raise "No categorizer set up for duration tracker #{self.inspect}" unless options[:category]
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
unless options[:multiple]
|
34
|
+
@categorizer = create_lambda(options[:category])
|
35
|
+
@durationizer = create_lambda(options[:duration])
|
36
|
+
end
|
37
|
+
|
38
|
+
@categories = {}
|
35
39
|
end
|
36
40
|
|
37
41
|
# Get the duration information fron the request and store it in the different categories.
|
38
42
|
# <tt>request</tt> The request.
|
39
43
|
def update(request)
|
40
44
|
if options[:multiple]
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
categories.each_with_index do |category, index|
|
46
|
-
@categories[category] ||= {:hits => 0, :cumulative => 0.0}
|
47
|
-
@categories[category][:hits] += 1
|
48
|
-
@categories[category][:cumulative] += durations[index]
|
49
|
-
end
|
50
|
-
else
|
51
|
-
raise "Capture mismatch for multiple values in a request"
|
52
|
-
end
|
45
|
+
found_categories = request.every(options[:category])
|
46
|
+
found_durations = request.every(options[:duration])
|
47
|
+
raise "Capture mismatch for multiple values in a request" unless found_categories.length == found_durations.length
|
48
|
+
found_categories.each_with_index { |cat, index| update_statistics(cat, found_durations[index]) }
|
53
49
|
else
|
54
50
|
category = @categorizer.call(request)
|
55
51
|
duration = @durationizer.call(request)
|
56
|
-
|
57
|
-
if duration.kind_of?(Numeric) && !category.nil?
|
58
|
-
@categories[category] ||= {:hits => 0, :cumulative => 0.0, :min => duration, :max => duration }
|
59
|
-
@categories[category][:hits] += 1
|
60
|
-
@categories[category][:cumulative] += duration
|
61
|
-
@categories[category][:min] = duration if duration < @categories[category][:min]
|
62
|
-
@categories[category][:max] = duration if duration > @categories[category][:max]
|
63
|
-
end
|
52
|
+
update_statistics(category, duration) if duration.kind_of?(Numeric) && category
|
64
53
|
end
|
65
54
|
end
|
66
|
-
|
67
|
-
# Get the number of hits of a specific category.
|
68
|
-
# <tt>cat</tt> The category
|
69
|
-
def hits(cat)
|
70
|
-
categories[cat][:hits]
|
71
|
-
end
|
72
|
-
|
73
|
-
# Get the total duration of a specific category.
|
74
|
-
# <tt>cat</tt> The category
|
75
|
-
def cumulative_duration(cat)
|
76
|
-
categories[cat][:cumulative]
|
77
|
-
end
|
78
|
-
|
79
|
-
# Get the minimal duration of a specific category.
|
80
|
-
# <tt>cat</tt> The category
|
81
|
-
def min_duration(cat)
|
82
|
-
categories[cat][:min]
|
83
|
-
end
|
84
|
-
|
85
|
-
# Get the maximum duration of a specific category.
|
86
|
-
# <tt>cat</tt> The category
|
87
|
-
def max_duration(cat)
|
88
|
-
categories[cat][:max]
|
89
|
-
end
|
90
|
-
|
91
|
-
# Get the average duration of a specific category.
|
92
|
-
# <tt>cat</tt> The category
|
93
|
-
def average_duration(cat)
|
94
|
-
categories[cat][:cumulative] / categories[cat][:hits]
|
95
|
-
end
|
96
|
-
|
97
|
-
# Get the average duration of a all categories.
|
98
|
-
def overall_average_duration
|
99
|
-
overall_cumulative_duration / overall_hits
|
100
|
-
end
|
101
|
-
|
102
|
-
# Get the cumlative duration of a all categories.
|
103
|
-
def overall_cumulative_duration
|
104
|
-
categories.inject(0.0) { |sum, (name, cat)| sum + cat[:cumulative] }
|
105
|
-
end
|
106
|
-
|
107
|
-
# Get the total hits of a all categories.
|
108
|
-
def overall_hits
|
109
|
-
categories.inject(0) { |sum, (name, cat)| sum + cat[:hits] }
|
110
|
-
end
|
111
55
|
|
112
|
-
# Return categories sorted by hits.
|
113
|
-
def sorted_by_hits
|
114
|
-
sorted_by(:hits)
|
115
|
-
end
|
116
|
-
|
117
|
-
# Return categories sorted by cumulative duration.
|
118
|
-
def sorted_by_cumulative
|
119
|
-
sorted_by(:cumulative)
|
120
|
-
end
|
121
|
-
|
122
|
-
# Return categories sorted by cumulative duration.
|
123
|
-
def sorted_by_average
|
124
|
-
sorted_by { |cat| cat[:cumulative] / cat[:hits] }
|
125
|
-
end
|
126
|
-
|
127
|
-
# Return categories sorted by a given key.
|
128
|
-
# <tt>by</tt> The key.
|
129
|
-
def sorted_by(by = nil)
|
130
|
-
if block_given?
|
131
|
-
categories.sort { |a, b| yield(b[1]) <=> yield(a[1]) }
|
132
|
-
else
|
133
|
-
categories.sort { |a, b| b[1][by] <=> a[1][by] }
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
56
|
# Block function to build a result table using a provided sorting function.
|
138
57
|
# <tt>output</tt> The output object.
|
139
58
|
# <tt>amount</tt> The number of rows in the report table (default 10).
|
140
59
|
# === Options
|
141
60
|
# * </tt>:title</tt> The title of the table
|
142
61
|
# * </tt>:sort</tt> The key to sort on (:hits, :cumulative, :average, :min or :max)
|
143
|
-
def report_table(output,
|
144
|
-
|
145
|
-
output.
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
62
|
+
def report_table(output, sort, options = {}, &block)
|
63
|
+
output.puts
|
64
|
+
top_categories = output.slice_results(sorted_by(sort))
|
65
|
+
output.with_style(:top_line => true) do
|
66
|
+
output.table(*statistics_header(:title => options[:title], :highlight => sort)) do |rows|
|
67
|
+
top_categories.each { |(cat, info)| rows << statistics_row(cat) }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Display a duration
|
73
|
+
def display_value(time)
|
74
|
+
case time
|
75
|
+
when nil then '-'
|
76
|
+
when 0...60 then "%0.02fs" % time
|
77
|
+
when 60...3600 then "%dm%02ds" % [time / 60, (time % 60).round]
|
78
|
+
else "%dh%02dm%02ds" % [time / 3600, (time % 3600) / 60, (time % 60).round]
|
159
79
|
end
|
160
80
|
end
|
161
81
|
|
@@ -164,33 +84,20 @@ module RequestLogAnalyzer::Tracker
|
|
164
84
|
# Any options for the report should have been set during initialize.
|
165
85
|
# <tt>output</tt> The output object
|
166
86
|
def report(output)
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
options[:top] ||= 20
|
171
|
-
|
172
|
-
options[:report].each do |report|
|
173
|
-
case report
|
174
|
-
when :average
|
175
|
-
report_table(output, options[:top], :title => "#{options[:title]} - top #{options[:top]} by average time", :sort => :average) { |cat| cat[:cumulative] / cat[:hits] }
|
176
|
-
when :cumulative
|
177
|
-
report_table(output, options[:top], :title => "#{options[:title]} - top #{options[:top]} by cumulative time", :sort => :cumulative) { |cat| cat[:cumulative] }
|
178
|
-
when :hits
|
179
|
-
report_table(output, options[:top], :title => "#{options[:title]} - top #{options[:top]} by hits", :sort => :hits) { |cat| cat[:hits] }
|
180
|
-
else
|
181
|
-
raise "Unknown duration report specified: #{report}!"
|
182
|
-
end
|
87
|
+
sortings = output.options[:sort] || [:sum, :mean]
|
88
|
+
sortings.each do |sorting|
|
89
|
+
report_table(output, sorting, :title => "#{title} - by #{sorting}")
|
183
90
|
end
|
184
91
|
end
|
185
|
-
|
92
|
+
|
186
93
|
# Returns the title of this tracker for reports
|
187
94
|
def title
|
188
95
|
options[:title] || 'Request duration'
|
189
96
|
end
|
190
|
-
|
97
|
+
|
191
98
|
# Returns all the categories and the tracked duration as a hash than can be exported to YAML
|
192
99
|
def to_yaml_object
|
193
|
-
return nil if @categories.empty?
|
100
|
+
return nil if @categories.empty?
|
194
101
|
@categories
|
195
102
|
end
|
196
103
|
end
|
@@ -1,10 +1,9 @@
|
|
1
1
|
module RequestLogAnalyzer::Tracker
|
2
|
-
|
2
|
+
|
3
3
|
# Catagorize requests by frequency.
|
4
|
-
# Count and analyze requests for a specific attribute
|
4
|
+
# Count and analyze requests for a specific attribute
|
5
5
|
#
|
6
6
|
# === Options
|
7
|
-
# * <tt>:amount</tt> The amount of lines in the report
|
8
7
|
# * <tt>:category</tt> Proc that handles the request categorization.
|
9
8
|
# * <tt>:if</tt> Proc that has to return !nil for a request to be passed to the tracker.
|
10
9
|
# * <tt>:line_type</tt> The line type that contains the duration field (determined by the category proc).
|
@@ -23,41 +22,42 @@ module RequestLogAnalyzer::Tracker
|
|
23
22
|
# DELETE | 512 hits (1.1%) |
|
24
23
|
class Frequency < Base
|
25
24
|
|
26
|
-
attr_reader :
|
25
|
+
attr_reader :categories
|
27
26
|
|
28
27
|
# Check if categories are set up
|
29
28
|
def prepare
|
30
29
|
raise "No categorizer set up for category tracker #{self.inspect}" unless options[:category]
|
31
|
-
@
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
@categorizer = create_lambda(options[:category])
|
31
|
+
|
32
|
+
# Initialize the categories. Use the list of category names to
|
33
|
+
@categories = {}
|
34
|
+
options[:all_categories].each { |cat| @categories[cat] = 0 } if options[:all_categories].kind_of?(Enumerable)
|
35
35
|
end
|
36
|
-
|
37
|
-
# Check HTTP method of a request and store that in the
|
36
|
+
|
37
|
+
# Check HTTP method of a request and store that in the categories hash.
|
38
38
|
# <tt>request</tt> The request.
|
39
39
|
def update(request)
|
40
|
-
cat =
|
41
|
-
if
|
42
|
-
@
|
43
|
-
@
|
40
|
+
cat = @categorizer.call(request)
|
41
|
+
if cat || options[:nils]
|
42
|
+
@categories[cat] ||= 0
|
43
|
+
@categories[cat] += 1
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
47
|
# Return the amount of times a HTTP method has been encountered
|
48
48
|
# <tt>cat</tt> The HTTP method (:get, :put, :post or :delete)
|
49
49
|
def frequency(cat)
|
50
|
-
|
50
|
+
categories[cat] || 0
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
# Return the overall frequency
|
54
54
|
def overall_frequency
|
55
|
-
|
55
|
+
categories.inject(0) { |carry, item| carry + item[1] }
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
# Return the methods sorted by frequency
|
59
59
|
def sorted_by_frequency
|
60
|
-
@
|
60
|
+
@categories.sort { |a, b| b[1] <=> a[1] }
|
61
61
|
end
|
62
62
|
|
63
63
|
# Generate a HTTP method frequency report to the given output object.
|
@@ -65,29 +65,28 @@ module RequestLogAnalyzer::Tracker
|
|
65
65
|
# <tt>output</tt> The output object
|
66
66
|
def report(output)
|
67
67
|
output.title(options[:title]) if options[:title]
|
68
|
-
|
69
|
-
if @
|
70
|
-
output << "None found.\n"
|
68
|
+
|
69
|
+
if @categories.empty?
|
70
|
+
output << "None found.\n"
|
71
71
|
else
|
72
|
-
|
73
|
-
total_hits
|
74
|
-
sorted_frequencies = sorted_frequencies.slice(0...options[:amount]) if options[:amount]
|
72
|
+
sorted_categories = output.slice_results(sorted_by_frequency)
|
73
|
+
total_hits = overall_frequency
|
75
74
|
|
76
|
-
output.table({:align => :left}, {:align => :right }, {:align => :right}, {:type => :ratio, :width => :rest}) do |rows|
|
77
|
-
|
75
|
+
output.table({:align => :left}, {:align => :right }, {:align => :right}, {:type => :ratio, :width => :rest}) do |rows|
|
76
|
+
sorted_categories.each do |(cat, count)|
|
78
77
|
rows << [cat, "#{count} hits", '%0.1f%%' % ((count.to_f / total_hits.to_f) * 100.0), (count.to_f / total_hits.to_f)]
|
79
78
|
end
|
80
79
|
end
|
81
80
|
|
82
81
|
end
|
83
82
|
end
|
84
|
-
|
85
|
-
# Returns a hash with the
|
83
|
+
|
84
|
+
# Returns a hash with the categories of every category that can be exported to YAML
|
86
85
|
def to_yaml_object
|
87
|
-
return nil if @
|
88
|
-
@
|
86
|
+
return nil if @categories.empty?
|
87
|
+
@categories
|
89
88
|
end
|
90
|
-
|
89
|
+
|
91
90
|
# Returns the title of this tracker for reports
|
92
91
|
def title
|
93
92
|
options[:title] || 'Request frequency'
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module RequestLogAnalyzer::Tracker
|
2
|
-
|
2
|
+
|
3
3
|
# Determines the average hourly spread of the parsed requests.
|
4
4
|
# This spread is shown in a graph form.
|
5
5
|
#
|
@@ -30,50 +30,51 @@ module RequestLogAnalyzer::Tracker
|
|
30
30
|
# ................
|
31
31
|
class HourlySpread < Base
|
32
32
|
|
33
|
-
attr_reader :
|
33
|
+
attr_reader :hour_frequencies, :first, :last
|
34
34
|
|
35
35
|
# Check if timestamp field is set in the options and prepare the result time graph.
|
36
36
|
def prepare
|
37
37
|
options[:field] ||= :timestamp
|
38
|
-
@
|
38
|
+
@hour_frequencies = (0...24).map { 0 }
|
39
|
+
@first, @last = 99999999999999, 0
|
39
40
|
end
|
40
41
|
|
41
42
|
# Check if the timestamp in the request and store it.
|
42
43
|
# <tt>request</tt> The request.
|
43
44
|
def update(request)
|
44
45
|
timestamp = request.first(options[:field])
|
45
|
-
|
46
|
-
@
|
47
|
-
@
|
48
|
-
@last = timestamp if @last.nil? || timestamp > @last
|
46
|
+
@hour_frequencies[timestamp.to_s[8..9].to_i] +=1
|
47
|
+
@first = timestamp if timestamp < @first
|
48
|
+
@last = timestamp if timestamp > @last
|
49
49
|
end
|
50
50
|
|
51
51
|
# Total amount of requests tracked
|
52
52
|
def total_requests
|
53
|
-
@
|
53
|
+
@hour_frequencies.inject(0) { |sum, value| sum + value }
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
|
+
|
56
57
|
# First timestamp encountered
|
57
58
|
def first_timestamp
|
58
59
|
DateTime.parse(@first.to_s, '%Y%m%d%H%M%S') rescue nil
|
59
60
|
end
|
60
|
-
|
61
|
+
|
61
62
|
# Last timestamp encountered
|
62
63
|
def last_timestamp
|
63
64
|
DateTime.parse(@last.to_s, '%Y%m%d%H%M%S') rescue nil
|
64
65
|
end
|
65
|
-
|
66
|
+
|
66
67
|
# Difference between last and first timestamp.
|
67
68
|
def timespan
|
68
69
|
last_timestamp - first_timestamp
|
69
70
|
end
|
70
|
-
|
71
|
+
|
71
72
|
# Generate an hourly spread report to the given output object.
|
72
73
|
# Any options for the report should have been set during initialize.
|
73
74
|
# <tt>output</tt> The output object
|
74
75
|
def report(output)
|
75
76
|
output.title(title)
|
76
|
-
|
77
|
+
|
77
78
|
if total_requests == 0
|
78
79
|
output << "None found.\n"
|
79
80
|
return
|
@@ -81,23 +82,23 @@ module RequestLogAnalyzer::Tracker
|
|
81
82
|
|
82
83
|
days = [1, timespan].max
|
83
84
|
output.table({}, {:align => :right}, {:type => :ratio, :width => :rest, :treshold => 0.15}) do |rows|
|
84
|
-
@
|
85
|
-
ratio
|
85
|
+
@hour_frequencies.each_with_index do |requests, index|
|
86
|
+
ratio = requests.to_f / total_requests.to_f
|
86
87
|
requests_per_day = (requests / days).ceil
|
87
|
-
rows << ["#{index.to_s.rjust(3)}:00", "%d hits" % requests_per_day, ratio]
|
88
|
+
rows << ["#{index.to_s.rjust(3)}:00", "%d hits/day" % requests_per_day, ratio]
|
88
89
|
end
|
89
90
|
end
|
90
91
|
end
|
91
|
-
|
92
|
+
|
92
93
|
# Returns the title of this tracker for reports
|
93
94
|
def title
|
94
95
|
options[:title] || "Request distribution per hour"
|
95
96
|
end
|
96
|
-
|
97
|
+
|
97
98
|
# Returns the found frequencies per hour as a hash for YAML exporting
|
98
99
|
def to_yaml_object
|
99
100
|
yaml_object = {}
|
100
|
-
@
|
101
|
+
@hour_frequencies.each_with_index do |freq, hour|
|
101
102
|
yaml_object["#{hour}:00 - #{hour+1}:00"] = freq
|
102
103
|
end
|
103
104
|
yaml_object
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module RequestLogAnalyzer::Tracker
|
2
|
-
|
2
|
+
|
3
3
|
# Determines the datetime of the first request and the last request
|
4
4
|
# Also determines the amount of days inbetween these.
|
5
5
|
#
|
@@ -19,32 +19,32 @@ module RequestLogAnalyzer::Tracker
|
|
19
19
|
# Total time analyzed: 7 days
|
20
20
|
class Timespan < Base
|
21
21
|
|
22
|
-
attr_reader :first, :last
|
22
|
+
attr_reader :first, :last
|
23
23
|
|
24
24
|
# Check if timestamp field is set in the options.
|
25
25
|
def prepare
|
26
26
|
options[:field] ||= :timestamp
|
27
|
+
@first, @last = 99999999999999, 0
|
27
28
|
end
|
28
|
-
|
29
|
+
|
29
30
|
# Check if the timestamp in the request and store it.
|
30
31
|
# <tt>request</tt> The request.
|
31
32
|
def update(request)
|
32
33
|
timestamp = request[options[:field]]
|
33
|
-
|
34
|
-
@
|
35
|
-
@last = timestamp if @last.nil? || timestamp > @last
|
34
|
+
@first = timestamp if timestamp < @first
|
35
|
+
@last = timestamp if timestamp > @last
|
36
36
|
end
|
37
37
|
|
38
38
|
# First timestamp encountered
|
39
39
|
def first_timestamp
|
40
40
|
DateTime.parse(@first.to_s, '%Y%m%d%H%M%S') rescue nil
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
# Last timestamp encountered
|
44
44
|
def last_timestamp
|
45
|
-
DateTime.parse(@last.to_s, '%Y%m%d%H%M%S') rescue nil
|
45
|
+
DateTime.parse(@last.to_s, '%Y%m%d%H%M%S') rescue nil
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
# Difference between last and first timestamp.
|
49
49
|
def timespan
|
50
50
|
last_timestamp - first_timestamp
|
@@ -55,27 +55,27 @@ module RequestLogAnalyzer::Tracker
|
|
55
55
|
# <tt>output</tt> The output object
|
56
56
|
def report(output)
|
57
57
|
output.title(options[:title]) if options[:title]
|
58
|
-
|
59
|
-
if @last && @first
|
60
|
-
output.with_style(:cell_separator => false) do
|
58
|
+
|
59
|
+
if @last > 0 && @first < 99999999999999
|
60
|
+
output.with_style(:cell_separator => false) do
|
61
61
|
output.table({:width => 20}, {}) do |rows|
|
62
62
|
rows << ['First request:', first_timestamp.strftime('%Y-%m-%d %H:%M:%I')]
|
63
|
-
rows << ['Last request:',
|
64
|
-
rows << ['Total time analyzed:', "#{timespan.ceil} days"]
|
63
|
+
rows << ['Last request:', last_timestamp.strftime('%Y-%m-%d %H:%M:%I')]
|
64
|
+
rows << ['Total time analyzed:', "#{timespan.ceil} days"]
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
# Returns the title of this tracker for reports
|
71
71
|
def title
|
72
72
|
options[:title] || 'Request timespan'
|
73
73
|
end
|
74
|
-
|
74
|
+
|
75
75
|
# A hash that can be exported to YAML with the first and last timestamp encountered.
|
76
76
|
def to_yaml_object
|
77
77
|
{ :first => first_timestamp, :last =>last_timestamp }
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
end
|
81
81
|
end
|