request-log-analyzer 1.13.1 → 1.13.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/bin/console +17 -0
- data/lib/cli/command_line_arguments.rb +29 -36
- data/lib/cli/database_console.rb +1 -3
- data/lib/cli/database_console_init.rb +11 -11
- data/lib/cli/progressbar.rb +30 -32
- data/lib/cli/tools.rb +20 -23
- data/lib/request_log_analyzer.rb +8 -8
- data/lib/request_log_analyzer/aggregator.rb +4 -7
- data/lib/request_log_analyzer/aggregator/database_inserter.rb +10 -13
- data/lib/request_log_analyzer/aggregator/echo.rb +5 -7
- data/lib/request_log_analyzer/aggregator/summarizer.rb +15 -18
- data/lib/request_log_analyzer/class_level_inheritable_attributes.rb +23 -0
- data/lib/request_log_analyzer/controller.rb +36 -42
- data/lib/request_log_analyzer/database.rb +4 -6
- data/lib/request_log_analyzer/database/base.rb +39 -41
- data/lib/request_log_analyzer/database/connection.rb +8 -10
- data/lib/request_log_analyzer/database/request.rb +1 -3
- data/lib/request_log_analyzer/database/source.rb +0 -2
- data/lib/request_log_analyzer/database/warning.rb +4 -6
- data/lib/request_log_analyzer/file_format.rb +46 -49
- data/lib/request_log_analyzer/file_format/amazon_s3.rb +15 -19
- data/lib/request_log_analyzer/file_format/apache.rb +42 -45
- data/lib/request_log_analyzer/file_format/delayed_job.rb +13 -15
- data/lib/request_log_analyzer/file_format/delayed_job2.rb +9 -11
- data/lib/request_log_analyzer/file_format/delayed_job21.rb +9 -11
- data/lib/request_log_analyzer/file_format/delayed_job3.rb +5 -8
- data/lib/request_log_analyzer/file_format/delayed_job4.rb +5 -8
- data/lib/request_log_analyzer/file_format/haproxy.rb +44 -48
- data/lib/request_log_analyzer/file_format/merb.rb +13 -17
- data/lib/request_log_analyzer/file_format/mysql.rb +21 -25
- data/lib/request_log_analyzer/file_format/nginx.rb +0 -2
- data/lib/request_log_analyzer/file_format/oink.rb +30 -31
- data/lib/request_log_analyzer/file_format/postgresql.rb +11 -15
- data/lib/request_log_analyzer/file_format/rack.rb +0 -2
- data/lib/request_log_analyzer/file_format/rails.rb +100 -104
- data/lib/request_log_analyzer/file_format/rails3.rb +19 -23
- data/lib/request_log_analyzer/file_format/rails_development.rb +0 -1
- data/lib/request_log_analyzer/file_format/w3c.rb +16 -18
- data/lib/request_log_analyzer/filter.rb +0 -2
- data/lib/request_log_analyzer/filter/anonymize.rb +4 -7
- data/lib/request_log_analyzer/filter/field.rb +3 -6
- data/lib/request_log_analyzer/filter/timespan.rb +2 -6
- data/lib/request_log_analyzer/line_definition.rb +16 -19
- data/lib/request_log_analyzer/log_processor.rb +10 -14
- data/lib/request_log_analyzer/mailer.rb +9 -12
- data/lib/request_log_analyzer/output.rb +12 -14
- data/lib/request_log_analyzer/output/fixed_width.rb +21 -28
- data/lib/request_log_analyzer/output/html.rb +11 -14
- data/lib/request_log_analyzer/request.rb +53 -33
- data/lib/request_log_analyzer/source.rb +2 -5
- data/lib/request_log_analyzer/source/log_parser.rb +9 -16
- data/lib/request_log_analyzer/tracker.rb +10 -12
- data/lib/request_log_analyzer/tracker/duration.rb +4 -6
- data/lib/request_log_analyzer/tracker/frequency.rb +9 -11
- data/lib/request_log_analyzer/tracker/hourly_spread.rb +8 -11
- data/lib/request_log_analyzer/tracker/numeric_value.rb +40 -44
- data/lib/request_log_analyzer/tracker/timespan.rb +5 -8
- data/lib/request_log_analyzer/tracker/traffic.rb +8 -10
- data/lib/request_log_analyzer/version.rb +1 -1
- data/request-log-analyzer.gemspec +6 -6
- data/spec/integration/command_line_usage_spec.rb +33 -33
- data/spec/integration/mailer_spec.rb +181 -185
- data/spec/integration/munin_plugins_rails_spec.rb +20 -20
- data/spec/integration/scout_spec.rb +40 -41
- data/spec/lib/helpers.rb +8 -9
- data/spec/lib/macros.rb +2 -4
- data/spec/lib/matchers.rb +20 -25
- data/spec/lib/mocks.rb +10 -11
- data/spec/lib/testing_format.rb +8 -10
- data/spec/spec_helper.rb +5 -1
- data/spec/unit/aggregator/database_inserter_spec.rb +23 -23
- data/spec/unit/aggregator/summarizer_spec.rb +7 -7
- data/spec/unit/controller/controller_spec.rb +14 -14
- data/spec/unit/controller/log_processor_spec.rb +3 -3
- data/spec/unit/database/base_class_spec.rb +36 -37
- data/spec/unit/database/connection_spec.rb +10 -10
- data/spec/unit/database/database_spec.rb +11 -11
- data/spec/unit/file_format/amazon_s3_format_spec.rb +66 -62
- data/spec/unit/file_format/apache_format_spec.rb +57 -52
- data/spec/unit/file_format/common_regular_expressions_spec.rb +18 -21
- data/spec/unit/file_format/delayed_job21_format_spec.rb +22 -16
- data/spec/unit/file_format/delayed_job2_format_spec.rb +22 -16
- data/spec/unit/file_format/delayed_job3_format_spec.rb +14 -10
- data/spec/unit/file_format/delayed_job4_format_spec.rb +14 -10
- data/spec/unit/file_format/delayed_job_format_spec.rb +12 -12
- data/spec/unit/file_format/file_format_api_spec.rb +19 -19
- data/spec/unit/file_format/format_autodetection_spec.rb +7 -7
- data/spec/unit/file_format/haproxy_format_spec.rb +53 -49
- data/spec/unit/file_format/inheritance_spec.rb +13 -0
- data/spec/unit/file_format/line_definition_spec.rb +35 -33
- data/spec/unit/file_format/merb_format_spec.rb +13 -11
- data/spec/unit/file_format/mysql_format_spec.rb +24 -24
- data/spec/unit/file_format/oink_format_spec.rb +29 -29
- data/spec/unit/file_format/postgresql_format_spec.rb +9 -9
- data/spec/unit/file_format/rack_format_spec.rb +36 -31
- data/spec/unit/file_format/rails3_format_spec.rb +46 -46
- data/spec/unit/file_format/rails_format_spec.rb +52 -53
- data/spec/unit/file_format/w3c_format_spec.rb +27 -24
- data/spec/unit/filter/anonymize_filter_spec.rb +7 -7
- data/spec/unit/filter/field_filter_spec.rb +26 -26
- data/spec/unit/filter/filter_spec.rb +4 -4
- data/spec/unit/filter/timespan_filter_spec.rb +22 -22
- data/spec/unit/mailer_spec.rb +21 -21
- data/spec/unit/request_spec.rb +29 -29
- data/spec/unit/source/log_parser_spec.rb +5 -5
- data/spec/unit/tracker/duration_tracker_spec.rb +23 -23
- data/spec/unit/tracker/frequency_tracker_spec.rb +29 -30
- data/spec/unit/tracker/hourly_spread_spec.rb +35 -35
- data/spec/unit/tracker/numeric_value_tracker_spec.rb +71 -72
- data/spec/unit/tracker/timespan_tracker_spec.rb +31 -31
- data/spec/unit/tracker/tracker_api_spec.rb +43 -44
- data/spec/unit/tracker/traffic_tracker_spec.rb +7 -7
- metadata +38 -35
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
module RequestLogAnalyzer::FileFormat
|
|
2
|
-
|
|
3
2
|
# FileFormat for W3C access logs.
|
|
4
3
|
class W3c < Base
|
|
5
|
-
|
|
6
4
|
extend CommonRegularExpressions
|
|
7
5
|
|
|
8
6
|
line_definition :access do |line|
|
|
9
7
|
line.header = true
|
|
10
8
|
line.footer = true
|
|
11
9
|
line.regexp = /^(#{timestamp('%Y-%m-%d %H:%M:%S')}) (#{ip_address}) (.*) (#{ip_address}) (\d+) (\w+) (\S+) \- (\d+) (\d+) (\d+) (\d+) (.*) (\S+)/
|
|
12
|
-
|
|
10
|
+
|
|
13
11
|
line.capture(:timestamp).as(:timestamp)
|
|
14
12
|
line.capture(:remote_ip)
|
|
15
13
|
line.capture(:username).as(:nillable_string)
|
|
@@ -18,9 +16,9 @@ module RequestLogAnalyzer::FileFormat
|
|
|
18
16
|
line.capture(:method)
|
|
19
17
|
line.capture(:path).as(:path)
|
|
20
18
|
line.capture(:http_status).as(:integer)
|
|
21
|
-
line.capture(:bytes_sent).as(:traffic, :
|
|
22
|
-
line.capture(:bytes_received).as(:traffic, :
|
|
23
|
-
line.capture(:duration).as(:duration, :
|
|
19
|
+
line.capture(:bytes_sent).as(:traffic, unit: :byte)
|
|
20
|
+
line.capture(:bytes_received).as(:traffic, unit: :byte)
|
|
21
|
+
line.capture(:duration).as(:duration, unit: :msec)
|
|
24
22
|
line.capture(:user_agent)
|
|
25
23
|
line.capture(:referer)
|
|
26
24
|
end
|
|
@@ -29,24 +27,24 @@ module RequestLogAnalyzer::FileFormat
|
|
|
29
27
|
analyze.timespan
|
|
30
28
|
analyze.hourly_spread
|
|
31
29
|
|
|
32
|
-
analyze.frequency :
|
|
33
|
-
analyze.frequency :
|
|
34
|
-
|
|
35
|
-
analyze.frequency :category => :path, :title => "Most popular URIs"
|
|
30
|
+
analyze.frequency category: :http_method, title: 'HTTP methods'
|
|
31
|
+
analyze.frequency category: :http_status, title: 'HTTP statuses'
|
|
36
32
|
|
|
37
|
-
analyze.frequency :
|
|
38
|
-
analyze.frequency :category => :referer, :title => "Referers"
|
|
33
|
+
analyze.frequency category: :path, title: 'Most popular URIs'
|
|
39
34
|
|
|
40
|
-
analyze.
|
|
41
|
-
analyze.
|
|
42
|
-
|
|
35
|
+
analyze.frequency category: :user_agent, title: 'User agents'
|
|
36
|
+
analyze.frequency category: :referer, title: 'Referers'
|
|
37
|
+
|
|
38
|
+
analyze.duration duration: :duration, category: :path, title: 'Request duration'
|
|
39
|
+
analyze.traffic traffic: :bytes_sent, category: :path, title: 'Traffic out'
|
|
40
|
+
analyze.traffic traffic: :bytes_received, category: :path, title: 'Traffic in'
|
|
43
41
|
end
|
|
44
|
-
|
|
42
|
+
|
|
45
43
|
class Request < RequestLogAnalyzer::Request
|
|
46
44
|
# Do not use DateTime.parse, but parse the timestamp ourselves to return a integer
|
|
47
45
|
# to speed up parsing.
|
|
48
|
-
def convert_timestamp(value,
|
|
49
|
-
"#{value[0,4]}#{value[5,2]}#{value[8,2]}#{value[11,2]}#{value[14,2]}#{value[17,2]}".to_i
|
|
46
|
+
def convert_timestamp(value, _definition)
|
|
47
|
+
"#{value[0, 4]}#{value[5, 2]}#{value[8, 2]}#{value[11, 2]}#{value[14, 2]}#{value[17, 2]}".to_i
|
|
50
48
|
end
|
|
51
49
|
end
|
|
52
50
|
end
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
module RequestLogAnalyzer::Filter
|
|
2
|
-
|
|
3
2
|
# Filter to anonymize parsed values
|
|
4
3
|
# Options
|
|
5
4
|
# * <tt>:mode</tt> :reject or :accept.
|
|
6
5
|
# * <tt>:field</tt> Specific field to accept or reject.
|
|
7
6
|
# * <tt>:value</tt> Value that the field should match to be accepted or rejected.
|
|
8
7
|
class Anonymize < Base
|
|
9
|
-
|
|
10
8
|
def generate_random_ip
|
|
11
9
|
"#{rand(256)}.#{rand(256)}.#{rand(256)}.#{rand(256)}"
|
|
12
10
|
end
|
|
13
11
|
|
|
14
12
|
def anonymize_url(value)
|
|
15
|
-
|
|
13
|
+
value.sub(/^https?\:\/\/[A-Za-z0-9\.-]+\//, 'http://example.com/')
|
|
16
14
|
end
|
|
17
15
|
|
|
18
16
|
def fuzz(value)
|
|
@@ -26,14 +24,13 @@ module RequestLogAnalyzer::Filter
|
|
|
26
24
|
request.attributes[key] = generate_random_ip
|
|
27
25
|
elsif key == :url
|
|
28
26
|
request.attributes[key] = anonymize_url(value)
|
|
29
|
-
elsif [
|
|
30
|
-
|
|
27
|
+
elsif [:duration, :view, :db, :type, :after_filters_time, :before_filters_time,
|
|
28
|
+
:action_time].include?(key)
|
|
31
29
|
request.attributes[key] = fuzz(value)
|
|
32
30
|
end
|
|
33
31
|
end
|
|
34
32
|
|
|
35
|
-
|
|
33
|
+
request
|
|
36
34
|
end
|
|
37
35
|
end
|
|
38
|
-
|
|
39
36
|
end
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
module RequestLogAnalyzer::Filter
|
|
2
|
-
|
|
3
2
|
# Filter to select or reject a specific field
|
|
4
3
|
# Options
|
|
5
4
|
# * <tt>:mode</tt> :reject or :accept.
|
|
6
5
|
# * <tt>:field</tt> Specific field to accept or reject.
|
|
7
6
|
# * <tt>:value</tt> Value that the field should match to be accepted or rejected.
|
|
8
7
|
class Field < Base
|
|
9
|
-
|
|
10
8
|
attr_reader :field, :value, :mode
|
|
11
9
|
|
|
12
10
|
def initialize(file_format, options = {})
|
|
@@ -20,7 +18,7 @@ module RequestLogAnalyzer::Filter
|
|
|
20
18
|
@field = @options[:field].to_sym
|
|
21
19
|
|
|
22
20
|
# Convert the timestamp to the correct formats for quick timestamp comparisons
|
|
23
|
-
if @options[:value].
|
|
21
|
+
if @options[:value].is_a?(String) && @options[:value][0, 1] == '/' && @options[:value][-1, 1] == '/'
|
|
24
22
|
@value = Regexp.new(@options[:value][1..-2])
|
|
25
23
|
else
|
|
26
24
|
@value = @options[:value] # TODO: convert value?
|
|
@@ -35,8 +33,7 @@ module RequestLogAnalyzer::Filter
|
|
|
35
33
|
found_field = request.every(@field).any? { |value| @value === value.to_s }
|
|
36
34
|
return nil if !found_field && @mode == :select
|
|
37
35
|
return nil if found_field && @mode == :reject
|
|
38
|
-
|
|
36
|
+
request
|
|
39
37
|
end
|
|
40
38
|
end
|
|
41
|
-
|
|
42
|
-
end
|
|
39
|
+
end
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
module RequestLogAnalyzer::Filter
|
|
2
|
-
|
|
3
2
|
# Reject all requests not in given timespan
|
|
4
3
|
# Options
|
|
5
4
|
# * <tt>:after</tt> Only keep requests after this DateTime.
|
|
6
5
|
# * <tt>:before</tt> Only keep requests before this DateTime.
|
|
7
6
|
class Timespan < Base
|
|
8
|
-
|
|
9
7
|
attr_reader :before, :after
|
|
10
8
|
|
|
11
9
|
def initialize(file_format, options = {})
|
|
@@ -15,7 +13,6 @@ module RequestLogAnalyzer::Filter
|
|
|
15
13
|
setup_filter
|
|
16
14
|
end
|
|
17
15
|
|
|
18
|
-
|
|
19
16
|
# Convert the timestamp to the correct formats for quick timestamp comparisons.
|
|
20
17
|
# These are stored in the before and after attr_reader fields.
|
|
21
18
|
def setup_filter
|
|
@@ -38,8 +35,7 @@ module RequestLogAnalyzer::Filter
|
|
|
38
35
|
return request
|
|
39
36
|
end
|
|
40
37
|
|
|
41
|
-
|
|
38
|
+
nil
|
|
42
39
|
end
|
|
43
40
|
end
|
|
44
|
-
|
|
45
|
-
end
|
|
41
|
+
end
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
module RequestLogAnalyzer
|
|
2
|
-
|
|
3
2
|
# The line definition class is used to specify what lines should be parsed from the log file.
|
|
4
3
|
# It contains functionality to match a line against the definition and parse the information
|
|
5
4
|
# from this line. This is used by the LogParser class when parsing a log file..
|
|
6
5
|
class LineDefinition
|
|
7
|
-
|
|
8
6
|
class Definer
|
|
9
|
-
|
|
10
7
|
attr_accessor :line_definitions
|
|
11
8
|
|
|
12
9
|
def initialize
|
|
@@ -16,7 +13,7 @@ module RequestLogAnalyzer
|
|
|
16
13
|
def initialize_copy(other)
|
|
17
14
|
@line_definitions = other.line_definitions.dup
|
|
18
15
|
end
|
|
19
|
-
|
|
16
|
+
|
|
20
17
|
def define_line(name, arg = {}, &block)
|
|
21
18
|
if block_given?
|
|
22
19
|
@line_definitions[name] = RequestLogAnalyzer::LineDefinition.define(name, &block)
|
|
@@ -32,14 +29,14 @@ module RequestLogAnalyzer
|
|
|
32
29
|
|
|
33
30
|
class CaptureDefiner
|
|
34
31
|
attr_accessor :capture_hash
|
|
35
|
-
|
|
32
|
+
|
|
36
33
|
def initialize(hash)
|
|
37
34
|
@capture_hash = hash
|
|
38
35
|
end
|
|
39
|
-
|
|
36
|
+
|
|
40
37
|
def as(type, type_options = {})
|
|
41
|
-
@capture_hash.merge!(type_options.merge(:
|
|
42
|
-
|
|
38
|
+
@capture_hash.merge!(type_options.merge(type: type))
|
|
39
|
+
self
|
|
43
40
|
end
|
|
44
41
|
end
|
|
45
42
|
|
|
@@ -57,15 +54,15 @@ module RequestLogAnalyzer
|
|
|
57
54
|
@captures = []
|
|
58
55
|
@teaser = nil
|
|
59
56
|
@compound = []
|
|
60
|
-
definition.each { |key, value|
|
|
57
|
+
definition.each { |key, value| send("#{key}=".to_sym, value) }
|
|
61
58
|
end
|
|
62
59
|
|
|
63
|
-
def self.define(name, &
|
|
64
|
-
definition =
|
|
60
|
+
def self.define(name, &_block)
|
|
61
|
+
definition = new(name)
|
|
65
62
|
yield(definition) if block_given?
|
|
66
|
-
|
|
63
|
+
definition
|
|
67
64
|
end
|
|
68
|
-
|
|
65
|
+
|
|
69
66
|
def capture(name)
|
|
70
67
|
new_capture_hash = {}
|
|
71
68
|
new_capture_hash[:name] = name
|
|
@@ -73,7 +70,7 @@ module RequestLogAnalyzer
|
|
|
73
70
|
captures << new_capture_hash
|
|
74
71
|
CaptureDefiner.new(new_capture_hash)
|
|
75
72
|
end
|
|
76
|
-
|
|
73
|
+
|
|
77
74
|
def all_captured_variables
|
|
78
75
|
captures.map { |c| c[:name] } + captures.map { |c| c[:provides] }.compact.map { |pr| pr.keys }.flatten
|
|
79
76
|
end
|
|
@@ -86,7 +83,7 @@ module RequestLogAnalyzer
|
|
|
86
83
|
def matches(line, &warning_handler)
|
|
87
84
|
if @teaser.nil? || @teaser =~ line
|
|
88
85
|
if match_data = line.match(@regexp)
|
|
89
|
-
return { :
|
|
86
|
+
return { line_definition: self, captures: match_data.captures }
|
|
90
87
|
else
|
|
91
88
|
if @teaser && warning_handler
|
|
92
89
|
warning_handler.call(:teaser_check_failed, "Teaser matched for #{name.inspect}, but full line did not:\n#{line.inspect}")
|
|
@@ -100,7 +97,7 @@ module RequestLogAnalyzer
|
|
|
100
97
|
return false
|
|
101
98
|
end
|
|
102
99
|
|
|
103
|
-
|
|
100
|
+
alias_method :=~, :matches
|
|
104
101
|
|
|
105
102
|
# matches the line and converts the captured values using the request's
|
|
106
103
|
# convert_value function.
|
|
@@ -124,13 +121,13 @@ module RequestLogAnalyzer
|
|
|
124
121
|
|
|
125
122
|
# Add items directly to the resulting hash from the converted value
|
|
126
123
|
# if it is a hash and they are set in the :provides hash for this line definition
|
|
127
|
-
if converted.
|
|
124
|
+
if converted.is_a?(Hash) && capture[:provides].is_a?(Hash)
|
|
128
125
|
capture[:provides].each do |name, type|
|
|
129
|
-
value_hash[name] ||= request.convert_value(converted[name],
|
|
126
|
+
value_hash[name] ||= request.convert_value(converted[name], type: type)
|
|
130
127
|
end
|
|
131
128
|
end
|
|
132
129
|
end
|
|
133
|
-
|
|
130
|
+
value_hash
|
|
134
131
|
end
|
|
135
132
|
|
|
136
133
|
# Returns true if this line captures values of the given name
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
module RequestLogAnalyzer
|
|
2
|
-
|
|
3
2
|
# The Logprocessor class is used to perform simple processing actions over log files.
|
|
4
3
|
# It will go over the log file/stream line by line, pass the line to a processor and
|
|
5
4
|
# write the result back to the output file or stream. The processor can alter the
|
|
@@ -11,7 +10,6 @@ module RequestLogAnalyzer
|
|
|
11
10
|
# sources. A compact, information packed log will remain/.
|
|
12
11
|
#
|
|
13
12
|
class LogProcessor
|
|
14
|
-
|
|
15
13
|
attr_reader :mode, :options, :sources, :file_format
|
|
16
14
|
attr_accessor :output_file
|
|
17
15
|
|
|
@@ -20,11 +18,10 @@ module RequestLogAnalyzer
|
|
|
20
18
|
# processing mode. Currently, only :strip is supported.
|
|
21
19
|
# <tt>arguments</tt> The parsed command line arguments (a CommandLine::Arguments instance)
|
|
22
20
|
def self.build(command, arguments)
|
|
23
|
-
|
|
24
21
|
options = {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
discard_teaser_lines: arguments[:discard_teaser_lines],
|
|
23
|
+
keep_junk_lines: arguments[:keep_junk_lines]
|
|
24
|
+
}
|
|
28
25
|
|
|
29
26
|
log_processor = RequestLogAnalyzer::LogProcessor.new(arguments[:format].to_sym, command, options)
|
|
30
27
|
log_processor.output_file = arguments[:output] if arguments[:output]
|
|
@@ -33,7 +30,7 @@ module RequestLogAnalyzer
|
|
|
33
30
|
log_processor.sources << input
|
|
34
31
|
end
|
|
35
32
|
|
|
36
|
-
|
|
33
|
+
log_processor
|
|
37
34
|
end
|
|
38
35
|
|
|
39
36
|
# Initializes a new LogProcessor instance.
|
|
@@ -60,7 +57,7 @@ module RequestLogAnalyzer
|
|
|
60
57
|
# <tt>io</tt> The IO instance to process.
|
|
61
58
|
def process_io(io)
|
|
62
59
|
case mode
|
|
63
|
-
when :strip
|
|
60
|
+
when :strip then io.each_line { |line| @output << strip_line(line) }
|
|
64
61
|
end
|
|
65
62
|
end
|
|
66
63
|
|
|
@@ -68,7 +65,7 @@ module RequestLogAnalyzer
|
|
|
68
65
|
# found, an empty line is returned, which will strip the line from the output.
|
|
69
66
|
# <tt>line</tt> The line to strip
|
|
70
67
|
def strip_line(line)
|
|
71
|
-
file_format.line_definitions.any? { |
|
|
68
|
+
file_format.line_definitions.any? { |_name, definition| definition =~ line } ? line : ''
|
|
72
69
|
end
|
|
73
70
|
|
|
74
71
|
# Runs the log processing by setting up the output stream and iterating over all the
|
|
@@ -82,9 +79,9 @@ module RequestLogAnalyzer
|
|
|
82
79
|
end
|
|
83
80
|
|
|
84
81
|
@sources.each do |source|
|
|
85
|
-
if source.
|
|
82
|
+
if source.is_a?(String) && File.exist?(source)
|
|
86
83
|
process_file(source)
|
|
87
|
-
elsif source.
|
|
84
|
+
elsif source.is_a?(IO)
|
|
88
85
|
process_io(source)
|
|
89
86
|
elsif ['-', 'STDIN'].include?(source)
|
|
90
87
|
process_io($stdin)
|
|
@@ -92,8 +89,7 @@ module RequestLogAnalyzer
|
|
|
92
89
|
end
|
|
93
90
|
|
|
94
91
|
ensure
|
|
95
|
-
@output.close if @output.
|
|
92
|
+
@output.close if @output.is_a?(File)
|
|
96
93
|
end
|
|
97
94
|
end
|
|
98
|
-
|
|
99
|
-
end
|
|
95
|
+
end
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
module RequestLogAnalyzer
|
|
2
|
-
|
|
3
2
|
# Mail report to a specified emailaddress
|
|
4
3
|
class Mailer
|
|
5
|
-
|
|
6
4
|
attr_accessor :data, :to, :host, :port
|
|
7
5
|
|
|
8
6
|
# Initialize a mailer
|
|
@@ -23,7 +21,7 @@ module RequestLogAnalyzer
|
|
|
23
21
|
@port = 25
|
|
24
22
|
@options = options
|
|
25
23
|
@host, @port = host.split(':') if @host.include?(':')
|
|
26
|
-
@data = []
|
|
24
|
+
@data = []
|
|
27
25
|
end
|
|
28
26
|
|
|
29
27
|
# Send all data in @data to the email address used during initialization.
|
|
@@ -32,10 +30,10 @@ module RequestLogAnalyzer
|
|
|
32
30
|
from = @options[:from] || 'contact@railsdoctors.com'
|
|
33
31
|
from_alias = @options[:from_alias] || 'Request-log-analyzer reporter'
|
|
34
32
|
to_alias = @options[:to_alias] || to
|
|
35
|
-
subject = @options[:subject] || "Request log analyzer report - generated on #{Time.now
|
|
36
|
-
content_type =
|
|
37
|
-
content_type = 'Content-Type: text/html; charset="ISO-8859-1";' if @data.map{|l| l.include?('html')}.include?(true)
|
|
38
|
-
|
|
33
|
+
subject = @options[:subject] || "Request log analyzer report - generated on #{Time.now}"
|
|
34
|
+
content_type = ''
|
|
35
|
+
content_type = 'Content-Type: text/html; charset="ISO-8859-1";' if @data.map { |l| l.include?('html') }.include?(true)
|
|
36
|
+
msg = <<END_OF_MESSAGE
|
|
39
37
|
From: #{from_alias} <#{from}>
|
|
40
38
|
To: #{to_alias} <#{@to}>
|
|
41
39
|
Subject: #{subject}
|
|
@@ -50,16 +48,15 @@ END_OF_MESSAGE
|
|
|
50
48
|
end
|
|
51
49
|
end
|
|
52
50
|
|
|
53
|
-
|
|
51
|
+
[msg, from, to]
|
|
54
52
|
end
|
|
55
53
|
|
|
56
|
-
def <<
|
|
54
|
+
def <<(string)
|
|
57
55
|
data << string
|
|
58
56
|
end
|
|
59
57
|
|
|
60
|
-
def puts
|
|
58
|
+
def puts(string)
|
|
61
59
|
data << string
|
|
62
60
|
end
|
|
63
|
-
|
|
64
61
|
end
|
|
65
|
-
end
|
|
62
|
+
end
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
# Module for generating output
|
|
2
2
|
module RequestLogAnalyzer::Output
|
|
3
|
-
|
|
4
3
|
# Loads a Output::Base subclass instance.
|
|
5
4
|
def self.load(file_format, *args)
|
|
6
|
-
|
|
7
5
|
klass = nil
|
|
8
|
-
if file_format.
|
|
6
|
+
if file_format.is_a?(RequestLogAnalyzer::Output::Base)
|
|
9
7
|
# this already is a file format! return itself
|
|
10
8
|
return file_format
|
|
11
9
|
|
|
12
|
-
elsif file_format.
|
|
10
|
+
elsif file_format.is_a?(Class) && file_format.ancestors.include?(RequestLogAnalyzer::Output::Base)
|
|
13
11
|
# a usable class is provided. Use this format class.
|
|
14
12
|
klass = file_format
|
|
15
13
|
|
|
16
|
-
elsif file_format.
|
|
14
|
+
elsif file_format.is_a?(String) && File.exist?(file_format)
|
|
17
15
|
# load a format from a ruby file
|
|
18
16
|
require file_format
|
|
19
17
|
const = RequestLogAnalyzer.to_camelcase(File.basename(file_format, '.rb'))
|
|
@@ -22,7 +20,7 @@ module RequestLogAnalyzer::Output
|
|
|
22
20
|
elsif Object.const_defined?(const)
|
|
23
21
|
klass = Object.const_get(const)
|
|
24
22
|
else
|
|
25
|
-
|
|
23
|
+
fail "Cannot load class #{const} from #{file_format}!"
|
|
26
24
|
end
|
|
27
25
|
|
|
28
26
|
else
|
|
@@ -31,16 +29,15 @@ module RequestLogAnalyzer::Output
|
|
|
31
29
|
end
|
|
32
30
|
|
|
33
31
|
# check the returned klass to see if it can be used
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
fail "Could not load a file format from #{file_format.inspect}" if klass.nil?
|
|
33
|
+
fail 'Invalid FileFormat class' unless klass.is_a?(Class) && klass.ancestors.include?(RequestLogAnalyzer::Output::Base)
|
|
36
34
|
|
|
37
35
|
klass.create(*args) # return an instance of the class
|
|
38
36
|
end
|
|
39
|
-
|
|
37
|
+
|
|
40
38
|
# Base Class used for generating output for reports.
|
|
41
39
|
# All output should inherit fromt this class.
|
|
42
40
|
class Base
|
|
43
|
-
|
|
44
41
|
attr_accessor :io, :options, :style
|
|
45
42
|
|
|
46
43
|
# Initialize a report
|
|
@@ -49,9 +46,9 @@ module RequestLogAnalyzer::Output
|
|
|
49
46
|
def initialize(io, options = {})
|
|
50
47
|
@io = io
|
|
51
48
|
@options = options
|
|
52
|
-
@style = options[:style] || { :
|
|
49
|
+
@style = options[:style] || { cell_separator: true, table_border: false }
|
|
53
50
|
end
|
|
54
|
-
|
|
51
|
+
|
|
55
52
|
def report_tracker(tracker)
|
|
56
53
|
tracker.report(self)
|
|
57
54
|
end
|
|
@@ -74,7 +71,7 @@ module RequestLogAnalyzer::Output
|
|
|
74
71
|
|
|
75
72
|
def slice_results(array)
|
|
76
73
|
return array if options[:amount] == :all
|
|
77
|
-
|
|
74
|
+
array.slice(0, options[:amount]) # otherwise
|
|
78
75
|
end
|
|
79
76
|
|
|
80
77
|
# Generate a report table and push it into the output object.
|
|
@@ -98,10 +95,11 @@ module RequestLogAnalyzer::Output
|
|
|
98
95
|
# end
|
|
99
96
|
# end
|
|
100
97
|
#
|
|
101
|
-
def table(*
|
|
98
|
+
def table(*_columns, &_block)
|
|
102
99
|
end
|
|
103
100
|
|
|
104
101
|
protected
|
|
102
|
+
|
|
105
103
|
# Check if a given table defination hash includes a header (title)
|
|
106
104
|
# <tt>columns</tt> The columns hash
|
|
107
105
|
def table_has_header?(columns)
|