wvanbergen-request-log-analyzer 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{README → README.textile} +29 -36
- data/Rakefile +3 -70
- data/TODO +43 -8
- data/bin/request-log-analyzer +32 -99
- data/lib/base/summarizer.rb +14 -0
- data/lib/bashcolorizer.rb +1 -1
- data/lib/command_line/arguments.rb +15 -2
- data/lib/command_line/flag.rb +12 -0
- data/lib/rails_analyzer/summarizer.rb +12 -4
- data/lib/rails_analyzer/virtual_mongrel.rb +91 -0
- data/lib/request_log_analyzer/aggregator/base.rb +34 -0
- data/lib/request_log_analyzer/aggregator/database.rb +86 -0
- data/lib/request_log_analyzer/aggregator/echo.rb +10 -0
- data/lib/request_log_analyzer/aggregator/summarizer.rb +53 -0
- data/lib/request_log_analyzer/controller.rb +90 -0
- data/lib/request_log_analyzer/file_format/merb.rb +30 -0
- data/lib/request_log_analyzer/file_format/rails.rb +84 -0
- data/lib/request_log_analyzer/file_format.rb +91 -0
- data/lib/request_log_analyzer/log_parser.rb +122 -0
- data/lib/request_log_analyzer/request.rb +72 -0
- data/lib/request_log_analyzer.rb +5 -0
- data/output/blockers.rb +2 -4
- data/output/errors.rb +1 -2
- data/output/hourly_spread.rb +3 -3
- data/output/mean_db_time.rb +1 -2
- data/output/mean_rendering_time.rb +2 -3
- data/output/mean_time.rb +2 -3
- data/output/most_requested.rb +1 -2
- data/output/timespan.rb +10 -8
- data/output/total_db_time.rb +2 -3
- data/output/total_time.rb +2 -3
- data/output/usage.rb +3 -2
- data/spec/controller_spec.rb +33 -0
- data/spec/database_inserter_spec.rb +81 -0
- data/{test/log_fragments/merb_1.log → spec/fixtures/merb.log} +0 -0
- data/spec/fixtures/multiple_files_1.log +5 -0
- data/spec/fixtures/multiple_files_2.log +2 -0
- data/{test/log_fragments/fragment_1.log → spec/fixtures/rails_1x.log} +5 -5
- data/{test/log_fragments/fragment_3.log → spec/fixtures/rails_22.log} +2 -2
- data/spec/fixtures/rails_22_cached.log +10 -0
- data/spec/fixtures/rails_unordered.log +24 -0
- data/{test/log_fragments/fragment_2.log → spec/fixtures/syslog_1x.log} +0 -0
- data/spec/fixtures/test_file_format.log +11 -0
- data/spec/fixtures/test_language_combined.log +14 -0
- data/spec/fixtures/test_order.log +16 -0
- data/spec/line_definition_spec.rb +34 -0
- data/spec/log_parser_spec.rb +92 -0
- data/spec/merb_format_spec.rb +58 -0
- data/spec/rails_format_spec.rb +95 -0
- data/spec/request_spec.rb +76 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/summarizer_spec.rb +109 -0
- data/tasks/github-gem.rake +177 -0
- data/tasks/request_log_analyzer.rake +10 -0
- data/tasks/rspec.rake +6 -0
- data/test/base_summarizer_test.rb +30 -0
- metadata +46 -22
- data/bin/request-log-database +0 -81
- data/lib/base/log_parser.rb +0 -78
- data/lib/base/record_inserter.rb +0 -139
- data/lib/merb_analyzer/log_parser.rb +0 -26
- data/lib/rails_analyzer/log_parser.rb +0 -35
- data/lib/rails_analyzer/record_inserter.rb +0 -39
- data/test/merb_log_parser_test.rb +0 -39
- data/test/rails_log_parser_test.rb +0 -95
- data/test/record_inserter_test.rb +0 -45
- data/test/tasks.rake +0 -8
data/{README → README.textile}
RENAMED
@@ -1,96 +1,93 @@
|
|
1
|
-
Request log analyzer
|
2
|
-
--------------------------------
|
1
|
+
h1. Request log analyzer
|
3
2
|
|
4
3
|
This is a simple command line tool to analyze request log files of both Rails and
|
5
4
|
Merb. Its purpose is to find what actions are best candidates for optimization.
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
* Analyzes Rails logs (all versions) and Merb logs
|
7
|
+
* Can combine multiple files (handy if you are using logrotate)
|
8
|
+
* Uses several metrics (cumulative time, average time, blockers, DB time, etc)
|
9
|
+
* Low memory footprint (server-safe)
|
10
|
+
* MIT licensed
|
11
|
+
* Fast
|
11
12
|
|
13
|
+
h2. Installation
|
12
14
|
|
13
|
-
|
14
|
-
--------------------------------
|
15
|
-
gem sources -a http://gems.github.com
|
16
|
-
sudo gem install wvanbergen-request-log-analyzer
|
15
|
+
@sudo gem install wvanbergen-request-log-analyzer --source http://gems.github.com@
|
17
16
|
|
18
|
-
Usage
|
19
|
-
--------------------------------
|
17
|
+
h2. Usage
|
20
18
|
|
19
|
+
<pre>
|
21
20
|
Usage: request-log-analyzer [FILE] [OPTION]
|
22
21
|
Analyze the given log FILE with the given OPTION
|
23
|
-
Example: request-log-analyzer mongrel.log
|
22
|
+
Example: request-log-analyzer mongrel.log -z
|
24
23
|
|
25
|
-
--fast, -
|
24
|
+
--fast, -f: Only use completed requests
|
26
25
|
--guess-database-time, -g: Guesses the database duration of requests if they are not in the log
|
27
26
|
--output, -o: Comma-separated list of reports to show
|
28
27
|
--amount, -c: Displays the top <amount> elements in the reports
|
29
28
|
--colorize, -z: Fancy bash coloring
|
29
|
+
--install rails, -i rails: Install Rails task rake log:analyze
|
30
|
+
</pre>
|
30
31
|
|
31
32
|
|
32
|
-
|
33
|
-
Example
|
34
|
-
--------------------------------
|
33
|
+
h2. Example result
|
35
34
|
|
36
35
|
Note that this example was shortened for your viewing pleasure.
|
37
|
-
|
36
|
+
@$ request-log-analyzer /var/log/my_app.log -o 5@
|
38
37
|
|
38
|
+
<pre>
|
39
39
|
Request log analyzer, by Willem van Bergen and Bart ten Brinke
|
40
40
|
|
41
|
+
Processing started, failed, completed log lines from /var/log/my_app.log...
|
42
|
+
|
41
43
|
Processing all log lines...
|
42
44
|
========================================================================
|
43
|
-
Successfully analyzed 58908 requests from log file
|
44
|
-
|
45
45
|
Timestamp first request: 2008-07-13T06:25:58+00:00
|
46
46
|
Timestamp last request: 2008-07-20T06:18:53+00:00
|
47
47
|
Total time analyzed: 7 days
|
48
|
-
Methods: DELETE (1%), GET (50%), POST (22%), PUT (25%).
|
49
48
|
|
50
|
-
|
49
|
+
Total requests analyzed 58908 requests from log file
|
50
|
+
Methods: GET (50.6%), POST (22.8%), PUT (25.4%), DELETE (1.1%), unknown (0.0%).
|
51
|
+
|
52
|
+
Top 5 most requested actions
|
51
53
|
========================================================================
|
52
54
|
/overview/:date/ : 19359 requests
|
53
55
|
/overview/day/:date/ : 6365 requests
|
54
56
|
/overview/:date/set/ : 5589 requests
|
55
57
|
/overview/ : 3985 requests
|
56
58
|
/clients/:id/ : 1976 requests
|
57
|
-
........
|
58
59
|
|
59
|
-
Top
|
60
|
+
Top 5 actions by time - cumulative
|
60
61
|
========================================================================
|
61
62
|
/overview/:date/ : 9044.582s [19359 requests]
|
62
63
|
/overview/ : 8478.767s [3985 requests]
|
63
64
|
/overview/:date/set/ : 3309.041s [5589 requests]
|
64
65
|
/clients/:id/products/:id/ : 1479.911s [924 requests]
|
65
66
|
/clients/:id/ : 750.080s [1976 requests]
|
66
|
-
........
|
67
67
|
|
68
|
-
Top
|
68
|
+
Top 5 actions by time - per request mean
|
69
69
|
========================================================================
|
70
70
|
/overview/ : 2.128s [3985 requests]
|
71
71
|
/clients/:id/products/:id/ : 1.602s [924 requests]
|
72
72
|
/overview/:date/set/ : 0.592s [5589 requests]
|
73
73
|
/overview/:date/ : 0.467s [19359 requests]
|
74
74
|
/clients/:id/ : 0.380s [1976 requests]
|
75
|
-
........
|
76
75
|
|
77
|
-
Top
|
76
|
+
Top 5 worst DB offenders - cumulative time
|
78
77
|
========================================================================
|
79
78
|
/overview/:date/ : 8773.993s [19359 requests]
|
80
79
|
/overview/ : 8394.754s [3985 requests]
|
81
80
|
/overview/:date/set/ : 3307.928s [5589 requests]
|
82
81
|
/clients/:id/products/:id/ : 1425.220s [924 requests]
|
83
82
|
/clients/:id/ : 535.229s [1976 requests]
|
84
|
-
........
|
85
83
|
|
86
|
-
Top
|
84
|
+
Top 5 worst DB offenders - mean time
|
87
85
|
========================================================================
|
88
86
|
/overview/:id/:id/:id/print/ : 6.994s [448 requests]
|
89
87
|
/overview/ : 2.128s [3985 requests]
|
90
88
|
/clients/:id/products/:id/ : 1.602s [924 requests]
|
91
89
|
/overview/:date/set/ : 0.592s [5589 requests]
|
92
90
|
/overview/:date/ : 0.467s [19359 requests]
|
93
|
-
........
|
94
91
|
|
95
92
|
Mongrel process blockers (> 1.0 seconds)
|
96
93
|
========================================================================
|
@@ -99,7 +96,6 @@ Mongrel process blockers (> 1.0 seconds)
|
|
99
96
|
/overview/:date/set/ : 1149.235s [803 requests]
|
100
97
|
/overview/:id/:id/:id/print/new/ : 613.693s [341 requests]
|
101
98
|
/clients/:id/products/:id/ : 1370.693s [313 requests]
|
102
|
-
........
|
103
99
|
|
104
100
|
Requests graph - per hour
|
105
101
|
========================================================================
|
@@ -120,13 +116,10 @@ Requests graph - per hour
|
|
120
116
|
|
121
117
|
Errors
|
122
118
|
========================================================================
|
123
|
-
ArgumentError: [237 requests]
|
124
|
-
-> invalid date
|
125
119
|
StaleObjectError: [28 requests]
|
126
120
|
-> Attempted to update a stale object
|
127
|
-
RuntimeError: [3 requests]
|
128
|
-
-> Cannot destroy rule before it was created
|
129
121
|
StatementError: [2 requests]
|
130
122
|
-> Mysql::Error: Deadlock found when trying to get lock; try restarting transaction
|
131
123
|
NoMethodError: [1 requests]
|
132
124
|
-> undefined method `code' for nil:NilClass
|
125
|
+
</pre>
|
data/Rakefile
CHANGED
@@ -1,72 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
load 'test/tasks.rake'
|
1
|
+
Dir[File.dirname(__FILE__) + "/tasks/*.rake"].each { |file| load(file) }
|
4
2
|
|
5
|
-
desc 'Default: run
|
6
|
-
task :default => :
|
7
|
-
|
8
|
-
|
9
|
-
namespace :gem do
|
3
|
+
desc 'Default: run RSpec for request-log-analyzer.'
|
4
|
+
task :default => :spec
|
10
5
|
|
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
CHANGED
@@ -2,17 +2,52 @@ TODO items for Rails-log-analyzer
|
|
2
2
|
=================================
|
3
3
|
Contact willem AT vanbergen DOT org if you want to help out with the development.
|
4
4
|
|
5
|
-
|
5
|
+
General:
|
6
|
+
- Add more tests / specs
|
7
|
+
|
8
|
+
Datamining:
|
6
9
|
- Add query functionality for the resulting database file (interactive reports?)
|
7
|
-
- Link request processing line to request completed line
|
10
|
+
- Link request processing line to request completed line (VirtualMongrel?)
|
11
|
+
- Fix the database inserter and make it more robust for future changes
|
8
12
|
|
9
13
|
Rails integration:
|
10
|
-
- Create script that calls request-log-analyzer
|
11
14
|
- Optionally use local or specific routes.rb file to parse URLs
|
12
|
-
- Add rake tasks to Rails application when included
|
13
15
|
|
14
|
-
|
15
|
-
- Add useful rake tasks
|
16
|
-
- Add more tests
|
17
|
-
- Fix multiple file handling
|
16
|
+
Other:
|
18
17
|
- World domination
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
Datamining should look something like this:
|
23
|
+
|
24
|
+
> request-log-analyzer myapp.log --interactive
|
25
|
+
Request log analyzer builds a new database.
|
26
|
+
Columns come from the log_parser as the LOG_LINES store all the keys that they can detect.
|
27
|
+
Also add some extra columns like hashed_request_url etc.
|
28
|
+
|
29
|
+
Request log analyzer then parses the logfile for its individual requests using something like the
|
30
|
+
virtual mongrel (we need a new name for this, database_summarizer agregator? sheepdog?) combined with our
|
31
|
+
default log parser.
|
32
|
+
|
33
|
+
When this is done the user enters an interactive mode (like irb).
|
34
|
+
> Filters: None
|
35
|
+
> Total requests in database: 53232
|
36
|
+
> $
|
37
|
+
|
38
|
+
The user can add filters like this:
|
39
|
+
> $ FILTER SQL ["date > ?", Date.today-1]
|
40
|
+
|
41
|
+
The user will then see this:
|
42
|
+
> Filters:
|
43
|
+
> 1. ["date > ?", Date.today-1]
|
44
|
+
> Total requests: 2120
|
45
|
+
> $
|
46
|
+
|
47
|
+
At any point the user can destroy filters, show the raw requests or show reports
|
48
|
+
> $ REPORT ALL
|
49
|
+
|
50
|
+
The request remaining after the filter chain will then be processed through the summarizer and then trough the
|
51
|
+
output templates, generating reports specificly for the selected dataset.
|
52
|
+
Partials should also be possible
|
53
|
+
> $ REPORT TIMESPAN
|
data/bin/request-log-analyzer
CHANGED
@@ -1,61 +1,23 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
|
+
require File.dirname(__FILE__) + '/../lib/request_log_analyzer'
|
2
3
|
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
4
|
|
12
5
|
puts "Request log analyzer, by Willem van Bergen and Bart ten Brinke\n\n"
|
13
6
|
|
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
7
|
# Parse the arguments given via commandline
|
50
8
|
begin
|
51
|
-
|
52
|
-
|
53
|
-
command_line.
|
54
|
-
|
55
|
-
command_line.
|
56
|
-
command_line.flag(:
|
57
|
-
command_line.flag(:
|
58
|
-
|
9
|
+
arguments = CommandLine::Arguments.parse do |command_line|
|
10
|
+
|
11
|
+
#command_line.flag(:install, :alias => :i) # command_line.command(:install)
|
12
|
+
|
13
|
+
command_line.flag(:format, :default => 'rails')
|
14
|
+
command_line.flag(:aggregator, :alias => :a, :multiple => true)
|
15
|
+
command_line.flag(:database, :alias => :d)
|
16
|
+
|
17
|
+
command_line.switch(:combined_requests, :c)
|
18
|
+
command_line.switch(:colorize, :z)
|
19
|
+
#command_line.switch(:estimate_database_time, :e)
|
20
|
+
#command_line.switch(:fast, :f) #
|
59
21
|
end
|
60
22
|
|
61
23
|
rescue CommandLine::Error => e
|
@@ -65,51 +27,22 @@ rescue CommandLine::Error => e
|
|
65
27
|
exit(0)
|
66
28
|
end
|
67
29
|
|
68
|
-
|
69
|
-
if
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
else
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
#
|
84
|
-
|
85
|
-
|
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
|
30
|
+
# if arguments[:install]
|
31
|
+
# if arguments[:install] == 'rails'
|
32
|
+
# require 'ftools'
|
33
|
+
# if File.directory?('./lib/tasks/')
|
34
|
+
# File.copy(File.dirname(__FILE__) + '/../tasks/request_log_analyzer.rake', './lib/tasks/request_log_analyze.rake')
|
35
|
+
# puts "Installed rake tasks."
|
36
|
+
# puts "To use, run: rake log:analyze"
|
37
|
+
# else
|
38
|
+
# puts "Cannot find /lib/tasks folder. Are you in your Rails directory?"
|
39
|
+
# puts "Installation aborted."
|
40
|
+
# end
|
41
|
+
# else
|
42
|
+
# raise "Cannot perform this install type!"
|
43
|
+
# end
|
44
|
+
# exit(0)
|
45
|
+
# end
|
46
|
+
|
47
|
+
# Run the request_log_analyzer!
|
48
|
+
request_log_analyzer = RequestLogAnalyzer::Controller.build(arguments).run!
|
data/lib/base/summarizer.rb
CHANGED
@@ -67,5 +67,19 @@ module Base
|
|
67
67
|
errors = min_count.nil? ? @errors.to_a : @errors.delete_if { |k, v| v[:count] < min_count}.to_a
|
68
68
|
errors.sort { |a, b| a[1][field.to_sym] <=> b[1][field.to_sym] }
|
69
69
|
end
|
70
|
+
|
71
|
+
# Compare date strings fast
|
72
|
+
# Assumes date formats: "2008-07-14 12:11:20" or alike.
|
73
|
+
# <tt>first_date</tt> The first date string
|
74
|
+
# <tt>second_date</tt> The second date string
|
75
|
+
# Returns -1 if first_date < second_date, nil if equal
|
76
|
+
# and 1 if first_date > second_date (<=>)
|
77
|
+
def hamburger_compare_string_dates first_date, second_date
|
78
|
+
return nil if first_date.nil? || second_date.nil?
|
79
|
+
|
80
|
+
first_date.gsub(/[^0-9|\s]/,'').to_i \
|
81
|
+
<=> \
|
82
|
+
second_date.gsub(/[^0-9|\s]/,'').to_i
|
83
|
+
end
|
70
84
|
end
|
71
85
|
end
|
data/lib/bashcolorizer.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# <tt>text</tt> The text to colorize.
|
3
3
|
# <tt>color_code</tt> The color code string to set
|
4
4
|
# <tt>color</tt> Does not color if false. Defaults to ($arguments && $arguments[:colorize])
|
5
|
-
def colorize(text, color_code, color = $
|
5
|
+
def colorize(text, color_code, color = $colorize)
|
6
6
|
color ? "#{color_code}#{text}\e[0m" : text
|
7
7
|
end
|
8
8
|
|
@@ -46,7 +46,7 @@ module CommandLine
|
|
46
46
|
# <tt>flag</tt> A flag symbol like :fast
|
47
47
|
# Options
|
48
48
|
# * <tt>:expects</tt> Expects a value after the flag
|
49
|
-
def flag(flag, options)
|
49
|
+
def flag(flag, options = {})
|
50
50
|
options[:expects] = String unless options.has_key?(:expects)
|
51
51
|
argument = Flag.new(flag, options)
|
52
52
|
@flag_definitions[argument.to_argument] = argument
|
@@ -85,7 +85,14 @@ module CommandLine
|
|
85
85
|
if flag.expects_argument?
|
86
86
|
|
87
87
|
if @arguments.length > (i + 1) && @arguments[i + 1]
|
88
|
-
|
88
|
+
|
89
|
+
if flag.multiple?
|
90
|
+
@flags[flag.name] ||= []
|
91
|
+
@flags[flag.name] << @arguments[i + 1]
|
92
|
+
else
|
93
|
+
@flags[flag.name] = @arguments[i + 1]
|
94
|
+
end
|
95
|
+
|
89
96
|
i += 1
|
90
97
|
else
|
91
98
|
raise CommandLine::FlagExpectsArgument.new(arg)
|
@@ -115,6 +122,12 @@ module CommandLine
|
|
115
122
|
# Check if the parsed arguments meet their requirements.
|
116
123
|
# Raises CommandLineexception on error.
|
117
124
|
def check_parsed_arguments!
|
125
|
+
|
126
|
+
@flag_definitions.each do |flag, definition|
|
127
|
+
@flags[definition.name] ||= [] if definition.multiple? && !definition.default?
|
128
|
+
@flags[definition.name] ||= definition.default if definition.default?
|
129
|
+
end
|
130
|
+
|
118
131
|
if @begins_with_command && @command.nil?
|
119
132
|
raise CommandLine::CommandMissing.new
|
120
133
|
end
|
data/lib/command_line/flag.rb
CHANGED
@@ -6,6 +6,8 @@ module CommandLine
|
|
6
6
|
attr_reader :name
|
7
7
|
attr_reader :alias
|
8
8
|
attr_reader :argument
|
9
|
+
attr_reader :default
|
10
|
+
attr_reader :multiple
|
9
11
|
|
10
12
|
# Initialize new Flag
|
11
13
|
# <tt>name</tt> The name of the flag
|
@@ -15,6 +17,8 @@ module CommandLine
|
|
15
17
|
@alias = definition[:alias].to_sym if definition[:alias]
|
16
18
|
@required = definition.has_key?(:required) && definition[:required] == true
|
17
19
|
@argument = definition[:expects] if definition[:expects]
|
20
|
+
@multiple = definition[:multiple] || false
|
21
|
+
@default = definition[:default] if definition[:default]
|
18
22
|
end
|
19
23
|
|
20
24
|
# Argument representation of the flag (--fast)
|
@@ -37,6 +41,14 @@ module CommandLine
|
|
37
41
|
!@required
|
38
42
|
end
|
39
43
|
|
44
|
+
def multiple?
|
45
|
+
@multiple
|
46
|
+
end
|
47
|
+
|
48
|
+
def default?
|
49
|
+
!@default.nil?
|
50
|
+
end
|
51
|
+
|
40
52
|
# Check if flag is required
|
41
53
|
def required?
|
42
54
|
@required
|
@@ -22,16 +22,24 @@ module RailsAnalyzer
|
|
22
22
|
case request[:type]
|
23
23
|
when :started
|
24
24
|
if request[:timestamp]
|
25
|
-
@first_request_at
|
26
|
-
|
27
|
-
|
25
|
+
if @first_request_at.nil? || hamburger_compare_string_dates(request[:timestamp], @first_request_at) == -1
|
26
|
+
@first_request_at = request[:timestamp]
|
27
|
+
end
|
28
|
+
|
29
|
+
if @last_request_at.nil? || hamburger_compare_string_dates(request[:timestamp], @last_request_at) == 1
|
30
|
+
@last_request_at = request[:timestamp]
|
31
|
+
end
|
32
|
+
|
33
|
+
@request_time_graph[request[:timestamp][11..12].to_i] +=1
|
28
34
|
end
|
35
|
+
|
29
36
|
if request[:method]
|
30
37
|
@methods[request[:method].to_sym] ||= 0
|
31
38
|
@methods[request[:method].to_sym] += 1
|
32
39
|
else
|
33
40
|
@methods[:unknown] += 1
|
34
41
|
end
|
42
|
+
|
35
43
|
when :completed
|
36
44
|
@request_count += 1
|
37
45
|
hash = block_given? ? yield(request) : request.hash
|
@@ -42,7 +50,7 @@ module RailsAnalyzer
|
|
42
50
|
@actions[hash][:count] += 1
|
43
51
|
@actions[hash][:total_time] += request[:duration]
|
44
52
|
@actions[hash][:total_db_time] += request[:db] if request[:db]
|
45
|
-
@actions[hash][:total_db_time] += request[:duration] - request[:rendering] if @calculate_database
|
53
|
+
@actions[hash][:total_db_time] += request[:duration] - request[:rendering] if @calculate_database && request[:duration] && request[:rendering]
|
46
54
|
|
47
55
|
@actions[hash][:total_rendering_time] += request[:rendering] if request[:rendering]
|
48
56
|
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# Can calculate request counts, duratations, mean times etc. of all the requests given.
|
2
|
+
class VirtualMongrel
|
3
|
+
STATUS = [:started, :completed]
|
4
|
+
|
5
|
+
attr_reader :status
|
6
|
+
attr_reader :start_line
|
7
|
+
attr_reader :start_time
|
8
|
+
attr_reader :die_line
|
9
|
+
attr_reader :die_time
|
10
|
+
attr_reader :calculate_database
|
11
|
+
attr_reader :running_mongrels
|
12
|
+
|
13
|
+
attr_reader :data_hash
|
14
|
+
|
15
|
+
def initialize(options = {})
|
16
|
+
@status = :started
|
17
|
+
|
18
|
+
@start_line = options[:start_line] || 0
|
19
|
+
@die_line = options[:die_line] || @start_line + 10
|
20
|
+
|
21
|
+
@start_time = options[:start_time] || 0
|
22
|
+
@die_time = options[:die_time] || @start_time + 10
|
23
|
+
|
24
|
+
@data_hash = {}
|
25
|
+
@calculate_database = false
|
26
|
+
@running_mongrels = options[:running_mongrels] || 1
|
27
|
+
end
|
28
|
+
|
29
|
+
def update_running_mongrels(number)
|
30
|
+
@running_mongrels = number if number > @running_mongrels
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def group(request, &block)
|
35
|
+
case request[:type]
|
36
|
+
when :started
|
37
|
+
data_hash.store(:timestamp, request[:timestamp])
|
38
|
+
data_hash.store(:method, request[:method])
|
39
|
+
@status = :started
|
40
|
+
|
41
|
+
when :completed
|
42
|
+
data_hash.store(:url, request[:url])
|
43
|
+
data_hash.store(:hashed_request, request_hasher(request))
|
44
|
+
data_hash.store(:rendering, request[:rendering])
|
45
|
+
data_hash.store(:duration, request[:duration])
|
46
|
+
data_hash.store(:db_time, request[:db])
|
47
|
+
|
48
|
+
if @calculate_database && request[:duration] && request[:rendering]
|
49
|
+
data_hash.store(:db_time, request[:duration] - request[:request])
|
50
|
+
end
|
51
|
+
|
52
|
+
@status = :completed
|
53
|
+
|
54
|
+
when :failed
|
55
|
+
data_hash.store(:error, request[:error])
|
56
|
+
data_hash.store(:exception_string, request[:exception_string])
|
57
|
+
@status = :completed
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Substitutes variable elements in a url (like the id field) with a fixed string (like ":id")
|
63
|
+
# This is used to aggregate simular requests.
|
64
|
+
# <tt>request</tt> The request to evaluate.
|
65
|
+
# Returns uniformed url string.
|
66
|
+
# Raises on mailformed request.
|
67
|
+
def request_hasher(request)
|
68
|
+
if request[:url]
|
69
|
+
url = request[:url].downcase.split(/^http[s]?:\/\/[A-z0-9\.-]+/).last.split('?').first # only the relevant URL part
|
70
|
+
url << '/' if url[-1] != '/'[0] && url.length > 1 # pad a trailing slash for consistency
|
71
|
+
|
72
|
+
url.gsub!(/\/\d+-\d+-\d+(\/|$)/, '/:date') # Combine all (year-month-day) queries
|
73
|
+
url.gsub!(/\/\d+-\d+(\/|$)/, '/:month') # Combine all date (year-month) queries
|
74
|
+
url.gsub!(/\/\d+[\w-]*/, '/:id') # replace identifiers in URLs
|
75
|
+
|
76
|
+
return url
|
77
|
+
elsif request[:controller] && request[:action]
|
78
|
+
return "#{request[:controller]}##{request[:action]}"
|
79
|
+
else
|
80
|
+
raise 'Cannot hash this request! ' + request.inspect
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Store this mongrel in the database
|
85
|
+
def save
|
86
|
+
puts 'Saving mongrel!'
|
87
|
+
puts "Number of other running mongrels (certainty) #{running_mongrels}"
|
88
|
+
puts data_hash.to_s
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|