request-log-analyzer 1.3.4 → 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.
Files changed (86) 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 +4 -3
  7. data/lib/cli/progressbar.rb +10 -10
  8. data/lib/cli/tools.rb +3 -3
  9. data/lib/request_log_analyzer/aggregator/database_inserter.rb +19 -17
  10. data/lib/request_log_analyzer/aggregator/echo.rb +14 -9
  11. data/lib/request_log_analyzer/aggregator/summarizer.rb +26 -26
  12. data/lib/request_log_analyzer/aggregator.rb +11 -15
  13. data/lib/request_log_analyzer/controller.rb +162 -89
  14. data/lib/request_log_analyzer/database/base.rb +22 -21
  15. data/lib/request_log_analyzer/database/connection.rb +3 -3
  16. data/lib/request_log_analyzer/database/request.rb +22 -0
  17. data/lib/request_log_analyzer/database/source.rb +13 -0
  18. data/lib/request_log_analyzer/database/warning.rb +14 -0
  19. data/lib/request_log_analyzer/database.rb +28 -103
  20. data/lib/request_log_analyzer/file_format/amazon_s3.rb +20 -20
  21. data/lib/request_log_analyzer/file_format/apache.rb +37 -30
  22. data/lib/request_log_analyzer/file_format/merb.rb +30 -13
  23. data/lib/request_log_analyzer/file_format/rack.rb +11 -0
  24. data/lib/request_log_analyzer/file_format/rails.rb +143 -73
  25. data/lib/request_log_analyzer/file_format/rails_development.rb +4 -49
  26. data/lib/request_log_analyzer/file_format.rb +16 -28
  27. data/lib/request_log_analyzer/filter/anonymize.rb +8 -7
  28. data/lib/request_log_analyzer/filter/field.rb +9 -9
  29. data/lib/request_log_analyzer/filter/timespan.rb +12 -10
  30. data/lib/request_log_analyzer/filter.rb +12 -16
  31. data/lib/request_log_analyzer/line_definition.rb +15 -14
  32. data/lib/request_log_analyzer/log_processor.rb +27 -29
  33. data/lib/request_log_analyzer/mailer.rb +15 -9
  34. data/lib/request_log_analyzer/output/fixed_width.rb +48 -48
  35. data/lib/request_log_analyzer/output/html.rb +20 -20
  36. data/lib/request_log_analyzer/output.rb +53 -12
  37. data/lib/request_log_analyzer/request.rb +88 -69
  38. data/lib/request_log_analyzer/source/database_loader.rb +10 -14
  39. data/lib/request_log_analyzer/source/log_parser.rb +57 -50
  40. data/lib/request_log_analyzer/source.rb +10 -12
  41. data/lib/request_log_analyzer/tracker/duration.rb +41 -134
  42. data/lib/request_log_analyzer/tracker/frequency.rb +31 -32
  43. data/lib/request_log_analyzer/tracker/hourly_spread.rb +21 -21
  44. data/lib/request_log_analyzer/tracker/timespan.rb +17 -17
  45. data/lib/request_log_analyzer/tracker/traffic.rb +106 -0
  46. data/lib/request_log_analyzer/tracker.rb +139 -32
  47. data/lib/request_log_analyzer.rb +5 -5
  48. data/request-log-analyzer.gemspec +19 -15
  49. data/spec/database.yml +6 -0
  50. data/spec/fixtures/rails_22.log +1 -1
  51. data/spec/integration/command_line_usage_spec.rb +7 -1
  52. data/spec/lib/helpers.rb +7 -7
  53. data/spec/lib/macros.rb +3 -3
  54. data/spec/lib/matchers.rb +41 -27
  55. data/spec/lib/mocks.rb +19 -15
  56. data/spec/lib/testing_format.rb +9 -9
  57. data/spec/spec_helper.rb +6 -6
  58. data/spec/unit/aggregator/database_inserter_spec.rb +16 -16
  59. data/spec/unit/aggregator/summarizer_spec.rb +4 -4
  60. data/spec/unit/controller/controller_spec.rb +2 -2
  61. data/spec/unit/controller/log_processor_spec.rb +1 -1
  62. data/spec/unit/database/base_class_spec.rb +26 -33
  63. data/spec/unit/database/connection_spec.rb +3 -3
  64. data/spec/unit/database/database_spec.rb +33 -38
  65. data/spec/unit/file_format/amazon_s3_format_spec.rb +5 -5
  66. data/spec/unit/file_format/apache_format_spec.rb +48 -11
  67. data/spec/unit/file_format/file_format_api_spec.rb +13 -13
  68. data/spec/unit/file_format/line_definition_spec.rb +24 -17
  69. data/spec/unit/file_format/merb_format_spec.rb +41 -45
  70. data/spec/unit/file_format/rails_format_spec.rb +157 -117
  71. data/spec/unit/filter/anonymize_filter_spec.rb +2 -2
  72. data/spec/unit/filter/field_filter_spec.rb +13 -13
  73. data/spec/unit/filter/filter_spec.rb +1 -1
  74. data/spec/unit/filter/timespan_filter_spec.rb +15 -15
  75. data/spec/unit/mailer_spec.rb +30 -0
  76. data/spec/unit/{source/request_spec.rb → request_spec.rb} +30 -30
  77. data/spec/unit/source/log_parser_spec.rb +27 -27
  78. data/spec/unit/tracker/duration_tracker_spec.rb +115 -78
  79. data/spec/unit/tracker/frequency_tracker_spec.rb +74 -63
  80. data/spec/unit/tracker/hourly_spread_spec.rb +28 -20
  81. data/spec/unit/tracker/timespan_tracker_spec.rb +25 -13
  82. data/spec/unit/tracker/tracker_api_spec.rb +117 -42
  83. data/spec/unit/tracker/traffic_tracker_spec.rb +107 -0
  84. data/tasks/github-gem.rake +125 -75
  85. data/tasks/request_log_analyzer.rake +2 -2
  86. metadata +19 -10
