request-log-analyzer 1.7.0 → 1.10.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.
- data/.gitignore +4 -1
- data/DESIGN.rdoc +0 -1
- data/Gemfile +2 -0
- data/README.rdoc +16 -5
- data/bin/request-log-analyzer +4 -2
- data/lib/cli/database_console_init.rb +1 -1
- data/lib/cli/tools.rb +2 -1
- data/lib/mixins/gets_memory_protection.rb +1 -1
- data/lib/request_log_analyzer/aggregator/summarizer.rb +2 -2
- data/lib/request_log_analyzer/aggregator.rb +3 -3
- data/lib/request_log_analyzer/controller.rb +42 -36
- data/lib/request_log_analyzer/database.rb +5 -3
- data/lib/request_log_analyzer/file_format/apache.rb +19 -28
- data/lib/request_log_analyzer/file_format/delayed_job2.rb +38 -0
- data/lib/request_log_analyzer/file_format/delayed_job21.rb +39 -0
- data/lib/request_log_analyzer/file_format/haproxy.rb +138 -0
- data/lib/request_log_analyzer/file_format/oink.rb +96 -0
- data/lib/request_log_analyzer/file_format/postgresql.rb +7 -6
- data/lib/request_log_analyzer/file_format/rails.rb +4 -4
- data/lib/request_log_analyzer/file_format/rails3.rb +34 -11
- data/lib/request_log_analyzer/file_format/w3c.rb +56 -0
- data/lib/request_log_analyzer/file_format.rb +33 -10
- data/lib/request_log_analyzer/filter.rb +3 -5
- data/lib/request_log_analyzer/output/fancy_html.rb +0 -5
- data/lib/request_log_analyzer/output.rb +6 -7
- data/lib/request_log_analyzer/request.rb +17 -3
- data/lib/request_log_analyzer/source/log_parser.rb +3 -1
- data/lib/request_log_analyzer/source.rb +2 -6
- data/lib/request_log_analyzer/tracker/duration.rb +7 -0
- data/lib/request_log_analyzer/tracker/numeric_value.rb +123 -12
- data/lib/request_log_analyzer/tracker/traffic.rb +7 -0
- data/lib/request_log_analyzer/tracker.rb +6 -5
- data/lib/request_log_analyzer.rb +36 -17
- data/request-log-analyzer.gemspec +9 -6
- data/spec/fixtures/oink_22.log +19 -0
- data/spec/fixtures/oink_22_failure.log +17 -0
- data/spec/fixtures/postgresql.log +29 -2980
- data/spec/integration/command_line_usage_spec.rb +4 -4
- data/spec/integration/mailer_spec.rb +20 -4
- data/spec/integration/munin_plugins_rails_spec.rb +1 -1
- data/spec/integration/scout_spec.rb +1 -1
- data/spec/lib/helpers.rb +2 -2
- data/spec/lib/macros.rb +1 -1
- data/spec/lib/matchers.rb +1 -1
- data/spec/lib/mocks.rb +1 -1
- data/spec/spec_helper.rb +10 -10
- data/spec/unit/aggregator/database_inserter_spec.rb +1 -1
- data/spec/unit/aggregator/summarizer_spec.rb +1 -1
- data/spec/unit/controller/controller_spec.rb +1 -1
- data/spec/unit/controller/log_processor_spec.rb +1 -1
- data/spec/unit/database/base_class_spec.rb +1 -1
- data/spec/unit/database/connection_spec.rb +1 -1
- data/spec/unit/database/database_spec.rb +1 -1
- data/spec/unit/file_format/amazon_s3_format_spec.rb +1 -1
- data/spec/unit/file_format/apache_format_spec.rb +18 -1
- data/spec/unit/file_format/common_regular_expressions_spec.rb +1 -1
- data/spec/unit/file_format/delayed_job21_format_spec.rb +60 -0
- data/spec/unit/file_format/delayed_job2_format_spec.rb +60 -0
- data/spec/unit/file_format/delayed_job_format_spec.rb +2 -2
- data/spec/unit/file_format/file_format_api_spec.rb +2 -2
- data/spec/unit/file_format/format_autodetection_spec.rb +7 -7
- data/spec/unit/file_format/haproxy_format_spec.rb +84 -0
- data/spec/unit/file_format/line_definition_spec.rb +1 -1
- data/spec/unit/file_format/merb_format_spec.rb +1 -1
- data/spec/unit/file_format/mysql_format_spec.rb +1 -1
- data/spec/unit/file_format/oink_format_spec.rb +105 -0
- data/spec/unit/file_format/postgresql_format_spec.rb +24 -46
- data/spec/unit/file_format/rack_format_spec.rb +1 -1
- data/spec/unit/file_format/rails3_format_spec.rb +41 -14
- data/spec/unit/file_format/rails_format_spec.rb +11 -1
- data/spec/unit/file_format/w3c_format_spec.rb +46 -0
- data/spec/unit/filter/anonymize_filter_spec.rb +1 -1
- data/spec/unit/filter/field_filter_spec.rb +1 -1
- data/spec/unit/filter/filter_spec.rb +1 -1
- data/spec/unit/filter/timespan_filter_spec.rb +1 -1
- data/spec/unit/mailer_spec.rb +1 -1
- data/spec/unit/request_spec.rb +1 -1
- data/spec/unit/source/log_parser_spec.rb +1 -1
- data/spec/unit/tracker/duration_tracker_spec.rb +4 -2
- data/spec/unit/tracker/frequency_tracker_spec.rb +1 -1
- data/spec/unit/tracker/hourly_spread_spec.rb +1 -1
- data/spec/unit/tracker/numeric_value_tracker_spec.rb +38 -17
- data/spec/unit/tracker/timespan_tracker_spec.rb +1 -1
- data/spec/unit/tracker/tracker_api_spec.rb +1 -1
- data/spec/unit/tracker/traffic_tracker_spec.rb +1 -1
- data/tasks/github-gem.rake +97 -60
- data/tasks/request_log_analyzer.rake +102 -0
- metadata +198 -153
data/.gitignore
CHANGED
data/DESIGN.rdoc
CHANGED
data/Gemfile
ADDED
data/README.rdoc
CHANGED
|
@@ -4,11 +4,11 @@ This is a simple command line tool to analyze request log files in various forma
|
|
|
4
4
|
|
|
5
5
|
* Analyzes log files. Currently supports: Amazon S3, Apache, Delayed::Job, Merb, Mysql, PostgreSQL, Rack, Rails and more.
|
|
6
6
|
* Combines multiple files and decompresses compressed files, which comes in handy if you are using logrotate.
|
|
7
|
-
* Uses several metrics, including cumulative request time, mean request time, process blockers, database and rendering time, HTTP methods and statuses, Rails action cache statistics, etc.) (Sample output: http://
|
|
7
|
+
* Uses several metrics, including cumulative request time, mean request time, process blockers, database and rendering time, HTTP methods and statuses, Rails action cache statistics, etc.) (Sample output: http://github.com/wvanbergen/request-log-analyzer/wiki/sample-output)
|
|
8
8
|
* Low memory footprint and reasonably fast, so it is safe to run on a production server.
|
|
9
9
|
* MIT licensed
|
|
10
10
|
|
|
11
|
-
See the project wiki at http://
|
|
11
|
+
See the project wiki at http://github.com/wvanbergen/request-log-analyzer/wiki for documentation and additional information.
|
|
12
12
|
|
|
13
13
|
== Installation & basic usage
|
|
14
14
|
|
|
@@ -22,7 +22,17 @@ request-log-analyzer like this:
|
|
|
22
22
|
|
|
23
23
|
$ request-log-analyzer log/production.log
|
|
24
24
|
|
|
25
|
-
For more details, other file formats, and available command line options, see the project's wiki at http://
|
|
25
|
+
For more details, other file formats, and available command line options, see the project's wiki at http://github.com/wvanbergen/request-log-analyzer/wiki
|
|
26
|
+
|
|
27
|
+
== Benchmarking on different rubies
|
|
28
|
+
|
|
29
|
+
$ time ./bin/request-log-analyzer 40MBRailsFile.log
|
|
30
|
+
|
|
31
|
+
ruby-1.9.2-p0 15.19s user 0.95s system 99% cpu 16.143 total
|
|
32
|
+
ruby-1.8.7 25.21s user 1.02s system 99% cpu 26.238 total
|
|
33
|
+
jruby-1.5.3 32.64s user 4.84s system 99% cpu 37.629 total
|
|
34
|
+
rbx 46.60s user 2.71s system 108% cpu 45.324 total
|
|
35
|
+
macruby 75.00s user 20.10s system 110% cpu 1:26.13 total
|
|
26
36
|
|
|
27
37
|
== Additional information
|
|
28
38
|
|
|
@@ -33,6 +43,7 @@ Do you have a rails application that is not performing as it should? If you need
|
|
|
33
43
|
an expert to analyze your application, feel free to contact either Willem van
|
|
34
44
|
Bergen (willem@railsdoctors.com) or Bart ten Brinke (bart@railsdoctors.com).
|
|
35
45
|
|
|
36
|
-
* Project wiki at GitHub: http://
|
|
46
|
+
* Project wiki at GitHub: http://github.com/wvanbergen/request-log-analyzer/wiki
|
|
47
|
+
* Issue tracker at GitHub: http://github.com/wvanbergen/request-log-analyzer/issues
|
|
37
48
|
* railsdoctors homepage: http://railsdoctors.com
|
|
38
|
-
* wvanbergen's blog posts: http://techblog.floorplanner.com/tag/request-log-analyzer
|
|
49
|
+
* wvanbergen's blog posts: http://techblog.floorplanner.com/tag/request-log-analyzer
|
data/bin/request-log-analyzer
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# encoding: utf-8
|
|
3
3
|
|
|
4
|
-
$:.unshift(File.
|
|
4
|
+
$:.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
|
|
5
5
|
require 'request_log_analyzer'
|
|
6
6
|
require 'cli/command_line_arguments'
|
|
7
7
|
require 'cli/progressbar'
|
|
@@ -34,6 +34,7 @@ begin
|
|
|
34
34
|
command_line.option(:file, :alias => :e)
|
|
35
35
|
command_line.option(:mail, :alias => :m)
|
|
36
36
|
command_line.option(:mailhost, :default => 'localhost')
|
|
37
|
+
command_line.option(:mailsubject)
|
|
37
38
|
command_line.option(:parse_strategy, :default => 'assume-correct')
|
|
38
39
|
command_line.option(:yaml)
|
|
39
40
|
command_line.option(:dump) # To be deprecated
|
|
@@ -85,12 +86,14 @@ rescue CommandLine::Error => e
|
|
|
85
86
|
puts " --file <filename> Redirect output to file."
|
|
86
87
|
puts " --mail <emailaddress> Send report to an email address."
|
|
87
88
|
puts " --mailhost <server> Use the given server as the SMTP server for sending email."
|
|
89
|
+
puts " --mailsubject <text> Overwrite default mailsubject."
|
|
88
90
|
puts " --no-progress Hide the progress bar."
|
|
89
91
|
puts " --output <format> Output format. Supports 'html' and 'fixed_width'."
|
|
90
92
|
puts " --report-width <amount> Width of ASCII report. Defaults to terminal width."
|
|
91
93
|
puts " --report-amount <amount> Maximum numer of results per report."
|
|
92
94
|
puts " --silent Skip the commercials (includes --no-progress)."
|
|
93
95
|
puts " --yaml <filename> Dump the results in YAML format in the given file."
|
|
96
|
+
puts " --parse-strategy <strategy> Legal values are 'assume-correct' or 'cautious'."
|
|
94
97
|
puts
|
|
95
98
|
puts "Examples:"
|
|
96
99
|
puts " request-log-analyzer production.log"
|
|
@@ -112,7 +115,6 @@ when :console
|
|
|
112
115
|
require 'cli/database_console'
|
|
113
116
|
DatabaseConsole.new(arguments).run!
|
|
114
117
|
when :strip
|
|
115
|
-
require File.dirname(__FILE__) + '/../lib/request_log_analyzer/log_processor'
|
|
116
118
|
RequestLogAnalyzer::LogProcessor.build(:strip, arguments).run!
|
|
117
119
|
else
|
|
118
120
|
unless arguments[:silent]
|
data/lib/cli/tools.rb
CHANGED
|
@@ -34,7 +34,8 @@ def install_rake_tasks(install_type = :rails)
|
|
|
34
34
|
if install_type.to_sym == :rails
|
|
35
35
|
require 'ftools'
|
|
36
36
|
if File.directory?('./lib/tasks/')
|
|
37
|
-
File.
|
|
37
|
+
task_file = File.expand_path('../../tasks/request_log_analyzer.rake', File.dirname(__FILE__))
|
|
38
|
+
File.copy(task_file, './lib/tasks/request_log_analyze.rake')
|
|
38
39
|
puts "Installed rake tasks."
|
|
39
40
|
puts "To use, run: rake rla:report"
|
|
40
41
|
else
|
|
@@ -32,7 +32,7 @@ module RequestLogAnalyzer::Aggregator
|
|
|
32
32
|
# <tt>optiont</tt> The options to pass to the trackers.
|
|
33
33
|
def track(tracker_klass, value_field = {}, other_options = {})
|
|
34
34
|
options = value_field.kind_of?(Symbol) ? other_options.merge(:value => value_field) : value_field.merge(other_options)
|
|
35
|
-
tracker_klass = RequestLogAnalyzer::Tracker.const_get(RequestLogAnalyzer
|
|
35
|
+
tracker_klass = RequestLogAnalyzer::Tracker.const_get(RequestLogAnalyzer.to_camelcase(tracker_klass)) if tracker_klass.kind_of?(Symbol)
|
|
36
36
|
@trackers << tracker_klass.new(options)
|
|
37
37
|
end
|
|
38
38
|
end
|
|
@@ -85,7 +85,7 @@ module RequestLogAnalyzer::Aggregator
|
|
|
85
85
|
trackers_export = @trackers.inject({}) do |export, tracker|
|
|
86
86
|
export[tracker.title] = tracker.to_yaml_object; export
|
|
87
87
|
end
|
|
88
|
-
YAML
|
|
88
|
+
YAML.dump(trackers_export)
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
# Call report on all trackers.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
module RequestLogAnalyzer::Aggregator
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
autoload :Echo, 'request_log_analyzer/aggregator/echo'
|
|
4
|
+
autoload :Summarizer, 'request_log_analyzer/aggregator/summarizer'
|
|
5
|
+
autoload :DatabaseInserter, 'request_log_analyzer/aggregator/database_inserter'
|
|
6
6
|
|
|
7
7
|
# The base class of an aggregator. This class provides the interface to which
|
|
8
8
|
# every aggregator should comply (by simply subclassing this class).
|
|
@@ -20,11 +20,11 @@ module RequestLogAnalyzer
|
|
|
20
20
|
# Builds a RequestLogAnalyzer::Controller given parsed command line arguments
|
|
21
21
|
# <tt>arguments<tt> A CommandLine::Arguments hash containing parsed commandline parameters.
|
|
22
22
|
def self.build_from_arguments(arguments)
|
|
23
|
-
|
|
24
|
-
require '
|
|
25
|
-
|
|
23
|
+
|
|
24
|
+
require 'mixins/gets_memory_protection' if arguments[:gets_memory_protection]
|
|
25
|
+
|
|
26
26
|
options = {}
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
# Copy fields
|
|
29
29
|
options[:database] = arguments[:database]
|
|
30
30
|
options[:reset_database] = arguments[:reset_database]
|
|
@@ -33,7 +33,7 @@ module RequestLogAnalyzer
|
|
|
33
33
|
options[:mail] = arguments[:mail]
|
|
34
34
|
options[:no_progress] = arguments[:no_progress]
|
|
35
35
|
options[:format] = arguments[:format]
|
|
36
|
-
options[:output] = arguments[:output]
|
|
36
|
+
options[:output] = arguments[:output]
|
|
37
37
|
options[:file] = arguments[:file]
|
|
38
38
|
options[:after] = arguments[:after]
|
|
39
39
|
options[:before] = arguments[:before]
|
|
@@ -44,23 +44,25 @@ module RequestLogAnalyzer
|
|
|
44
44
|
options[:report_width] = arguments[:report_width]
|
|
45
45
|
options[:report_sort] = arguments[:report_sort]
|
|
46
46
|
options[:report_amount] = arguments[:report_amount]
|
|
47
|
-
options[:mailhost] = arguments[:mailhost]
|
|
48
|
-
options[:
|
|
49
|
-
|
|
47
|
+
options[:mailhost] = arguments[:mailhost]
|
|
48
|
+
options[:mailsubject] = arguments[:mailsubject]
|
|
49
|
+
options[:silent] = arguments[:silent]
|
|
50
|
+
options[:parse_strategy] = arguments[:parse_strategy]
|
|
51
|
+
|
|
50
52
|
# Apache format workaround
|
|
51
53
|
if arguments[:rails_format]
|
|
52
54
|
options[:format] = {:rails => arguments[:rails_format]}
|
|
53
55
|
elsif arguments[:apache_format]
|
|
54
56
|
options[:format] = {:apache => arguments[:apache_format]}
|
|
55
57
|
end
|
|
56
|
-
|
|
58
|
+
|
|
57
59
|
# Handle output format casing
|
|
58
60
|
if options[:output].class == String
|
|
59
61
|
options[:output] = 'FancyHTML' if options[:output] =~ /^fancy_?html$/i
|
|
60
62
|
options[:output] = 'HTML' if options[:output] =~ /^html$/i
|
|
61
63
|
options[:output] = 'FixedWidth' if options[:output] =~ /^fixed_?width$/i
|
|
62
64
|
end
|
|
63
|
-
|
|
65
|
+
|
|
64
66
|
# Register sources
|
|
65
67
|
if arguments.parameters.length == 1
|
|
66
68
|
file = arguments.parameters[0]
|
|
@@ -75,7 +77,7 @@ module RequestLogAnalyzer
|
|
|
75
77
|
else
|
|
76
78
|
options.store(:source_files, arguments.parameters)
|
|
77
79
|
end
|
|
78
|
-
|
|
80
|
+
|
|
79
81
|
# Guess file format
|
|
80
82
|
if !options[:format] && options[:source_files]
|
|
81
83
|
options[:format] = :rails # Default
|
|
@@ -89,7 +91,7 @@ module RequestLogAnalyzer
|
|
|
89
91
|
end
|
|
90
92
|
end
|
|
91
93
|
end
|
|
92
|
-
|
|
94
|
+
|
|
93
95
|
build(options)
|
|
94
96
|
end
|
|
95
97
|
|
|
@@ -107,6 +109,7 @@ module RequestLogAnalyzer
|
|
|
107
109
|
# * <tt>:format</tt> :rails, {:apache => 'FORMATSTRING'}, :merb, :amazon_s3, :mysql or RequestLogAnalyzer::FileFormat class. (Defaults to :rails).
|
|
108
110
|
# * <tt>:mail</tt> Email the results to this email address.
|
|
109
111
|
# * <tt>:mailhost</tt> Email the results to this mail server.
|
|
112
|
+
# * <tt>:mailsubject</tt> Email subject.
|
|
110
113
|
# * <tt>:no_progress</tt> Do not display the progress bar (increases parsing speed).
|
|
111
114
|
# * <tt>:output</tt> 'FixedWidth', 'HTML' or RequestLogAnalyzer::Output class. Defaults to 'FixedWidth'.
|
|
112
115
|
# * <tt>:reject</tt> Reject specific {:field => :value} combination (expects a single hash).
|
|
@@ -121,7 +124,7 @@ module RequestLogAnalyzer
|
|
|
121
124
|
# RequestLogAnalyzer::Controller.build(
|
|
122
125
|
# :output => :HTML,
|
|
123
126
|
# :mail => 'root@localhost',
|
|
124
|
-
# :after => Time.now - 24*60*60,
|
|
127
|
+
# :after => Time.now - 24*60*60,
|
|
125
128
|
# :source_files => '/var/log/passenger.log'
|
|
126
129
|
# ).run!
|
|
127
130
|
#
|
|
@@ -138,57 +141,60 @@ module RequestLogAnalyzer
|
|
|
138
141
|
options[:report_sort] ||= 'sum,mean'
|
|
139
142
|
options[:boring] ||= false
|
|
140
143
|
options[:silent] ||= false
|
|
141
|
-
|
|
144
|
+
|
|
142
145
|
options[:no_progress] = true if options[:silent]
|
|
143
|
-
|
|
146
|
+
|
|
144
147
|
# Deprecation warnings
|
|
145
148
|
if options[:dump]
|
|
146
149
|
warn "[DEPRECATION] `:dump` is deprecated. Please use `:yaml` instead."
|
|
147
150
|
options[:yaml] = options[:dump]
|
|
148
151
|
end
|
|
149
|
-
|
|
152
|
+
|
|
150
153
|
# Set the output class
|
|
151
154
|
output_args = {}
|
|
152
155
|
output_object = nil
|
|
153
156
|
if options[:output].is_a?(Class)
|
|
154
157
|
output_class = options[:output]
|
|
155
158
|
else
|
|
156
|
-
output_class = RequestLogAnalyzer::Output
|
|
159
|
+
output_class = RequestLogAnalyzer::Output.const_get(options[:output])
|
|
157
160
|
end
|
|
158
|
-
|
|
161
|
+
|
|
159
162
|
output_sort = options[:report_sort].split(',').map { |s| s.to_sym }
|
|
160
163
|
output_amount = options[:report_amount] == 'all' ? :all : options[:report_amount].to_i
|
|
161
|
-
|
|
164
|
+
|
|
162
165
|
if options[:file]
|
|
163
166
|
output_object = %w[File StringIO].include?(options[:file].class.name) ? options[:file] : File.new(options[:file], "w+")
|
|
164
167
|
output_args = {:width => 80, :color => false, :characters => :ascii, :sort => output_sort, :amount => output_amount }
|
|
165
168
|
elsif options[:mail]
|
|
166
|
-
output_object = RequestLogAnalyzer::Mailer.new(options[:mail], options[:mailhost])
|
|
169
|
+
output_object = RequestLogAnalyzer::Mailer.new(options[:mail], options[:mailhost], :subject => options[:mailsubject])
|
|
167
170
|
output_args = {:width => 80, :color => false, :characters => :ascii, :sort => output_sort, :amount => output_amount }
|
|
168
171
|
else
|
|
169
172
|
output_object = STDOUT
|
|
170
173
|
output_args = {:width => options[:report_width].to_i, :color => !options[:boring],
|
|
171
174
|
:characters => (options[:boring] ? :ascii : :utf), :sort => output_sort, :amount => output_amount }
|
|
172
175
|
end
|
|
173
|
-
|
|
176
|
+
|
|
174
177
|
output_instance = output_class.new(output_object, output_args)
|
|
175
|
-
|
|
178
|
+
|
|
176
179
|
# Create the controller with the correct file format
|
|
177
180
|
if options[:format].kind_of?(Hash)
|
|
178
181
|
file_format = RequestLogAnalyzer::FileFormat.load(options[:format].keys[0], options[:format].values[0])
|
|
179
182
|
else
|
|
180
183
|
file_format = RequestLogAnalyzer::FileFormat.load(options[:format])
|
|
181
184
|
end
|
|
182
|
-
|
|
185
|
+
|
|
183
186
|
# Kickstart the controller
|
|
184
|
-
controller =
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
187
|
+
controller =
|
|
188
|
+
Controller.new(RequestLogAnalyzer::Source::LogParser.new(file_format,
|
|
189
|
+
:source_files => options[:source_files],
|
|
190
|
+
:parse_strategy => options[:parse_strategy]),
|
|
191
|
+
{ :output => output_instance,
|
|
192
|
+
:database => options[:database], # FUGLY!
|
|
193
|
+
:yaml => options[:yaml],
|
|
194
|
+
:reset_database => options[:reset_database],
|
|
195
|
+
:no_progress => options[:no_progress],
|
|
196
|
+
:silent => options[:silent]
|
|
197
|
+
})
|
|
192
198
|
|
|
193
199
|
# register filters
|
|
194
200
|
if options[:after] || options[:before]
|
|
@@ -210,7 +216,7 @@ module RequestLogAnalyzer
|
|
|
210
216
|
end
|
|
211
217
|
end
|
|
212
218
|
|
|
213
|
-
if options[:
|
|
219
|
+
if options[:select]
|
|
214
220
|
options[:select].each do |(field, value)|
|
|
215
221
|
controller.add_filter(:field, :mode => :select, :field => field, :value => value)
|
|
216
222
|
end
|
|
@@ -224,7 +230,7 @@ module RequestLogAnalyzer
|
|
|
224
230
|
|
|
225
231
|
file_format.setup_environment(controller)
|
|
226
232
|
return controller
|
|
227
|
-
end
|
|
233
|
+
end
|
|
228
234
|
|
|
229
235
|
# Builds a new Controller for the given log file format.
|
|
230
236
|
# <tt>format</tt> Logfile format. Defaults to :rails
|
|
@@ -242,7 +248,7 @@ module RequestLogAnalyzer
|
|
|
242
248
|
@filters = []
|
|
243
249
|
@output = options[:output]
|
|
244
250
|
@interrupted = false
|
|
245
|
-
|
|
251
|
+
|
|
246
252
|
# Register the request format for this session after checking its validity
|
|
247
253
|
raise "Invalid file format!" unless @source.file_format.valid?
|
|
248
254
|
|
|
@@ -281,7 +287,7 @@ module RequestLogAnalyzer
|
|
|
281
287
|
# Adds an aggregator to the controller. The aggregator will be called for every request
|
|
282
288
|
# that is parsed from the provided sources (see add_source)
|
|
283
289
|
def add_aggregator(agg)
|
|
284
|
-
agg = RequestLogAnalyzer::Aggregator.const_get(RequestLogAnalyzer
|
|
290
|
+
agg = RequestLogAnalyzer::Aggregator.const_get(RequestLogAnalyzer.to_camelcase(agg)) if agg.kind_of?(Symbol)
|
|
285
291
|
@aggregators << agg.new(@source, @options)
|
|
286
292
|
end
|
|
287
293
|
|
|
@@ -289,7 +295,7 @@ module RequestLogAnalyzer
|
|
|
289
295
|
|
|
290
296
|
# Adds a request filter to the controller.
|
|
291
297
|
def add_filter(filter, filter_options = {})
|
|
292
|
-
filter = RequestLogAnalyzer::Filter.const_get(RequestLogAnalyzer
|
|
298
|
+
filter = RequestLogAnalyzer::Filter.const_get(RequestLogAnalyzer.to_camelcase(filter)) if filter.kind_of?(Symbol)
|
|
293
299
|
@filters << filter.new(source.file_format, @options.merge(filter_options))
|
|
294
300
|
end
|
|
295
301
|
|
|
@@ -3,9 +3,11 @@ require 'active_record'
|
|
|
3
3
|
|
|
4
4
|
class RequestLogAnalyzer::Database
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
autoload :Connection, 'request_log_analyzer/database/connection'
|
|
7
|
+
autoload :Base, 'request_log_analyzer/database/base'
|
|
8
|
+
autoload :Request, 'request_log_analyzer/database/request'
|
|
9
|
+
autoload :Source, 'request_log_analyzer/database/source'
|
|
10
|
+
autoload :Warning, 'request_log_analyzer/database/warning'
|
|
9
11
|
|
|
10
12
|
include RequestLogAnalyzer::Database::Connection
|
|
11
13
|
|
|
@@ -30,20 +30,23 @@ module RequestLogAnalyzer::FileFormat
|
|
|
30
30
|
|
|
31
31
|
# A hash that defines how the log format directives should be parsed.
|
|
32
32
|
LOG_DIRECTIVES = {
|
|
33
|
-
'%' => { :regexp => '%', :captures => [] },
|
|
34
|
-
'h' => { :regexp => '([A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+)', :captures => [{:name => :remote_host, :type => :string}] },
|
|
35
|
-
'a' => { :regexp => "(#{ip_address})", :captures => [{:name => :remote_ip, :type => :string}] },
|
|
36
|
-
'b' => { :regexp => '(\d+|-)', :captures => [{:name => :bytes_sent, :type => :traffic}] },
|
|
37
|
-
'c' => { :regexp => '(\+|\-|\X)', :captures => [{:name => :connection_status, :type => :integer}] },
|
|
38
|
-
'D' => { :regexp => '(\d+|-)', :captures => [{:name => :duration, :type => :duration, :unit => :musec}] },
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
'
|
|
43
|
-
'
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
|
|
33
|
+
'%' => { nil => { :regexp => '%', :captures => [] } },
|
|
34
|
+
'h' => { nil => { :regexp => '([A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+)', :captures => [{:name => :remote_host, :type => :string}] } },
|
|
35
|
+
'a' => { nil => { :regexp => "(#{ip_address})", :captures => [{:name => :remote_ip, :type => :string}] } },
|
|
36
|
+
'b' => { nil => { :regexp => '(\d+|-)', :captures => [{:name => :bytes_sent, :type => :traffic}] } },
|
|
37
|
+
'c' => { nil => { :regexp => '(\+|\-|\X)', :captures => [{:name => :connection_status, :type => :integer}] } },
|
|
38
|
+
'D' => { nil => { :regexp => '(\d+|-)', :captures => [ {:name => :duration, :type => :duration, :unit => :musec }] },
|
|
39
|
+
'micro' => { :regexp => '(\d+|-)', :captures => [ {:name => :duration, :type => :duration, :unit => :musec }] },
|
|
40
|
+
'milli' => { :regexp => '(\d+|-)', :captures => [ {:name => :duration, :type => :duration, :unit => :msec }] }
|
|
41
|
+
},
|
|
42
|
+
'l' => { nil => { :regexp => '([\w-]+)', :captures => [{:name => :remote_logname, :type => :nillable_string}] } },
|
|
43
|
+
'T' => { nil => { :regexp => '((?:\d+(?:\.\d+))|-)', :captures => [{:name => :duration, :type => :duration, :unit => :sec}] } },
|
|
44
|
+
't' => { nil => { :regexp => "\\[(#{APACHE_TIMESTAMP})?\\]", :captures => [{:name => :timestamp, :type => :timestamp}] } },
|
|
45
|
+
's' => { nil => { :regexp => '(\d{3})', :captures => [{:name => :http_status, :type => :integer}] } },
|
|
46
|
+
'u' => { nil => { :regexp => '(\w+|-)', :captures => [{:name => :user, :type => :nillable_string}] } },
|
|
47
|
+
'U' => { nil => { :regexp => '(\/\S*)', :captures => [{:name => :path, :type => :string}] } },
|
|
48
|
+
'r' => { nil => { :regexp => '([A-Z]+) (\S+) HTTP\/(\d+(?:\.\d+)*)', :captures => [{:name => :http_method, :type => :string},
|
|
49
|
+
{:name => :path, :type => :path}, {:name => :http_version, :type => :string}]} },
|
|
47
50
|
'i' => { 'Referer' => { :regexp => '(\S+)', :captures => [{:name => :referer, :type => :nillable_string}] },
|
|
48
51
|
'User-agent' => { :regexp => '(.*)', :captures => [{:name => :user_agent, :type => :user_agent}] }
|
|
49
52
|
}
|
|
@@ -72,14 +75,13 @@ module RequestLogAnalyzer::FileFormat
|
|
|
72
75
|
|
|
73
76
|
if variable
|
|
74
77
|
# Check if we recognize the log directive
|
|
75
|
-
directive = LOG_DIRECTIVES[variable]
|
|
76
|
-
directive = directive[arg] if directive && arg
|
|
78
|
+
directive = LOG_DIRECTIVES[variable][arg] rescue nil
|
|
77
79
|
|
|
78
80
|
if directive
|
|
79
81
|
line_regexp << directive[:regexp] # Parse the value of the directive
|
|
80
82
|
captures += directive[:captures] # Add the directive's information to the captures
|
|
81
83
|
else
|
|
82
|
-
puts "%#{
|
|
84
|
+
puts "Apache log directive %#{arg}#{variable} is not yet supported by RLA, the field will be ignored."
|
|
83
85
|
line_regexp << '.*' # Just accept any input for this literal
|
|
84
86
|
end
|
|
85
87
|
end
|
|
@@ -126,22 +128,11 @@ module RequestLogAnalyzer::FileFormat
|
|
|
126
128
|
"#{value[7,4]}#{MONTHS[value[3,3]]}#{value[0,2]}#{value[12,2]}#{value[15,2]}#{value[18,2]}".to_i
|
|
127
129
|
end
|
|
128
130
|
|
|
129
|
-
# This function can be overridden to rewrite the path for better categorization in the
|
|
130
|
-
# reports.
|
|
131
|
-
def convert_path(value, definition)
|
|
132
|
-
value
|
|
133
|
-
end
|
|
134
|
-
|
|
135
131
|
# This function can be overridden to simplify the user agent string for better
|
|
136
132
|
# categorization in the reports
|
|
137
133
|
def convert_user_agent(value, definition)
|
|
138
134
|
value # TODO
|
|
139
135
|
end
|
|
140
|
-
|
|
141
|
-
# Make sure that the string '-' is parsed as a nil value.
|
|
142
|
-
def convert_nillable_string(value, definition)
|
|
143
|
-
value == '-' ? nil : value
|
|
144
|
-
end
|
|
145
136
|
end
|
|
146
137
|
end
|
|
147
138
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module RequestLogAnalyzer::FileFormat
|
|
2
|
+
|
|
3
|
+
# The DelayedJob2 file format parsed log files that are created by DelayedJob 2.0.
|
|
4
|
+
# By default, the log file can be found in RAILS_ROOT/log/delayed_job.log
|
|
5
|
+
class DelayedJob2 < Base
|
|
6
|
+
|
|
7
|
+
extend CommonRegularExpressions
|
|
8
|
+
|
|
9
|
+
line_definition :job_lock do |line|
|
|
10
|
+
line.header = true
|
|
11
|
+
line.regexp = /(#{timestamp('%Y-%m-%dT%H:%M:%S%z')}): \* \[Worker\(\S+ host:(\S+) pid:(\d+)\)\] acquired lock on (\S+)/
|
|
12
|
+
|
|
13
|
+
line.capture(:timestamp).as(:timestamp)
|
|
14
|
+
line.capture(:host)
|
|
15
|
+
line.capture(:pid).as(:integer)
|
|
16
|
+
line.capture(:job)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
line_definition :job_completed do |line|
|
|
20
|
+
line.footer = true
|
|
21
|
+
line.regexp = /(#{timestamp('%Y-%m-%dT%H:%M:%S%z')}): \* \[JOB\] \S+ host:(\S+) pid:(\d+) completed after (\d+\.\d+)/
|
|
22
|
+
line.capture(:timestamp).as(:timestamp)
|
|
23
|
+
line.capture(:host)
|
|
24
|
+
line.capture(:pid).as(:integer)
|
|
25
|
+
line.capture(:duration).as(:duration, :unit => :sec)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
report do |analyze|
|
|
29
|
+
analyze.timespan
|
|
30
|
+
analyze.hourly_spread
|
|
31
|
+
|
|
32
|
+
analyze.frequency :job, :line_type => :job_completed, :title => "Completed jobs"
|
|
33
|
+
#analyze.frequency :job, :if => lambda { |request| request[:attempts] == 1 }, :title => "Failed jobs"
|
|
34
|
+
|
|
35
|
+
analyze.duration :duration, :category => :job, :line_type => :job_completed, :title => "Job duration"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module RequestLogAnalyzer::FileFormat
|
|
2
|
+
|
|
3
|
+
# The DelayedJob21 file format parsed log files that are created by DelayedJob 2.1 or higher.
|
|
4
|
+
# By default, the log file can be found in RAILS_ROOT/log/delayed_job.log
|
|
5
|
+
class DelayedJob21 < Base
|
|
6
|
+
|
|
7
|
+
extend CommonRegularExpressions
|
|
8
|
+
|
|
9
|
+
line_definition :job_lock do |line|
|
|
10
|
+
line.header = true
|
|
11
|
+
line.regexp = /(#{timestamp('%Y-%m-%dT%H:%M:%S%z')}): \[Worker\(\S+ host:(\S+) pid:(\d+)\)\] acquired lock on (\S+)/
|
|
12
|
+
|
|
13
|
+
line.capture(:timestamp).as(:timestamp)
|
|
14
|
+
line.capture(:host)
|
|
15
|
+
line.capture(:pid).as(:integer)
|
|
16
|
+
line.capture(:job)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
line_definition :job_completed do |line|
|
|
20
|
+
line.footer = true
|
|
21
|
+
line.regexp = /(#{timestamp('%Y-%m-%dT%H:%M:%S%z')}): \[Worker\(\S+ host:(\S+) pid:(\d+)\)\] (\S+) completed after (\d+\.\d+)/
|
|
22
|
+
line.capture(:timestamp).as(:timestamp)
|
|
23
|
+
line.capture(:host)
|
|
24
|
+
line.capture(:pid).as(:integer)
|
|
25
|
+
line.capture(:job)
|
|
26
|
+
line.capture(:duration).as(:duration, :unit => :sec)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
report do |analyze|
|
|
30
|
+
analyze.timespan
|
|
31
|
+
analyze.hourly_spread
|
|
32
|
+
|
|
33
|
+
analyze.frequency :job, :line_type => :job_completed, :title => "Completed jobs"
|
|
34
|
+
#analyze.frequency :job, :if => lambda { |request| request[:attempts] == 1 }, :title => "Failed jobs"
|
|
35
|
+
|
|
36
|
+
analyze.duration :duration, :category => :job, :line_type => :job_completed, :title => "Job duration"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|