request-log-analyzer 1.0.2 → 1.6.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/.gitignore +10 -0
- data/DESIGN.rdoc +41 -0
- data/LICENSE +4 -4
- data/README.rdoc +38 -0
- data/Rakefile +6 -3
- data/bin/request-log-analyzer +70 -72
- data/lib/cli/command_line_arguments.rb +53 -53
- data/lib/cli/database_console.rb +26 -0
- data/lib/cli/database_console_init.rb +43 -0
- data/lib/cli/progressbar.rb +166 -189
- data/lib/cli/tools.rb +49 -0
- data/lib/request_log_analyzer/aggregator/database_inserter.rb +83 -0
- data/lib/request_log_analyzer/aggregator/echo.rb +17 -12
- data/lib/request_log_analyzer/aggregator/summarizer.rb +101 -63
- data/lib/request_log_analyzer/{aggregator/base.rb → aggregator.rb} +17 -13
- data/lib/request_log_analyzer/controller.rb +251 -98
- data/lib/request_log_analyzer/database/base.rb +114 -0
- data/lib/request_log_analyzer/database/connection.rb +38 -0
- data/lib/request_log_analyzer/database/request.rb +22 -0
- data/lib/request_log_analyzer/database/source.rb +13 -0
- data/lib/request_log_analyzer/database/warning.rb +14 -0
- data/lib/request_log_analyzer/database.rb +102 -0
- data/lib/request_log_analyzer/file_format/amazon_s3.rb +74 -0
- data/lib/request_log_analyzer/file_format/apache.rb +147 -0
- data/lib/request_log_analyzer/file_format/delayed_job.rb +55 -0
- data/lib/request_log_analyzer/file_format/merb.rb +65 -29
- data/lib/request_log_analyzer/file_format/mysql.rb +101 -0
- data/lib/request_log_analyzer/file_format/postgresql.rb +68 -0
- data/lib/request_log_analyzer/file_format/rack.rb +9 -0
- data/lib/request_log_analyzer/file_format/rails.rb +164 -78
- data/lib/request_log_analyzer/file_format/rails3.rb +86 -0
- data/lib/request_log_analyzer/file_format/rails_development.rb +12 -0
- data/lib/request_log_analyzer/file_format.rb +252 -58
- data/lib/request_log_analyzer/filter/anonymize.rb +39 -0
- data/lib/request_log_analyzer/filter/field.rb +19 -13
- data/lib/request_log_analyzer/filter/timespan.rb +25 -12
- data/lib/request_log_analyzer/filter.rb +30 -0
- data/lib/request_log_analyzer/line_definition.rb +69 -96
- data/lib/request_log_analyzer/log_processor.rb +31 -53
- data/lib/request_log_analyzer/mailer.rb +65 -0
- data/lib/request_log_analyzer/output/fancy_html.rb +49 -0
- data/lib/request_log_analyzer/output/fixed_width.rb +220 -0
- data/lib/request_log_analyzer/output/html.rb +187 -0
- data/lib/request_log_analyzer/output.rb +117 -0
- data/lib/request_log_analyzer/request.rb +125 -40
- data/lib/request_log_analyzer/source/database_loader.rb +87 -0
- data/lib/request_log_analyzer/source/log_parser.rb +297 -0
- data/lib/request_log_analyzer/source.rb +72 -0
- data/lib/request_log_analyzer/tracker/duration.rb +28 -64
- data/lib/request_log_analyzer/tracker/frequency.rb +108 -0
- data/lib/request_log_analyzer/tracker/hourly_spread.rb +76 -49
- data/lib/request_log_analyzer/tracker/numeric_value.rb +223 -0
- data/lib/request_log_analyzer/tracker/timespan.rb +54 -27
- data/lib/request_log_analyzer/tracker/traffic.rb +40 -0
- data/lib/request_log_analyzer/tracker.rb +101 -0
- data/lib/request_log_analyzer.rb +43 -13
- data/request-log-analyzer.gemspec +41 -0
- data/spec/database.yml +23 -0
- data/spec/fixtures/apache_combined.log +5 -0
- data/spec/fixtures/apache_common.log +10 -0
- data/spec/fixtures/decompression.log +12 -0
- data/spec/fixtures/decompression.log.bz2 +0 -0
- data/spec/fixtures/decompression.log.gz +0 -0
- data/spec/fixtures/decompression.log.zip +0 -0
- data/spec/fixtures/decompression.tar.gz +0 -0
- data/spec/fixtures/decompression.tgz +0 -0
- data/spec/fixtures/header_and_footer.log +6 -0
- data/spec/fixtures/merb_prefixed.log +9 -0
- data/spec/fixtures/mysql_slow_query.log +110 -0
- data/spec/fixtures/postgresql.log +2980 -0
- data/spec/fixtures/rails.db +0 -0
- data/spec/fixtures/rails_22.log +1 -1
- data/spec/fixtures/sinatra.log +99 -0
- data/spec/integration/command_line_usage_spec.rb +84 -0
- data/spec/integration/mailer_spec.rb +179 -0
- data/spec/integration/munin_plugins_rails_spec.rb +58 -0
- data/spec/integration/scout_spec.rb +152 -0
- data/spec/lib/helpers.rb +72 -0
- data/spec/lib/macros.rb +18 -0
- data/spec/lib/matchers.rb +77 -0
- data/spec/lib/mocks.rb +77 -0
- data/spec/lib/testing_format.rb +46 -0
- data/spec/spec_helper.rb +16 -59
- data/spec/unit/aggregator/database_inserter_spec.rb +93 -0
- data/spec/unit/aggregator/summarizer_spec.rb +26 -0
- data/spec/unit/controller/controller_spec.rb +41 -0
- data/spec/unit/controller/log_processor_spec.rb +18 -0
- data/spec/unit/database/base_class_spec.rb +183 -0
- data/spec/unit/database/connection_spec.rb +34 -0
- data/spec/unit/database/database_spec.rb +133 -0
- data/spec/unit/file_format/amazon_s3_format_spec.rb +67 -0
- data/spec/unit/file_format/apache_format_spec.rb +203 -0
- data/spec/unit/file_format/common_regular_expressions_spec.rb +53 -0
- data/spec/unit/file_format/delayed_job_format_spec.rb +83 -0
- data/spec/unit/file_format/file_format_api_spec.rb +69 -0
- data/spec/unit/file_format/format_autodetection_spec.rb +40 -0
- data/spec/unit/file_format/line_definition_spec.rb +75 -0
- data/spec/unit/file_format/merb_format_spec.rb +52 -0
- data/spec/unit/file_format/mysql_format_spec.rb +154 -0
- data/spec/unit/file_format/postgresql_format_spec.rb +65 -0
- data/spec/unit/file_format/rack_format_spec.rb +50 -0
- data/spec/unit/file_format/rails3_format_spec.rb +120 -0
- data/spec/unit/file_format/rails_format_spec.rb +180 -0
- data/spec/unit/filter/anonymize_filter_spec.rb +21 -0
- data/spec/unit/filter/field_filter_spec.rb +66 -0
- data/spec/unit/filter/filter_spec.rb +17 -0
- data/spec/unit/filter/timespan_filter_spec.rb +58 -0
- data/spec/unit/mailer_spec.rb +42 -0
- data/spec/unit/request_spec.rb +111 -0
- data/spec/unit/source/log_parser_spec.rb +119 -0
- data/spec/unit/tracker/duration_tracker_spec.rb +49 -0
- data/spec/unit/tracker/frequency_tracker_spec.rb +88 -0
- data/spec/unit/tracker/hourly_spread_spec.rb +79 -0
- data/spec/unit/tracker/numeric_value_tracker_spec.rb +166 -0
- data/spec/unit/tracker/timespan_tracker_spec.rb +73 -0
- data/spec/unit/tracker/tracker_api_spec.rb +125 -0
- data/spec/unit/tracker/traffic_tracker_spec.rb +28 -0
- data/tasks/github-gem.rake +290 -139
- data/tasks/request_log_analyzer.rake +23 -7
- metadata +221 -94
- data/DESIGN +0 -14
- data/HACKING +0 -7
- data/README.textile +0 -36
- data/lib/cli/bashcolorizer.rb +0 -60
- data/lib/request_log_analyzer/aggregator/database.rb +0 -148
- data/lib/request_log_analyzer/filter/base.rb +0 -29
- data/lib/request_log_analyzer/log_parser.rb +0 -173
- data/lib/request_log_analyzer/source/base.rb +0 -42
- data/lib/request_log_analyzer/source/log_file.rb +0 -170
- data/lib/request_log_analyzer/tracker/base.rb +0 -54
- data/lib/request_log_analyzer/tracker/category.rb +0 -71
- data/spec/controller_spec.rb +0 -40
- data/spec/database_inserter_spec.rb +0 -101
- data/spec/file_format_spec.rb +0 -78
- data/spec/file_formats/spec_format.rb +0 -26
- data/spec/filter_spec.rb +0 -137
- data/spec/line_definition_spec.rb +0 -124
- data/spec/log_parser_spec.rb +0 -68
- data/spec/log_processor_spec.rb +0 -57
- data/spec/merb_format_spec.rb +0 -38
- data/spec/rails_format_spec.rb +0 -76
- data/spec/request_spec.rb +0 -72
- data/spec/summarizer_spec.rb +0 -9
- data/tasks/rspec.rake +0 -6
data/.gitignore
ADDED
data/DESIGN.rdoc
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
=== Request-log-analyzer
|
|
2
|
+
RLA is set up like a simple pipe and filter system.
|
|
3
|
+
|
|
4
|
+
This allows you to easily add extra reports, filters and outputs.
|
|
5
|
+
-> Aggregator (database)
|
|
6
|
+
Source -> Filter -> Filter -> Aggregator (summary report) -> Output
|
|
7
|
+
-> Aggregator (...)
|
|
8
|
+
|
|
9
|
+
When the pipeline has been constructed, we Start chunk producer (source) and push requests through pipeline.
|
|
10
|
+
|
|
11
|
+
Controller.start
|
|
12
|
+
|
|
13
|
+
=== Source
|
|
14
|
+
RequestLogAnalyzer::Source is an Object that pushes requests into the chain.
|
|
15
|
+
At the moment you can only use the log-parser as a source.
|
|
16
|
+
It accepts files or stdin and can parse then into request objects using a RequestLogAnalyzer::FileFormat definition.
|
|
17
|
+
In the future we want to be able to have a generated request database as source as this will make interactive
|
|
18
|
+
down drilling possible.
|
|
19
|
+
|
|
20
|
+
=== Filter
|
|
21
|
+
The filters are all subclasses of the RequestLogAnalyzer::Filter class.
|
|
22
|
+
They accept a request object, manipulate or drop it, and then pass the request object on to the next filter
|
|
23
|
+
in the chain.
|
|
24
|
+
At the moment there are three types of filters available: Anonymize, Field and Timespan.
|
|
25
|
+
|
|
26
|
+
=== Aggregator
|
|
27
|
+
The Aggregators all inherit from the RequestLogAnalyzer::Aggregator class.
|
|
28
|
+
All the requests that come out of the Filterchain are fed into all the aggregators in parallel.
|
|
29
|
+
These aggregators can do anything what they want with the given request.
|
|
30
|
+
For example: the Database aggregator will just store all the requests into a SQLite database while the Summarizer will
|
|
31
|
+
generate a wide range of statistical reports from them.
|
|
32
|
+
|
|
33
|
+
=== Running the pipeline
|
|
34
|
+
All Aggregators are asked to report what they have done. For example the database will report: I stuffed x requests
|
|
35
|
+
into SQLite database Y. The Summarizer will output its reports.
|
|
36
|
+
|
|
37
|
+
Controller.report
|
|
38
|
+
|
|
39
|
+
The output is pushed to a RequestLogAnalyzer::Output object, which takes care of the output.
|
|
40
|
+
It can generate either ASCII, UTF8 or even HTML output.
|
|
41
|
+
|
data/LICENSE
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
Copyright (c) 2008 Willem van Bergen
|
|
2
|
-
|
|
1
|
+
Copyright (c) 2008-2009 Willem van Bergen & Bart ten Brinke
|
|
2
|
+
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
4
|
a copy of this software and associated documentation files (the
|
|
5
5
|
"Software"), to deal in the Software without restriction, including
|
|
@@ -7,10 +7,10 @@ without limitation the rights to use, copy, modify, merge, publish,
|
|
|
7
7
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
8
|
permit persons to whom the Software is furnished to do so, subject to
|
|
9
9
|
the following conditions:
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
The above copyright notice and this permission notice shall be
|
|
12
12
|
included in all copies or substantial portions of the Software.
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
15
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
16
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
data/README.rdoc
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
= Request-log-analyzer
|
|
2
|
+
|
|
3
|
+
This is a simple command line tool to analyze request log files in various formats to produce a performance report. Its purpose is to find what actions are best candidates for optimization.
|
|
4
|
+
|
|
5
|
+
* Analyzes log files. Currently supports: Amazon S3, Apache, Delayed::Job, Merb, Mysql, PostgreSQL, Rack, Rails and more.
|
|
6
|
+
* Combines multiple files and decompresses compressed files, which comes in handy if you are using logrotate.
|
|
7
|
+
* Uses several metrics, including cumulative request time, mean request time, process blockers, database and rendering time, HTTP methods and statuses, Rails action cache statistics, etc.) (Sample output: http://wiki.github.com/wvanbergen/request-log-analyzer/sample-output)
|
|
8
|
+
* Low memory footprint and reasonably fast, so it is safe to run on a production server.
|
|
9
|
+
* MIT licensed
|
|
10
|
+
|
|
11
|
+
See the project wiki at http://wiki.github.com/wvanbergen/request-log-analyzer for documentation and additional information.
|
|
12
|
+
|
|
13
|
+
== Installation & basic usage
|
|
14
|
+
|
|
15
|
+
Install request-log-analyzer as a Ruby gem (you might need to run this command
|
|
16
|
+
as root by prepending +sudo+ to it):
|
|
17
|
+
|
|
18
|
+
$ gem install request-log-analyzer
|
|
19
|
+
|
|
20
|
+
To analyze a Rails log file and produce a performance report, run
|
|
21
|
+
request-log-analyzer like this:
|
|
22
|
+
|
|
23
|
+
$ request-log-analyzer log/production.log
|
|
24
|
+
|
|
25
|
+
For more details, other file formats, and available command line options, see the project's wiki at http://wiki.github.com/wvanbergen/request-log-analyzer
|
|
26
|
+
|
|
27
|
+
== Additional information
|
|
28
|
+
|
|
29
|
+
Request-log-analyzer was designed and built by Willem van Bergen and Bart ten
|
|
30
|
+
Brinke.
|
|
31
|
+
|
|
32
|
+
Do you have a rails application that is not performing as it should? If you need
|
|
33
|
+
an expert to analyze your application, feel free to contact either Willem van
|
|
34
|
+
Bergen (willem@railsdoctors.com) or Bart ten Brinke (bart@railsdoctors.com).
|
|
35
|
+
|
|
36
|
+
* Project wiki at GitHub: http://wiki.github.com/wvanbergen/request-log-analyzer
|
|
37
|
+
* railsdoctors homepage: http://railsdoctors.com
|
|
38
|
+
* wvanbergen's blog posts: http://techblog.floorplanner.com/tag/request-log-analyzer
|
data/Rakefile
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
Dir[File.dirname(__FILE__) + "/tasks/*.rake"].each { |file| load(file) }
|
|
2
|
-
|
|
3
|
-
desc 'Default: run RSpec for request-log-analyzer.'
|
|
4
|
-
task :default => :spec
|
|
5
2
|
|
|
3
|
+
# Create rake tasks for a gem manages by github. The tasks are created in the
|
|
4
|
+
# gem namespace
|
|
5
|
+
GithubGem::RakeTasks.new(:gem)
|
|
6
|
+
|
|
7
|
+
# Set the RSpec runner with specdoc output as default task.
|
|
8
|
+
task :default => "spec:specdoc"
|
data/bin/request-log-analyzer
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
#!/usr/bin/ruby
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
end
|
|
10
|
-
rescue
|
|
11
|
-
default
|
|
12
|
-
end
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# encoding: utf-8
|
|
3
|
+
|
|
4
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
|
5
|
+
require 'request_log_analyzer'
|
|
6
|
+
require 'cli/command_line_arguments'
|
|
7
|
+
require 'cli/progressbar'
|
|
8
|
+
require 'cli/tools'
|
|
13
9
|
|
|
14
10
|
# Parse the arguments given via commandline
|
|
15
11
|
begin
|
|
@@ -18,106 +14,108 @@ begin
|
|
|
18
14
|
command_line.command(:install) do |install|
|
|
19
15
|
install.parameters = 1
|
|
20
16
|
end
|
|
21
|
-
|
|
17
|
+
|
|
18
|
+
command_line.command(:console) do |cons|
|
|
19
|
+
cons.option(:database, :alias => :d, :required => true)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
22
|
command_line.command(:strip) do |strip|
|
|
23
23
|
strip.minimum_parameters = 1
|
|
24
|
-
strip.option(:format, :alias => :f, :default => 'rails')
|
|
24
|
+
strip.option(:format, :alias => :f, :default => 'rails')
|
|
25
25
|
strip.option(:output, :alias => :o)
|
|
26
26
|
strip.switch(:discard_teaser_lines, :t)
|
|
27
|
-
strip.switch(:keep_junk_lines, :j)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
command_line.command(:anonymize) do |anonymize|
|
|
31
|
-
anonymize.minimum_parameters = 1
|
|
32
|
-
anonymize.option(:format, :alias => :f, :default => 'rails')
|
|
33
|
-
anonymize.option(:output, :alias => :o)
|
|
34
|
-
anonymize.switch(:discard_teaser_lines, :t)
|
|
35
|
-
anonymize.switch(:keep_junk_lines, :j)
|
|
27
|
+
strip.switch(:keep_junk_lines, :j)
|
|
36
28
|
end
|
|
37
|
-
|
|
38
|
-
command_line.option(:format, :alias => :f
|
|
29
|
+
|
|
30
|
+
command_line.option(:format, :alias => :f)
|
|
31
|
+
command_line.option(:apache_format)
|
|
32
|
+
command_line.option(:rails_format)
|
|
33
|
+
|
|
39
34
|
command_line.option(:file, :alias => :e)
|
|
40
|
-
command_line.
|
|
41
|
-
|
|
35
|
+
command_line.option(:mail, :alias => :m)
|
|
36
|
+
command_line.option(:mailhost, :default => 'localhost')
|
|
37
|
+
command_line.option(:parse_strategy, :default => 'assume-correct')
|
|
38
|
+
command_line.option(:yaml)
|
|
39
|
+
command_line.option(:dump) # To be deprecated
|
|
40
|
+
|
|
42
41
|
command_line.option(:aggregator, :alias => :a, :multiple => true)
|
|
43
|
-
|
|
42
|
+
|
|
43
|
+
command_line.option(:database, :alias => :d)
|
|
44
|
+
command_line.switch(:reset_database)
|
|
44
45
|
|
|
45
46
|
# filtering options
|
|
46
47
|
command_line.option(:select, :multiple => true, :parameters => 2)
|
|
47
48
|
command_line.option(:reject, :multiple => true, :parameters => 2)
|
|
48
49
|
command_line.option(:after)
|
|
49
|
-
command_line.option(:before)
|
|
50
|
-
|
|
50
|
+
command_line.option(:before)
|
|
51
|
+
|
|
51
52
|
command_line.switch(:boring, :b)
|
|
52
|
-
command_line.option(:
|
|
53
|
-
|
|
53
|
+
command_line.option(:output, :alias => :o, :default => 'fixedwidth')
|
|
54
|
+
command_line.option(:report_width, :default => terminal_width - 1)
|
|
55
|
+
command_line.option(:report_amount, :default => 20)
|
|
56
|
+
command_line.option(:report_sort, :default => 'sum,mean')
|
|
57
|
+
|
|
54
58
|
command_line.switch(:debug)
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
command_line.switch(:no_progress)
|
|
60
|
+
|
|
61
|
+
command_line.minimum_parameters = 1
|
|
57
62
|
end
|
|
58
|
-
|
|
63
|
+
|
|
59
64
|
rescue CommandLine::Error => e
|
|
65
|
+
puts "Request-log-analyzer, by Willem van Bergen and Bart ten Brinke - version #{RequestLogAnalyzer::VERSION}"
|
|
66
|
+
puts "Website: http://railsdoctors.com"
|
|
67
|
+
puts
|
|
60
68
|
puts "ARGUMENT ERROR: " + e.message if e.message
|
|
61
69
|
puts
|
|
62
70
|
puts "Usage: request-log-analyzer [LOGFILES*] <OPTIONS>"
|
|
63
71
|
puts
|
|
64
72
|
puts "Input options:"
|
|
65
|
-
puts " --format <format>, -f: Uses the specified log file format. Defaults to rails."
|
|
66
73
|
puts " --after <date> Only consider requests from <date> or later."
|
|
67
|
-
puts " --before <date> Only consider requests before <date>."
|
|
68
|
-
puts " --
|
|
69
|
-
puts " --reject <field> <value> Only consider requests where <field> does not match <value>."
|
|
74
|
+
puts " --before <date> Only consider requests before <date>."
|
|
75
|
+
puts " --format <format>, -f: Log file format. amazon_s3, apache, merb, mysql, rack or rails."
|
|
76
|
+
puts " --reject <field> <value> Only consider requests where <field> does not match <value>."
|
|
77
|
+
puts " --select <field> <value> Only consider requests where <field> matches <value>."
|
|
70
78
|
puts
|
|
71
79
|
puts "Output options:"
|
|
72
80
|
puts " --boring, -b Output reports without ASCII colors."
|
|
73
81
|
puts " --database <filename>, -d: Creates an SQLite3 database of all the parsed request information."
|
|
74
82
|
puts " --debug Print debug information while parsing."
|
|
75
|
-
puts " --file <filename>
|
|
76
|
-
puts
|
|
83
|
+
puts " --file <filename> Redirect output to file."
|
|
84
|
+
puts " --mail <emailaddress> Send report to an email address."
|
|
85
|
+
puts " --mailhost <server> Use the given server as the SMTP server for sending email."
|
|
86
|
+
puts " --no-progress Hide the progress bar."
|
|
87
|
+
puts " --output <format> Output format. Supports 'html' and 'fixed_width'."
|
|
88
|
+
puts " --report-width <amount> Width of ASCII report. Defaults to terminal width."
|
|
89
|
+
puts " --report-amount <amount> Maximum numer of results per report."
|
|
90
|
+
puts " --yaml <filename> Dump the results in YAML format in the given file."
|
|
91
|
+
puts
|
|
77
92
|
puts "Examples:"
|
|
78
|
-
puts " request-log-analyzer
|
|
79
|
-
puts " request-log-analyzer
|
|
93
|
+
puts " request-log-analyzer production.log"
|
|
94
|
+
puts " request-log-analyzer mongrel.0.log mongrel.1.log --output HTML --mail root@localhost"
|
|
80
95
|
puts " request-log-analyzer --format merb -d requests.db production.log"
|
|
96
|
+
puts " request-log-analyzer mysql_slow_query.log --reject query /SQL_NO_CACHE/"
|
|
81
97
|
puts
|
|
82
98
|
puts "To install rake tasks in your Rails application, "
|
|
83
99
|
puts "run the following command in your application's root directory:"
|
|
84
100
|
puts
|
|
85
101
|
puts " request-log-analyzer install rails"
|
|
86
|
-
exit(0)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def install_rake_tasks(install_type)
|
|
90
|
-
if install_type == 'rails'
|
|
91
|
-
require 'ftools'
|
|
92
|
-
if File.directory?('./lib/tasks/')
|
|
93
|
-
File.copy(File.dirname(__FILE__) + '/../tasks/request_log_analyzer.rake', './lib/tasks/request_log_analyze.rake')
|
|
94
|
-
puts "Installed rake tasks."
|
|
95
|
-
puts "To use, run: rake log:analyze"
|
|
96
|
-
else
|
|
97
|
-
puts "Cannot find /lib/tasks folder. Are you in your Rails directory?"
|
|
98
|
-
puts "Installation aborted."
|
|
99
|
-
end
|
|
100
|
-
else
|
|
101
|
-
raise "Cannot perform this install type! (#{install_type})"
|
|
102
|
-
end
|
|
102
|
+
exit(0)
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
-
|
|
106
105
|
case arguments.command
|
|
107
106
|
when :install
|
|
108
107
|
install_rake_tasks(arguments.parameters[0])
|
|
108
|
+
when :console
|
|
109
|
+
require 'cli/database_console'
|
|
110
|
+
DatabaseConsole.new(arguments).run!
|
|
109
111
|
when :strip
|
|
110
|
-
require File.dirname(__FILE__) + '/../lib/request_log_analyzer/log_processor'
|
|
112
|
+
require File.dirname(__FILE__) + '/../lib/request_log_analyzer/log_processor'
|
|
111
113
|
RequestLogAnalyzer::LogProcessor.build(:strip, arguments).run!
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
puts "Request log analyzer, by Willem van Bergen and Bart ten Brinke - Version 1.0\n\n"
|
|
117
|
-
|
|
118
|
-
# Run the request_log_analyzer!
|
|
119
|
-
RequestLogAnalyzer::Controller.build(arguments, terminal_width).run!
|
|
114
|
+
else
|
|
115
|
+
puts "Request-log-analyzer, by Willem van Bergen and Bart ten Brinke - version #{RequestLogAnalyzer::VERSION}"
|
|
116
|
+
puts "Website: http://railsdoctors.com"
|
|
117
|
+
puts
|
|
120
118
|
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
# Run the request_log_analyzer!
|
|
120
|
+
RequestLogAnalyzer::Controller.build_from_arguments(arguments).run!
|
|
123
121
|
end
|
|
@@ -71,7 +71,7 @@ module CommandLine
|
|
|
71
71
|
def required?
|
|
72
72
|
@required
|
|
73
73
|
end
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
# Check if flag is optional
|
|
76
76
|
def optional?
|
|
77
77
|
!@required
|
|
@@ -85,22 +85,22 @@ module CommandLine
|
|
|
85
85
|
!@default_value.nil?
|
|
86
86
|
end
|
|
87
87
|
end
|
|
88
|
-
|
|
88
|
+
|
|
89
89
|
class Arguments
|
|
90
90
|
|
|
91
91
|
class Definition
|
|
92
|
-
|
|
92
|
+
|
|
93
93
|
ENDLESS_PARAMETERS = 99999
|
|
94
|
-
|
|
94
|
+
|
|
95
95
|
attr_reader :commands, :options, :parameters
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
def initialize(parent)
|
|
98
98
|
@parent = parent
|
|
99
99
|
@options = {}
|
|
100
100
|
@commands = {}
|
|
101
101
|
@parameters = nil
|
|
102
102
|
end
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
def [](option_name)
|
|
105
105
|
option_symbol = CommandLine::Option.rewrite(option_name)
|
|
106
106
|
if the_option = @options.detect { |(name, odef)| odef =~ option_symbol }
|
|
@@ -109,22 +109,22 @@ module CommandLine
|
|
|
109
109
|
raise CommandLine::UnknownOption, option_name
|
|
110
110
|
end
|
|
111
111
|
end
|
|
112
|
-
|
|
112
|
+
|
|
113
113
|
def minimum_parameters=(count_specifier)
|
|
114
114
|
@parameters = count_specifier..ENDLESS_PARAMETERS
|
|
115
115
|
end
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
def parameters=(count_specifier)
|
|
118
118
|
@parameters = count_specifier
|
|
119
119
|
end
|
|
120
|
-
|
|
121
|
-
alias :files= :parameters=
|
|
122
|
-
|
|
120
|
+
|
|
121
|
+
alias :files= :parameters=
|
|
122
|
+
|
|
123
123
|
def option(name, options = {})
|
|
124
124
|
clo = CommandLine::Option.new(name, options)
|
|
125
125
|
@options[clo.name] = clo
|
|
126
126
|
end
|
|
127
|
-
|
|
127
|
+
|
|
128
128
|
def switch(name, switch_alias = nil)
|
|
129
129
|
option(name, :alias => switch_alias, :parameters => 0)
|
|
130
130
|
end
|
|
@@ -134,35 +134,35 @@ module CommandLine
|
|
|
134
134
|
yield(command_definition) if block_given?
|
|
135
135
|
@commands[CommandLine::Option.rewrite(name)] = command_definition
|
|
136
136
|
end
|
|
137
|
-
|
|
137
|
+
|
|
138
138
|
def has_command?(command)
|
|
139
139
|
@commands[CommandLine::Option.rewrite(command)]
|
|
140
140
|
end
|
|
141
141
|
end
|
|
142
|
-
|
|
143
|
-
OPTION_REGEXP = /^\-\-([A-z0-9-]+)$/;
|
|
144
|
-
ALIASES_REGEXP = /^\-([A-z0-9]+)$/
|
|
145
|
-
|
|
142
|
+
|
|
143
|
+
OPTION_REGEXP = /^\-\-([A-Za-z0-9-]+)$/;
|
|
144
|
+
ALIASES_REGEXP = /^\-([A-Aa-z0-9]+)$/
|
|
145
|
+
|
|
146
146
|
attr_reader :definition
|
|
147
147
|
attr_reader :tokens
|
|
148
148
|
attr_reader :command, :options, :parameters
|
|
149
|
-
|
|
149
|
+
|
|
150
150
|
def self.parse(tokens = $*, &block)
|
|
151
151
|
cla = Arguments.new
|
|
152
152
|
cla.define(&block)
|
|
153
|
-
return cla.parse!(tokens)
|
|
153
|
+
return cla.parse!(tokens)
|
|
154
154
|
end
|
|
155
|
-
|
|
155
|
+
|
|
156
156
|
def initialize
|
|
157
157
|
@tokens = []
|
|
158
|
-
@definition = Definition.new(self)
|
|
159
|
-
@current_definition = @definition
|
|
158
|
+
@definition = Definition.new(self)
|
|
159
|
+
@current_definition = @definition
|
|
160
160
|
end
|
|
161
|
-
|
|
161
|
+
|
|
162
162
|
def define(&block)
|
|
163
163
|
yield(@definition)
|
|
164
164
|
end
|
|
165
|
-
|
|
165
|
+
|
|
166
166
|
def [](option)
|
|
167
167
|
if the_option = @options.detect { |(key, value)| key =~ option }
|
|
168
168
|
the_option[1]
|
|
@@ -170,31 +170,31 @@ module CommandLine
|
|
|
170
170
|
@current_definition[option].default_value
|
|
171
171
|
end
|
|
172
172
|
end
|
|
173
|
-
|
|
173
|
+
|
|
174
174
|
def next_token
|
|
175
175
|
@current_token = @tokens.shift
|
|
176
176
|
return @current_token
|
|
177
177
|
end
|
|
178
|
-
|
|
178
|
+
|
|
179
179
|
def next_parameter
|
|
180
180
|
parameter_candidate = @tokens.first
|
|
181
181
|
parameter = (parameter_candidate.nil? || OPTION_REGEXP =~ parameter_candidate || ALIASES_REGEXP =~ parameter_candidate) ? nil : @tokens.shift
|
|
182
182
|
return parameter
|
|
183
183
|
end
|
|
184
|
-
|
|
184
|
+
|
|
185
185
|
def parse!(tokens)
|
|
186
|
-
@current_definition = @definition
|
|
186
|
+
@current_definition = @definition
|
|
187
187
|
@first_token = true
|
|
188
188
|
@tokens = tokens.clone
|
|
189
|
-
|
|
189
|
+
|
|
190
190
|
@options = {}
|
|
191
191
|
@parameters = []
|
|
192
192
|
@command = nil
|
|
193
|
-
|
|
193
|
+
|
|
194
194
|
prepare_result!
|
|
195
|
-
|
|
195
|
+
|
|
196
196
|
while next_token
|
|
197
|
-
|
|
197
|
+
|
|
198
198
|
if @first_token && command_definition = @definition.has_command?(@current_token)
|
|
199
199
|
@current_definition = command_definition
|
|
200
200
|
@command = CommandLine::Option.rewrite(@current_token)
|
|
@@ -204,34 +204,34 @@ module CommandLine
|
|
|
204
204
|
when OPTION_REGEXP; handle_option($1)
|
|
205
205
|
else; handle_other_parameter(@current_token)
|
|
206
206
|
end
|
|
207
|
-
@first_token = false
|
|
207
|
+
@first_token = false
|
|
208
208
|
end
|
|
209
|
-
|
|
209
|
+
|
|
210
210
|
end
|
|
211
|
-
|
|
211
|
+
|
|
212
212
|
validate_arguments!
|
|
213
|
-
|
|
214
|
-
return self
|
|
213
|
+
|
|
214
|
+
return self
|
|
215
215
|
end
|
|
216
|
-
|
|
216
|
+
|
|
217
217
|
protected
|
|
218
|
-
|
|
218
|
+
|
|
219
219
|
def prepare_result!
|
|
220
220
|
multiple_options = Hash[*@current_definition.options.select { |name, o| o.multiple? }.flatten]
|
|
221
221
|
multiple_options.each { |name, definition| @options[definition] = [] }
|
|
222
222
|
end
|
|
223
|
-
|
|
223
|
+
|
|
224
224
|
def validate_arguments!
|
|
225
225
|
if @current_definition.parameters && !(@current_definition.parameters === @parameters.length)
|
|
226
226
|
raise CommandLine::ParametersOutOfRange.new(@current_definition.parameters, @parameters.length)
|
|
227
227
|
end
|
|
228
|
-
|
|
228
|
+
|
|
229
229
|
required_options = Hash[*@current_definition.options.select { |name, o| o.required? }.flatten]
|
|
230
230
|
required_options.each do |name, definition|
|
|
231
|
-
raise CommandLine::RequiredOptionMissing, definition unless self[name]
|
|
231
|
+
raise CommandLine::RequiredOptionMissing, definition unless self[name]
|
|
232
232
|
end
|
|
233
233
|
end
|
|
234
|
-
|
|
234
|
+
|
|
235
235
|
def handle_alias_expansion(aliases)
|
|
236
236
|
aliases.reverse.scan(/./) do |alias_char|
|
|
237
237
|
if option_definition = @current_definition[alias_char]
|
|
@@ -241,24 +241,24 @@ module CommandLine
|
|
|
241
241
|
end
|
|
242
242
|
end
|
|
243
243
|
end
|
|
244
|
-
|
|
244
|
+
|
|
245
245
|
def handle_other_parameter(parameter)
|
|
246
246
|
@parameters << parameter
|
|
247
247
|
end
|
|
248
|
-
|
|
248
|
+
|
|
249
249
|
def handle_option(option_name)
|
|
250
250
|
option_definition = @current_definition[option_name]
|
|
251
251
|
raise CommandLine::UnknownOption, option_name if option_definition.nil?
|
|
252
|
-
|
|
252
|
+
|
|
253
253
|
if option_definition.multiple?
|
|
254
|
-
@options[option_definition] << option_definition.parse(self)
|
|
254
|
+
@options[option_definition] << option_definition.parse(self)
|
|
255
255
|
else
|
|
256
256
|
@options[option_definition] = option_definition.parse(self)
|
|
257
257
|
end
|
|
258
258
|
end
|
|
259
|
-
|
|
259
|
+
|
|
260
260
|
end
|
|
261
|
-
|
|
261
|
+
|
|
262
262
|
# Commandline parsing errors and exceptions
|
|
263
263
|
class Error < Exception
|
|
264
264
|
end
|
|
@@ -267,7 +267,7 @@ module CommandLine
|
|
|
267
267
|
class RequiredOptionMissing < CommandLine::Error
|
|
268
268
|
def initialize(option)
|
|
269
269
|
super("You have to provide the #{option.name} option!")
|
|
270
|
-
end
|
|
270
|
+
end
|
|
271
271
|
end
|
|
272
272
|
|
|
273
273
|
# Missing a required file
|
|
@@ -282,14 +282,14 @@ module CommandLine
|
|
|
282
282
|
else
|
|
283
283
|
super("The command expected #{expected} parameters, but found #{actual}!")
|
|
284
284
|
end
|
|
285
|
-
end
|
|
285
|
+
end
|
|
286
286
|
end
|
|
287
287
|
|
|
288
288
|
# Missing a required flag argument
|
|
289
289
|
class ParameterExpected < CommandLine::Error
|
|
290
290
|
def initialize(option)
|
|
291
291
|
super("The option #{option.inspect} expects a parameter!")
|
|
292
|
-
end
|
|
292
|
+
end
|
|
293
293
|
end
|
|
294
294
|
|
|
295
295
|
# Encountered an unkown flag
|
|
@@ -297,5 +297,5 @@ module CommandLine
|
|
|
297
297
|
def initialize(option_identifier)
|
|
298
298
|
super("#{option_identifier.inspect} not recognized as a valid option!")
|
|
299
299
|
end
|
|
300
|
-
end
|
|
300
|
+
end
|
|
301
301
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
class DatabaseConsole
|
|
3
|
+
|
|
4
|
+
IRB = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
|
5
|
+
|
|
6
|
+
def initialize(arguments)
|
|
7
|
+
@arguments = arguments
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def run!
|
|
11
|
+
libraries = ['irb/completion', 'rubygems', './lib/request_log_analyzer', './lib/cli/database_console_init']
|
|
12
|
+
libaries_string = libraries.map { |l| "-r #{l}" }.join(' ')
|
|
13
|
+
|
|
14
|
+
ENV['RLA_DBCONSOLE_DATABASE'] = @arguments[:database]
|
|
15
|
+
if @arguments[:apache_format]
|
|
16
|
+
ENV['RLA_DBCONSOLE_FORMAT'] = 'apache'
|
|
17
|
+
ENV['RLA_DBCONSOLE_FORMAT_ARGUMENT'] = @arguments[:apache_format]
|
|
18
|
+
else
|
|
19
|
+
ENV['RLA_DBCONSOLE_FORMAT'] = @arguments[:format]
|
|
20
|
+
end
|
|
21
|
+
# ENV['RLA_DBCONSOLE_FORMAT_ARGS'] = arguments['database']
|
|
22
|
+
|
|
23
|
+
exec("#{IRB} #{libaries_string} --simple-prompt")
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Setup the include path
|
|
2
|
+
$:.unshift(File.dirname(__FILE__) + '/..')
|
|
3
|
+
|
|
4
|
+
$database = RequestLogAnalyzer::Database.new(ENV['RLA_DBCONSOLE_DATABASE'])
|
|
5
|
+
$database.load_database_schema!
|
|
6
|
+
$database.register_default_orm_classes!
|
|
7
|
+
|
|
8
|
+
require 'cli/tools'
|
|
9
|
+
|
|
10
|
+
def wordwrap(string, max = 80, indent = "")
|
|
11
|
+
strings = [""]
|
|
12
|
+
string.split(", ").each do |item|
|
|
13
|
+
if strings.last.length == 0 || strings.last.length + item.length <= max
|
|
14
|
+
strings.last << item << ', '
|
|
15
|
+
else
|
|
16
|
+
strings << (item + ', ')
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
strings.map(&:strip).join("\n#{indent}").slice(0..-2)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class Request
|
|
23
|
+
def inspect
|
|
24
|
+
request_inspect = "Request[id: #{id}]"
|
|
25
|
+
request_inspect << " <#{lines.first.source.filename}>" if lines.first.source
|
|
26
|
+
|
|
27
|
+
inspected_lines = lines.map do |line|
|
|
28
|
+
inspect_line = " - #{line.line_type} (line #{line.lineno})"
|
|
29
|
+
if (inspect_attributes = line.attributes.reject { |(k, v)| [:id, :source_id, :request_id, :lineno].include?(k.to_sym) }).any?
|
|
30
|
+
inspect_attributes = inspect_attributes.map { |(k,v)| "#{k} = #{v.inspect}" }.join(', ')
|
|
31
|
+
inspect_line << "\n " + wordwrap(inspect_attributes, terminal_width - 6, " ")
|
|
32
|
+
end
|
|
33
|
+
inspect_line
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
request_inspect << "\n" << inspected_lines.join("\n") << "\n\n"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
puts "request-log-analyzer database console"
|
|
41
|
+
puts "-------------------------------------"
|
|
42
|
+
puts "The following ActiveRecord classes are available:"
|
|
43
|
+
puts $database.orm_classes.map { |k| k.name.split('::').last }.join(", ")
|