@@ -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,51 +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
- request = request.attributes
45
- timestamp = request[options[:field]]
46
-
47
- @request_time_graph[timestamp.to_s[8..9].to_i] +=1
48
- @first = timestamp if @first.nil? || timestamp < @first
49
- @last = timestamp if @last.nil? || timestamp > @last
45
+ timestamp = request.first(options[:field])
46
+ @hour_frequencies[timestamp.to_s[8..9].to_i] +=1
47
+ @first = timestamp if timestamp < @first
48
+ @last = timestamp if timestamp > @last
50
49
  end
51
50
 
52
51
  # Total amount of requests tracked
53
52
  def total_requests
54
- @request_time_graph.inject(0) { |sum, value| sum + value }
53
+ @hour_frequencies.inject(0) { |sum, value| sum + value }
55
54
  end
56
-
55
+
56
+
57
57
  # First timestamp encountered
58
58
  def first_timestamp
59
59
  DateTime.parse(@first.to_s, '%Y%m%d%H%M%S') rescue nil
60
60
  end
61
-
61
+
62
62
  # Last timestamp encountered
63
63
  def last_timestamp
64
64
  DateTime.parse(@last.to_s, '%Y%m%d%H%M%S') rescue nil
65
65
  end
66
-
66
+
67
67
  # Difference between last and first timestamp.
68
68
  def timespan
69
69
  last_timestamp - first_timestamp
70
70
  end
71
-
71
+
72
72
  # Generate an hourly spread report to the given output object.
73
73
  # Any options for the report should have been set during initialize.
74
74
  # <tt>output</tt> The output object
75
75
  def report(output)
76
76
  output.title(title)
77
-
77
+
78
78
  if total_requests == 0
79
79
  output << "None found.\n"
80
80
  return
@@ -82,23 +82,23 @@ module RequestLogAnalyzer::Tracker
82
82
 
83
83
  days = [1, timespan].max
84
84
  output.table({}, {:align => :right}, {:type => :ratio, :width => :rest, :treshold => 0.15}) do |rows|
85
- @request_time_graph.each_with_index do |requests, index|
86
- 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
87
87
  requests_per_day = (requests / days).ceil
88
- 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]
89
89
  end
90
90
  end
91
91
  end
92
-
92
+
93
93
  # Returns the title of this tracker for reports
94
94
  def title
95
95
  options[:title] || "Request distribution per hour"
96
96
  end
97
-
97
+
98
98
  # Returns the found frequencies per hour as a hash for YAML exporting
99
99
  def to_yaml_object
100
100
  yaml_object = {}
101
- @request_time_graph.each_with_index do |freq, hour|
101
+ @hour_frequencies.each_with_index do |freq, hour|
102
102
  yaml_object["#{hour}:00 - #{hour+1}:00"] = freq
103
103
  end
