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.
- data/README.rdoc +4 -3
- data/bin/request-log-analyzer +4 -5
- data/lib/cli/command_line_arguments.rb +2 -2
- data/lib/request_log_analyzer/aggregator/summarizer.rb +2 -3
- data/lib/request_log_analyzer/{aggregator/base.rb → aggregator.rb} +5 -1
- data/lib/request_log_analyzer/controller.rb +11 -16
- data/lib/request_log_analyzer/file_format/merb.rb +32 -26
- data/lib/request_log_analyzer/file_format/rails.rb +73 -71
- data/lib/request_log_analyzer/file_format/rails_development.rb +93 -95
- data/lib/request_log_analyzer/file_format.rb +71 -38
- data/lib/request_log_analyzer/filter/anonimize.rb +1 -1
- data/lib/request_log_analyzer/filter.rb +38 -0
- data/lib/request_log_analyzer/line_definition.rb +1 -1
- data/lib/request_log_analyzer/output/fixed_width.rb +133 -117
- data/lib/request_log_analyzer/output/html.rb +138 -60
- data/lib/request_log_analyzer/output.rb +6 -8
- data/lib/request_log_analyzer/request.rb +3 -1
- data/lib/request_log_analyzer/source/{log_file.rb → log_parser.rb} +15 -6
- data/lib/request_log_analyzer/{source/base.rb → source.rb} +5 -0
- data/lib/request_log_analyzer/tracker/category.rb +7 -8
- data/lib/request_log_analyzer/tracker/duration.rb +15 -12
- data/lib/request_log_analyzer/tracker/hourly_spread.rb +8 -8
- data/lib/request_log_analyzer/tracker/timespan.rb +10 -10
- data/lib/request_log_analyzer/tracker.rb +58 -0
- data/lib/request_log_analyzer.rb +28 -6
- data/spec/controller_spec.rb +5 -4
- data/spec/database_inserter_spec.rb +5 -8
- data/spec/file_format_spec.rb +2 -2
- data/spec/file_formats/spec_format.rb +2 -1
- data/spec/filter_spec.rb +0 -3
- data/spec/log_parser_spec.rb +6 -6
- data/spec/merb_format_spec.rb +38 -38
- data/spec/rails_format_spec.rb +2 -2
- data/spec/request_spec.rb +2 -2
- data/spec/spec_helper.rb +3 -37
- data/tasks/github-gem.rake +2 -1
- metadata +7 -8
- data/lib/request_log_analyzer/filter/base.rb +0 -32
- data/lib/request_log_analyzer/log_parser.rb +0 -173
- 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
|
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
|
-
*
|
42
|
+
* barttenbrinke's blog posts: http://movesonrails.com/articles/tag/analyzer
|
data/bin/request-log-analyzer
CHANGED
@@ -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
|
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
|
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
|
-
|
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::
|
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.
|
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
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
32
|
+
|
33
|
+
report do |analyze|
|
34
|
+
# FIX ME
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -1,90 +1,92 @@
|
|
1
|
-
|
1
|
+
module RequestLogAnalyzer::FileFormat
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
48
|
+
line.footer = true
|
49
|
+
line.teaser = /Completed in /
|
50
|
+
line.regexp = Regexp.new("(?:#{RAILS_21_COMPLETED}|#{RAILS_22_COMPLETED})")
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
82
|
-
|
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
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
1
|
+
module RequestLogAnalyzer::FileFormat
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
# [4;36;1mUser Load (0.4ms)[0m [0;1mSELECT * FROM `users` WHERE (`users`.`id` = 18205844) [0m
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
# [4;35;1mCACHE (0.0ms)[0m [0mSELECT * FROM `users` WHERE (`users`.`id` = 0) [0m
|
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
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
72
|
+
line.footer = true
|
73
|
+
line.teaser = /Completed in /
|
74
|
+
line.regexp = Regexp.new("(?:#{RAILS_21_COMPLETED}|#{RAILS_22_COMPLETED})")
|
73
75
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
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
|
-
|
106
|
-
|
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
|
-
|
109
|
-
|
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
|