lackac-request-log-analyzer 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README +132 -0
- data/Rakefile +72 -0
- data/TODO +18 -0
- data/bin/request-log-analyzer +115 -0
- data/bin/request-log-database +81 -0
- data/lib/base/log_parser.rb +68 -0
- data/lib/base/record_inserter.rb +139 -0
- data/lib/base/summarizer.rb +71 -0
- data/lib/bashcolorizer.rb +60 -0
- data/lib/command_line/arguments.rb +129 -0
- data/lib/command_line/exceptions.rb +37 -0
- data/lib/command_line/flag.rb +51 -0
- data/lib/merb_analyzer/log_parser.rb +26 -0
- data/lib/merb_analyzer/summarizer.rb +61 -0
- data/lib/rails_analyzer/log_parser.rb +25 -0
- data/lib/rails_analyzer/record_inserter.rb +39 -0
- data/lib/rails_analyzer/summarizer.rb +67 -0
- data/lib/ruby-progressbar/progressbar.en.rd +103 -0
- data/lib/ruby-progressbar/progressbar.ja.rd +100 -0
- data/lib/ruby-progressbar/progressbar.rb +236 -0
- data/output/blockers.rb +11 -0
- data/output/errors.rb +9 -0
- data/output/hourly_spread.rb +28 -0
- data/output/mean_db_time.rb +7 -0
- data/output/mean_rendering_time.rb +7 -0
- data/output/mean_time.rb +7 -0
- data/output/most_requested.rb +6 -0
- data/output/timespan.rb +15 -0
- data/output/total_db_time.rb +6 -0
- data/output/total_time.rb +6 -0
- data/output/usage.rb +15 -0
- data/test/log_fragments/fragment_1.log +59 -0
- data/test/log_fragments/fragment_2.log +5 -0
- data/test/log_fragments/merb_1.log +84 -0
- data/test/merb_log_parser_test.rb +39 -0
- data/test/rails_log_parser_test.rb +86 -0
- data/test/record_inserter_test.rb +45 -0
- data/test/tasks.rake +8 -0
- metadata +105 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Willem van Bergen / Bart ten Brinke
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
Request log analyzer
|
2
|
+
--------------------------------
|
3
|
+
|
4
|
+
This is a simple command line tool to analyze request log files of both Rails and
|
5
|
+
Merb. Its purpose is to find what actions are best candidates for optimization.
|
6
|
+
|
7
|
+
This tool will parse all requests in the logfile and aggregate the
|
8
|
+
information. Once it is finished parsing the log file, it will show the
|
9
|
+
requests that take op most server time. Different metrics are used (cumulative
|
10
|
+
time, average time, blockers, DB time, etc)
|
11
|
+
|
12
|
+
|
13
|
+
Installation
|
14
|
+
--------------------------------
|
15
|
+
gem sources -a http://gems.github.com
|
16
|
+
sudo gem install wvanbergen-request-log-analyzer
|
17
|
+
|
18
|
+
Usage
|
19
|
+
--------------------------------
|
20
|
+
|
21
|
+
Usage: request-log-analyzer [FILE] [OPTION]
|
22
|
+
Analyze the given log FILE with the given OPTION
|
23
|
+
Example: request-log-analyzer mongrel.log
|
24
|
+
|
25
|
+
--fast, -t: Only use completed requests
|
26
|
+
--guess-database-time, -g: Guesses the database duration of requests if they are not in the log
|
27
|
+
--output, -o: Comma-separated list of reports to show
|
28
|
+
--amount, -c: Displays the top <amount> elements in the reports
|
29
|
+
--colorize, -z: Fancy bash coloring
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
Example
|
34
|
+
--------------------------------
|
35
|
+
|
36
|
+
Note that this example was shortened for your viewing pleasure.
|
37
|
+
$ request-log-analyzer /var/log/my_app.log
|
38
|
+
|
39
|
+
Request log analyzer, by Willem van Bergen and Bart ten Brinke
|
40
|
+
|
41
|
+
Processing all log lines...
|
42
|
+
========================================================================
|
43
|
+
Successfully analyzed 58908 requests from log file
|
44
|
+
|
45
|
+
Timestamp first request: 2008-07-13T06:25:58+00:00
|
46
|
+
Timestamp last request: 2008-07-20T06:18:53+00:00
|
47
|
+
Total time analyzed: 7 days
|
48
|
+
Methods: DELETE (1%), GET (50%), POST (22%), PUT (25%).
|
49
|
+
|
50
|
+
Top 10 most requested actions
|
51
|
+
========================================================================
|
52
|
+
/overview/:date/ : 19359 requests
|
53
|
+
/overview/day/:date/ : 6365 requests
|
54
|
+
/overview/:date/set/ : 5589 requests
|
55
|
+
/overview/ : 3985 requests
|
56
|
+
/clients/:id/ : 1976 requests
|
57
|
+
........
|
58
|
+
|
59
|
+
Top 10 actions by time - cumulative
|
60
|
+
========================================================================
|
61
|
+
/overview/:date/ : 9044.582s [19359 requests]
|
62
|
+
/overview/ : 8478.767s [3985 requests]
|
63
|
+
/overview/:date/set/ : 3309.041s [5589 requests]
|
64
|
+
/clients/:id/products/:id/ : 1479.911s [924 requests]
|
65
|
+
/clients/:id/ : 750.080s [1976 requests]
|
66
|
+
........
|
67
|
+
|
68
|
+
Top 10 actions by time - per request mean
|
69
|
+
========================================================================
|
70
|
+
/overview/ : 2.128s [3985 requests]
|
71
|
+
/clients/:id/products/:id/ : 1.602s [924 requests]
|
72
|
+
/overview/:date/set/ : 0.592s [5589 requests]
|
73
|
+
/overview/:date/ : 0.467s [19359 requests]
|
74
|
+
/clients/:id/ : 0.380s [1976 requests]
|
75
|
+
........
|
76
|
+
|
77
|
+
Top 10 worst DB offenders - cumulative time
|
78
|
+
========================================================================
|
79
|
+
/overview/:date/ : 8773.993s [19359 requests]
|
80
|
+
/overview/ : 8394.754s [3985 requests]
|
81
|
+
/overview/:date/set/ : 3307.928s [5589 requests]
|
82
|
+
/clients/:id/products/:id/ : 1425.220s [924 requests]
|
83
|
+
/clients/:id/ : 535.229s [1976 requests]
|
84
|
+
........
|
85
|
+
|
86
|
+
Top 10 worst DB offenders - mean time
|
87
|
+
========================================================================
|
88
|
+
/overview/:id/:id/:id/print/ : 6.994s [448 requests]
|
89
|
+
/overview/ : 2.128s [3985 requests]
|
90
|
+
/clients/:id/products/:id/ : 1.602s [924 requests]
|
91
|
+
/overview/:date/set/ : 0.592s [5589 requests]
|
92
|
+
/overview/:date/ : 0.467s [19359 requests]
|
93
|
+
........
|
94
|
+
|
95
|
+
Mongrel process blockers (> 1.0 seconds)
|
96
|
+
========================================================================
|
97
|
+
/overview/:date/ : 7494.233s [3144 requests]
|
98
|
+
/overview/ : 8320.293s [1549 requests]
|
99
|
+
/overview/:date/set/ : 1149.235s [803 requests]
|
100
|
+
/overview/:id/:id/:id/print/new/ : 613.693s [341 requests]
|
101
|
+
/clients/:id/products/:id/ : 1370.693s [313 requests]
|
102
|
+
........
|
103
|
+
|
104
|
+
Requests graph - per hour
|
105
|
+
========================================================================
|
106
|
+
........
|
107
|
+
7:00 - 2731 : XXXXXXX
|
108
|
+
8:00 - 6139 : XXXXXXXXXXXXXXXX
|
109
|
+
9:00 - 7465 : XXXXXXXXXXXXXXXXXXXX
|
110
|
+
10:00 - 7118 : XXXXXXXXXXXXXXXXXXX
|
111
|
+
11:00 - 7409 : XXXXXXXXXXXXXXXXXXX
|
112
|
+
12:00 - 6450 : XXXXXXXXXXXXXXXXX
|
113
|
+
13:00 - 5377 : XXXXXXXXXXXXXX
|
114
|
+
14:00 - 6058 : XXXXXXXXXXXXXXXX
|
115
|
+
15:00 - 4156 : XXXXXXXXXXX
|
116
|
+
16:00 - 2767 : XXXXXXX
|
117
|
+
17:00 - 1598 : XXXX
|
118
|
+
18:00 - 792 : XX
|
119
|
+
........
|
120
|
+
|
121
|
+
Errors
|
122
|
+
========================================================================
|
123
|
+
ArgumentError: [237 requests]
|
124
|
+
-> invalid date
|
125
|
+
StaleObjectError: [28 requests]
|
126
|
+
-> Attempted to update a stale object
|
127
|
+
RuntimeError: [3 requests]
|
128
|
+
-> Cannot destroy rule before it was created
|
129
|
+
StatementError: [2 requests]
|
130
|
+
-> Mysql::Error: Deadlock found when trying to get lock; try restarting transaction
|
131
|
+
NoMethodError: [1 requests]
|
132
|
+
-> undefined method `code' for nil:NilClass
|
data/Rakefile
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
load 'test/tasks.rake'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests for request-log-analyzer.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
|
9
|
+
namespace :gem do
|
10
|
+
|
11
|
+
desc "Sets the version and date of the gem. Requires the VERSION environment variable."
|
12
|
+
task :version => [:manifest] do
|
13
|
+
|
14
|
+
require 'date'
|
15
|
+
|
16
|
+
new_version = ENV['VERSION']
|
17
|
+
raise "VERSION is required" unless /\d+(\.\d+)*/ =~ new_version
|
18
|
+
|
19
|
+
spec_file = Dir['*.gemspec'].first
|
20
|
+
|
21
|
+
spec = File.read(spec_file)
|
22
|
+
spec.gsub!(/^(\s*s\.version\s*=\s*)('|")(.+)('|")(\s*)$/) { "#{$1}'#{new_version}'#{$5}" }
|
23
|
+
spec.gsub!(/^(\s*s\.date\s*=\s*)('|")(.+)('|")(\s*)$/) { "#{$1}'#{Date.today.strftime('%Y-%m-%d')}'#{$5}" }
|
24
|
+
File.open(spec_file, 'w') { |f| f << spec }
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Creates a git tag for the provided VERSION"
|
28
|
+
task :tag => [:version] do
|
29
|
+
|
30
|
+
new_version = ENV['VERSION']
|
31
|
+
raise "VERSION is required" unless /\d+(\.\d+)*/ =~ new_version
|
32
|
+
|
33
|
+
sh "git add request-log-analyzer.gemspec"
|
34
|
+
sh "git commit -m \"Set gem version to #{new_version}\""
|
35
|
+
sh "git push origin"
|
36
|
+
sh "git tag -a \"request-log-analyzer-#{new_version}\" -m \"Tagged version #{new_version}\""
|
37
|
+
sh "git push --tags"
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "Builds a ruby gem for request-log-analyzer"
|
41
|
+
task :build => [:manifest] do
|
42
|
+
system %[gem build request-log-analyzer.gemspec]
|
43
|
+
end
|
44
|
+
|
45
|
+
desc %{Update ".manifest" with the latest list of project filenames. Respect\
|
46
|
+
.gitignore by excluding everything that git ignores. Update `files` and\
|
47
|
+
`test_files` arrays in "*.gemspec" file if it's present.}
|
48
|
+
task :manifest do
|
49
|
+
list = Dir['**/*'].sort
|
50
|
+
spec_file = Dir['*.gemspec'].first
|
51
|
+
list -= [spec_file] if spec_file
|
52
|
+
|
53
|
+
File.read('.gitignore').each_line do |glob|
|
54
|
+
glob = glob.chomp.sub(/^\//, '')
|
55
|
+
list -= Dir[glob]
|
56
|
+
list -= Dir["#{glob}/**/*"] if File.directory?(glob) and !File.symlink?(glob)
|
57
|
+
puts "excluding #{glob}"
|
58
|
+
end
|
59
|
+
|
60
|
+
if spec_file
|
61
|
+
spec = File.read spec_file
|
62
|
+
spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do
|
63
|
+
assignment = $1
|
64
|
+
bunch = $2 ? list.grep(/^test.*_test\.rb$/) : list
|
65
|
+
'%s%%w(%s)' % [assignment, bunch.join(' ')]
|
66
|
+
end
|
67
|
+
|
68
|
+
File.open(spec_file, 'w') {|f| f << spec }
|
69
|
+
end
|
70
|
+
File.open('.manifest', 'w') {|f| f << list.join("\n") }
|
71
|
+
end
|
72
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
TODO items for Rails-log-analyzer
|
2
|
+
=================================
|
3
|
+
Contact willem AT vanbergen DOT org if you want to help out with the development.
|
4
|
+
|
5
|
+
Database:
|
6
|
+
- Add query functionality for the resulting database file (interactive reports?)
|
7
|
+
- Link request processing line to request completed line
|
8
|
+
|
9
|
+
Rails integration:
|
10
|
+
- Create script that calls request-log-analyzer
|
11
|
+
- Optionally use local or specific routes.rb file to parse URLs
|
12
|
+
- Add rake tasks to Rails application when included
|
13
|
+
|
14
|
+
General:
|
15
|
+
- Add useful rake tasks
|
16
|
+
- Add more tests
|
17
|
+
- World domination
|
18
|
+
- Fix multiple file handling
|
@@ -0,0 +1,115 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
require File.dirname(__FILE__) + '/../lib/command_line/arguments'
|
3
|
+
require File.dirname(__FILE__) + '/../lib/base/log_parser'
|
4
|
+
require File.dirname(__FILE__) + '/../lib/base/summarizer'
|
5
|
+
require File.dirname(__FILE__) + '/../lib/rails_analyzer/log_parser'
|
6
|
+
require File.dirname(__FILE__) + '/../lib/rails_analyzer/summarizer'
|
7
|
+
require File.dirname(__FILE__) + '/../lib/merb_analyzer/log_parser'
|
8
|
+
require File.dirname(__FILE__) + '/../lib/merb_analyzer/summarizer'
|
9
|
+
require File.dirname(__FILE__) + '/../lib/bashcolorizer'
|
10
|
+
require File.dirname(__FILE__) + '/../lib/ruby-progressbar/progressbar.rb'
|
11
|
+
|
12
|
+
puts "Request log analyzer, by Willem van Bergen and Bart ten Brinke\n\n"
|
13
|
+
|
14
|
+
# Substitutes variable elements in a url (like the id field) with a fixed string (like ":id")
|
15
|
+
# This is used to aggregate simular requests.
|
16
|
+
# <tt>request</tt> The request to evaluate.
|
17
|
+
# Returns uniformed url string.
|
18
|
+
# Raises on mailformed request.
|
19
|
+
def request_hasher(request)
|
20
|
+
if request[:url]
|
21
|
+
url = request[:url].downcase.split(/^http[s]?:\/\/[A-z0-9\.-]+/).last.split('?').first # only the relevant URL part
|
22
|
+
url << '/' if url[-1] != '/'[0] && url.length > 1 # pad a trailing slash for consistency
|
23
|
+
|
24
|
+
url.gsub!(/\/\d+-\d+-\d+(\/|$)/, '/:date') # Combine all (year-month-day) queries
|
25
|
+
url.gsub!(/\/\d+-\d+(\/|$)/, '/:month') # Combine all date (year-month) queries
|
26
|
+
url.gsub!(/\/\d+[\w-]*/, '/:id') # replace identifiers in URLs
|
27
|
+
|
28
|
+
return url
|
29
|
+
elsif request[:controller] && request[:action]
|
30
|
+
return "#{request[:controller]}##{request[:action]}"
|
31
|
+
else
|
32
|
+
raise 'Cannot hash this request! ' + request.inspect
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Print results using a ASCII table.
|
37
|
+
# <tt>summarizer</tt> The summarizer containg information to draw the table.
|
38
|
+
# <tt>field</tt> The field containing the data to be printed
|
39
|
+
# <tt>amount</tt> The length of the table (defaults to 20)
|
40
|
+
def print_table(summarizer, field, amount = 20)
|
41
|
+
summarizer.sort_actions_by(field).reverse[0, amount.to_i].each do |a|
|
42
|
+
# As we show count by default, show totaltime if we sort by count
|
43
|
+
field = :total_time if field == :count
|
44
|
+
|
45
|
+
puts "%-50s: %10.03fs [#{green("%d requests")}]" % [a[0], a[1][field], a[1][:count]]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Parse the arguments given via commandline
|
50
|
+
begin
|
51
|
+
$arguments = CommandLine::Arguments.parse do |command_line|
|
52
|
+
command_line.switch(:guess_database_time, :g)
|
53
|
+
command_line.switch(:fast, :f)
|
54
|
+
command_line.switch(:colorize, :z)
|
55
|
+
command_line.switch(:merb, :m)
|
56
|
+
command_line.flag(:output, :alias => :o)
|
57
|
+
command_line.flag(:amount, :alias => :c)
|
58
|
+
command_line.required_files = 1
|
59
|
+
end
|
60
|
+
|
61
|
+
rescue CommandLine::Error => e
|
62
|
+
puts "ARGUMENT ERROR: " + e.message
|
63
|
+
puts
|
64
|
+
load File.dirname(__FILE__) + "/../output/usage.rb"
|
65
|
+
exit(0)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
if $arguments[:merb]
|
70
|
+
$summarizer = MerbAnalyzer::Summarizer.new(:calculate_database => $arguments[:guess_database_time])
|
71
|
+
else
|
72
|
+
$summarizer = RailsAnalyzer::Summarizer.new(:calculate_database => $arguments[:guess_database_time])
|
73
|
+
end
|
74
|
+
|
75
|
+
if $arguments[:fast]
|
76
|
+
line_types = [:completed]
|
77
|
+
elsif $arguments[:merb]
|
78
|
+
line_types = MerbAnalyzer::LogParser::LOG_LINES.keys
|
79
|
+
else
|
80
|
+
line_types = RailsAnalyzer::LogParser::LOG_LINES.keys
|
81
|
+
end
|
82
|
+
|
83
|
+
# Walk through al the files given via the arguments.
|
84
|
+
$arguments.files.each do |log_file|
|
85
|
+
puts "Processing #{line_types.join(', ')} log lines from #{log_file}..."
|
86
|
+
|
87
|
+
if $arguments[:merb]
|
88
|
+
parser = MerbAnalyzer::LogParser.new(log_file)
|
89
|
+
else
|
90
|
+
parser = RailsAnalyzer::LogParser.new(log_file)
|
91
|
+
end
|
92
|
+
|
93
|
+
# add progress bar
|
94
|
+
unless $arguments[:fast]
|
95
|
+
pbar = ProgressBar.new(green(log_file), File.size(log_file))
|
96
|
+
parser.progress { |pos, total| (pos == :finished) ? pbar.finish : pbar.set(pos) }
|
97
|
+
end
|
98
|
+
|
99
|
+
parser.each(*line_types) do |request|
|
100
|
+
$summarizer.group(request) { |r| request_hasher(r) }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Select the reports to output and generate them.
|
105
|
+
output_reports = $arguments[:output].split(',') rescue [:timespan, :most_requested, :total_time, :mean_time, :total_db_time, :mean_db_time, :mean_rendering_time, :blockers, :hourly_spread, :errors]
|
106
|
+
|
107
|
+
output_reports.each do |report|
|
108
|
+
report_location = "#{File.dirname(__FILE__)}/../output/#{report}.rb"
|
109
|
+
|
110
|
+
if File.exist?(report_location)
|
111
|
+
load report_location
|
112
|
+
else
|
113
|
+
puts "\nERROR: Output report #{report} not found!"
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/../lib/command_line/arguments'
|
4
|
+
require File.dirname(__FILE__) + '/../lib/base/log_parser'
|
5
|
+
require File.dirname(__FILE__) + '/../lib/base/record_inserter'
|
6
|
+
require File.dirname(__FILE__) + '/../lib/rails_analyzer/log_parser'
|
7
|
+
require File.dirname(__FILE__) + '/../lib/rails_analyzer/record_inserter'
|
8
|
+
require File.dirname(__FILE__) + '/../lib/bashcolorizer'
|
9
|
+
require File.dirname(__FILE__) + '/../lib/ruby-progressbar/progressbar.rb'
|
10
|
+
|
11
|
+
|
12
|
+
puts "Rails log parser, by Willem van Bergen and Bart ten Brinke\n\n"
|
13
|
+
|
14
|
+
begin
|
15
|
+
|
16
|
+
$arguments = CommandLine::Arguments.parse do |command_line|
|
17
|
+
command_line.switch(:guess_database_time, :g)
|
18
|
+
command_line.switch(:reset_database, :r)
|
19
|
+
command_line.flag(:database, :alias => :d, :required => false)
|
20
|
+
command_line.required_files = 1
|
21
|
+
end
|
22
|
+
|
23
|
+
rescue CommandLine::Error => e
|
24
|
+
puts "ARGUMENT ERROR: " + e.message
|
25
|
+
puts
|
26
|
+
puts "Usage: ruby parsetodb.rb [LOGFILES*] <OPTIONS>"
|
27
|
+
puts
|
28
|
+
puts "Options:"
|
29
|
+
puts " --database, -t: The database file to use"
|
30
|
+
puts " --reset-database, -r: Resets the database before inserting new records"
|
31
|
+
puts " --guess-database-time, -g: Guesses the database duration of requests"
|
32
|
+
puts
|
33
|
+
puts "Examples:"
|
34
|
+
puts " ./parsetodb.rb development.log"
|
35
|
+
puts " ./parsetodb.rb mongrel.0.log mongrel.1.log mongrel.2.log -g -d mongrel.db"
|
36
|
+
puts
|
37
|
+
|
38
|
+
exit(0)
|
39
|
+
end
|
40
|
+
|
41
|
+
log_files = $arguments.files
|
42
|
+
db_file = $arguments[:database] || log_files.first + '.db'
|
43
|
+
|
44
|
+
if $arguments[:reset_database] && File.exist?(db_file)
|
45
|
+
File.delete(db_file)
|
46
|
+
puts "Database file cleared."
|
47
|
+
end
|
48
|
+
|
49
|
+
records_inserted = 0
|
50
|
+
inserter = RailsAnalyzer::RecordInserter.insert_batch_into(db_file) do |db|
|
51
|
+
log_files.each do |log_file|
|
52
|
+
|
53
|
+
puts "Processing all log lines from #{log_file}..."
|
54
|
+
parser = RailsAnalyzer::LogParser.new(log_file)
|
55
|
+
|
56
|
+
pbar = ProgressBar.new(green(log_file), File.size(log_file))
|
57
|
+
parser.progress { |pos, total| (pos == :finished) ? pbar.finish : pbar.set(pos) }
|
58
|
+
|
59
|
+
parser.each do |request|
|
60
|
+
db.insert(request)
|
61
|
+
records_inserted += 1
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
if $arguments[:guess_database_time]
|
66
|
+
puts "Calculating database times..."
|
67
|
+
db.calculate_db_durations!
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
started = inserter.count(:started)
|
72
|
+
completed = inserter.count(:completed)
|
73
|
+
failed = inserter.count(:failed)
|
74
|
+
|
75
|
+
puts
|
76
|
+
puts "Inserted #{records_inserted} records from #{log_files.length} files."
|
77
|
+
puts "Parse warnings: #{inserter.warning_count}. Check the parse_warnings table in the database for details."
|
78
|
+
puts
|
79
|
+
puts "Requests started: #{started}"
|
80
|
+
puts "Requests completed: #{completed}"
|
81
|
+
puts "Requests failed: #{failed}"
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Base
|
2
|
+
# Parse a log file
|
3
|
+
class LogParser
|
4
|
+
|
5
|
+
LOG_LINES = {}
|
6
|
+
|
7
|
+
# LogParser initializer
|
8
|
+
# <tt>file</tt> The fileobject this LogParser wil operate on.
|
9
|
+
def initialize(file, options = {})
|
10
|
+
@file_name = file
|
11
|
+
@options = options
|
12
|
+
@file_size = File.size(@file_name)
|
13
|
+
|
14
|
+
self.initialize_hook(options) if self.respond_to?(:initialize_hook)
|
15
|
+
end
|
16
|
+
|
17
|
+
def progress(&block)
|
18
|
+
@progress_handler = block
|
19
|
+
end
|
20
|
+
|
21
|
+
# Output a warning
|
22
|
+
# <tt>message</tt> The warning message (object)
|
23
|
+
def warn(message)
|
24
|
+
puts " -> " + message.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
# Finds a log line and then parses the information in the line.
|
28
|
+
# Yields a hash containing the information found.
|
29
|
+
# <tt>*line_types</tt> The log line types to look for (defaults to LOG_LINES.keys).
|
30
|
+
# Yeilds a Hash when it encounters a chunk of information.
|
31
|
+
def each(*line_types, &block)
|
32
|
+
log_lines_hash = self.class::LOG_LINES
|
33
|
+
|
34
|
+
|
35
|
+
# parse everything by default
|
36
|
+
line_types = log_lines_hash.keys if line_types.empty?
|
37
|
+
|
38
|
+
File.open(@file_name) do |file|
|
39
|
+
|
40
|
+
file.each_line do |line|
|
41
|
+
|
42
|
+
#@progress_handler.call(file.pos, @file_size) if @progress_handler
|
43
|
+
|
44
|
+
line_types.each do |line_type|
|
45
|
+
if log_lines_hash[line_type][:teaser] =~ line
|
46
|
+
if log_lines_hash[line_type][:regexp] =~ line
|
47
|
+
request = { :type => line_type, :line => file.lineno }
|
48
|
+
log_lines_hash[line_type][:params].each do |key, value|
|
49
|
+
request[key] = case value
|
50
|
+
when Numeric; $~[value]
|
51
|
+
when Array; $~[value.first].send(value.last)
|
52
|
+
else; nil
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
yield(request) if block_given?
|
58
|
+
else
|
59
|
+
warn("Unparsable #{line_type} line: " + line[0..79]) unless line_type == :failed
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
@progress_handler.call(:finished, @file_size) if @progress_handler
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|