104
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
@@ -0,0 +1,106 @@
1
+ module RequestLogAnalyzer::Tracker
2
+
3
+ # Analyze the average and total traffic of requests
4
+ #
5
+ # === Options
6
+ # * <tt>:category</tt> Proc that handles request categorization for given fileformat (REQUEST_CATEGORIZER)
7
+ # * <tt>:traffic</tt> The field containing the duration in the request hash.
8
+ # * <tt>:if</tt> Proc that has to return !nil for a request to be passed to the tracker.
9
+ # * <tt>:line_type</tt> The line type that contains the duration field (determined by the category proc).
10
+ # * <tt>:title</tt> Title do be displayed above the report
11
+ # * <tt>:unless</tt> Handle request if this proc is false for the handled request.
12
+ class Traffic < Base
13
+
14
+ attr_reader :categories
15
+
16
+ include RequestLogAnalyzer::Tracker::StatisticsTracking
17
+
18
+ # Check if duration and catagory option have been received,
19
+ def prepare
20
+ raise "No traffic field set up for category tracker #{self.inspect}" unless options[:traffic]
21
+ raise "No categorizer set up for duration tracker #{self.inspect}" unless options[:category]
22
+
23
+ @categorizer = create_lambda(options[:category])
24
+ @trafficizer = create_lambda(options[:traffic])
25
+ @categories = {}
26
+ end
27
+
28
+
29
+ # Get the duration information from the request and store it in the different categories.
30
+ # <tt>request</tt> The request.
31
+ def update(request)
32
+ category = @categorizer.call(request)
33
+ traffic = @trafficizer.call(request)
34
+ update_statistics(category, traffic) if traffic.kind_of?(Numeric) && !category.nil?
35
+ end
36
+
37
+ # Block function to build a result table using a provided sorting function.
38
+ # <tt>output</tt> The output object.
39
+ # <tt>amount</tt> The number of rows in the report table (default 10).
40
+ # === Options
41
+ # * </tt>:title</tt> The title of the table
42
+ # * </tt>:sort</tt> The key to sort on (:hits, :cumulative, :average, :min or :max)
43
+ def report_table(output, sort, options = {}, &block)
44
+ output.puts
45
+
46
+ top_categories = output.slice_results(sorted_by(sort))
47
+ output.with_style(:top_line => true) do
48
+ output.table(*statistics_header(:title => options[:title],:highlight => sort)) do |rows|
49
+ top_categories.each { |(cat, info)| rows.push(statistics_row(cat)) }
50
+ end
51
+ end
52
+ output.puts
53
+ end
54
+
55
+ # Formats the traffic number using x B/kB/MB/GB etc notation
56
+ def display_value(bytes)
57
+ return "-" if bytes.nil?
58
+ return "0 B" if bytes.zero?
59
+ case Math.log10(bytes).floor
60
+ when 1...4 then '%d B' % bytes
61
+ when 4...7 then '%d kB' % (bytes / 1000)
62
+ when 7...10 then '%d MB' % (bytes / 1000_000)
63
+ when 10...13 then '%d GB' % (bytes / 1000_000_000)
64
+ else '%d TB' % (bytes / 1000_000_000_000)
65
+ end
66
+ end
67
+
68
+ # Generate a request duration report to the given output object
69
+ # By default colulative and average duration are generated.
70
+ # Any options for the report should have been set during initialize.
71
+ # <tt>output</tt> The output object
72
+ def report(output)
73
+
74
+ sortings = output.options[:sort] || [:sum, :mean]
75
+
76
+ sortings.each do |report|
77
+ case report
78
+ when :mean
79
+ report_table(output, :mean, :title => "#{title} - sorted by mean")
80
+ when :stddev
81
+ report_table(output, :stddev, :title => "#{title} - sorted by standard deviation")
82
+ when :sum
83
+ report_table(output, :sum, :title => "#{title} - sorted by sum")
84
+ when :hits
85
+ report_table(output, :hits, :title => "#{title} - sorted by hits")
86
+ else
87
+ raise "Unknown duration report specified: #{report}!"
88
+ end
89
+ end
90
+
91
+ output.puts
92
+ output.puts "#{output.colorize(title, :white, :bold)} - observed total: " + output.colorize(display_value(sum_overall), :brown, :bold)
93
+ end
94
+
95
+ # Returns the title of this tracker for reports
96
+ def title
97
+ options[:title] || 'Request traffic'
98
+ end
99
+
100
+ # Returns all the categories and the tracked duration as a hash than can be exported to YAML
101
+ def to_yaml_object
102
+ return nil if @categories.empty?
103
+ @categories
104
+ end
105
+ end
106
+ end
@@ -16,84 +16,191 @@ module RequestLogAnalyzer::Tracker
16
16
  #
17
17
  # For example :if => lambda { |request| request[:duration] && request[:duration] > 1.0 }
18
18
  class Base
19
-
19
+
20
20
  attr_reader :options
21
-
21
+
22
22
  # Initialize the class
23
23
  # Note that the options are only applicable if should_update? is not overwritten
24
24
  # by the inheriting class.
25
- #
25
+ #
26
26
  # === Options
27
27
  # * <tt>:if</tt> Handle request if this proc is true for the handled request.
28
28
  # * <tt>:unless</tt> Handle request if this proc is false for the handled request.
29
29
  # * <tt>:line_type</tt> Line type this tracker will accept.
30
30
  def initialize(options ={})
31
31
  @options = options
32
+ setup_should_update_checks!
33
+ end
34
+
35
+ # Sets up the tracker's should_update? checks.
36
+ def setup_should_update_checks!
37
+ @should_update_checks = []
38
+ @should_update_checks.push( lambda { |request| request.has_line_type?(options[:line_type]) } ) if options[:line_type]
39
+ @should_update_checks.push(options[:if]) if options[:if].respond_to?(:call)
40
+ @should_update_checks.push( lambda { |request| request[options[:if]] }) if options[:if].kind_of?(Symbol)
41
+ @should_update_checks.push( lambda { |request| !options[:unless].call(request) }) if options[:unless].respond_to?(:call)
42
+ @should_update_checks.push( lambda { |request| !request[options[:unless]] }) if options[:unless].kind_of?(Symbol)
32
43
  end
