request-log-analyzer 1.3.5 → 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 +13 -14
- 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 +21 -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 +17 -18
- data/lib/request_log_analyzer/file_format/apache.rb +26 -27
- data/lib/request_log_analyzer/file_format/merb.rb +30 -13
- data/lib/request_log_analyzer/file_format/rack.rb +4 -4
- 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 +40 -41
- 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 +75 -68
- 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 +39 -132
- 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 +36 -116
- data/lib/request_log_analyzer/tracker.rb +139 -32
- data/lib/request_log_analyzer.rb +4 -4
- 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 +15 -14
- 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 +13 -13
- 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 +81 -79
- data/tasks/github-gem.rake +125 -75
- data/tasks/request_log_analyzer.rake +2 -2
- metadata +13 -8
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
module RequestLogAnalyzer
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
# The line definition class is used to specify what lines should be parsed from the log file.
|
|
4
4
|
# It contains functionality to match a line against the definition and parse the information
|
|
5
5
|
# from this line. This is used by the LogParser class when parsing a log file..
|
|
6
6
|
class LineDefinition
|
|
7
7
|
|
|
8
8
|
class Definer
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
attr_accessor :line_definitions
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
def initialize
|
|
13
13
|
@line_definitions = {}
|
|
14
14
|
end
|
|
@@ -16,7 +16,7 @@ module RequestLogAnalyzer
|
|
|
16
16
|
def initialize_copy(other)
|
|
17
17
|
@line_definitions = other.line_definitions.dup
|
|
18
18
|
end
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
def method_missing(name, *args, &block)
|
|
21
21
|
if block_given?
|
|
22
22
|
@line_definitions[name] = RequestLogAnalyzer::LineDefinition.define(name, &block)
|
|
@@ -29,25 +29,26 @@ module RequestLogAnalyzer
|
|
|
29
29
|
attr_reader :name
|
|
30
30
|
attr_accessor :teaser, :regexp, :captures
|
|
31
31
|
attr_accessor :header, :footer
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
alias_method :header?, :header
|
|
34
34
|
alias_method :footer?, :footer
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
# Initializes the LineDefinition instance with a hash containing the different elements of
|
|
37
37
|
# the definition.
|
|
38
38
|
def initialize(name, definition = {})
|
|
39
39
|
@name = name
|
|
40
40
|
@captures = []
|
|
41
|
+
@teaser = nil
|
|
41
42
|
definition.each { |key, value| self.send("#{key.to_s}=".to_sym, value) }
|
|
42
43
|
end
|
|
43
|
-
|
|
44
|
+
|
|
44
45
|
def self.define(name, &block)
|
|
45
46
|
definition = self.new(name)
|
|
46
47
|
yield(definition) if block_given?
|
|
47
48
|
return definition
|
|
48
49
|
end
|
|
49
|
-
|
|
50
|
-
# Checks whether a given line matches this definition.
|
|
50
|
+
|
|
51
|
+
# Checks whether a given line matches this definition.
|
|
51
52
|
# It will return false if a line does not match. If the line matches, a hash is returned
|
|
52
53
|
# with all the fields parsed from that line as content.
|
|
53
54
|
# If the line definition has a teaser-check, a :teaser_check_failed warning will be emitted
|
|
@@ -66,7 +67,7 @@ module RequestLogAnalyzer
|
|
|
66
67
|
return false
|
|
67
68
|
end
|
|
68
69
|
end
|
|
69
|
-
|
|
70
|
+
|
|
70
71
|
alias :=~ :matches
|
|
71
72
|
|
|
72
73
|
# matches the line and converts the captured values using the request's
|
|
@@ -84,17 +85,17 @@ module RequestLogAnalyzer
|
|
|
84
85
|
def convert_captured_values(values, request)
|
|
85
86
|
value_hash = {}
|
|
86
87
|
captures.each_with_index do |capture, index|
|
|
87
|
-
|
|
88
|
+
|
|
88
89
|
# convert the value using the request convert_value function
|
|
89
90
|
converted = request.convert_value(values[index], capture)
|
|
90
91
|
value_hash[capture[:name]] ||= converted
|
|
91
|
-
|
|
92
|
+
|
|
92
93
|
# Add items directly to the resulting hash from the converted value
|
|
93
94
|
# if it is a hash and they are set in the :provides hash for this line definition
|
|
94
95
|
if converted.kind_of?(Hash) && capture[:provides].kind_of?(Hash)
|
|
95
96
|
capture[:provides].each do |name, type|
|
|
96
97
|
value_hash[name] ||= request.convert_value(converted[name], { :type => type })
|
|
97
|
-
end
|
|
98
|
+
end
|
|
98
99
|
end
|
|
99
100
|
end
|
|
100
101
|
return value_hash
|
|
@@ -106,5 +107,5 @@ module RequestLogAnalyzer
|
|
|
106
107
|
end
|
|
107
108
|
|
|
108
109
|
end
|
|
109
|
-
|
|
110
|
+
|
|
110
111
|
end
|
|
@@ -1,55 +1,53 @@
|
|
|
1
1
|
module RequestLogAnalyzer
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
# The Logprocessor class is used to perform simple processing actions over log files.
|
|
4
|
-
# It will go over the log file/stream line by line, pass the line to a processor and
|
|
5
|
-
# write the result back to the output file or stream. The processor can alter the
|
|
4
|
+
# It will go over the log file/stream line by line, pass the line to a processor and
|
|
5
|
+
# write the result back to the output file or stream. The processor can alter the
|
|
6
6
|
# contents of the line, remain it intact or remove it altogether, based on the current
|
|
7
7
|
# file format
|
|
8
8
|
#
|
|
9
9
|
# Currently, one processors is supported:
|
|
10
|
-
# * :strip will remove all irrelevent lines (according to the file format) from the
|
|
10
|
+
# * :strip will remove all irrelevent lines (according to the file format) from the
|
|
11
11
|
# sources. A compact, information packed log will remain/.
|
|
12
12
|
#
|
|
13
13
|
class LogProcessor
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
attr_reader :mode, :options, :sources
|
|
14
|
+
|
|
15
|
+
attr_reader :mode, :options, :sources, :file_format
|
|
18
16
|
attr_accessor :output_file
|
|
19
|
-
|
|
17
|
+
|
|
20
18
|
# Builds a logprocessor instance from the arguments given on the command line
|
|
21
|
-
# <tt>command</tt> The command hat was used to start the log processor. This will set the
|
|
19
|
+
# <tt>command</tt> The command hat was used to start the log processor. This will set the
|
|
22
20
|
# processing mode. Currently, only :strip is supported.
|
|
23
21
|
# <tt>arguments</tt> The parsed command line arguments (a CommandLine::Arguments instance)
|
|
24
22
|
def self.build(command, arguments)
|
|
25
|
-
|
|
26
|
-
options = {
|
|
27
|
-
:discard_teaser_lines => arguments[:discard_teaser_lines],
|
|
28
|
-
:keep_junk_lines => arguments[:keep_junk_lines],
|
|
23
|
+
|
|
24
|
+
options = {
|
|
25
|
+
:discard_teaser_lines => arguments[:discard_teaser_lines],
|
|
26
|
+
:keep_junk_lines => arguments[:keep_junk_lines],
|
|
29
27
|
}
|
|
30
|
-
|
|
28
|
+
|
|
31
29
|
log_processor = RequestLogAnalyzer::LogProcessor.new(arguments[:format].to_sym, command, options)
|
|
32
30
|
log_processor.output_file = arguments[:output] if arguments[:output]
|
|
33
31
|
|
|
34
32
|
arguments.parameters.each do |input|
|
|
35
33
|
log_processor.sources << input
|
|
36
34
|
end
|
|
37
|
-
|
|
35
|
+
|
|
38
36
|
return log_processor
|
|
39
37
|
end
|
|
40
|
-
|
|
38
|
+
|
|
41
39
|
# Initializes a new LogProcessor instance.
|
|
42
40
|
# <tt>format</tt> The file format to use (e.g. :rails).
|
|
43
41
|
# <tt>mode</tt> The processing mode
|
|
44
42
|
# <tt>options</tt> A hash with options to take into account
|
|
45
43
|
def initialize(format, mode, options = {})
|
|
46
|
-
@options
|
|
47
|
-
@mode
|
|
48
|
-
@sources
|
|
44
|
+
@options = options
|
|
45
|
+
@mode = mode
|
|
46
|
+
@sources = []
|
|
47
|
+
@file_format = format
|
|
49
48
|
$output_file = nil
|
|
50
|
-
self.register_file_format(format)
|
|
51
49
|
end
|
|
52
|
-
|
|
50
|
+
|
|
53
51
|
# Processes input files by opening it and sending the filestream to <code>process_io</code>,
|
|
54
52
|
# in which the actual processing is performed.
|
|
55
53
|
# <tt>file</tt> The file to process
|
|
@@ -65,7 +63,7 @@ module RequestLogAnalyzer
|
|
|
65
63
|
when :strip; io.each_line { |line| @output << strip_line(line) }
|
|
66
64
|
end
|
|
67
65
|
end
|
|
68
|
-
|
|
66
|
+
|
|
69
67
|
# Returns the line itself if the string matches any of the line definitions. If no match is
|
|
70
68
|
# found, an empty line is returned, which will strip the line from the output.
|
|
71
69
|
# <tt>line</tt> The line to strip
|
|
@@ -74,25 +72,25 @@ module RequestLogAnalyzer
|
|
|
74
72
|
end
|
|
75
73
|
|
|
76
74
|
# Runs the log processing by setting up the output stream and iterating over all the
|
|
77
|
-
# input sources. Input sources can either be filenames (String instances) or IO streams
|
|
75
|
+
# input sources. Input sources can either be filenames (String instances) or IO streams
|
|
78
76
|
# (IO instances). The strings "-" and "STDIN" will be substituted for the $stdin variable.
|
|
79
77
|
def run!
|
|
80
78
|
if @output_file.nil?
|
|
81
|
-
@output = $stdout
|
|
79
|
+
@output = $stdout
|
|
82
80
|
else
|
|
83
|
-
@output = File.new(@output_file, 'a')
|
|
81
|
+
@output = File.new(@output_file, 'a')
|
|
84
82
|
end
|
|
85
|
-
|
|
83
|
+
|
|
86
84
|
@sources.each do |source|
|
|
87
85
|
if source.kind_of?(String) && File.exist?(source)
|
|
88
86
|
process_file(source)
|
|
89
87
|
elsif source.kind_of?(IO)
|
|
90
88
|
process_io(source)
|
|
91
89
|
elsif ['-', 'STDIN'].include?(source)
|
|
92
|
-
process_io($stdin)
|
|
90
|
+
process_io($stdin)
|
|
93
91
|
end
|
|
94
92
|
end
|
|
95
|
-
|
|
93
|
+
|
|
96
94
|
ensure
|
|
97
95
|
@output.close if @output.kind_of?(File)
|
|
98
96
|
end
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
module RequestLogAnalyzer
|
|
2
2
|
|
|
3
3
|
class Mailer
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
attr_accessor :data, :to, :host
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
# Initialize a mailer
|
|
8
8
|
# <tt>to</tt> to address
|
|
9
9
|
# <tt>host</tt> the mailer host
|
|
10
10
|
# <tt>options</tt> Specific style options
|
|
11
|
+
# Options
|
|
12
|
+
# <tt>:debug</tt> Do not actually mail
|
|
11
13
|
def initialize(to, host = 'localhost', options = {})
|
|
12
|
-
require 'net/smtp'
|
|
14
|
+
require 'net/smtp'
|
|
13
15
|
@to = to
|
|
14
16
|
@host = host
|
|
15
17
|
@options = options
|
|
16
18
|
@data = []
|
|
17
19
|
end
|
|
18
|
-
|
|
20
|
+
|
|
19
21
|
def mail
|
|
20
22
|
from = @options[:from] || 'contact@railsdoctors.com'
|
|
21
23
|
from_alias = @options[:from_alias] || 'Request-log-analyzer reporter'
|
|
@@ -28,16 +30,20 @@ Subject: #{subject}
|
|
|
28
30
|
|
|
29
31
|
#{@data.to_s}
|
|
30
32
|
END_OF_MESSAGE
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
|
|
34
|
+
unless @options[:debug]
|
|
35
|
+
Net::SMTP.start(@host) do |smtp|
|
|
36
|
+
smtp.send_message msg, from, to
|
|
37
|
+
end
|
|
34
38
|
end
|
|
39
|
+
|
|
40
|
+
return [msg, from, to]
|
|
35
41
|
end
|
|
36
|
-
|
|
42
|
+
|
|
37
43
|
def << string
|
|
38
44
|
data << string
|
|
39
45
|
end
|
|
40
|
-
|
|
46
|
+
|
|
41
47
|
def puts string
|
|
42
48
|
data << string
|
|
43
49
|
end
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
module RequestLogAnalyzer::Output
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
# Fixed Width output class.
|
|
5
5
|
# Outputs a fixed width ASCII or UF8 report.
|
|
6
6
|
class FixedWidth < Base
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
# Mixin module. Will disable any colorizing.
|
|
9
9
|
module Monochrome
|
|
10
10
|
def colorize(text, *options)
|
|
11
11
|
text
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
# Colorize module
|
|
16
16
|
module Color
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
STYLES = { :normal => 0, :bold => 1, :underscore => 4, :blink => 5, :inverse => 7, :concealed => 8 }
|
|
19
19
|
COLORS = { :black => 0, :blue => 4, :green => 2, :cyan => 6, :red => 1, :purple => 5, :brown => 3, :white => 7 }
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
# Colorize text
|
|
22
22
|
# <tt>text</tt> The text to colorize
|
|
23
23
|
# Options
|
|
@@ -25,18 +25,18 @@ module RequestLogAnalyzer::Output
|
|
|
25
25
|
# * <tt>:color</tt> The foreground color to paint. Defined in Color::COLORS
|
|
26
26
|
# * <tt>:on</tt> Alias for :background
|
|
27
27
|
# * <tt>:style</tt> Font style, defined in Color::STYLES
|
|
28
|
-
#
|
|
28
|
+
#
|
|
29
29
|
# Returns ASCII colored string
|
|
30
30
|
def colorize(text, *options)
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
font_style = ''
|
|
33
33
|
foreground_color = '0'
|
|
34
34
|
background_color = ''
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
options.each do |option|
|
|
37
37
|
if option.kind_of?(Symbol)
|
|
38
38
|
foreground_color = "3#{COLORS[option]}" if COLORS.include?(option)
|
|
39
|
-
font_style = "#{STYLES[option]};" if STYLES.include?(option)
|
|
39
|
+
font_style = "#{STYLES[option]};" if STYLES.include?(option)
|
|
40
40
|
elsif option.kind_of?(Hash)
|
|
41
41
|
options.each do |key, value|
|
|
42
42
|
case key
|
|
@@ -50,16 +50,16 @@ module RequestLogAnalyzer::Output
|
|
|
50
50
|
end
|
|
51
51
|
return "\e[#{background_color}#{font_style}#{foreground_color}m#{text}\e[0m"
|
|
52
52
|
end
|
|
53
|
-
|
|
53
|
+
|
|
54
54
|
end
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
attr_reader :characters
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
CHARACTERS = {
|
|
59
59
|
:ascii => { :horizontal_line => '-', :vertical_line => '|', :block => '=' },
|
|
60
60
|
:utf => { :horizontal_line => '━', :vertical_line => '┃', :block => '░' }
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
# Initialize a report
|
|
64
64
|
# <tt>io</tt> iO Object (file, STDOUT, etc.)
|
|
65
65
|
# <tt>options</tt>
|
|
@@ -71,25 +71,25 @@ module RequestLogAnalyzer::Output
|
|
|
71
71
|
@options[:width] ||= 80
|
|
72
72
|
@options[:characters] ||= :utf
|
|
73
73
|
@characters = CHARACTERS[@options[:characters]]
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
color_module = @options[:color] ? Color : Monochrome
|
|
76
|
-
(class << self; self; end).send(:include, color_module)
|
|
76
|
+
(class << self; self; end).send(:include, color_module)
|
|
77
77
|
end
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
# Write a string to the output object.
|
|
80
80
|
# <tt>str</tt> The string to write.
|
|
81
81
|
def print(str)
|
|
82
82
|
@io << str
|
|
83
83
|
end
|
|
84
|
-
|
|
84
|
+
|
|
85
85
|
alias :<< :print
|
|
86
|
-
|
|
86
|
+
|
|
87
87
|
# Write a string to the output object with a newline at the end.
|
|
88
88
|
# <tt>str</tt> The string to write.
|
|
89
89
|
def puts(str = '')
|
|
90
90
|
@io << str << "\n"
|
|
91
91
|
end
|
|
92
|
-
|
|
92
|
+
|
|
93
93
|
# Write the title of a report
|
|
94
94
|
# <tt>title</tt> The title to write
|
|
95
95
|
def title(title)
|
|
@@ -97,9 +97,9 @@ module RequestLogAnalyzer::Output
|
|
|
97
97
|
puts colorize(title, :bold, :white)
|
|
98
98
|
line(:green)
|
|
99
99
|
end
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
# Write a line
|
|
102
|
-
def line(*font)
|
|
102
|
+
def line(*font)
|
|
103
103
|
puts colorize(characters[:horizontal_line] * @options[:width], *font)
|
|
104
104
|
end
|
|
105
105
|
|
|
@@ -113,7 +113,7 @@ module RequestLogAnalyzer::Output
|
|
|
113
113
|
"#{text} (#{colorize(url, :blue, :bold)})"
|
|
114
114
|
end
|
|
115
115
|
end
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
# Generate a header for a report
|
|
118
118
|
def header
|
|
119
119
|
if io.kind_of?(File)
|
|
@@ -123,7 +123,7 @@ module RequestLogAnalyzer::Output
|
|
|
123
123
|
puts "Website: #{link('http://github.com/wvanbergen/request-log-analyzer')}"
|
|
124
124
|
end
|
|
125
125
|
end
|
|
126
|
-
|
|
126
|
+
|
|
127
127
|
# Generate a footer for a report
|
|
128
128
|
def footer
|
|
129
129
|
puts
|
|
@@ -132,12 +132,12 @@ module RequestLogAnalyzer::Output
|
|
|
132
132
|
line(:green)
|
|
133
133
|
puts "Thanks for using #{colorize('request-log-analyzer', :white, :bold)}!"
|
|
134
134
|
end
|
|
135
|
-
|
|
135
|
+
|
|
136
136
|
# Generate a report table and push it into the output object.
|
|
137
137
|
# <tt>*colums<tt> Columns hash
|
|
138
138
|
# <tt>&block</tt>: A block yeilding the rows.
|
|
139
139
|
def table(*columns, &block)
|
|
140
|
-
|
|
140
|
+
|
|
141
141
|
rows = Array.new
|
|
142
142
|
yield(rows)
|
|
143
143
|
|
|
@@ -147,7 +147,7 @@ module RequestLogAnalyzer::Output
|
|
|
147
147
|
result.each_with_index { |length, index| result[index] = ([length, lengths[index]].max rescue length) }
|
|
148
148
|
end
|
|
149
149
|
columns.each_with_index { |col, index| col[:actual_width] ||= max_cell_widths[index] }
|
|
150
|
-
|
|
150
|
+
|
|
151
151
|
# determine actual column width
|
|
152
152
|
column_widths = columns.map do |column|
|
|
153
153
|
if column[:width] == :rest
|
|
@@ -162,29 +162,28 @@ module RequestLogAnalyzer::Output
|
|
|
162
162
|
column[:actual_width]
|
|
163
163
|
end
|
|
164
164
|
end
|
|
165
|
-
|
|
165
|
+
|
|
166
166
|
if column_widths.include?(nil)
|
|
167
167
|
fill_column = columns[column_widths.index(nil)]
|
|
168
168
|
width_left = options[:width] - ((columns.length - 1) * (style[:cell_separator] ? 3 : 1)) - column_widths.compact.inject(0) { |sum, col| sum + col}
|
|
169
|
-
column_widths[column_widths.index(nil)] =
|
|
170
|
-
when :ratio; width_left # max out
|
|
171
|
-
else; [width_left, fill_column[:actual_width]].min
|
|
172
|
-
end
|
|
169
|
+
column_widths[column_widths.index(nil)] = width_left
|
|
173
170
|
end
|
|
174
|
-
|
|
171
|
+
|
|
172
|
+
line(:green) if @style[:top_line]
|
|
173
|
+
|
|
175
174
|
# Print table header
|
|
176
175
|
if table_has_header?(columns)
|
|
177
176
|
column_titles = []
|
|
178
177
|
columns.each_with_index do |column, index|
|
|
179
|
-
width = column_widths[index]
|
|
178
|
+
width = column_widths[index]
|
|
180
179
|
alignment = (column[:align] == :right ? '' : '-')
|
|
181
180
|
column_titles.push(colorize("%#{alignment}#{width}s" % column[:title].to_s[0...width], :bold))
|
|
182
181
|
end
|
|
183
|
-
|
|
182
|
+
|
|
184
183
|
puts column_titles.join(style[:cell_separator] ? " #{characters[:vertical_line]} " : ' ')
|
|
185
184
|
line(:green)
|
|
186
185
|
end
|
|
187
|
-
|
|
186
|
+
|
|
188
187
|
# Print the rows
|
|
189
188
|
rows.each do |row|
|
|
190
189
|
row_values = []
|
|
@@ -195,12 +194,12 @@ module RequestLogAnalyzer::Output
|
|
|
195
194
|
if width > 4
|
|
196
195
|
if column[:treshold] && column[:treshold] < row[index].to_f
|
|
197
196
|
bar = ''
|
|
198
|
-
bar << characters[:block] * (width.to_f * column[:treshold]).round
|
|
199
|
-
bar << colorize(characters[:block] * (width.to_f * (row[index].to_f - column[:treshold])).round, :red)
|
|
200
|
-
row_values.push(bar)
|
|
197
|
+
bar << characters[:block] * (width.to_f * column[:treshold]).round
|
|
198
|
+
bar << colorize(characters[:block] * (width.to_f * (row[index].to_f - column[:treshold])).round, :red)
|
|
199
|
+
row_values.push(bar)
|
|
201
200
|
else
|
|
202
201
|
# Create a bar by combining block characters
|
|
203
|
-
row_values.push(characters[:block] * (width.to_f * row[index].to_f).round)
|
|
202
|
+
row_values.push(characters[:block] * (width.to_f * row[index].to_f).round)
|
|
204
203
|
end
|
|
205
204
|
else
|
|
206
205
|
# Too few characters for a ratio bar. Display nothing
|
|
@@ -213,9 +212,9 @@ module RequestLogAnalyzer::Output
|
|
|
213
212
|
row_values.push(cell_value)
|
|
214
213
|
end
|
|
215
214
|
end
|
|
216
|
-
puts row_values.join(style[:cell_separator] ? " #{characters[:vertical_line]} " : ' ')
|
|
215
|
+
puts row_values.join(style[:cell_separator] ? " #{characters[:vertical_line]} " : ' ')
|
|
217
216
|
end
|
|
218
217
|
end
|
|
219
|
-
|
|
218
|
+
|
|
220
219
|
end
|
|
221
220
|
end
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
module RequestLogAnalyzer::Output
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
# HTML Output class. Generated a HTML-formatted report, including CSS.
|
|
4
4
|
class HTML < Base
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
# def initialize(io, options = {})
|
|
7
7
|
# super(io, options)
|
|
8
8
|
# end
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
# Print a string to the io object.
|
|
11
11
|
def print(str)
|
|
12
12
|
@io << str
|
|
@@ -26,10 +26,10 @@ module RequestLogAnalyzer::Output
|
|
|
26
26
|
|
|
27
27
|
# Render a single line
|
|
28
28
|
# <tt>*font</tt> The font.
|
|
29
|
-
def line(*font)
|
|
29
|
+
def line(*font)
|
|
30
30
|
@io.puts(tag(:hr))
|
|
31
31
|
end
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
# Write a link
|
|
34
34
|
# <tt>text</tt> The text in the link
|
|
35
35
|
# <tt>url</tt> The url to link to.
|
|
@@ -44,29 +44,29 @@ module RequestLogAnalyzer::Output
|
|
|
44
44
|
def table(*columns, &block)
|
|
45
45
|
rows = Array.new
|
|
46
46
|
yield(rows)
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
@io << tag(:table, {:id => 'mytable', :cellspacing => 0}) do |content|
|
|
49
49
|
if table_has_header?(columns)
|
|
50
50
|
content << tag(:tr) do
|
|
51
51
|
columns.map { |col| tag(:th, col[:title]) }.join("\n")
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
odd = false
|
|
56
56
|
rows.each do |row|
|
|
57
57
|
odd = !odd
|
|
58
58
|
content << tag(:tr) do
|
|
59
59
|
if odd
|
|
60
|
-
row.map { |cell| tag(:td, cell, :class => 'alt') }.join("\n")
|
|
60
|
+
row.map { |cell| tag(:td, cell, :class => 'alt') }.join("\n")
|
|
61
61
|
else
|
|
62
|
-
row.map { |cell| tag(:td, cell) }.join("\n")
|
|
62
|
+
row.map { |cell| tag(:td, cell) }.join("\n")
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
66
|
end
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
end
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
# Genrate HTML header and associated stylesheet
|
|
71
71
|
def header
|
|
72
72
|
@io << "<html>"
|
|
@@ -85,13 +85,13 @@ module RequestLogAnalyzer::Output
|
|
|
85
85
|
a {
|
|
86
86
|
color: #c75f3e;
|
|
87
87
|
}
|
|
88
|
-
|
|
88
|
+
|
|
89
89
|
.color_bar {
|
|
90
90
|
border: 1px solid;
|
|
91
91
|
height:10px;
|
|
92
92
|
background: #CAE8EA;
|
|
93
93
|
}
|
|
94
|
-
|
|
94
|
+
|
|
95
95
|
#mytable {
|
|
96
96
|
width: 700px;
|
|
97
97
|
padding: 0;
|
|
@@ -101,7 +101,7 @@ module RequestLogAnalyzer::Output
|
|
|
101
101
|
|
|
102
102
|
caption {
|
|
103
103
|
padding: 0 0 5px 0;
|
|
104
|
-
width: 700px;
|
|
104
|
+
width: 700px;
|
|
105
105
|
font: italic 11px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
|
|
106
106
|
text-align: right;
|
|
107
107
|
}
|
|
@@ -138,7 +138,7 @@ module RequestLogAnalyzer::Output
|
|
|
138
138
|
@io << tag(:h1, 'Request-log-analyzer summary report')
|
|
139
139
|
@io << tag(:p, "Version #{RequestLogAnalyzer::VERSION} - written by Willem van Bergen and Bart ten Brinke")
|
|
140
140
|
end
|
|
141
|
-
|
|
141
|
+
|
|
142
142
|
# Generate a footer for a report
|
|
143
143
|
def footer
|
|
144
144
|
@io << tag(:hr) << tag(:h2, 'Thanks for using request-log-analyzer')
|
|
@@ -146,9 +146,9 @@ module RequestLogAnalyzer::Output
|
|
|
146
146
|
@io << tag(:p, 'If you need an expert who can analyze your application, mail to ' + link('contact@railsdoctors.com', 'mailto:contact@railsdoctors.com') + ' or visit us at ' + link('http://railsdoctors.com', 'http://railsdoctors.com') + '.')
|
|
147
147
|
@io << "</body></html>\n"
|
|
148
148
|
end
|
|
149
|
-
|
|
150
|
-
protected
|
|
151
|
-
|
|
149
|
+
|
|
150
|
+
protected
|
|
151
|
+
|
|
152
152
|
# HTML tag writer helper
|
|
153
153
|
# <tt>tag</tt> The tag to generate
|
|
154
154
|
# <tt>content</tt> The content inside the tag
|
|
@@ -158,7 +158,7 @@ module RequestLogAnalyzer::Output
|
|
|
158
158
|
attributes = content.nil? ? '' : ' ' + content.map { |(key, value)| "#{key}=\"#{value}\"" }.join(' ')
|
|
159
159
|
content_string = ''
|
|
160
160
|
content = yield(content_string)
|
|
161
|
-
content = content_string unless content_string.empty?
|
|
161
|
+
content = content_string unless content_string.empty?
|
|
162
162
|
"<#{tag}#{attributes}>#{content}</#{tag}>"
|
|
163
163
|
else
|
|
164
164
|
attributes = attributes.nil? ? '' : ' ' + attributes.map { |(key, value)| "#{key}=\"#{value}\"" }.join(' ')
|
|
@@ -172,6 +172,6 @@ module RequestLogAnalyzer::Output
|
|
|
172
172
|
end
|
|
173
173
|
end
|
|
174
174
|
end
|
|
175
|
-
end
|
|
175
|
+
end
|
|
176
176
|
end
|
|
177
177
|
end
|