lackac-request-log-analyzer 0.1.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/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
|