wvanbergen-request-log-analyzer 1.1.0 → 1.1.1

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 (40) hide show
  1. data/README.rdoc +4 -3
  2. data/bin/request-log-analyzer +4 -5
  3. data/lib/cli/command_line_arguments.rb +2 -2
  4. data/lib/request_log_analyzer/aggregator/summarizer.rb +2 -3
  5. data/lib/request_log_analyzer/{aggregator/base.rb → aggregator.rb} +5 -1
  6. data/lib/request_log_analyzer/controller.rb +11 -16
  7. data/lib/request_log_analyzer/file_format/merb.rb +32 -26
  8. data/lib/request_log_analyzer/file_format/rails.rb +73 -71
  9. data/lib/request_log_analyzer/file_format/rails_development.rb +93 -95
  10. data/lib/request_log_analyzer/file_format.rb +71 -38
  11. data/lib/request_log_analyzer/filter/anonimize.rb +1 -1
  12. data/lib/request_log_analyzer/filter.rb +38 -0
  13. data/lib/request_log_analyzer/line_definition.rb +1 -1
  14. data/lib/request_log_analyzer/output/fixed_width.rb +133 -117
  15. data/lib/request_log_analyzer/output/html.rb +138 -60
  16. data/lib/request_log_analyzer/output.rb +6 -8
  17. data/lib/request_log_analyzer/request.rb +3 -1
  18. data/lib/request_log_analyzer/source/{log_file.rb → log_parser.rb} +15 -6
  19. data/lib/request_log_analyzer/{source/base.rb → source.rb} +5 -0
  20. data/lib/request_log_analyzer/tracker/category.rb +7 -8
  21. data/lib/request_log_analyzer/tracker/duration.rb +15 -12
  22. data/lib/request_log_analyzer/tracker/hourly_spread.rb +8 -8
  23. data/lib/request_log_analyzer/tracker/timespan.rb +10 -10
  24. data/lib/request_log_analyzer/tracker.rb +58 -0
  25. data/lib/request_log_analyzer.rb +28 -6
  26. data/spec/controller_spec.rb +5 -4
  27. data/spec/database_inserter_spec.rb +5 -8
  28. data/spec/file_format_spec.rb +2 -2
  29. data/spec/file_formats/spec_format.rb +2 -1
  30. data/spec/filter_spec.rb +0 -3
  31. data/spec/log_parser_spec.rb +6 -6
  32. data/spec/merb_format_spec.rb +38 -38
  33. data/spec/rails_format_spec.rb +2 -2
  34. data/spec/request_spec.rb +2 -2
  35. data/spec/spec_helper.rb +3 -37
  36. data/tasks/github-gem.rake +2 -1
  37. metadata +7 -8
  38. data/lib/request_log_analyzer/filter/base.rb +0 -32
  39. data/lib/request_log_analyzer/log_parser.rb +0 -173
  40. data/lib/request_log_analyzer/tracker/base.rb +0 -54
data/README.rdoc CHANGED
@@ -5,7 +5,7 @@ Merb to produce a performance report. Its purpose is to find what actions are be
5
5
 
6
6
  * Analyzes Rails log files (all versions)
7
7
  * Can combine multiple files (handy if you are using logrotate)
8
- * Uses several metrics, including cumulative request time, average request time, process blockers, database and rendering time, HTTP methods and states, Rails action cache statistics, etc.) (Sample output: http://wiki.github.com/wvanbergen/request-log-analyzer/sample-output)
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)
10
10
  * Fast
11
11
  * MIT licensed
@@ -32,10 +32,11 @@ To analyze a log file and produce a performance report, run request-log-analyzer
32
32
 
33
33
  $ request-log-analyzer log/production.log
34
34
 
35
- For more details and available command line options, see the project's wiki:http://wiki.github.com/wvanbergen/request-log-analyzer/basic-usage
35
+ For more details and available command line options, see the project's wiki: http://wiki.github.com/wvanbergen/request-log-analyzer/basic-usage
36
36
 
37
37
  == Additional information
38
38
 
39
39
  * Project wiki at GitHub: http://wiki.github.com/wvanbergen/request-log-analyzer
40
+ * RDoc documentation: http://wvanbergen.github.com/request-log-analyzer
40
41
  * wvanbergen's blog posts: http://techblog.floorplanner.com/tag/request-log-analyzer
41
- * bart ten brinke's blog posts: http://movesonrails.com
42
+ * barttenbrinke's blog posts: http://movesonrails.com/articles/tag/analyzer
@@ -115,11 +115,10 @@ when :anonymize
115
115
  require File.dirname(__FILE__) + '/../lib/request_log_analyzer/log_processor'
116
116
  RequestLogAnalyzer::LogProcessor.build(:anonymize, arguments).run!
117
117
  else
118
- puts "Request log analyzer, by Willem van Bergen and Bart ten Brinke - Version 1.0\n\n"
119
-
118
+ puts "Request-log-analyzer, by Willem van Bergen and Bart ten Brinke - version #{RequestLogAnalyzer::VERSION}"
119
+ puts "Website: http://github.com/wvanbergen/request-log-analyzer"
120
+ puts
121
+
120
122
  # Run the request_log_analyzer!