33
44
 
45
+ # Creates a lambda expression to return a static field from a request. If the
46
+ # argument already is a lambda exprssion, it will simply return the argument.
47
+ def create_lambda(arg)
48
+ case arg
49
+ when Proc then arg
50
+ when Symbol then lambda { |request| request[arg] }
51
+ else raise "Canot create a lambda expression from this argument: #{arg.inspect}!"
52
+ end
53
+ end
54
+
34
55
  # Hook things that need to be done before running here.
35
56
  def prepare
36
57
  end
37
-
58
+
38
59
  # Will be called with each request.
39
60
  # <tt>request</tt> The request to track data in.
40
61
  def update(request)
41
62
  end
42
-
63
+
43
64
  # Hook things that need to be done after running here.
44
65
  def finalize
45
66
  end
46
-
67
+
47
68
  # Determine if we should run the update function at all.
48
69
  # Usually the update function will be heavy, so a light check is done here
49
70
  # determining if we need to call update at all.
50
71
  #
51
- # Default this checks if defined:
72
+ # Default this checks if defined:
52
73
  # * :line_type is also in the request hash.
53
74
  # * :if is true for this request.
54
75
  # * :unless if false for this request
55
76
  #
56
77
  # <tt>request</tt> The request object.
57
78
  def should_update?(request)
58
- return false if options[:line_type] && !request.has_line_type?(options[:line_type])
59
-
60
- if options[:if]
61
- if options[:if].kind_of?(Symbol)
62
- return false unless request[options[:if]]
63
- elsif options[:if].respond_to?(:call)
64
- return false unless options[:if].call(request)
65
- end
66
- end
67
-
68
- if options[:unless]
69
- if options[:unless].kind_of?(Symbol)
70
- return false if request[options[:unless]]
71
- elsif options[:unless].respond_to?(:call)
72
- return false if options[:unless].call(request)
73
- end
74
- end
75
-
76
- return true
79
+ @should_update_checks.all? { |c| c.call(request) }
77
80
  end
78
-
81
+
79
82
  # Hook report generation here.
80
83
  # Defaults to self.inspect
81
84
  # <tt>output</tt> The output object the report will be passed to.
82
85
  def report(output)
83
86
  output << self.inspect
84
- output << "\n"
87
+ output << "\n"
85
88
  end
86
-
89
+
87
90
  # The title of this tracker. Used for reporting.
88
91
  def title
89
92
  self.class.to_s
90
93
  end
91
-
94
+
92
95
  # This method is called by RequestLogAnalyzer::Aggregator:Summarizer to retrieve an
93
96
  # object with all the results of this tracker, that can be dumped to YAML format.
94
97
  def to_yaml_object
95
98
  nil
96
99
  end
