request-log-analyzer 1.3.7 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/LICENSE +3 -3
  2. data/README.rdoc +1 -1
  3. data/bin/request-log-analyzer +17 -14
  4. data/lib/cli/command_line_arguments.rb +51 -51
  5. data/lib/cli/database_console.rb +3 -3
  6. data/lib/cli/database_console_init.rb +2 -2
  7. data/lib/cli/progressbar.rb +10 -10
  8. data/lib/cli/tools.rb +3 -3
  9. data/lib/request_log_analyzer.rb +4 -4
  10. data/lib/request_log_analyzer/aggregator.rb +10 -10
  11. data/lib/request_log_analyzer/aggregator/database_inserter.rb +9 -9
  12. data/lib/request_log_analyzer/aggregator/echo.rb +14 -9
  13. data/lib/request_log_analyzer/aggregator/summarizer.rb +26 -26
  14. data/lib/request_log_analyzer/controller.rb +153 -69
  15. data/lib/request_log_analyzer/database.rb +13 -13
  16. data/lib/request_log_analyzer/database/base.rb +17 -17
  17. data/lib/request_log_analyzer/database/connection.rb +3 -3
  18. data/lib/request_log_analyzer/database/request.rb +2 -2
  19. data/lib/request_log_analyzer/database/source.rb +1 -1
  20. data/lib/request_log_analyzer/file_format.rb +15 -15
  21. data/lib/request_log_analyzer/file_format/amazon_s3.rb +16 -16
  22. data/lib/request_log_analyzer/file_format/apache.rb +20 -19
  23. data/lib/request_log_analyzer/file_format/merb.rb +12 -12
  24. data/lib/request_log_analyzer/file_format/rack.rb +4 -4
  25. data/lib/request_log_analyzer/file_format/rails.rb +146 -70
  26. data/lib/request_log_analyzer/file_format/rails_development.rb +4 -49
  27. data/lib/request_log_analyzer/filter.rb +6 -6
  28. data/lib/request_log_analyzer/filter/anonymize.rb +6 -6
  29. data/lib/request_log_analyzer/filter/field.rb +9 -9
  30. data/lib/request_log_analyzer/filter/timespan.rb +12 -10
  31. data/lib/request_log_analyzer/line_definition.rb +15 -14
  32. data/lib/request_log_analyzer/log_processor.rb +22 -22
  33. data/lib/request_log_analyzer/mailer.rb +15 -9
  34. data/lib/request_log_analyzer/output.rb +53 -12
  35. data/lib/request_log_analyzer/output/fixed_width.rb +40 -41
  36. data/lib/request_log_analyzer/output/html.rb +20 -20
  37. data/lib/request_log_analyzer/request.rb +35 -36
  38. data/lib/request_log_analyzer/source.rb +7 -7
  39. data/lib/request_log_analyzer/source/database_loader.rb +7 -7
  40. data/lib/request_log_analyzer/source/log_parser.rb +48 -43
  41. data/lib/request_log_analyzer/tracker.rb +128 -14
  42. data/lib/request_log_analyzer/tracker/duration.rb +39 -132
  43. data/lib/request_log_analyzer/tracker/frequency.rb +31 -32
  44. data/lib/request_log_analyzer/tracker/hourly_spread.rb +20 -19
  45. data/lib/request_log_analyzer/tracker/timespan.rb +17 -17
  46. data/lib/request_log_analyzer/tracker/traffic.rb +36 -116
  47. data/request-log-analyzer.gemspec +19 -15
  48. data/spec/fixtures/rails_22.log +1 -1
  49. data/spec/integration/command_line_usage_spec.rb +1 -1
  50. data/spec/lib/helpers.rb +7 -7
  51. data/spec/lib/macros.rb +3 -3
  52. data/spec/lib/matchers.rb +41 -27
  53. data/spec/lib/mocks.rb +15 -14
  54. data/spec/lib/testing_format.rb +9 -9
  55. data/spec/spec_helper.rb +6 -6
  56. data/spec/unit/aggregator/database_inserter_spec.rb +13 -13
  57. data/spec/unit/aggregator/summarizer_spec.rb +4 -4
  58. data/spec/unit/controller/controller_spec.rb +2 -2
  59. data/spec/unit/controller/log_processor_spec.rb +1 -1
  60. data/spec/unit/database/base_class_spec.rb +19 -19
  61. data/spec/unit/database/connection_spec.rb +3 -3
  62. data/spec/unit/database/database_spec.rb +25 -25
  63. data/spec/unit/file_format/amazon_s3_format_spec.rb +5 -5
  64. data/spec/unit/file_format/apache_format_spec.rb +13 -13
  65. data/spec/unit/file_format/file_format_api_spec.rb +13 -13
  66. data/spec/unit/file_format/line_definition_spec.rb +24 -17
  67. data/spec/unit/file_format/merb_format_spec.rb +41 -45
  68. data/spec/unit/file_format/rails_format_spec.rb +157 -117
  69. data/spec/unit/filter/anonymize_filter_spec.rb +2 -2
  70. data/spec/unit/filter/field_filter_spec.rb +13 -13
  71. data/spec/unit/filter/filter_spec.rb +1 -1
  72. data/spec/unit/filter/timespan_filter_spec.rb +15 -15
  73. data/spec/unit/mailer_spec.rb +30 -0
  74. data/spec/unit/{source/request_spec.rb → request_spec.rb} +30 -30
  75. data/spec/unit/source/log_parser_spec.rb +27 -27
  76. data/spec/unit/tracker/duration_tracker_spec.rb +115 -78
  77. data/spec/unit/tracker/frequency_tracker_spec.rb +74 -63
  78. data/spec/unit/tracker/hourly_spread_spec.rb +28 -20
  79. data/spec/unit/tracker/timespan_tracker_spec.rb +25 -13
  80. data/spec/unit/tracker/tracker_api_spec.rb +13 -13
  81. data/spec/unit/tracker/traffic_tracker_spec.rb +81 -79
  82. data/tasks/github-gem.rake +125 -75
  83. data/tasks/request_log_analyzer.rake +2 -2
  84. 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}" unless options[:category]