121
123
  RequestLogAnalyzer::Controller.build(arguments).run!
122
-
123
- puts
124
- puts "Thanks for using request-log-analyzer"
125
124
  end
@@ -140,8 +140,8 @@ module CommandLine
140
140
  end
141
141
  end
142
142
 
143
- OPTION_REGEXP = /^\-\-([A-z0-9-]+)$/;
144
- ALIASES_REGEXP = /^\-([A-z0-9]+)$/
143
+ OPTION_REGEXP = /^\-\-([A-Za-z0-9-]+)$/;
144
+ ALIASES_REGEXP = /^\-([A-Aa-z0-9]+)$/
145
145
 
146
146
  attr_reader :definition
147
147
  attr_reader :tokens
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../tracker/base'
1
+ require File.dirname(__FILE__) + '/../tracker'
2
2
 
3
3
  module RequestLogAnalyzer::Aggregator
4
4
 
@@ -33,8 +33,7 @@ module RequestLogAnalyzer::Aggregator
33
33
  end
34
34
 
35
35
  def track(tracker_klass, options = {})
36
- require "#{File.dirname(__FILE__)}/../tracker/#{tracker_klass}"
37
- tracker_klass = RequestLogAnalyzer::Tracker.const_get(tracker_klass.to_s.split(/[^a-z0-9]/i).map{ |w| w.capitalize }.join('')) if tracker_klass.kind_of?(Symbol)
36
+ tracker_klass = RequestLogAnalyzer::Tracker.const_get(RequestLogAnalyzer::to_camelcase(tracker_klass)) if tracker_klass.kind_of?(Symbol)
38
37
  @trackers << tracker_klass.new(options)
39
38
  end
40
39
  end
@@ -1,5 +1,9 @@
1
1
  module RequestLogAnalyzer::Aggregator
2
-
2
+
3
+ def self.const_missing(const)
4
+ RequestLogAnalyzer::load_default_class_file(self, const)
5
+ end
6
+
3
7
  # The base class of an aggregator. This class provides the interface to which
4
8
  # every aggregator should comply (by simply subclassing this class).
5
9
  class Base
@@ -62,7 +62,7 @@ module RequestLogAnalyzer
62
62
  options.store(:source_files, arguments.parameters)
63
63
  end
64
64
 
65
- controller = Controller.new(RequestLogAnalyzer::Source::LogFile.new(file_format, options), options)
65
+ controller = Controller.new(RequestLogAnalyzer::Source::LogParser.new(file_format, options), options)
66
66
 
67
67
  options[:assume_correct_order] = arguments[:assume_correct_order]
68
68
 
@@ -147,12 +147,8 @@ module RequestLogAnalyzer
147
147
 
148
148
  # Adds an aggregator to the controller. The aggregator will be called for every request
149
149
  # that is parsed from the provided sources (see add_source)
150
- def add_aggregator(agg)
151
- if agg.kind_of?(Symbol)
152
- require File.dirname(__FILE__) + "/aggregator/#{agg}"
153
- agg = RequestLogAnalyzer::Aggregator.const_get(agg.to_s.split(/[^a-z0-9]/i).map{ |w| w.capitalize }.join(''))
154
- end
155
-
150
+ def add_aggregator(agg)
151
+ agg = RequestLogAnalyzer::Aggregator.const_get(RequestLogAnalyzer::to_camelcase(agg)) if agg.kind_of?(Symbol)
156
152
  @aggregators << agg.new(@source, @options)
157
153
  end
158
154
 
@@ -160,11 +156,7 @@ module RequestLogAnalyzer
160
156
 
161
157
  # Adds a request filter to the controller.
162
158
  def add_filter(filter, filter_options = {})
163
- if filter.kind_of?(Symbol)
164
- require File.dirname(__FILE__) + "/filter/#{filter}"
165
- filter = RequestLogAnalyzer::Filter.const_get(filter.to_s.split(/[^a-z0-9]/i).map{ |w| w.capitalize }.join(''))
166
- end
167
-
159
+ filter = RequestLogAnalyzer::Filter.const_get(RequestLogAnalyzer::to_camelcase(filter)) if filter.kind_of?(Symbol)
168
160
  @filters << filter.new(file_format, @options.merge(filter_options))
169
161
  end
170
162
 
@@ -178,13 +170,11 @@ module RequestLogAnalyzer
178
170
  # 6. Finalize Source
179
171
  def run!
180
172
 
181
-
182
-
183
173
  @filters.each { |filter| filter.prepare }
184
174
  @aggregators.each { |agg| agg.prepare }
185
175
 
186
176
  begin
187
- @source.requests do |request|
177
+ @source.each_request do |request|
188
178
  @filters.each { |filter| request = filter.filter(request) }
189
179
  @aggregators.each { |agg| agg.aggregate(request) } if request
190
180
  end
@@ -201,7 +191,12 @@ module RequestLogAnalyzer
201
191
 
202
192
  @source.finalize
203
193
 
204
-
194
+ if @output.io.kind_of?(File)
195
+ puts
196
+ puts "Report written to: " + File.expand_path(@output.io.path)
197
+ puts "Thanks for using request-log-analyzer!"
198
+ @output.io.close
199
+ end
205
200
  end
