wvanbergen-request-log-analyzer 0.2.2 → 0.3.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/{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
|