request-log-analyzer 1.0.2 → 1.6.3
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/.gitignore +10 -0
- data/DESIGN.rdoc +41 -0
- data/LICENSE +4 -4
- data/README.rdoc +38 -0
- data/Rakefile +6 -3
- data/bin/request-log-analyzer +70 -72
- data/lib/cli/command_line_arguments.rb +53 -53
- data/lib/cli/database_console.rb +26 -0
- data/lib/cli/database_console_init.rb +43 -0
- data/lib/cli/progressbar.rb +166 -189
- data/lib/cli/tools.rb +49 -0
- data/lib/request_log_analyzer/aggregator/database_inserter.rb +83 -0
- data/lib/request_log_analyzer/aggregator/echo.rb +17 -12
- data/lib/request_log_analyzer/aggregator/summarizer.rb +101 -63
- data/lib/request_log_analyzer/{aggregator/base.rb → aggregator.rb} +17 -13
- data/lib/request_log_analyzer/controller.rb +251 -98
- data/lib/request_log_analyzer/database/base.rb +114 -0
- data/lib/request_log_analyzer/database/connection.rb +38 -0
- data/lib/request_log_analyzer/database/request.rb +22 -0
- data/lib/request_log_analyzer/database/source.rb +13 -0
- data/lib/request_log_analyzer/database/warning.rb +14 -0
- data/lib/request_log_analyzer/database.rb +102 -0
- data/lib/request_log_analyzer/file_format/amazon_s3.rb +74 -0
- data/lib/request_log_analyzer/file_format/apache.rb +147 -0
- data/lib/request_log_analyzer/file_format/delayed_job.rb +55 -0
- data/lib/request_log_analyzer/file_format/merb.rb +65 -29
- data/lib/request_log_analyzer/file_format/mysql.rb +101 -0
- data/lib/request_log_analyzer/file_format/postgresql.rb +68 -0
- data/lib/request_log_analyzer/file_format/rack.rb +9 -0
- data/lib/request_log_analyzer/file_format/rails.rb +164 -78
- data/lib/request_log_analyzer/file_format/rails3.rb +86 -0
- data/lib/request_log_analyzer/file_format/rails_development.rb +12 -0
- data/lib/request_log_analyzer/file_format.rb +252 -58
- data/lib/request_log_analyzer/filter/anonymize.rb +39 -0
- data/lib/request_log_analyzer/filter/field.rb +19 -13
- data/lib/request_log_analyzer/filter/timespan.rb +25 -12
- data/lib/request_log_analyzer/filter.rb +30 -0
- data/lib/request_log_analyzer/line_definition.rb +69 -96
- data/lib/request_log_analyzer/log_processor.rb +31 -53
- data/lib/request_log_analyzer/mailer.rb +65 -0
- data/lib/request_log_analyzer/output/fancy_html.rb +49 -0
- data/lib/request_log_analyzer/output/fixed_width.rb +220 -0
- data/lib/request_log_analyzer/output/html.rb +187 -0
- data/lib/request_log_analyzer/output.rb +117 -0
- data/lib/request_log_analyzer/request.rb +125 -40
- data/lib/request_log_analyzer/source/database_loader.rb +87 -0
- data/lib/request_log_analyzer/source/log_parser.rb +297 -0
- data/lib/request_log_analyzer/source.rb +72 -0
- data/lib/request_log_analyzer/tracker/duration.rb +28 -64
- data/lib/request_log_analyzer/tracker/frequency.rb +108 -0
- data/lib/request_log_analyzer/tracker/hourly_spread.rb +76 -49
- data/lib/request_log_analyzer/tracker/numeric_value.rb +223 -0
- data/lib/request_log_analyzer/tracker/timespan.rb +54 -27
- data/lib/request_log_analyzer/tracker/traffic.rb +40 -0
- data/lib/request_log_analyzer/tracker.rb +101 -0
- data/lib/request_log_analyzer.rb +43 -13
- data/request-log-analyzer.gemspec +41 -0
- data/spec/database.yml +23 -0
- data/spec/fixtures/apache_combined.log +5 -0
- data/spec/fixtures/apache_common.log +10 -0
- data/spec/fixtures/decompression.log +12 -0
- data/spec/fixtures/decompression.log.bz2 +0 -0
- data/spec/fixtures/decompression.log.gz +0 -0
- data/spec/fixtures/decompression.log.zip +0 -0
- data/spec/fixtures/decompression.tar.gz +0 -0
- data/spec/fixtures/decompression.tgz +0 -0
- data/spec/fixtures/header_and_footer.log +6 -0
- data/spec/fixtures/merb_prefixed.log +9 -0
- data/spec/fixtures/mysql_slow_query.log +110 -0
- data/spec/fixtures/postgresql.log +2980 -0
- data/spec/fixtures/rails.db +0 -0
- data/spec/fixtures/rails_22.log +1 -1
- data/spec/fixtures/sinatra.log +99 -0
- data/spec/integration/command_line_usage_spec.rb +84 -0
- data/spec/integration/mailer_spec.rb +179 -0
- data/spec/integration/munin_plugins_rails_spec.rb +58 -0
- data/spec/integration/scout_spec.rb +152 -0
- data/spec/lib/helpers.rb +72 -0
- data/spec/lib/macros.rb +18 -0
- data/spec/lib/matchers.rb +77 -0
- data/spec/lib/mocks.rb +77 -0
- data/spec/lib/testing_format.rb +46 -0
- data/spec/spec_helper.rb +16 -59
- data/spec/unit/aggregator/database_inserter_spec.rb +93 -0
- data/spec/unit/aggregator/summarizer_spec.rb +26 -0
- data/spec/unit/controller/controller_spec.rb +41 -0
- data/spec/unit/controller/log_processor_spec.rb +18 -0
- data/spec/unit/database/base_class_spec.rb +183 -0
- data/spec/unit/database/connection_spec.rb +34 -0
- data/spec/unit/database/database_spec.rb +133 -0
- data/spec/unit/file_format/amazon_s3_format_spec.rb +67 -0
- data/spec/unit/file_format/apache_format_spec.rb +203 -0
- data/spec/unit/file_format/common_regular_expressions_spec.rb +53 -0
- data/spec/unit/file_format/delayed_job_format_spec.rb +83 -0
- data/spec/unit/file_format/file_format_api_spec.rb +69 -0
- data/spec/unit/file_format/format_autodetection_spec.rb +40 -0
- data/spec/unit/file_format/line_definition_spec.rb +75 -0
- data/spec/unit/file_format/merb_format_spec.rb +52 -0
- data/spec/unit/file_format/mysql_format_spec.rb +154 -0
- data/spec/unit/file_format/postgresql_format_spec.rb +65 -0
- data/spec/unit/file_format/rack_format_spec.rb +50 -0
- data/spec/unit/file_format/rails3_format_spec.rb +120 -0
- data/spec/unit/file_format/rails_format_spec.rb +180 -0
- data/spec/unit/filter/anonymize_filter_spec.rb +21 -0
- data/spec/unit/filter/field_filter_spec.rb +66 -0
- data/spec/unit/filter/filter_spec.rb +17 -0
- data/spec/unit/filter/timespan_filter_spec.rb +58 -0
- data/spec/unit/mailer_spec.rb +42 -0
- data/spec/unit/request_spec.rb +111 -0
- data/spec/unit/source/log_parser_spec.rb +119 -0
- data/spec/unit/tracker/duration_tracker_spec.rb +49 -0
- data/spec/unit/tracker/frequency_tracker_spec.rb +88 -0
- data/spec/unit/tracker/hourly_spread_spec.rb +79 -0
- data/spec/unit/tracker/numeric_value_tracker_spec.rb +166 -0
- data/spec/unit/tracker/timespan_tracker_spec.rb +73 -0
- data/spec/unit/tracker/tracker_api_spec.rb +125 -0
- data/spec/unit/tracker/traffic_tracker_spec.rb +28 -0
- data/tasks/github-gem.rake +290 -139
- data/tasks/request_log_analyzer.rake +23 -7
- metadata +221 -94
- data/DESIGN +0 -14
- data/HACKING +0 -7
- data/README.textile +0 -36
- data/lib/cli/bashcolorizer.rb +0 -60
- data/lib/request_log_analyzer/aggregator/database.rb +0 -148
- data/lib/request_log_analyzer/filter/base.rb +0 -29
- data/lib/request_log_analyzer/log_parser.rb +0 -173
- data/lib/request_log_analyzer/source/base.rb +0 -42
- data/lib/request_log_analyzer/source/log_file.rb +0 -170
- data/lib/request_log_analyzer/tracker/base.rb +0 -54
- data/lib/request_log_analyzer/tracker/category.rb +0 -71
- data/spec/controller_spec.rb +0 -40
- data/spec/database_inserter_spec.rb +0 -101
- data/spec/file_format_spec.rb +0 -78
- data/spec/file_formats/spec_format.rb +0 -26
- data/spec/filter_spec.rb +0 -137
- data/spec/line_definition_spec.rb +0 -124
- data/spec/log_parser_spec.rb +0 -68
- data/spec/log_processor_spec.rb +0 -57
- data/spec/merb_format_spec.rb +0 -38
- data/spec/rails_format_spec.rb +0 -76
- data/spec/request_spec.rb +0 -72
- data/spec/summarizer_spec.rb +0 -9
- data/tasks/rspec.rake +0 -6
|
@@ -1,90 +1,176 @@
|
|
|
1
|
-
|
|
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
|
|
15
|
-
|
|
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
|
|
20
|
-
|
|
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
|
|
1
|
+
module RequestLogAnalyzer::FileFormat
|
|
31
2
|
|
|
3
|
+
# Default FileFormat class for Rails logs.
|
|
4
|
+
#
|
|
5
|
+
# Instances will be created dynamically based on the lines you want it to parse. You can
|
|
6
|
+
# specify what lines should be included in the parser by providing a list to the create
|
|
7
|
+
# method as first argument.
|
|
8
|
+
class Rails < Base
|
|
32
9
|
|
|
33
|
-
|
|
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.+)\]/
|
|
10
|
+
extend CommonRegularExpressions
|
|
36
11
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
12
|
+
# Creates a Rails FileFormat instance.
|
|
13
|
+
#
|
|
14
|
+
# The lines that will be parsed can be defined by the argument to this function,
|
|
15
|
+
# which should be an array of line names, or a list of line names as comma separated
|
|
16
|
+
# string. The resulting report depends on the lines that will be parsed. You can
|
|
17
|
+
# also provide s string that describes a common set of lines, like "production",
|
|
18
|
+
# "development" or "production".
|
|
19
|
+
def self.create(lines = 'production')
|
|
20
|
+
definitions_hash = line_definer.line_definitions.clone
|
|
21
|
+
|
|
22
|
+
lines = lines.to_s.split(',') if lines.kind_of?(String)
|
|
23
|
+
lines = [lines.to_s] if lines.kind_of?(Symbol)
|
|
24
|
+
|
|
25
|
+
lines.each do |line|
|
|
26
|
+
line = line.to_sym
|
|
27
|
+
if LINE_COLLECTIONS.has_key?(line)
|
|
28
|
+
LINE_COLLECTIONS[line].each { |l| definitions_hash[l] = LINE_DEFINITIONS[l] }
|
|
29
|
+
elsif LINE_DEFINITIONS.has_key?(line)
|
|
30
|
+
definitions_hash[line] = LINE_DEFINITIONS[line]
|
|
31
|
+
else
|
|
32
|
+
raise "Unrecognized Rails log line name: #{line.inspect}!"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
40
35
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# but the line contains exactly the same information.
|
|
44
|
-
line_definition :completed do |line|
|
|
45
|
-
|
|
46
|
-
line.footer = true
|
|
47
|
-
line.teaser = /Completed in /
|
|
48
|
-
line.regexp = Regexp.new("(?:#{RAILS_21_COMPLETED}|#{RAILS_22_COMPLETED})")
|
|
36
|
+
return self.new(definitions_hash, report_trackers(definitions_hash))
|
|
37
|
+
end
|
|
49
38
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
39
|
+
# Creates trackers based on the specified line definitions.
|
|
40
|
+
#
|
|
41
|
+
# The more lines that will be parsed, the more information will appear in the report.
|
|
42
|
+
def self.report_trackers(lines)
|
|
43
|
+
analyze = RequestLogAnalyzer::Aggregator::Summarizer::Definer.new
|
|
44
|
+
|
|
45
|
+
analyze.timespan
|
|
46
|
+
analyze.hourly_spread
|
|
47
|
+
|
|
48
|
+
analyze.frequency :category => REQUEST_CATEGORIZER, :title => 'Most requested'
|
|
49
|
+
analyze.frequency :method, :title => 'HTTP methods'
|
|
50
|
+
analyze.frequency :status, :title => 'HTTP statuses returned'
|
|
51
|
+
|
|
52
|
+
if lines.has_key?(:cache_hit)
|
|
53
|
+
analyze.frequency(:category => lambda { |request| request =~ :cache_hit ? 'Cache hit' : 'No hit' },
|
|
54
|
+
:title => 'Rails action cache hits')
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
analyze.duration :duration, :category => REQUEST_CATEGORIZER, :title => "Request duration", :line_type => :completed
|
|
58
|
+
analyze.duration :view, :category => REQUEST_CATEGORIZER, :title => "View rendering time", :line_type => :completed
|
|
59
|
+
analyze.duration :db, :category => REQUEST_CATEGORIZER, :title => "Database time", :line_type => :completed
|
|
60
|
+
|
|
61
|
+
analyze.frequency :category => REQUEST_CATEGORIZER, :title => 'Process blockers (> 1 sec duration)',
|
|
62
|
+
:if => lambda { |request| request[:duration] && request[:duration] > 1.0 }
|
|
63
|
+
|
|
64
|
+
if lines.has_key?(:failure)
|
|
65
|
+
analyze.frequency :error, :title => 'Failed requests', :line_type => :failure
|
|
66
|
+
end
|
|
62
67
|
|
|
68
|
+
if lines.has_key?(:rendered)
|
|
69
|
+
analyze.duration :render_duration, :category => :render_file, :multiple => true, :title => 'Partial rendering duration'
|
|
70
|
+
end
|
|
63
71
|
|
|
72
|
+
if lines.has_key?(:query_executed)
|
|
73
|
+
analyze.duration :query_duration, :category => :query_sql, :multiple => true, :title => 'Query duration'
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
return analyze.trackers + report_definer.trackers
|
|
77
|
+
end
|
|
64
78
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
end
|
|
79
|
+
# Rails < 2.1 completed line example
|
|
80
|
+
# Completed in 0.21665 (4 reqs/sec) | Rendering: 0.00926 (4%) | DB: 0.00000 (0%) | 200 OK [http://demo.nu/employees]
|
|
81
|
+
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.+)\]/
|
|
69
82
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
analyze.category :error, :title => 'Failed requests', :line_type => :failed, :amount => 20
|
|
86
|
-
end
|
|
83
|
+
# Rails > 2.1 completed line example
|
|
84
|
+
# Completed in 614ms (View: 120, DB: 31) | 200 OK [http://floorplanner.local/demo]
|
|
85
|
+
RAILS_22_COMPLETED = /Completed in (\d+)ms \((?:View: (\d+), )?DB: (\d+)\) \| (\d\d\d).+\[(http.+)\]/
|
|
86
|
+
|
|
87
|
+
# A hash of definitions for all common lines in Rails logs.
|
|
88
|
+
LINE_DEFINITIONS = {
|
|
89
|
+
:processing => RequestLogAnalyzer::LineDefinition.new(:processing, :header => true,
|
|
90
|
+
:teaser => /Processing /,
|
|
91
|
+
:regexp => /Processing ((?:\w+::)*\w+)#(\w+)(?: to (\w+))? \(for (#{ip_address}) at (#{timestamp('%Y-%m-%d %H:%M:%S')})\) \[([A-Z]+)\]/,
|
|
92
|
+
:captures => [{ :name => :controller, :type => :string },
|
|
93
|
+
{ :name => :action, :type => :string },
|
|
94
|
+
{ :name => :format, :type => :string, :default => 'html' },
|
|
95
|
+
{ :name => :ip, :type => :string },
|
|
96
|
+
{ :name => :timestamp, :type => :timestamp },
|
|
97
|
+
{ :name => :method, :type => :string }]),
|
|
87
98
|
|
|
99
|
+
:completed => RequestLogAnalyzer::LineDefinition.new(:completed, :footer => true,
|
|
100
|
+
:teaser => /Completed in /,
|
|
101
|
+
:regexp => Regexp.union(RAILS_21_COMPLETED, RAILS_22_COMPLETED),
|
|
102
|
+
:captures => [{ :name => :duration, :type => :duration, :unit => :sec }, # First old variant capture
|
|
103
|
+
{ :name => :view, :type => :duration, :unit => :sec },
|
|
104
|
+
{ :name => :db, :type => :duration, :unit => :sec },
|
|
105
|
+
{ :name => :status, :type => :integer },
|
|
106
|
+
{ :name => :url, :type => :string }, # Last old variant capture
|
|
107
|
+
{ :name => :duration, :type => :duration, :unit => :msec }, # First new variant capture
|
|
108
|
+
{ :name => :view, :type => :duration, :unit => :msec },
|
|
109
|
+
{ :name => :db, :type => :duration, :unit => :msec },
|
|
110
|
+
{ :name => :status, :type => :integer },
|
|
111
|
+
{ :name => :url, :type => :string }]), # Last new variant capture
|
|
88
112
|
|
|
113
|
+
:failure => RequestLogAnalyzer::LineDefinition.new(:failure, :footer => true,
|
|
114
|
+
:teaser => /((?:[A-Z]\w*[a-z]\w+\:\:)*[A-Z]\w*[a-z]\w+) \((.*)\)(?: on line #(\d+) of (.+))?\:/,
|
|
115
|
+
:regexp => /((?:[A-Z]\w*[a-z]\w+\:\:)*[A-Z]\w*[a-z]\w+) \((.*)\)(?: on line #(\d+) of (.+))?\:\s*$/,
|
|
116
|
+
:captures => [{ :name => :error, :type => :string },
|
|
117
|
+
{ :name => :message, :type => :string },
|
|
118
|
+
{ :name => :line, :type => :integer },
|
|
119
|
+
{ :name => :file, :type => :string }]),
|
|
89
120
|
|
|
90
|
-
|
|
121
|
+
:cache_hit => RequestLogAnalyzer::LineDefinition.new(:cache_hit,
|
|
122
|
+
:regexp => /Filter chain halted as \[\#<ActionController::Filters::AroundFilter.*\@method=.*(?:Caching::Actions::ActionCacheFilter|action_controller\/caching\/actions\.rb).*\] did_not_yield/),
|
|
123
|
+
|
|
124
|
+
:parameters => RequestLogAnalyzer::LineDefinition.new(:parameters,
|
|
125
|
+
:teaser => / Parameters:/,
|
|
126
|
+
:regexp => / Parameters:\s+(\{.*\})/,
|
|
127
|
+
:captures => [{ :name => :params, :type => :eval }]),
|
|
128
|
+
|
|
129
|
+
:rendered => RequestLogAnalyzer::LineDefinition.new(:rendered,
|
|
130
|
+
:teaser => /Rendered /,
|
|
131
|
+
:regexp => /Rendered (\w+(?:\/\w+)+) \((\d+\.\d+)ms\)/,
|
|
132
|
+
:captures => [{ :name => :render_file, :type => :string },
|
|
133
|
+
{ :name => :render_duration, :type => :duration, :unit => :msec }]),
|
|
134
|
+
|
|
135
|
+
:query_executed => RequestLogAnalyzer::LineDefinition.new(:query_executed,
|
|
136
|
+
:regexp => /\s+(?:\e\[4;36;1m)?((?:\w+::)*\w+) Load \((\d+\.\d+)ms\)(?:\e\[0m)?\s+(?:\e\[0;1m)?([^\e]+) ?(?:\e\[0m)?/,
|
|
137
|
+
:captures => [{ :name => :query_class, :type => :string },
|
|
138
|
+
{ :name => :query_duration, :type => :duration, :unit => :msec },
|
|
139
|
+
{ :name => :query_sql, :type => :sql }]),
|
|
140
|
+
|
|
141
|
+
:query_cached => RequestLogAnalyzer::LineDefinition.new(:query_cached,
|
|
142
|
+
:regexp => /\s+(?:\e\[4;35;1m)?CACHE \((\d+\.\d+)ms\)(?:\e\[0m)?\s+(?:\e\[0m)?([^\e]+) ?(?:\e\[0m)?/,
|
|
143
|
+
:captures => [{ :name => :cached_duration, :type => :duration, :unit => :msec },
|
|
144
|
+
{ :name => :cached_sql, :type => :sql }])
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Definitions of common combinations of lines that can be parsed
|
|
148
|
+
LINE_COLLECTIONS = {
|
|
149
|
+
:minimal => [:processing, :completed],
|
|
150
|
+
:production => [:processing, :completed, :failure, :cache_hit],
|
|
151
|
+
:development => [:processing, :completed, :failure, :rendered, :query_executed, :query_cached],
|
|
152
|
+
:all => LINE_DEFINITIONS.keys
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# Simple function to categorize Rails requests using controller/actions/format and method.
|
|
157
|
+
REQUEST_CATEGORIZER = Proc.new do |request|
|
|
158
|
+
"#{request[:controller]}##{request[:action]}.#{request[:format]} [#{request[:method]}]"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Define a custom Request class for the Rails file format to speed up timestamp handling
|
|
162
|
+
# and to ensure that a format is always set.
|
|
163
|
+
class Request < RequestLogAnalyzer::Request
|
|
164
|
+
|
|
165
|
+
# Do not use DateTime.parse
|
|
166
|
+
def convert_timestamp(value, definition)
|
|
167
|
+
value.gsub(/[^0-9]/, '')[0...14].to_i
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Sanitizes SQL queries so that they can be grouped
|
|
171
|
+
def convert_sql(sql, definition)
|
|
172
|
+
sql.gsub(/\b\d+\b/, ':int').gsub(/`([^`]+)`/, '\1').gsub(/'[^']*'/, ':string').rstrip
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module RequestLogAnalyzer::FileFormat
|
|
2
|
+
|
|
3
|
+
# Default FileFormat class for Rails 3 logs.
|
|
4
|
+
#
|
|
5
|
+
# For now, this is just a basic implementation. It will probaby change after
|
|
6
|
+
# Rails 3 final has been released.
|
|
7
|
+
class Rails3 < Base
|
|
8
|
+
|
|
9
|
+
extend CommonRegularExpressions
|
|
10
|
+
|
|
11
|
+
# Started GET "/queries" for 127.0.0.1 at 2010-02-25 16:15:18
|
|
12
|
+
line_definition :started do |line|
|
|
13
|
+
line.header = true
|
|
14
|
+
line.teaser = /Started /
|
|
15
|
+
line.regexp = /Started ([A-Z]+) "([^"]+)" for (#{ip_address}) at (#{timestamp('%Y-%m-%d %H:%M:%S')})/
|
|
16
|
+
|
|
17
|
+
line.capture(:method)
|
|
18
|
+
line.capture(:path)
|
|
19
|
+
line.capture(:ip)
|
|
20
|
+
line.capture(:timestamp).as(:timestamp)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Processing by QueriesController#index as HTML
|
|
24
|
+
line_definition :processing do |line|
|
|
25
|
+
line.teaser = /Processing by /
|
|
26
|
+
line.regexp = /Processing by (\w+)\#(\w+) as (\w+)/
|
|
27
|
+
|
|
28
|
+
line.capture(:controller)
|
|
29
|
+
line.capture(:action)
|
|
30
|
+
line.capture(:format)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Completed in 9ms (Views: 4.9ms | ActiveRecord: 0.5ms) with 200
|
|
34
|
+
line_definition :completed do |line|
|
|
35
|
+
line.footer = true
|
|
36
|
+
line.teaser = /Completed /
|
|
37
|
+
line.regexp = /Completed (\d+) .* in (\d+)ms \([^\)]*\)/
|
|
38
|
+
|
|
39
|
+
line.capture(:status).as(:integer)
|
|
40
|
+
line.capture(:duration).as(:duration, :unit => :msec)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# ActionView::Template::Error (undefined local variable or method `field' for #<Class>) on line #3 of /Users/willem/Code/warehouse/app/views/queries/execute.csv.erb:
|
|
44
|
+
line_definition :failure do |line|
|
|
45
|
+
line.footer = true
|
|
46
|
+
line.regexp = /((?:[A-Z]\w*[a-z]\w+\:\:)*[A-Z]\w*[a-z]\w+) \((.*)\)(?: on line #(\d+) of (.+))?\:\s*$/
|
|
47
|
+
|
|
48
|
+
line.capture(:error)
|
|
49
|
+
line.capture(:message)
|
|
50
|
+
line.capture(:line).as(:integer)
|
|
51
|
+
line.capture(:file)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# # Not parsed at the moment:
|
|
55
|
+
# SQL (0.2ms) SET SQL_AUTO_IS_NULL=0
|
|
56
|
+
# Query Load (0.4ms) SELECT `queries`.* FROM `queries`
|
|
57
|
+
# Rendered collection (0.0ms)
|
|
58
|
+
# Rendered queries/index.html.erb (0.6ms)
|
|
59
|
+
|
|
60
|
+
REQUEST_CATEGORIZER = lambda { |request| "#{request[:controller]}##{request[:action]}.#{request[:format]}" }
|
|
61
|
+
|
|
62
|
+
report do |analyze|
|
|
63
|
+
|
|
64
|
+
analyze.timespan
|
|
65
|
+
analyze.hourly_spread
|
|
66
|
+
|
|
67
|
+
analyze.frequency :category => REQUEST_CATEGORIZER, :title => 'Most requested'
|
|
68
|
+
analyze.frequency :method, :title => 'HTTP methods'
|
|
69
|
+
analyze.frequency :status, :title => 'HTTP statuses returned'
|
|
70
|
+
|
|
71
|
+
analyze.duration :duration, :category => REQUEST_CATEGORIZER, :title => "Request duration", :line_type => :completed
|
|
72
|
+
# analyze.duration :view, :category => REQUEST_CATEGORIZER, :title => "View rendering time", :line_type => :completed
|
|
73
|
+
# analyze.duration :db, :category => REQUEST_CATEGORIZER, :title => "Database time", :line_type => :completed
|
|
74
|
+
|
|
75
|
+
analyze.frequency :category => REQUEST_CATEGORIZER, :title => 'Process blockers (> 1 sec duration)',
|
|
76
|
+
:if => lambda { |request| request[:duration] && request[:duration] > 1.0 }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class Request < RequestLogAnalyzer::Request
|
|
80
|
+
def convert_timestamp(value, defintion)
|
|
81
|
+
value.gsub(/[^0-9]/, '')[0...14].to_i
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module RequestLogAnalyzer::FileFormat
|
|
2
|
+
|
|
3
|
+
# The RailsDevelopment FileFormat is an extention to the default Rails file format. It includes
|
|
4
|
+
# all lines of the normal Rails file format, but parses SQL queries and partial rendering lines
|
|
5
|
+
# as well.
|
|
6
|
+
class RailsDevelopment < Rails
|
|
7
|
+
def self.create
|
|
8
|
+
# puts 'DEPRECATED: use --rails-format development instead!'
|
|
9
|
+
super('development')
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|