97
-
98
- end
100
+ end
101
+
102
+ module StatisticsTracking
103
+
104
+ # Update sthe running calculation of statistics with the newly found numeric value.
105
+ # <tt>category</tt>:: The category for which to update the running statistics calculations
106
+ # <tt>number</tt>:: The numeric value to update the calculations with.
107
+ def update_statistics(category, number)
108
+ @categories[category] ||= {:hits => 0, :sum => 0, :mean => 0.0, :sum_of_squares => 0.0, :min => number, :max => number }
109
+ delta = number - @categories[category][:mean]
110
+
111
+ @categories[category][:hits] += 1
112
+ @categories[category][:mean] += (delta / @categories[category][:hits])
113
+ @categories[category][:sum_of_squares] += delta * (number - @categories[category][:mean])
114
+ @categories[category][:sum] += number
115
+ @categories[category][:min] = number if number < @categories[category][:min]
116
+ @categories[category][:max] = number if number > @categories[category][:max]
117
+ end
118
+
119
+ # Get the number of hits of a specific category.
120
+ # <tt>cat</tt> The category
121
+ def hits(cat)
122
+ @categories[cat][:hits]
123
+ end
124
+
125
+ # Get the total duration of a specific category.
126
+ # <tt>cat</tt> The category
127
+ def sum(cat)
128
+ @categories[cat][:sum]
129
+ end
130
+
131
+ # Get the minimal duration of a specific category.
132
+ # <tt>cat</tt> The category
133
+ def min(cat)
134
+ @categories[cat][:min]
135
+ end
136
+
137
+ # Get the maximum duration of a specific category.
138
+ # <tt>cat</tt> The category
139
+ def max(cat)
140
+ @categories[cat][:max]
141
+ end
142
+
143
+ # Get the average duration of a specific category.
144
+ # <tt>cat</tt> The category
145
+ def mean(cat)
146
+ @categories[cat][:mean]
147
+ end
148
+
149
+ # Get the standard deviation of the duration of a specific category.
150
+ # <tt>cat</tt> The category
151
+ def stddev(cat)
152
+ Math.sqrt(variance(cat))
153
+ end
154
+
155
+ # Get the variance of the duration of a specific category.
156
+ # <tt>cat</tt> The category
157
+ def variance(cat)
158
+ return 0.0 if @categories[cat][:hits] <= 1
159
+ (@categories[cat][:sum_of_squares] / (@categories[cat][:hits] - 1))
160
+ end
161
+
162
+ # Get the average duration of a all categories.
163
+ def mean_overall
164
+ sum_overall / hits_overall
165
+ end
166
+
167
+ # Get the cumlative duration of a all categories.
168
+ def sum_overall
169
+ @categories.inject(0.0) { |sum, (name, cat)| sum + cat[:sum] }
170
+ end
171
+
172
+ # Get the total hits of a all categories.
173
+ def hits_overall
174
+ @categories.inject(0) { |sum, (name, cat)| sum + cat[:hits] }
175
+ end
176
+
177
+ # Return categories sorted by a given key.
178
+ # <tt>by</tt> The key to sort on. This parameter can be omitted if a sorting block is provided instead
179
+ def sorted_by(by = nil)
180
+ if block_given?
181
+ categories.sort { |a, b| yield(b[1]) <=> yield(a[1]) }
182
+ else
183
+ categories.sort { |a, b| send(by, b[0]) <=> send(by, a[0]) }
184
+ end
185
+ end
186
+
187
+ # Returns the column header for a statistics table to report on the statistics result
188
+ def statistics_header(options)
189
+ [
190
+ {:title => options[:title], :width => :rest},
191
+ {:title => 'Hits', :align => :right, :highlight => (options[:highlight] == :hits), :min_width => 4},
192
+ {:title => 'Sum', :align => :right, :highlight => (options[:highlight] == :sum), :min_width => 6},
193
+ {:title => 'Mean', :align => :right, :highlight => (options[:highlight] == :mean), :min_width => 6},
194
+ {:title => 'StdDev', :align => :right, :highlight => (options[:highlight] == :stddev), :min_width => 6},
195
+ {:title => 'Min', :align => :right, :highlight => (options[:highlight] == :min), :min_width => 6},
196
+ {:title => 'Max', :align => :right, :highlight => (options[:highlight] == :max), :min_width => 6}
197
+ ]
198
+ end
199
+
200
+ # Returns a row of statistics information for a report table, given a category
201
+ def statistics_row(cat)
202
+ [cat, hits(cat), display_value(sum(cat)), display_value(mean(cat)), display_value(stddev(cat)),
203
+ display_value(min(cat)), display_value(max(cat))]
204
+ end
205
+ end
99
206
  end
@@ -8,10 +8,10 @@ Encoding.default_external = 'binary' if defined? Encoding and Encoding.respond_t
8
8
  # - This module itselfs contains some functions to help with class and source file loading.
9
9
  # - The actual application resides in the RequestLogAnalyzer::Controller class.
10
10
  module RequestLogAnalyzer
11
-
11
+
12
12
  # The current version of request-log-analyzer.
13
- # This will be diplayed in output reports etc.
14
- VERSION = "1.3.4"
13
+ # Do not change the value by hand; it will be updated automatically by the gem release script.
14
+ VERSION = "1.4.0"
15
15
 
16
16
  # Loads constants in the RequestLogAnalyzer namespace using self.load_default_class_file(base, const)
17
17
  # <tt>const</tt>:: The constant that is not yet loaded in the RequestLogAnalyzer namespace. This should be passed as a string or symbol.
@@ -25,7 +25,7 @@ module RequestLogAnalyzer
25
25
  # <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.
26
26
  def self.load_default_class_file(base, const)
27
27
  require "#{to_underscore("#{base.name}::#{const}")}"
28
- base.const_get(const)
28
+ base.const_get(const) if base.const_defined?(const)
29
29
  end
30
30
 
31
31
  # Convert a string/symbol in camelcase (RequestLogAnalyzer::Controller) to underscores (request_log_analyzer/controller)
@@ -35,7 +35,7 @@ module RequestLogAnalyzer
35
35
  str.to_s.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
36
36
  end
37
37
 
38
- # Convert a string/symbol in underscores (<tt>request_log_analyzer/controller</tt>) to camelcase
38
+ # Convert a string/symbol in underscores (<tt>request_log_analyzer/controller</tt>) to camelcase
39
39
  # (<tt>RequestLogAnalyzer::Controller</tt>). This can be used to find the class that is defined in a given filename.
40
40
  # <tt>str</tt>:: The string to convert in the following format: <tt>module_name/class_name</tt>
41
41
  def self.to_camelcase(str)
@@ -1,36 +1,40 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "request-log-analyzer"
3
- s.version = "1.3.4"
4
- s.date = "2009-09-14"
5
3
 
4
+ # Do not set the version and date field manually, this is done by the release script
5
+ s.version = "1.4.0"
6
+ s.date = "2009-09-30"
7
+
6
8
  s.rubyforge_project = 'r-l-a'