31
+ raise "No categorizer set up for duration tracker #{self.inspect}" unless options[:category]
31
32
 
32
- @categorizer = options[:category].respond_to?(:call) ? options[:category] : lambda { |request| request[options[:category]] }
33
- @durationizer = options[:duration].respond_to?(:call) ? options[:duration] : lambda { |request| request[options[:duration]] }
34
- @categories = {}
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
- categories = request.every(options[:category])
42
- durations = request.every(options[:duration])
43
-
44
- if categories.length == durations.length
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, amount = 10, options = {}, &block)
144
-
145
- output.title(options[:title])
146
-
147
- top_categories = @categories.sort { |a, b| yield(b[1]) <=> yield(a[1]) }.slice(0...amount)
148
- output.table({:title => 'Category', :width => :rest},
149
- {:title => 'Hits', :align => :right, :highlight => (options[:sort] == :hits), :min_width => 4},
150
- {:title => 'Cumulative', :align => :right, :highlight => (options[:sort] == :cumulative), :min_width => 10},
151
- {:title => 'Average', :align => :right, :highlight => (options[:sort] == :average), :min_width => 8},
152
- {:title => 'Min', :align => :right, :highlight => (options[:sort] == :min)},
153
- {:title => 'Max', :align => :right, :highlight => (options[:sort] == :max)}) do |rows|
154
-
155
- top_categories.each do |(cat, info)|
156
- rows << [cat, info[:hits], "%0.02fs" % info[:cumulative], "%0.02fs" % (info[:cumulative] / info[:hits]),
157
- "%0.02fs" % info[:min], "%0.02fs" % info[:max]]
158
- end
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
- options[:title] ||= 'Request duration'
169
- options[:report] ||= [:cumulative, :average]
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 :frequencies
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
- @frequencies = {}
32
- if options[:all_categories].kind_of?(Enumerable)
33
- options[:all_categories].each { |cat| @frequencies[cat] = 0 }
34
- end
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 frequencies hash.
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 = options[:category].respond_to?(:call) ? options[:category].call(request) : request[options[:category]]
41
- if !cat.nil? || options[:nils]
42
- @frequencies[cat] ||= 0
43
- @frequencies[cat] += 1
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
- frequencies[cat] || 0
50
+ categories[cat] || 0
51
51
  end
52
-
52
+
53
53
  # Return the overall frequency
54
54
  def overall_frequency
55
- frequencies.inject(0) { |carry, item| carry + item[1] }
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
- @frequencies.sort { |a, b| b[1] <=> a[1] }
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 @frequencies.empty?
70
- output << "None found.\n"
68
+
69
+ if @categories.empty?
70
+ output << "None found.\n"
71
71
  else
72
- sorted_frequencies = @frequencies.sort { |a, b| b[1] <=> a[1] }
73
- total_hits = sorted_frequencies.inject(0) { |carry, item| carry + item[1] }
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
- sorted_frequencies.each do |(cat, count)|
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 frequencies of every category that can be exported to YAML
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 @frequencies.empty?
88
- @frequencies
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 :first, :last, :request_time_graph
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
- @request_time_graph = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
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
- @request_time_graph[timestamp.to_s[8..9].to_i] +=1
47
- @first = timestamp if @first.nil? || timestamp < @first
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
- @request_time_graph.inject(0) { |sum, value| sum + value }
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
- @request_time_graph.each_with_index do |requests, index|
85
- ratio = requests.to_f / total_requests.to_f
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
- @request_time_graph.each_with_index do |freq, hour|
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, :request_time_graph
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
- @first = timestamp if @first.nil? || timestamp < @first
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:', last_timestamp.strftime('%Y-%m-%d %H:%M:%I')]
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