206
201
 
207
202
  end
@@ -1,33 +1,39 @@
1
- module RequestLogAnalyzer::FileFormat::Merb
1
+ module RequestLogAnalyzer::FileFormat
2
+
3
+ class Merb < Base
2
4
 
3
- LINE_DEFINITIONS = {
4
-
5
5
  # ~ Started request handling: Fri Aug 29 11:10:23 +0200 2008
6
- :started => {
7
- :header => true,
8
- :teaser => /Started/,
9
- :regexp => /Started request handling\:\ (.+)/,
10
- :captures => [{ :name => :timestamp, :type => :timestamp, :anonymize => :slightly }]
11
- },
6
+ line_definition :started do |line|
7
+ line.header = true
8
+ line.teaser = /Started/
9
+ line.regexp = /Started request handling\:\ (.+)/
10
+ line.captures << { :name => :timestamp, :type => :timestamp, :anonymize => :slightly }
11
+ end
12
12
 
13
13
  # ~ Params: {"action"=>"create", "controller"=>"session"}
14
14
  # ~ Params: {"_method"=>"delete", "authenticity_token"=>"[FILTERED]", "action"=>"d}
15
- :params => {
16
- :teaser => /Params/,
17
- :regexp => /Params\:\ \{(.+)\}/,
18
- :captures => [{ :name => :raw_hash, :type => :string}]
19
- },
20
-
15
+ line_definition :params do |line|
16
+ line.teaser = /Params/
17
+ line.regexp = /Params\:\ \{(.+)\}/
18
+ line.captures << { :name => :raw_hash, :type => :string}
19
+ end
20
+
21
21
  # ~ {:dispatch_time=>0.006117, :after_filters_time=>6.1e-05, :before_filters_time=>0.000712, :action_time=>0.005833}
22
- :completed => {
23
- :footer => true,
24
- :teaser => /\{:dispatch_time/,
25
- :regexp => /\{\:dispatch_time=>(\d+\.\d+(?:e-?\d+)?), (?:\:after_filters_time=>(\d+\.\d+(?:e-?\d+)?), )?(?:\:before_filters_time=>(\d+\.\d+(?:e-?\d+)?), )?\:action_time=>(\d+\.\d+(?:e-?\d+)?)\}/,
26
- :captures => [{ :name => :dispatch_time, :type => :sec, :anonymize => :slightly },
27
- { :name => :after_filters_time, :type => :sec, :anonymize => :slightly },
28
- { :name => :before_filters_time, :type => :sec, :anonymize => :slightly },
29
- { :name => :action_time, :type => :sec, :anonymize => :slightly }]
30
- }
31
- }
22
+ line_definition :completed do |line|
23
+ line.footer = true
24
+ line.teaser = /\{:dispatch_time/
25
+ line.regexp = /\{\:dispatch_time=>(\d+\.\d+(?:e-?\d+)?), (?:\:after_filters_time=>(\d+\.\d+(?:e-?\d+)?), )?(?:\:before_filters_time=>(\d+\.\d+(?:e-?\d+)?), )?\:action_time=>(\d+\.\d+(?:e-?\d+)?)\}/
26
+ line.captures << { :name => :dispatch_time, :type => :sec, :anonymize => :slightly } \
27
+ << { :name => :after_filters_time, :type => :sec, :anonymize => :slightly } \
28
+ << { :name => :before_filters_time, :type => :sec, :anonymize => :slightly } \
29
+ << { :name => :action_time, :type => :sec, :anonymize => :slightly }
30
+ end
32
31
 
33
- end
32
+
33
+ report do |analyze|
34
+ # FIX ME
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -1,90 +1,92 @@
1
- class RequestLogAnalyzer::FileFormat::Rails < RequestLogAnalyzer::FileFormat
1
+ module RequestLogAnalyzer::FileFormat
2
2
 
3
- # Processing EmployeeController#index (for 123.123.123.123 at 2008-07-13 06:00:00) [GET]
4
- line_definition :processing do |line|
5
- line.header = true # this line is the first log line for a request
6
- line.teaser = /Processing /
7
- line.regexp = /Processing ((?:\w+::)?\w+)#(\w+)(?: to (\w+))? \(for (\d+\.\d+\.\d+\.\d+) at (\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d)\) \[([A-Z]+)\]/
8
- line.captures << { :name => :controller, :type => :string } \
9
- << { :name => :action, :type => :string } \
10
- << { :name => :format, :type => :string } \
11
- << { :name => :ip, :type => :string, :anonymize => :ip } \
12
- << { :name => :timestamp, :type => :timestamp, :anonymize => :slightly } \
13
- << { :name => :method, :type => :string }
14
- end
3
+ class Rails < Base
4
+
5
+ # Processing EmployeeController#index (for 123.123.123.123 at 2008-07-13 06:00:00) [GET]
6
+ line_definition :processing do |line|
7
+ line.header = true # this line is the first log line for a request
8
+ line.teaser = /Processing /
9
+ line.regexp = /Processing ((?:\w+::)?\w+)#(\w+)(?: to (\w+))? \(for (\d+\.\d+\.\d+\.\d+) at (\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d)\) \[([A-Z]+)\]/
10
+ line.captures << { :name => :controller, :type => :string } \
11
+ << { :name => :action, :type => :string } \
12
+ << { :name => :format, :type => :string } \
13
+ << { :name => :ip, :type => :string, :anonymize => :ip } \
14
+ << { :name => :timestamp, :type => :timestamp, :anonymize => :slightly } \
15
+ << { :name => :method, :type => :string }
16
+ end
15
17
 
16
- # Filter chain halted as [#<ActionController::Caching::Actions::ActionCacheFilter:0x2a999ad620 @check=nil, @options={:store_options=>{}, :layout=>nil, :cache_path=>#<Proc:0x0000002a999b8890@/app/controllers/cached_controller.rb:8>}>] rendered_or_redirected.
17
- line_definition :cache_hit do |line|
18
- line.regexp = /Filter chain halted as \[\#<ActionController::Caching::Actions::ActionCacheFilter:.+>\] rendered_or_redirected/
19
- end
18
+ # Filter chain halted as [#<ActionController::Caching::Actions::ActionCacheFilter:0x2a999ad620 @check=nil, @options={:store_options=>{}, :layout=>nil, :cache_path=>#<Proc:0x0000002a999b8890@/app/controllers/cached_controller.rb:8>}>] rendered_or_redirected.
19
+ line_definition :cache_hit do |line|
20
+ line.regexp = /Filter chain halted as \[\#<ActionController::Caching::Actions::ActionCacheFilter:.+>\] rendered_or_redirected/
21
+ end
20
22
 
21
- # RuntimeError (Cannot destroy employee): /app/models/employee.rb:198:in `before_destroy'
22
- line_definition :failed do |line|
23
- line.footer = true
24
- line.regexp = /((?:[A-Z]\w+\:\:)*[A-Z]\w+) \((.*)\)(?: on line #(\d+) of .+)?\:(.*)/
25
- line.captures << { :name => :error, :type => :string } \
26
- << { :name => :message, :type => :string } \
27
- << { :name => :line, :type => :integer } \
28
- << { :name => :file, :type => :string } \
29
- << { :name => :stack_trace, :type => :string, :anonymize => true }
30
- end
23
+ # RuntimeError (Cannot destroy employee): /app/models/employee.rb:198:in `before_destroy'
24
+ line_definition :failed do |line|
25
+ line.footer = true
26
+ line.regexp = /((?:[A-Z]\w+\:\:)*[A-Z]\w+) \((.*)\)(?: on line #(\d+) of .+)?\:(.*)/
27
+ line.captures << { :name => :error, :type => :string } \
28
+ << { :name => :message, :type => :string } \
29
+ << { :name => :line, :type => :integer } \
30
+ << { :name => :file, :type => :string } \
31
+ << { :name => :stack_trace, :type => :string, :anonymize => true }
32
+ end
31
33
 
32
34
 
33
- # Rails < 2.1 completed line example
34
- # Completed in 0.21665 (4 reqs/sec) | Rendering: 0.00926 (4%) | DB: 0.00000 (0%) | 200 OK [http://demo.nu/employees]
35
- RAILS_21_COMPLETED = /Completed in (\d+\.\d{5}) \(\d+ reqs\/sec\) (?:\| Rendering: (\d+\.\d{5}) \(\d+\%\) )?(?:\| DB: (\d+\.\d{5}) \(\d+\%\) )?\| (\d\d\d).+\[(http.+)\]/
35
+ # Rails < 2.1 completed line example
36
+ # Completed in 0.21665 (4 reqs/sec) | Rendering: 0.00926 (4%) | DB: 0.00000 (0%) | 200 OK [http://demo.nu/employees]
37
+ RAILS_21_COMPLETED = /Completed in (\d+\.\d{5}) \(\d+ reqs\/sec\) (?:\| Rendering: (\d+\.\d{5}) \(\d+\%\) )?(?:\| DB: (\d+\.\d{5}) \(\d+\%\) )?\| (\d\d\d).+\[(http.+)\]/
36
38
 
37
- # Rails > 2.1 completed line example
38
- # Completed in 614ms (View: 120, DB: 31) | 200 OK [http://floorplanner.local/demo]
39
- RAILS_22_COMPLETED = /Completed in (\d+)ms \((?:View: (\d+), )?DB: (\d+)\) \| (\d\d\d).+\[(http.+)\]/
39
+ # Rails > 2.1 completed line example
40
+ # Completed in 614ms (View: 120, DB: 31) | 200 OK [http://floorplanner.local/demo]
41
+ RAILS_22_COMPLETED = /Completed in (\d+)ms \((?:View: (\d+), )?DB: (\d+)\) \| (\d\d\d).+\[(http.+)\]/
40
42
 
41
- # The completed line uses a kind of hack to ensure that both old style logs and new style logs
42
- # are both parsed by the same regular expression. The format in Rails 2.2 was slightly changed,
43
- # but the line contains exactly the same information.
44
- line_definition :completed do |line|
43
+ # The completed line uses a kind of hack to ensure that both old style logs and new style logs
44
+ # are both parsed by the same regular expression. The format in Rails 2.2 was slightly changed,
45
+ # but the line contains exactly the same information.
46
+ line_definition :completed do |line|
45
47
 
46
- line.footer = true
47
- line.teaser = /Completed in /
48
- line.regexp = Regexp.new("(?:#{RAILS_21_COMPLETED}|#{RAILS_22_COMPLETED})")
48
+ line.footer = true
49
+ line.teaser = /Completed in /
50
+ line.regexp = Regexp.new("(?:#{RAILS_21_COMPLETED}|#{RAILS_22_COMPLETED})")
49
51
 
50
- line.captures << { :name => :duration, :type => :sec, :anonymize => :slightly } \
51
- << { :name => :view, :type => :sec, :anonymize => :slightly } \
52
- << { :name => :db, :type => :sec, :anonymize => :slightly } \
53
- << { :name => :status, :type => :integer } \
54
- << { :name => :url, :type => :string, :anonymize => :url } # Old variant
52
+ line.captures << { :name => :duration, :type => :sec, :anonymize => :slightly } \
53
+ << { :name => :view, :type => :sec, :anonymize => :slightly } \
54
+ << { :name => :db, :type => :sec, :anonymize => :slightly } \
55
+ << { :name => :status, :type => :integer } \
56
+ << { :name => :url, :type => :string, :anonymize => :url } # Old variant
55
57
 
56
- line.captures << { :name => :duration, :type => :msec, :anonymize => :slightly } \
57
- << { :name => :view, :type => :msec, :anonymize => :slightly } \
58
- << { :name => :db, :type => :msec, :anonymize => :slightly } \
59
- << { :name => :status, :type => :integer} \
60
- << { :name => :url, :type => :string, :anonymize => :url } # 2.2 variant
61
- end
58
+ line.captures << { :name => :duration, :type => :msec, :anonymize => :slightly } \
59
+ << { :name => :view, :type => :msec, :anonymize => :slightly } \
60
+ << { :name => :db, :type => :msec, :anonymize => :slightly } \
61
+ << { :name => :status, :type => :integer} \
62
+ << { :name => :url, :type => :string, :anonymize => :url } # 2.2 variant
63
+ end
62
64
 
63
65
 
64
66
 
65
- REQUEST_CATEGORIZER = Proc.new do |request|
66
- format = request[:format] || 'html'
67
- "#{request[:controller]}##{request[:action]}.#{format} [#{request[:method]}]"
68
- end
67
+ REQUEST_CATEGORIZER = Proc.new do |request|
68
+ format = request[:format] || 'html'
69
+ "#{request[:controller]}##{request[:action]}.#{format} [#{request[:method]}]"
70
+ end
69
71
 
70
- report do |analyze|
71
- analyze.timespan :line_type => :processing
72
- analyze.category :category => REQUEST_CATEGORIZER, :title => 'Top 20 hits', :amount => 20, :line_type => :processing
73
- analyze.category :method, :title => 'HTTP methods'
74
- analyze.category :status, :title => 'HTTP statuses returned'
75
- analyze.category :category => lambda { |request| request =~ :cache_hit ? 'Cache hit' : 'No hit' }, :title => 'Rails action cache hits'
72
+ report do |analyze|
73
+ analyze.timespan :line_type => :processing
74
+ analyze.category :category => REQUEST_CATEGORIZER, :title => 'Top 20 hits', :amount => 20, :line_type => :processing
75
+ analyze.category :method, :title => 'HTTP methods'
76
+ analyze.category :status, :title => 'HTTP statuses returned'
77
+ analyze.category :category => lambda { |request| request =~ :cache_hit ? 'Cache hit' : 'No hit' }, :title => 'Rails action cache hits'
76
78
 
77
- analyze.duration :duration, :category => REQUEST_CATEGORIZER, :title => "Request duration", :line_type => :completed
78
- analyze.duration :view, :category => REQUEST_CATEGORIZER, :title => "Database time", :line_type => :completed
79
- analyze.duration :db, :category => REQUEST_CATEGORIZER, :title => "View rendering time", :line_type => :completed
79
+ analyze.duration :duration, :category => REQUEST_CATEGORIZER, :title => "Request duration", :line_type => :completed
80
+ analyze.duration :view, :category => REQUEST_CATEGORIZER, :title => "Database time", :line_type => :completed
81
+ analyze.duration :db, :category => REQUEST_CATEGORIZER, :title => "View rendering time", :line_type => :completed
80
82
 
81
- analyze.category :category => REQUEST_CATEGORIZER, :title => 'Process blockers (> 1 sec duration)',
82
- :if => lambda { |request| request[:duration] && request[:duration] > 1.0 }, :amount => 20
83
+ analyze.category :category => REQUEST_CATEGORIZER, :title => 'Process blockers (> 1 sec duration)',
84
+ :if => lambda { |request| request[:duration] && request[:duration] > 1.0 }, :amount => 20
83
85
 
84
- analyze.hourly_spread :line_type => :processing
85
- analyze.category :error, :title => 'Failed requests', :line_type => :failed, :amount => 20
86
- end
87
-
86
+ analyze.hourly_spread :line_type => :processing
87
+ analyze.category :error, :title => 'Failed requests', :line_type => :failed, :amount => 20
88
+ end
88
89
 
90
+ end
89
91
 
90
- end
92
+ end
@@ -1,114 +1,112 @@
1
- class RequestLogAnalyzer::FileFormat::RailsDevelopment < RequestLogAnalyzer::FileFormat
1
+ module RequestLogAnalyzer::FileFormat
2
2
 
3
- # Processing EmployeeController#index (for 123.123.123.123 at 2008-07-13 06:00:00) [GET]
4
- line_definition :processing do |line|
5
- line.header = true # this line is the first log line for a request
6
- line.teaser = /Processing /
7
- line.regexp = /Processing ((?:\w+::)?\w+)#(\w+)(?: to (\w+))? \(for (\d+\.\d+\.\d+\.\d+) at (\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d)\) \[([A-Z]+)\]/
8
- line.captures << { :name => :controller, :type => :string } \
9
- << { :name => :action, :type => :string } \
10
- << { :name => :format, :type => :string } \
11
- << { :name => :ip, :type => :string, :anonymize => :ip } \
12
- << { :name => :timestamp, :type => :timestamp, :anonymize => :slightly } \
13
- << { :name => :method, :type => :string }
14
- end
3
+ class RailsDevelopment < Base
4
+
5
+ # Processing EmployeeController#index (for 123.123.123.123 at 2008-07-13 06:00:00) [GET]
6
+ line_definition :processing do |line|
7
+ line.header = true # this line is the first log line for a request
8
+ line.teaser = /Processing /
9
+ line.regexp = /Processing ((?:\w+::)?\w+)#(\w+)(?: to (\w+))? \(for (\d+\.\d+\.\d+\.\d+) at (\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d)\) \[([A-Z]+)\]/
10
+ line.captures << { :name => :controller, :type => :string } \
11
+ << { :name => :action, :type => :string } \
12
+ << { :name => :format, :type => :string } \
13
+ << { :name => :ip, :type => :string, :anonymize => :ip } \
14
+ << { :name => :timestamp, :type => :timestamp, :anonymize => :slightly } \
15
+ << { :name => :method, :type => :string }
16
+ end
15
17
 
16
- # Filter chain halted as [#<ActionController::Caching::Actions::ActionCacheFilter:0x2a999ad620 @check=nil, @options={:store_options=>{}, :layout=>nil, :cache_path=>#<Proc:0x0000002a999b8890@/app/controllers/cached_controller.rb:8>}>] rendered_or_redirected.
17
- line_definition :cache_hit do |line|
18
- line.regexp = /Filter chain halted as \[\#<ActionController::Caching::Actions::ActionCacheFilter:.+>\] rendered_or_redirected/
19
- end
18
+ # Filter chain halted as [#<ActionController::Caching::Actions::ActionCacheFilter:0x2a999ad620 @check=nil, @options={:store_options=>{}, :layout=>nil, :cache_path=>#<Proc:0x0000002a999b8890@/app/controllers/cached_controller.rb:8>}>] rendered_or_redirected.
19
+ line_definition :cache_hit do |line|
20
+ line.regexp = /Filter chain halted as \[\#<ActionController::Caching::Actions::ActionCacheFilter:.+>\] rendered_or_redirected/
21
+ end
20
22
 
21
- # Rendered layouts/_footer (2.9ms)
22
- line_definition :rendered do |line|
23
- line.teaser = /Rendered /
24
- line.regexp = /Rendered (\w+(?:\/\w+)+) \((\d+\.\d+)ms\)/
25
- line.captures << { :name => :render_file, :type => :string } \
26
- << { :name => :render_duration, :type => :msec }
27
- end
23
+ # Rendered layouts/_footer (2.9ms)
24
+ line_definition :rendered do |line|
25
+ line.teaser = /Rendered /
26
+ line.regexp = /Rendered (\w+(?:\/\w+)+) \((\d+\.\d+)ms\)/
27
+ line.captures << { :name => :render_file, :type => :string } \
28
+ << { :name => :render_duration, :type => :msec }
29
+ end
28
30
 
29
- # User Load (0.4ms) SELECT * FROM `users` WHERE (`users`.`id` = 18205844) 
30
- line_definition :query_executed do |line|
31
- line.regexp = /\s+(?:\e\[4;36;1m)?((?:\w+::)*\w+) Load \((\d+\.\d+)ms\)(?:\e\[0m)?\s+(?:\e\[0;1m)?(.+) (?:\e\[0m)?/
32
- line.captures << { :name => :query_class, :type => :string } \
33
- << { :name => :query_duration, :type => :msec } \
34
- << { :name => :query_sql, :type => :string }
35
- end
31
+ # User Load (0.4ms) SELECT * FROM `users` WHERE (`users`.`id` = 18205844) 
32
+ line_definition :query_executed do |line|
33
+ line.regexp = /\s+(?:\e\[4;36;1m)?((?:\w+::)*\w+) Load \((\d+\.\d+)ms\)(?:\e\[0m)?\s+(?:\e\[0;1m)?(.+) (?:\e\[0m)?/
34
+ line.captures << { :name => :query_class, :type => :string } \
35
+ << { :name => :query_duration, :type => :msec } \
36
+ << { :name => :query_sql, :type => :string }
37
+ end
36
38
 
37
- # CACHE (0.0ms) SELECT * FROM `users` WHERE (`users`.`id` = 0) 
38
- line_definition :query_cached do |line|
39
- line.teaser = /\s+(?:\e\[4;35;1m)?CACHE \((\d+\.\d+)ms\)(?:\e\[0m)?\s+(?:\e\[0m)?(.+) (?:\e\[0m)?/
40
- line.regexp = /\s+(?:\e\[4;35;1m)?CACHE \((\d+\.\d+)ms\)(?:\e\[0m)?\s+(?:\e\[0m)?(.+) (?:\e\[0m)?/
41
- line.captures << { :name => :cached_duration, :type => :msec } \
42
- << { :name => :cached_sql, :type => :string }
43
- end
39
+ # CACHE (0.0ms) SELECT * FROM `users` WHERE (`users`.`id` = 0) 
40
+ line_definition :query_cached do |line|
41
+ line.teaser = /\s+(?:\e\[4;35;1m)?CACHE \((\d+\.\d+)ms\)(?:\e\[0m)?\s+(?:\e\[0m)?(.+) (?:\e\[0m)?/
42
+ line.regexp = /\s+(?:\e\[4;35;1m)?CACHE \((\d+\.\d+)ms\)(?:\e\[0m)?\s+(?:\e\[0m)?(.+) (?:\e\[0m)?/
43
+ line.captures << { :name => :cached_duration, :type => :msec } \
44
+ << { :name => :cached_sql, :type => :string }
45
+ end
44
46
 
45
- # RuntimeError (Cannot destroy employee): /app/models/employee.rb:198:in `before_destroy'
46
- line_definition :failed do |line|
47
- line.footer = true
48
- line.regexp = /((?:[A-Z]\w+\:\:)*[A-Z]\w+) \((.*)\)(?: on line #(\d+) of .+)?\:(.*)/
49
- line.captures << { :name => :error, :type => :string } \
50
- << { :name => :message, :type => :string } \
51
- << { :name => :line, :type => :integer } \
52
- << { :name => :file, :type => :string } \
53
- << { :name => :stack_trace, :type => :string, :anonymize => true }
54
- end
47
+ # RuntimeError (Cannot destroy employee): /app/models/employee.rb:198:in `before_destroy'
48
+ line_definition :failed do |line|
49
+ line.footer = true
50
+ line.regexp = /((?:[A-Z]\w+\:\:)*[A-Z]\w+) \((.*)\)(?: on line #(\d+) of .+)?\:(.*)/
51
+ line.captures << { :name => :error, :type => :string } \
52
+ << { :name => :message, :type => :string } \
53
+ << { :name => :line, :type => :integer } \
54
+ << { :name => :file, :type => :string } \
55
+ << { :name => :stack_trace, :type => :string, :anonymize => true }
56
+ end
55
57
 
56
58
 
57
- # Rails < 2.1 completed line example
58
- # Completed in 0.21665 (4 reqs/sec) | Rendering: 0.00926 (4%) | DB: 0.00000 (0%) | 200 OK [http://demo.nu/employees]
59
- RAILS_21_COMPLETED = /Completed in (\d+\.\d{5}) \(\d+ reqs\/sec\) (?:\| Rendering: (\d+\.\d{5}) \(\d+\%\) )?(?:\| DB: (\d+\.\d{5}) \(\d+\%\) )?\| (\d\d\d).+\[(http.+)\]/
59
+ # Rails < 2.1 completed line example
60
+ # Completed in 0.21665 (4 reqs/sec) | Rendering: 0.00926 (4%) | DB: 0.00000 (0%) | 200 OK [http://demo.nu/employees]
61
+ RAILS_21_COMPLETED = /Completed in (\d+\.\d{5}) \(\d+ reqs\/sec\) (?:\| Rendering: (\d+\.\d{5}) \(\d+\%\) )?(?:\| DB: (\d+\.\d{5}) \(\d+\%\) )?\| (\d\d\d).+\[(http.+)\]/
60
62
 
61
- # Rails > 2.1 completed line example
62
- # Completed in 614ms (View: 120, DB: 31) | 200 OK [http://floorplanner.local/demo]
63
- RAILS_22_COMPLETED = /Completed in (\d+)ms \((?:View: (\d+), )?DB: (\d+)\) \| (\d\d\d).+\[(http.+)\]/
63
+ # Rails > 2.1 completed line example
64
+ # Completed in 614ms (View: 120, DB: 31) | 200 OK [http://floorplanner.local/demo]
65
+ RAILS_22_COMPLETED = /Completed in (\d+)ms \((?:View: (\d+), )?DB: (\d+)\) \| (\d\d\d).+\[(http.+)\]/
64
66
 
65
- # The completed line uses a kind of hack to ensure that both old style logs and new style logs
66
- # are both parsed by the same regular expression. The format in Rails 2.2 was slightly changed,
67
- # but the line contains exactly the same information.
68
- line_definition :completed do |line|
67
+ # The completed line uses a kind of hack to ensure that both old style logs and new style logs
68
+ # are both parsed by the same regular expression. The format in Rails 2.2 was slightly changed,
69
+ # but the line contains exactly the same information.
70
+ line_definition :completed do |line|
69
71
 
70
- line.footer = true
71
- line.teaser = /Completed in /
72
- line.regexp = Regexp.new("(?:#{RAILS_21_COMPLETED}|#{RAILS_22_COMPLETED})")
72
+ line.footer = true
73
+ line.teaser = /Completed in /
74
+ line.regexp = Regexp.new("(?:#{RAILS_21_COMPLETED}|#{RAILS_22_COMPLETED})")
73
75
 
74
- line.captures << { :name => :duration, :type => :sec, :anonymize => :slightly } \
75
- << { :name => :view, :type => :sec, :anonymize => :slightly } \
76
- << { :name => :db, :type => :sec, :anonymize => :slightly } \
77
- << { :name => :status, :type => :integer } \
78
- << { :name => :url, :type => :string, :anonymize => :url } # Old variant
76
+ line.captures << { :name => :duration, :type => :sec, :anonymize => :slightly } \
77
+ << { :name => :view, :type => :sec, :anonymize => :slightly } \
78
+ << { :name => :db, :type => :sec, :anonymize => :slightly } \
79
+ << { :name => :status, :type => :integer } \
80
+ << { :name => :url, :type => :string, :anonymize => :url } # Old variant
79
81
 
80
- line.captures << { :name => :duration, :type => :msec, :anonymize => :slightly } \
81
- << { :name => :view, :type => :msec, :anonymize => :slightly } \
82
- << { :name => :db, :type => :msec, :anonymize => :slightly } \
83
- << { :name => :status, :type => :integer} \
84
- << { :name => :url, :type => :string, :anonymize => :url } # 2.2 variant
85
- end
82
+ line.captures << { :name => :duration, :type => :msec, :anonymize => :slightly } \
83
+ << { :name => :view, :type => :msec, :anonymize => :slightly } \
84
+ << { :name => :db, :type => :msec, :anonymize => :slightly } \
85
+ << { :name => :status, :type => :integer} \
86
+ << { :name => :url, :type => :string, :anonymize => :url } # 2.2 variant
87
+ end
86
88
 
89
+ REQUEST_CATEGORIZER = Proc.new do |request|
90
+ format = request[:format] || 'html'
91
+ "#{request[:controller]}##{request[:action]}.#{format} [#{request[:method]}]"
92
+ end
87
93
 
88
-
89
- REQUEST_CATEGORIZER = Proc.new do |request|
90
- format = request[:format] || 'html'
91
- "#{request[:controller]}##{request[:action]}.#{format} [#{request[:method]}]"
92
- end
93
-
94
- report do |analyze|
95
- analyze.timespan :line_type => :processing
96
- analyze.category :category => REQUEST_CATEGORIZER, :title => 'Top 20 hits', :amount => 20, :line_type => :processing
97
- analyze.category :method, :title => 'HTTP methods'
98
- analyze.category :status, :title => 'HTTP statuses returned'
99
- analyze.category :category => lambda { |request| request =~ :cache_hit ? 'Cache hit' : 'No hit' }, :title => 'Rails action cache hits'
94
+ report do |analyze|
95
+ analyze.timespan :line_type => :processing
96
+ analyze.category :category => REQUEST_CATEGORIZER, :title => 'Top 20 hits', :amount => 20, :line_type => :processing
97
+ analyze.category :method, :title => 'HTTP methods'
98
+ analyze.category :status, :title => 'HTTP statuses returned'
99
+ analyze.category :category => lambda { |request| request =~ :cache_hit ? 'Cache hit' : 'No hit' }, :title => 'Rails action cache hits'
100
100
 
101
- analyze.duration :duration, :category => REQUEST_CATEGORIZER, :title => "Request duration", :line_type => :completed
102
- analyze.duration :view, :category => REQUEST_CATEGORIZER, :title => "Database time", :line_type => :completed
103
- analyze.duration :db, :category => REQUEST_CATEGORIZER, :title => "View rendering time", :line_type => :completed
101
+ analyze.duration :duration, :category => REQUEST_CATEGORIZER, :title => "Request duration", :line_type => :completed
102
+ analyze.duration :view, :category => REQUEST_CATEGORIZER, :title => "Database time", :line_type => :completed
103
+ analyze.duration :db, :category => REQUEST_CATEGORIZER, :title => "View rendering time", :line_type => :completed
104
104
 
105
- analyze.category :category => REQUEST_CATEGORIZER, :title => 'Process blockers (> 1 sec duration)',
106
- :if => lambda { |request| request[:duration] && request[:duration] > 1.0 }, :amount => 20
105
+ analyze.category :category => REQUEST_CATEGORIZER, :title => 'Process blockers (> 1 sec duration)',
106
+ :if => lambda { |request| request[:duration] && request[:duration] > 1.0 }, :amount => 20
107
107
 
108
- analyze.hourly_spread :line_type => :processing
109
- analyze.category :error, :title => 'Failed requests', :line_type => :failed, :amount => 20
108
+ analyze.hourly_spread :line_type => :processing
109
+ analyze.category :error, :title => 'Failed requests', :line_type => :failed, :amount => 20
110
+ end
110
111
  end
111
-
112
-
113
-
114
- end
112
+ end