7
-
9
+
8
10
  s.bindir = 'bin'
9
11
  s.executables = ['request-log-analyzer']
10
12
  s.default_executable = 'request-log-analyzer'
11
-
12
- s.summary = "A command line tool to analyze request logs for Apache, Rails, Merb and other application servers"
13
+
14
+ s.summary = "A command line tool to analyze request logs for Apache, Rails, Merb and other web application servers"
13
15
  s.description = <<-eos
14
16
  Request log analyzer's purpose is to find ot how your web application is being used and to focus your optimization efforts.
15
- This tool will parse all requests in the application's log file and aggregate the information. Once it is finished parsing
16
- the log file(s), it will show the requests that take op most server time using various metrics. It can also insert all
17
- parsed request information into a database so you can roll your own analysis. It supports Rails- and Merb-based applications
18
- and Apache access log files out of the box, but file formats of other applications can easily be supported by supplying an
17
+ This tool will parse all requests in the application's log file and aggregate the information. Once it is finished parsing
18
+ the log file(s), it will show the requests that take op most server time using various metrics. It can also insert all
19
+ parsed request information into a database so you can roll your own analysis. It supports Rails- and Merb-based applications
20
+ and Apache access log files out of the box, but file formats of other applications can easily be supported by supplying an
19
21
  easy to write log file format definition.
20
22
  eos
21
-
23
+
22
24
  s.rdoc_options << '--title' << s.name << '--main' << 'README.rdoc' << '--line-numbers' << '--inline-source'
23
25
  s.extra_rdoc_files = ['README.rdoc']
24
-
26
+
25
27
  s.requirements << "To use the database inserter, ActiveRecord and an appropriate database adapter are required."
26
-
28
+
27
29
  s.add_development_dependency('rspec', '>= 1.2.4')
28
30
  s.add_development_dependency('git', '>= 1.1.0')
29
-
31
+
30
32
  s.authors = ['Willem van Bergen', 'Bart ten Brinke']
31
33
  s.email = ['willem@railsdoctors.com', 'bart@railsdoctors.com']
32
34
  s.homepage = 'http://railsdoctors.com'
33
35
 
