request-log-analyzer 1.3.4 → 1.4.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/LICENSE +3 -3
- data/README.rdoc +1 -1
- data/bin/request-log-analyzer +17 -14
- data/lib/cli/command_line_arguments.rb +51 -51
- data/lib/cli/database_console.rb +3 -3
- data/lib/cli/database_console_init.rb +4 -3
- data/lib/cli/progressbar.rb +10 -10
- data/lib/cli/tools.rb +3 -3
- data/lib/request_log_analyzer/aggregator/database_inserter.rb +19 -17
- data/lib/request_log_analyzer/aggregator/echo.rb +14 -9
- data/lib/request_log_analyzer/aggregator/summarizer.rb +26 -26
- data/lib/request_log_analyzer/aggregator.rb +11 -15
- data/lib/request_log_analyzer/controller.rb +162 -89
- data/lib/request_log_analyzer/database/base.rb +22 -21
- data/lib/request_log_analyzer/database/connection.rb +3 -3
- 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 +28 -103
- data/lib/request_log_analyzer/file_format/amazon_s3.rb +20 -20
- data/lib/request_log_analyzer/file_format/apache.rb +37 -30
- data/lib/request_log_analyzer/file_format/merb.rb +30 -13
- data/lib/request_log_analyzer/file_format/rack.rb +11 -0
- data/lib/request_log_analyzer/file_format/rails.rb +143 -73
- data/lib/request_log_analyzer/file_format/rails_development.rb +4 -49
- data/lib/request_log_analyzer/file_format.rb +16 -28
- data/lib/request_log_analyzer/filter/anonymize.rb +8 -7
- data/lib/request_log_analyzer/filter/field.rb +9 -9
- data/lib/request_log_analyzer/filter/timespan.rb +12 -10
- data/lib/request_log_analyzer/filter.rb +12 -16
- data/lib/request_log_analyzer/line_definition.rb +15 -14
- data/lib/request_log_analyzer/log_processor.rb +27 -29
- data/lib/request_log_analyzer/mailer.rb +15 -9
- data/lib/request_log_analyzer/output/fixed_width.rb +48 -48
- data/lib/request_log_analyzer/output/html.rb +20 -20
- data/lib/request_log_analyzer/output.rb +53 -12
- data/lib/request_log_analyzer/request.rb +88 -69
- data/lib/request_log_analyzer/source/database_loader.rb +10 -14
- data/lib/request_log_analyzer/source/log_parser.rb +57 -50
- data/lib/request_log_analyzer/source.rb +10 -12
- data/lib/request_log_analyzer/tracker/duration.rb +41 -134
- data/lib/request_log_analyzer/tracker/frequency.rb +31 -32
- data/lib/request_log_analyzer/tracker/hourly_spread.rb +21 -21
- data/lib/request_log_analyzer/tracker/timespan.rb +17 -17
- data/lib/request_log_analyzer/tracker/traffic.rb +106 -0
- data/lib/request_log_analyzer/tracker.rb +139 -32
- data/lib/request_log_analyzer.rb +5 -5
- data/request-log-analyzer.gemspec +19 -15
- data/spec/database.yml +6 -0
- data/spec/fixtures/rails_22.log +1 -1
- data/spec/integration/command_line_usage_spec.rb +7 -1
- data/spec/lib/helpers.rb +7 -7
- data/spec/lib/macros.rb +3 -3
- data/spec/lib/matchers.rb +41 -27
- data/spec/lib/mocks.rb +19 -15
- data/spec/lib/testing_format.rb +9 -9
- data/spec/spec_helper.rb +6 -6
- data/spec/unit/aggregator/database_inserter_spec.rb +16 -16
- data/spec/unit/aggregator/summarizer_spec.rb +4 -4
- data/spec/unit/controller/controller_spec.rb +2 -2
- data/spec/unit/controller/log_processor_spec.rb +1 -1
- data/spec/unit/database/base_class_spec.rb +26 -33
- data/spec/unit/database/connection_spec.rb +3 -3
- data/spec/unit/database/database_spec.rb +33 -38
- data/spec/unit/file_format/amazon_s3_format_spec.rb +5 -5
- data/spec/unit/file_format/apache_format_spec.rb +48 -11
- data/spec/unit/file_format/file_format_api_spec.rb +13 -13
- data/spec/unit/file_format/line_definition_spec.rb +24 -17
- data/spec/unit/file_format/merb_format_spec.rb +41 -45
- data/spec/unit/file_format/rails_format_spec.rb +157 -117
- data/spec/unit/filter/anonymize_filter_spec.rb +2 -2
- data/spec/unit/filter/field_filter_spec.rb +13 -13
- data/spec/unit/filter/filter_spec.rb +1 -1
- data/spec/unit/filter/timespan_filter_spec.rb +15 -15
- data/spec/unit/mailer_spec.rb +30 -0
- data/spec/unit/{source/request_spec.rb → request_spec.rb} +30 -30
- data/spec/unit/source/log_parser_spec.rb +27 -27
- data/spec/unit/tracker/duration_tracker_spec.rb +115 -78
- data/spec/unit/tracker/frequency_tracker_spec.rb +74 -63
- data/spec/unit/tracker/hourly_spread_spec.rb +28 -20
- data/spec/unit/tracker/timespan_tracker_spec.rb +25 -13
- data/spec/unit/tracker/tracker_api_spec.rb +117 -42
- data/spec/unit/tracker/traffic_tracker_spec.rb +107 -0
- data/tasks/github-gem.rake +125 -75
- data/tasks/request_log_analyzer.rake +2 -2
- metadata +19 -10
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
module RequestLogAnalyzer::Aggregator
|
|
2
2
|
|
|
3
3
|
class Summarizer < Base
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
class Definer
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
attr_reader :trackers
|
|
8
8
|
|
|
9
9
|
# Initialize tracker array
|
|
@@ -16,17 +16,17 @@ module RequestLogAnalyzer::Aggregator
|
|
|
16
16
|
def initialize_copy(other)
|
|
17
17
|
@trackers = other.trackers.dup
|
|
18
18
|
end
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
# Drop all trackers
|
|
21
21
|
def reset!
|
|
22
22
|
@trackers = []
|
|
23
23
|
end
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
# Include missing trackers through method missing.
|
|
26
26
|
def method_missing(tracker_method, *args)
|
|
27
27
|
track(tracker_method, *args)
|
|
28
28
|
end
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
# Track the frequency of a specific category
|
|
31
31
|
# <tt>category_field</tt> Field to track
|
|
32
32
|
# <tt>options</tt> options are passed to new frequency tracker
|
|
@@ -37,7 +37,7 @@ module RequestLogAnalyzer::Aggregator
|
|
|
37
37
|
track(:frequency, category_field.merge(options))
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
# Track the duration of a specific category
|
|
42
42
|
# <tt>duration_field</tt> Field to track
|
|
43
43
|
# <tt>options</tt> options are passed to new frequency tracker
|
|
@@ -45,10 +45,10 @@ module RequestLogAnalyzer::Aggregator
|
|
|
45
45
|
if duration_field.kind_of?(Symbol)
|
|
46
46
|
track(:duration, options.merge(:duration => duration_field))
|
|
47
47
|
elsif duration_field.kind_of?(Hash)
|
|
48
|
-
track(:duration, duration_field.merge(options))
|
|
48
|
+
track(:duration, duration_field.merge(options))
|
|
49
49
|
end
|
|
50
|
-
end
|
|
51
|
-
|
|
50
|
+
end
|
|
51
|
+
|
|
52
52
|
# Helper function to initialize a tracker and add it to the tracker array.
|
|
53
53
|
# <tt>tracker_class</tt> The class to include
|
|
54
54
|
# <tt>optiont</tt> The options to pass to the trackers.
|
|
@@ -57,10 +57,10 @@ module RequestLogAnalyzer::Aggregator
|
|
|
57
57
|
@trackers << tracker_klass.new(options)
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
attr_reader :trackers
|
|
62
62
|
attr_reader :warnings_encountered
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
# Initialize summarizer.
|
|
65
65
|
# Generate trackers from speciefied source.file_format.report_trackers and set them up
|
|
66
66
|
def initialize(source, options = {})
|
|
@@ -69,16 +69,16 @@ module RequestLogAnalyzer::Aggregator
|
|
|
69
69
|
@trackers = source.file_format.report_trackers
|
|
70
70
|
setup
|
|
71
71
|
end
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
def setup
|
|
74
74
|
end
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
# Call prepare on all trackers.
|
|
77
77
|
def prepare
|
|
78
78
|
raise "No trackers set up in Summarizer!" if @trackers.nil? || @trackers.empty?
|
|
79
79
|
@trackers.each { |tracker| tracker.prepare }
|
|
80
80
|
end
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
# Pass all requests to trackers and let them update if necessary.
|
|
83
83
|
# <tt>request</tt> The request to pass.
|
|
84
84
|
def aggregate(request)
|
|
@@ -86,19 +86,19 @@ module RequestLogAnalyzer::Aggregator
|
|
|
86
86
|
tracker.update(request) if tracker.should_update?(request)
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
|
-
|
|
89
|
+
|
|
90
90
|
# Call finalize on all trackers. Saves a YAML dump if this is set in the options.
|
|
91
91
|
def finalize
|
|
92
92
|
@trackers.each { |tracker| tracker.finalize }
|
|
93
93
|
save_results_dump(options[:dump]) if options[:dump]
|
|
94
94
|
end
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
# Saves the results of all the trackers in YAML format to a file.
|
|
97
97
|
# <tt>filename</tt> The file to store the YAML dump in.
|
|
98
98
|
def save_results_dump(filename)
|
|
99
99
|
File.open(filename, 'w') { |file| file.write(to_yaml) }
|
|
100
100
|
end
|
|
101
|
-
|
|
101
|
+
|
|
102
102
|
# Exports all the tracker results to YAML. It will call the to_yaml_object method
|
|
103
103
|
# for every tracker and combines these into a single YAML export.
|
|
104
104
|
def to_yaml
|
|
@@ -108,7 +108,7 @@ module RequestLogAnalyzer::Aggregator
|
|
|
108
108
|
end
|
|
109
109
|
YAML::dump(trackers_export)
|
|
110
110
|
end
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
# Call report on all trackers.
|
|
113
113
|
# <tt>output</tt> RequestLogAnalyzer::Output object to output to
|
|
114
114
|
def report(output)
|
|
@@ -121,13 +121,13 @@ module RequestLogAnalyzer::Aggregator
|
|
|
121
121
|
end
|
|
122
122
|
report_footer(output)
|
|
123
123
|
end
|
|
124
|
-
|
|
124
|
+
|
|
125
125
|
# Generate report header.
|
|
126
126
|
# <tt>output</tt> RequestLogAnalyzer::Output object to output to
|
|
127
127
|
def report_header(output)
|
|
128
128
|
output.title("Request summary")
|
|
129
|
-
|
|
130
|
-
output.with_style(:cell_separator => false) do
|
|
129
|
+
|
|
130
|
+
output.with_style(:cell_separator => false) do
|
|
131
131
|
output.table({:width => 20}, {:font => :bold}) do |rows|
|
|
132
132
|
rows << ['Parsed lines:', source.parsed_lines]
|
|
133
133
|
rows << ['Skipped lines:', source.skipped_lines]
|
|
@@ -138,13 +138,13 @@ module RequestLogAnalyzer::Aggregator
|
|
|
138
138
|
end
|
|
139
139
|
output << "\n"
|
|
140
140
|
end
|
|
141
|
-
|
|
141
|
+
|
|
142
142
|
# Generate report footer.
|
|
143
143
|
# <tt>output</tt> RequestLogAnalyzer::Output object to output to
|
|
144
144
|
def report_footer(output)
|
|
145
145
|
if has_log_ordering_warnings?
|
|
146
146
|
output.title("Parse warnings")
|
|
147
|
-
|
|
147
|
+
|
|
148
148
|
output.puts "Parseable lines were ancountered without a header line before it. It"
|
|
149
149
|
output.puts "could be that logging is not setup correctly for your application."
|
|
150
150
|
output.puts "Visit this website for logging configuration tips:"
|
|
@@ -152,17 +152,17 @@ module RequestLogAnalyzer::Aggregator
|
|
|
152
152
|
output.puts
|
|
153
153
|
end
|
|
154
154
|
end
|
|
155
|
-
|
|
155
|
+
|
|
156
156
|
# Returns true if there were any warnings generated by the trackers
|
|
157
157
|
def has_warnings?
|
|
158
158
|
@warnings_encountered.inject(0) { |result, (key, value)| result += value } > 0
|
|
159
159
|
end
|
|
160
|
-
|
|
160
|
+
|
|
161
161
|
# Returns true if there were any log ordering warnings
|
|
162
162
|
def has_log_ordering_warnings?
|
|
163
163
|
@warnings_encountered[:no_current_request] && @warnings_encountered[:no_current_request] > 0
|
|
164
164
|
end
|
|
165
|
-
|
|
165
|
+
|
|
166
166
|
# Store an encountered warning
|
|
167
167
|
# <tt>type</tt> Type of warning
|
|
168
168
|
# <tt>message</tt> Warning message
|
|
@@ -1,50 +1,46 @@
|
|
|
1
1
|
module RequestLogAnalyzer::Aggregator
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
def self.const_missing(const)
|
|
4
4
|
RequestLogAnalyzer::load_default_class_file(self, const)
|
|
5
5
|
end
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
# The base class of an aggregator. This class provides the interface to which
|
|
8
8
|
# every aggregator should comply (by simply subclassing this class).
|
|
9
9
|
class Base
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
attr_reader :options
|
|
14
|
-
attr_reader :source
|
|
10
|
+
|
|
11
|
+
attr_reader :options, :source
|
|
15
12
|
|
|
16
13
|
# Intializes a new RequestLogAnalyzer::Aggregator::Base instance
|
|
17
14
|
# It will include the specific file format module.
|
|
18
15
|
def initialize(source, options = {})
|
|
19
16
|
@source = source
|
|
20
|
-
self.register_file_format(source.file_format)
|
|
21
17
|
@options = options
|
|
22
18
|
end
|
|
23
19
|
|
|
24
|
-
# The prepare function is called just before parsing starts. This function
|
|
20
|
+
# The prepare function is called just before parsing starts. This function
|
|
25
21
|
# can be used to initialie variables, etc.
|
|
26
22
|
def prepare
|
|
27
23
|
end
|
|
28
|
-
|
|
24
|
+
|
|
29
25
|
# The aggregate function is called for every request.
|
|
30
26
|
# Implement the aggregating functionality in this method
|
|
31
27
|
def aggregate(request)
|
|
32
28
|
end
|
|
33
|
-
|
|
29
|
+
|
|
34
30
|
# The finalize function is called after all sources are parsed and no more
|
|
35
31
|
# requests will be passed to the aggregator
|
|
36
32
|
def finalize
|
|
37
33
|
end
|
|
38
|
-
|
|
34
|
+
|
|
39
35
|
# The warning method is called if the parser eits a warning.
|
|
40
36
|
def warning(type, message, lineno)
|
|
41
|
-
end
|
|
42
|
-
|
|
37
|
+
end
|
|
38
|
+
|
|
43
39
|
# The report function is called at the end. Implement any result reporting
|
|
44
40
|
# in this function.
|
|
45
41
|
def report(output)
|
|
46
42
|
end
|
|
47
|
-
|
|
43
|
+
|
|
48
44
|
# The source_change function gets called when handling a source is started or finished.
|
|
49
45
|
def source_change(change, filename)
|
|
50
46
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module RequestLogAnalyzer
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
# The RequestLogAnalyzer::Controller class creates a LogParser instance for the
|
|
4
4
|
# requested file format, and connect it with sources and aggregators.
|
|
5
5
|
#
|
|
@@ -17,49 +17,44 @@ module RequestLogAnalyzer
|
|
|
17
17
|
# from several logrotated log files.
|
|
18
18
|
class Controller
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
attr_reader :aggregators
|
|
23
|
-
attr_reader :filters
|
|
24
|
-
attr_reader :log_parser
|
|
25
|
-
attr_reader :source
|
|
26
|
-
attr_reader :output
|
|
27
|
-
attr_reader :options
|
|
20
|
+
attr_reader :source, :filters, :aggregators, :output, :options
|
|
28
21
|
|
|
29
22
|
# Builds a RequestLogAnalyzer::Controller given parsed command line arguments
|
|
30
23
|
# <tt>arguments<tt> A CommandLine::Arguments hash containing parsed commandline parameters.
|
|
31
24
|
# <rr>report_with</tt> Width of the report. Defaults to 80.
|
|
32
|
-
def self.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
25
|
+
def self.build_from_arguments(arguments)
|
|
26
|
+
|
|
27
|
+
options = {}
|
|
28
|
+
|
|
29
|
+
# Copy fields
|
|
30
|
+
options[:database] = arguments[:database]
|
|
37
31
|
options[:reset_database] = arguments[:reset_database]
|
|
38
|
-
options[:debug]
|
|
39
|
-
options[:dump]
|
|
32
|
+
options[:debug] = arguments[:debug]
|
|
33
|
+
options[:dump] = arguments[:dump]
|
|
40
34
|
options[:parse_strategy] = arguments[:parse_strategy]
|
|
41
35
|
options[:no_progress] = arguments[:no_progress]
|
|
36
|
+
options[:format] = arguments[:format]
|
|
37
|
+
options[:output] = arguments[:output]
|
|
38
|
+
options[:file] = arguments[:file]
|
|
39
|
+
options[:format] = arguments[:format]
|
|
40
|
+
options[:after] = arguments[:after]
|
|
41
|
+
options[:before] = arguments[:before]
|
|
42
|
+
options[:reject] = arguments[:reject]
|
|
43
|
+
options[:select] = arguments[:select]
|
|
44
|
+
options[:boring] = arguments[:boring]
|
|
45
|
+
options[:aggregator] = arguments[:aggregator]
|
|
46
|
+
options[:report_width] = arguments[:report_width]
|
|
47
|
+
options[:report_sort] = arguments[:report_sort]
|
|
48
|
+
options[:report_amount] = arguments[:report_amount]
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
if arguments[:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
output_mail = RequestLogAnalyzer::Mailer.new(arguments[:mail])
|
|
49
|
-
options[:output] = output_class.new(output_mail, :width => 80, :color => false, :characters => :ascii)
|
|
50
|
-
else
|
|
51
|
-
options[:output] = output_class.new(STDOUT, :width => arguments[:report_width].to_i,
|
|
52
|
-
:color => !arguments[:boring], :characters => (arguments[:boring] ? :ascii : :utf))
|
|
50
|
+
# Apache format workaround
|
|
51
|
+
if arguments[:rails_format]
|
|
52
|
+
options[:format] = {:rails => arguments[:rails_format]}
|
|
53
|
+
elsif arguments[:apache_format]
|
|
54
|
+
options[:format] = {:apache => arguments[:apache_format]}
|
|
53
55
|
end
|
|
54
|
-
|
|
55
|
-
#
|
|
56
|
-
file_format = if arguments[:apache_format]
|
|
57
|
-
RequestLogAnalyzer::FileFormat.load(:apache, arguments[:apache_format])
|
|
58
|
-
else
|
|
59
|
-
RequestLogAnalyzer::FileFormat.load(arguments[:format])
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# register sources
|
|
56
|
+
|
|
57
|
+
# Register sources
|
|
63
58
|
if arguments.parameters.length == 1
|
|
64
59
|
file = arguments.parameters[0]
|
|
65
60
|
if file == '-' || file == 'STDIN'
|
|
@@ -74,49 +69,128 @@ module RequestLogAnalyzer
|
|
|
74
69
|
options.store(:source_files, arguments.parameters)
|
|
75
70
|
end
|
|
76
71
|
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
build(options)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Build a new controller using parameters (Base for new API)
|
|
76
|
+
# <tt>source</tt> The source file
|
|
77
|
+
# Options are passd on to the LogParser.
|
|
78
|
+
#
|
|
79
|
+
# Options
|
|
80
|
+
# * <tt>:database</tt> Database file
|
|
81
|
+
# * <tt>:reset_database</tt>
|
|
82
|
+
# * <tt>:debug</tt> Enables echo aggregator.
|
|
83
|
+
# * <tt>:dump</tt>
|
|
84
|
+
# * <tt>:parse_strategy</tt>
|
|
85
|
+
# * <tt>:no_progress</tt>
|
|
86
|
+
# * <tt>:output</tt> :fixed_width, :html or Output class. Defaults to fixed width.
|
|
87
|
+
# * <tt>:file</tt> Filestring or File or StringIO
|
|
88
|
+
# * <tt>:format</tt> :rails, {:apache => 'FORMATSTRING'}, :merb, etcetera or Format Class. Defaults to :rails.
|
|
89
|
+
# * <tt>:source_files</tt> File or STDIN
|
|
90
|
+
# * <tt>:after</tt> Drop all requests after this date (Date, DateTime, Time, or a String in "YYYY-MM-DD hh:mm:ss" format)
|
|
91
|
+
# * <tt>:before</tt> Drop all requests before this date (Date, DateTime, Time, or a String in "YYYY-MM-DD hh:mm:ss" format)
|
|
92
|
+
# * <tt>:reject</tt> Reject specific {:field => :value} combination. Expects single hash.
|
|
93
|
+
# * <tt>:select</tt> Select specific {:field => :value} combination. Expects single hash.
|
|
94
|
+
# * <tt>:aggregator</tt> Array of aggregators (ATM: STRINGS OR SYMBOLS ONLY!). Defaults to [:summarizer
|
|
95
|
+
# * <tt>:boring</tt> Do not show color on STDOUT. Defaults to False.
|
|
96
|
+
# * <tt>:report_width</tt> Width or reports in characters. Defaults to 80.
|
|
97
|
+
#
|
|
98
|
+
# TODO:
|
|
99
|
+
# Check if defaults work (Aggregator defaults seem wrong).
|
|
100
|
+
# Refactor :database => options[:database], :dump => options[:dump] away from contoller intialization.
|
|
101
|
+
def self.build(options)
|
|
102
|
+
# Defaults
|
|
103
|
+
options[:output] ||= :fixed_width
|
|
104
|
+
options[:format] ||= :rails
|
|
105
|
+
options[:aggregator] ||= [:summarizer]
|
|
106
|
+
options[:report_width] ||= 80
|
|
107
|
+
options[:report_amount] ||= 20
|
|
108
|
+
options[:report_sort] ||= 'sum,mean'
|
|
109
|
+
options[:boring] ||= false
|
|
110
|
+
|
|
111
|
+
# Set the output class
|
|
112
|
+
output_args = {}
|
|
113
|
+
output_object = nil
|
|
114
|
+
if options[:output].is_a? Class
|
|
115
|
+
output_class = options[:output]
|
|
116
|
+
else
|
|
117
|
+
output_class = RequestLogAnalyzer::Output::const_get(options[:output])
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
output_sort = options[:report_sort].split(',').map { |s| s.to_sym }
|
|
121
|
+
output_amount = options[:report_amount] == 'all' ? :all : options[:report_amount].to_i
|
|
122
|
+
|
|
123
|
+
if options[:file]
|
|
124
|
+
output_object = %w[File StringIO].include?(options[:file].class.name) ? options[:file] : File.new(options[:file], "w+")
|
|
125
|
+
output_args = {:width => 80, :color => false, :characters => :ascii, :sort => output_sort, :amount => output_amount }
|
|
126
|
+
elsif options[:mail]
|
|
127
|
+
output_object = RequestLogAnalyzer::Mailer.new(arguments[:mail])
|
|
128
|
+
output_args = {:width => 80, :color => false, :characters => :ascii, :sort => output_sort, :amount => output_amount }
|
|
129
|
+
else
|
|
130
|
+
output_object = STDOUT
|
|
131
|
+
output_args = {:width => options[:report_width].to_i, :color => !options[:boring],
|
|
132
|
+
:characters => (options[:boring] ? :ascii : :utf), :sort => output_sort, :amount => output_amount }
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
output_instance = output_class.new(output_object, output_args)
|
|
136
|
+
|
|
137
|
+
# Create the controller with the correct file format
|
|
138
|
+
if options[:format].kind_of?(Hash)
|
|
139
|
+
file_format = RequestLogAnalyzer::FileFormat.load(options[:format].keys[0], options[:format].values[0])
|
|
140
|
+
else
|
|
141
|
+
file_format = RequestLogAnalyzer::FileFormat.load(options[:format])
|
|
142
|
+
end
|
|
79
143
|
|
|
144
|
+
# Kickstart the controller
|
|
145
|
+
controller = Controller.new( RequestLogAnalyzer::Source::LogParser.new(file_format, :source_files => options[:source_files]),
|
|
146
|
+
{ :output => output_instance,
|
|
147
|
+
:database => options[:database], # FUGLY!
|
|
148
|
+
:dump => options[:dump],
|
|
149
|
+
:reset_database => options[:reset_database]})
|
|
150
|
+
|
|
80
151
|
# register filters
|
|
81
|
-
if
|
|
152
|
+
if options[:after] || options[:before]
|
|
82
153
|
filter_options = {}
|
|
83
|
-
|
|
84
|
-
|
|
154
|
+
[:after, :before].each do |filter|
|
|
155
|
+
case options[filter]
|
|
156
|
+
when Date, DateTime, Time
|
|
157
|
+
filter_options[filter] = options[filter]
|
|
158
|
+
when String
|
|
159
|
+
filter_options[filter] = DateTime.parse(options[filter])
|
|
160
|
+
end
|
|
161
|
+
end
|
|
85
162
|
controller.add_filter(:timespan, filter_options)
|
|
86
163
|
end
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
164
|
+
|
|
165
|
+
if options[:reject]
|
|
166
|
+
options[:reject].each do |(field, value)|
|
|
167
|
+
controller.add_filter(:field, :mode => :reject, :field => field, :value => value)
|
|
168
|
+
end
|
|
90
169
|
end
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
170
|
+
|
|
171
|
+
if options[:reject]
|
|
172
|
+
options[:select].each do |(field, value)|
|
|
173
|
+
controller.add_filter(:field, :mode => :select, :field => field, :value => value)
|
|
174
|
+
end
|
|
94
175
|
end
|
|
95
176
|
|
|
96
177
|
# register aggregators
|
|
97
|
-
|
|
178
|
+
options[:aggregator].each { |agg| controller.add_aggregator(agg.to_sym) }
|
|
179
|
+
controller.add_aggregator(:summarizer) if options[:aggregator].empty?
|
|
180
|
+
controller.add_aggregator(:echo) if options[:debug]
|
|
181
|
+
controller.add_aggregator(:database_inserter) if options[:database] && !options[:aggregator].include?('database')
|
|
98
182
|
|
|
99
|
-
# register the database
|
|
100
|
-
controller.add_aggregator(:summarizer) if arguments[:aggregator].empty?
|
|
101
|
-
controller.add_aggregator(:database_inserter) if arguments[:database] && !arguments[:aggregator].include?('database')
|
|
102
|
-
|
|
103
|
-
# register the echo aggregator in debug mode
|
|
104
|
-
controller.add_aggregator(:echo) if arguments[:debug]
|
|
105
|
-
|
|
106
183
|
file_format.setup_environment(controller)
|
|
107
|
-
|
|
108
184
|
return controller
|
|
109
|
-
end
|
|
185
|
+
end
|
|
110
186
|
|
|
111
187
|
# Builds a new Controller for the given log file format.
|
|
112
188
|
# <tt>format</tt> Logfile format. Defaults to :rails
|
|
113
189
|
# Options are passd on to the LogParser.
|
|
114
|
-
# * <tt>:aggregator</tt> Aggregator array.
|
|
115
190
|
# * <tt>:database</tt> Database the controller should use.
|
|
116
|
-
# * <tt>:
|
|
117
|
-
# * <tt>:silent</tt> Do not output any warnings.
|
|
118
|
-
# * <tt>:colorize</tt> Colorize output
|
|
191
|
+
# * <tt>:dump</tt> Yaml Dump the contrller should use.
|
|
119
192
|
# * <tt>:output</tt> All report outputs get << through this output.
|
|
193
|
+
# * <tt>:no_progress</tt> No progress bar
|
|
120
194
|
def initialize(source, options = {})
|
|
121
195
|
|
|
122
196
|
@source = source
|
|
@@ -124,20 +198,17 @@ module RequestLogAnalyzer
|
|
|
124
198
|
@aggregators = []
|
|
125
199
|
@filters = []
|
|
126
200
|
@output = options[:output]
|
|
201
|
+
@interrupted = false
|
|
127
202
|
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
# Pass all warnings to every aggregator so they can do something useful with them.
|
|
132
|
-
@source.warning = lambda { |type, message, lineno| @aggregators.each { |agg| agg.warning(type, message, lineno) } } if @source
|
|
203
|
+
# Register the request format for this session after checking its validity
|
|
204
|
+
raise "Invalid file format!" unless @source.file_format.valid?
|
|
133
205
|
|
|
134
|
-
#
|
|
135
|
-
@source.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
@source.source_changes = lambda { |change, filename| handle_source_change(change, filename) } if @source
|
|
206
|
+
# Install event handlers for wrnings, progress updates and source changes
|
|
207
|
+
@source.warning = lambda { |type, message, lineno| @aggregators.each { |agg| agg.warning(type, message, lineno) } }
|
|
208
|
+
@source.progress = lambda { |message, value| handle_progress(message, value) } unless options[:no_progress]
|
|
209
|
+
@source.source_changes = lambda { |change, filename| handle_source_change(change, filename) }
|
|
139
210
|
end
|
|
140
|
-
|
|
211
|
+
|
|
141
212
|
# Progress function.
|
|
142
213
|
# Expects :started with file, :progress with current line and :finished or :interrupted when done.
|
|
143
214
|
# <tt>message</tt> Current state (:started, :finished, :interupted or :progress).
|
|
@@ -158,46 +229,46 @@ module RequestLogAnalyzer
|
|
|
158
229
|
@progress_bar.set(value)
|
|
159
230
|
end
|
|
160
231
|
end
|
|
161
|
-
|
|
232
|
+
|
|
162
233
|
# Source change handler
|
|
163
234
|
def handle_source_change(change, filename)
|
|
164
235
|
@aggregators.each { |agg| agg.source_change(change, File.expand_path(filename, Dir.pwd)) }
|
|
165
236
|
end
|
|
166
|
-
|
|
167
|
-
# Adds an aggregator to the controller. The aggregator will be called for every request
|
|
237
|
+
|
|
238
|
+
# Adds an aggregator to the controller. The aggregator will be called for every request
|
|
168
239
|
# that is parsed from the provided sources (see add_source)
|
|
169
|
-
def add_aggregator(agg)
|
|
240
|
+
def add_aggregator(agg)
|
|
170
241
|
agg = RequestLogAnalyzer::Aggregator.const_get(RequestLogAnalyzer::to_camelcase(agg)) if agg.kind_of?(Symbol)
|
|
171
242
|
@aggregators << agg.new(@source, @options)
|
|
172
243
|
end
|
|
173
|
-
|
|
244
|
+
|
|
174
245
|
alias :>> :add_aggregator
|
|
175
|
-
|
|
246
|
+
|
|
176
247
|
# Adds a request filter to the controller.
|
|
177
248
|
def add_filter(filter, filter_options = {})
|
|
178
249
|
filter = RequestLogAnalyzer::Filter.const_get(RequestLogAnalyzer::to_camelcase(filter)) if filter.kind_of?(Symbol)
|
|
179
|
-
@filters << filter.new(file_format, @options.merge(filter_options))
|
|
250
|
+
@filters << filter.new(source.file_format, @options.merge(filter_options))
|
|
180
251
|
end
|
|
181
|
-
|
|
252
|
+
|
|
182
253
|
# Push a request through the entire filterchain (@filters).
|
|
183
254
|
# <tt>request</tt> The request to filter.
|
|
184
255
|
# Returns the filtered request or nil.
|
|
185
256
|
def filter_request(request)
|
|
186
|
-
@filters.each do |filter|
|
|
257
|
+
@filters.each do |filter|
|
|
187
258
|
request = filter.filter(request)
|
|
188
259
|
return nil if request.nil?
|
|
189
260
|
end
|
|
190
261
|
return request
|
|
191
262
|
end
|
|
192
|
-
|
|
263
|
+
|
|
193
264
|
# Push a request to all the aggregators (@aggregators).
|
|
194
|
-
# <tt>request</tt> The request to push to the aggregators.
|
|
265
|
+
# <tt>request</tt> The request to push to the aggregators.
|
|
195
266
|
def aggregate_request(request)
|
|
196
267
|
return false unless request
|
|
197
268
|
@aggregators.each { |agg| agg.aggregate(request) }
|
|
198
269
|
return true
|
|
199
270
|
end
|
|
200
|
-
|
|
271
|
+
|
|
201
272
|
# Runs RequestLogAnalyzer
|
|
202
273
|
# 1. Call prepare on every aggregator
|
|
203
274
|
# 2. Generate requests from source object
|
|
@@ -207,10 +278,12 @@ module RequestLogAnalyzer
|
|
|
207
278
|
# 5. Call report on every aggregator
|
|
208
279
|
# 6. Finalize Source
|
|
209
280
|
def run!
|
|
210
|
-
|
|
281
|
+
|
|
282
|
+
# @aggregators.each{|agg| p agg}
|
|
283
|
+
|
|
211
284
|
@aggregators.each { |agg| agg.prepare }
|
|
212
285
|
install_signal_handlers
|
|
213
|
-
|
|
286
|
+
|
|
214
287
|
@source.each_request do |request|
|
|
215
288
|
break if @interrupted
|
|
216
289
|
aggregate_request(filter_request(request))
|
|
@@ -221,9 +294,9 @@ module RequestLogAnalyzer
|
|
|
221
294
|
@output.header
|
|
222
295
|
@aggregators.each { |agg| agg.report(@output) }
|
|
223
296
|
@output.footer
|
|
224
|
-
|
|
297
|
+
|
|
225
298
|
@source.finalize
|
|
226
|
-
|
|
299
|
+
|
|
227
300
|
if @output.io.kind_of?(File)
|
|
228
301
|
puts
|
|
229
302
|
puts "Report written to: " + File.expand_path(@output.io.path)
|
|
@@ -235,7 +308,7 @@ module RequestLogAnalyzer
|
|
|
235
308
|
@output.io.mail
|
|
236
309
|
end
|
|
237
310
|
end
|
|
238
|
-
|
|
311
|
+
|
|
239
312
|
def install_signal_handlers
|
|
240
313
|
Signal.trap("INT") do
|
|
241
314
|
handle_progress(:interrupted)
|
|
@@ -243,6 +316,6 @@ module RequestLogAnalyzer
|
|
|
243
316
|
@interrupted = true
|
|
244
317
|
end
|
|
245
318
|
end
|
|
246
|
-
|
|
319
|
+
|
|
247
320
|
end
|
|
248
321
|
end
|