34
- s.files = %w(spec/unit/filter/anonymize_filter_spec.rb lib/request_log_analyzer/line_definition.rb lib/request_log_analyzer/output/html.rb lib/request_log_analyzer/controller.rb spec/fixtures/rails_22_cached.log spec/lib/macros.rb lib/request_log_analyzer/file_format/rails_development.rb spec/fixtures/apache_combined.log spec/fixtures/apache_common.log spec/fixtures/merb_prefixed.log lib/request_log_analyzer/file_format/amazon_s3.rb tasks/request_log_analyzer.rake spec/unit/file_format/file_format_api_spec.rb spec/unit/file_format/apache_format_spec.rb spec/integration/command_line_usage_spec.rb lib/request_log_analyzer/database.rb spec/fixtures/decompression.log.bz2 lib/request_log_analyzer/log_processor.rb lib/request_log_analyzer/tracker.rb lib/request_log_analyzer/filter.rb spec/fixtures/rails_unordered.log bin/request-log-analyzer request-log-analyzer.gemspec DESIGN.rdoc spec/unit/filter/timespan_filter_spec.rb spec/unit/aggregator/database_inserter_spec.rb spec/lib/matchers.rb lib/request_log_analyzer/filter/field.rb lib/request_log_analyzer/tracker/frequency.rb spec/fixtures/decompression.log.gz spec/fixtures/decompression.log spec/lib/testing_format.rb spec/fixtures/test_order.log spec/fixtures/rails.db lib/request_log_analyzer/output/fixed_width.rb lib/request_log_analyzer/filter/anonymize.rb lib/request_log_analyzer/tracker/timespan.rb lib/request_log_analyzer/database/base.rb lib/request_log_analyzer/aggregator.rb lib/cli/progressbar.rb lib/request_log_analyzer/mailer.rb README.rdoc spec/fixtures/merb.log lib/request_log_analyzer/tracker/hourly_spread.rb .gitignore spec/unit/tracker/tracker_api_spec.rb spec/unit/tracker/duration_tracker_spec.rb spec/unit/file_format/amazon_s3_format_spec.rb lib/request_log_analyzer/aggregator/echo.rb spec/unit/controller/log_processor_spec.rb spec/spec_helper.rb lib/request_log_analyzer.rb spec/database.yml Rakefile lib/request_log_analyzer/database/connection.rb spec/unit/filter/filter_spec.rb spec/fixtures/test_language_combined.log lib/request_log_analyzer/aggregator/database_inserter.rb lib/request_log_analyzer/aggregator/summarizer.rb lib/request_log_analyzer/file_format/rails.rb spec/fixtures/decompression.tar.gz spec/unit/filter/field_filter_spec.rb spec/unit/database/base_class_spec.rb lib/request_log_analyzer/filter/timespan.rb lib/request_log_analyzer/source/log_parser.rb spec/fixtures/decompression.tgz spec/unit/tracker/timespan_tracker_spec.rb spec/unit/tracker/hourly_spread_spec.rb spec/fixtures/header_and_footer.log lib/cli/tools.rb lib/request_log_analyzer/file_format/merb.rb spec/fixtures/multiple_files_1.log spec/unit/file_format/merb_format_spec.rb spec/unit/file_format/line_definition_spec.rb lib/request_log_analyzer/source.rb lib/request_log_analyzer/request.rb lib/cli/database_console.rb spec/unit/database/connection_spec.rb spec/unit/controller/controller_spec.rb spec/lib/mocks.rb spec/lib/helpers.rb lib/cli/database_console_init.rb lib/request_log_analyzer/output.rb lib/request_log_analyzer/file_format/apache.rb spec/fixtures/rails_1x.log spec/fixtures/decompression.log.zip spec/unit/source/request_spec.rb spec/unit/source/log_parser_spec.rb spec/fixtures/test_file_format.log tasks/github-gem.rake spec/unit/database/database_spec.rb lib/request_log_analyzer/tracker/duration.rb lib/request_log_analyzer/file_format.rb spec/unit/aggregator/summarizer_spec.rb spec/fixtures/rails_22.log spec/fixtures/multiple_files_2.log spec/fixtures/syslog_1x.log LICENSE lib/request_log_analyzer/source/database_loader.rb spec/unit/tracker/frequency_tracker_spec.rb spec/unit/file_format/rails_format_spec.rb lib/cli/command_line_arguments.rb)
35
- s.test_files = %w(spec/unit/filter/anonymize_filter_spec.rb spec/unit/file_format/file_format_api_spec.rb spec/unit/file_format/apache_format_spec.rb spec/integration/command_line_usage_spec.rb spec/unit/filter/timespan_filter_spec.rb spec/unit/aggregator/database_inserter_spec.rb spec/unit/tracker/tracker_api_spec.rb spec/unit/tracker/duration_tracker_spec.rb spec/unit/file_format/amazon_s3_format_spec.rb spec/unit/controller/log_processor_spec.rb spec/unit/filter/filter_spec.rb spec/unit/filter/field_filter_spec.rb spec/unit/database/base_class_spec.rb spec/unit/tracker/timespan_tracker_spec.rb spec/unit/tracker/hourly_spread_spec.rb spec/unit/file_format/merb_format_spec.rb spec/unit/file_format/line_definition_spec.rb spec/unit/database/connection_spec.rb spec/unit/controller/controller_spec.rb spec/unit/source/request_spec.rb spec/unit/source/log_parser_spec.rb spec/unit/database/database_spec.rb spec/unit/aggregator/summarizer_spec.rb spec/unit/tracker/frequency_tracker_spec.rb spec/unit/file_format/rails_format_spec.rb)
36
+ # The files and test_files directives are set automatically by the release script.
37
+ # Do not change them by hand, but make sure to add the files to the git repository.
38
+ s.files = %w(spec/unit/filter/anonymize_filter_spec.rb spec/fixtures/rails_22_cached.log lib/request_log_analyzer/line_definition.rb lib/request_log_analyzer/output/html.rb lib/request_log_analyzer/controller.rb spec/lib/macros.rb lib/request_log_analyzer/file_format/rails_development.rb spec/fixtures/apache_combined.log spec/fixtures/apache_common.log spec/fixtures/merb_prefixed.log lib/request_log_analyzer/file_format/amazon_s3.rb tasks/request_log_analyzer.rake spec/unit/file_format/file_format_api_spec.rb spec/unit/file_format/apache_format_spec.rb spec/integration/command_line_usage_spec.rb lib/request_log_analyzer/database.rb spec/fixtures/decompression.log.bz2 spec/fixtures/rails_unordered.log lib/request_log_analyzer/log_processor.rb lib/request_log_analyzer/tracker.rb lib/request_log_analyzer/filter.rb bin/request-log-analyzer request-log-analyzer.gemspec DESIGN.rdoc spec/unit/filter/timespan_filter_spec.rb spec/unit/aggregator/database_inserter_spec.rb spec/lib/matchers.rb lib/request_log_analyzer/filter/field.rb lib/request_log_analyzer/tracker/frequency.rb spec/fixtures/decompression.log.gz spec/fixtures/decompression.log spec/lib/testing_format.rb spec/fixtures/test_order.log spec/fixtures/rails.db lib/request_log_analyzer/output/fixed_width.rb lib/request_log_analyzer/filter/anonymize.rb lib/request_log_analyzer/tracker/timespan.rb lib/request_log_analyzer/database/base.rb lib/request_log_analyzer/aggregator.rb spec/unit/request_spec.rb lib/cli/progressbar.rb lib/request_log_analyzer/mailer.rb README.rdoc lib/request_log_analyzer/database/warning.rb spec/fixtures/merb.log lib/request_log_analyzer/tracker/hourly_spread.rb .gitignore spec/unit/tracker/tracker_api_spec.rb spec/unit/tracker/duration_tracker_spec.rb spec/unit/file_format/amazon_s3_format_spec.rb lib/request_log_analyzer/aggregator/echo.rb spec/unit/mailer_spec.rb spec/unit/controller/log_processor_spec.rb spec/spec_helper.rb lib/request_log_analyzer.rb spec/database.yml Rakefile lib/request_log_analyzer/database/connection.rb spec/unit/filter/filter_spec.rb spec/fixtures/test_language_combined.log lib/request_log_analyzer/aggregator/database_inserter.rb lib/request_log_analyzer/aggregator/summarizer.rb lib/request_log_analyzer/file_format/rack.rb lib/request_log_analyzer/database/source.rb lib/request_log_analyzer/file_format/rails.rb spec/fixtures/decompression.tar.gz spec/unit/tracker/traffic_tracker_spec.rb spec/unit/filter/field_filter_spec.rb spec/unit/database/base_class_spec.rb lib/request_log_analyzer/filter/timespan.rb lib/request_log_analyzer/source/log_parser.rb spec/fixtures/decompression.tgz spec/unit/tracker/timespan_tracker_spec.rb spec/unit/tracker/hourly_spread_spec.rb spec/fixtures/header_and_footer.log lib/cli/tools.rb lib/request_log_analyzer/file_format/merb.rb spec/fixtures/multiple_files_1.log spec/unit/file_format/merb_format_spec.rb spec/unit/file_format/line_definition_spec.rb lib/request_log_analyzer/source.rb lib/request_log_analyzer/request.rb lib/cli/database_console.rb spec/unit/database/connection_spec.rb spec/unit/controller/controller_spec.rb spec/lib/mocks.rb spec/lib/helpers.rb spec/fixtures/rails_1x.log lib/cli/database_console_init.rb lib/request_log_analyzer/output.rb lib/request_log_analyzer/file_format/apache.rb spec/fixtures/decompression.log.zip spec/unit/source/log_parser_spec.rb spec/fixtures/test_file_format.log tasks/github-gem.rake spec/unit/database/database_spec.rb lib/request_log_analyzer/tracker/duration.rb lib/request_log_analyzer/tracker/traffic.rb lib/request_log_analyzer/file_format.rb spec/unit/aggregator/summarizer_spec.rb spec/fixtures/syslog_1x.log spec/fixtures/rails_22.log lib/request_log_analyzer/database/request.rb spec/fixtures/multiple_files_2.log LICENSE lib/request_log_analyzer/source/database_loader.rb spec/unit/tracker/frequency_tracker_spec.rb spec/unit/file_format/rails_format_spec.rb lib/cli/command_line_arguments.rb)
39
+ s.test_files = %w(spec/unit/filter/anonymize_filter_spec.rb spec/unit/file_format/file_format_api_spec.rb spec/unit/file_format/apache_format_spec.rb spec/integration/command_line_usage_spec.rb spec/unit/filter/timespan_filter_spec.rb spec/unit/aggregator/database_inserter_spec.rb spec/unit/request_spec.rb spec/unit/tracker/tracker_api_spec.rb spec/unit/tracker/duration_tracker_spec.rb spec/unit/file_format/amazon_s3_format_spec.rb spec/unit/mailer_spec.rb spec/unit/controller/log_processor_spec.rb spec/unit/filter/filter_spec.rb spec/unit/tracker/traffic_tracker_spec.rb spec/unit/filter/field_filter_spec.rb spec/unit/database/base_class_spec.rb spec/unit/tracker/timespan_tracker_spec.rb spec/unit/tracker/hourly_spread_spec.rb spec/unit/file_format/merb_format_spec.rb spec/unit/file_format/line_definition_spec.rb spec/unit/database/connection_spec.rb spec/unit/controller/controller_spec.rb spec/unit/source/log_parser_spec.rb spec/unit/database/database_spec.rb spec/unit/aggregator/summarizer_spec.rb spec/unit/tracker/frequency_tracker_spec.rb spec/unit/file_format/rails_format_spec.rb)
36
40
  end
data/spec/database.yml CHANGED
@@ -1,3 +1,9 @@
1
+ # This file determines what databases are used to test the database
2
+ # functionality of request-log-analyzer. By default, only an SQLite3
3
+ # memory database is enabled.
4
+ #
5
+ # The syntax of this file is exactly the same as Rails's database.yml.
6
+
1
7
  sqlite3:
2
8
  adapter: "sqlite3"
3
9
  database: ":memory:"
@@ -9,4 +9,4 @@ Rendered shared/_analytics (0.2ms)
9
9
  Rendered layouts/_actions (0.6ms)
10
10
  Rendered layouts/_menu (2.2ms)
11
11
  Rendered layouts/_tabbar (0.5ms)
12
- Completed in 614ms (View: 120, DB: 31) | 200 OK [http://www.example.coml/demo]
12
+ Completed in 614ms (View: 120, DB: 31) | 200 OK [http://www.example.com/demo]