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.
Files changed (144) hide show
  1. data/.gitignore +10 -0
  2. data/DESIGN.rdoc +41 -0
  3. data/LICENSE +4 -4
  4. data/README.rdoc +38 -0
  5. data/Rakefile +6 -3
  6. data/bin/request-log-analyzer +70 -72
  7. data/lib/cli/command_line_arguments.rb +53 -53
  8. data/lib/cli/database_console.rb +26 -0
  9. data/lib/cli/database_console_init.rb +43 -0
  10. data/lib/cli/progressbar.rb +166 -189
  11. data/lib/cli/tools.rb +49 -0
  12. data/lib/request_log_analyzer/aggregator/database_inserter.rb +83 -0
  13. data/lib/request_log_analyzer/aggregator/echo.rb +17 -12
  14. data/lib/request_log_analyzer/aggregator/summarizer.rb +101 -63
  15. data/lib/request_log_analyzer/{aggregator/base.rb → aggregator.rb} +17 -13
  16. data/lib/request_log_analyzer/controller.rb +251 -98
  17. data/lib/request_log_analyzer/database/base.rb +114 -0
  18. data/lib/request_log_analyzer/database/connection.rb +38 -0
  19. data/lib/request_log_analyzer/database/request.rb +22 -0
  20. data/lib/request_log_analyzer/database/source.rb +13 -0
  21. data/lib/request_log_analyzer/database/warning.rb +14 -0
  22. data/lib/request_log_analyzer/database.rb +102 -0
  23. data/lib/request_log_analyzer/file_format/amazon_s3.rb +74 -0
  24. data/lib/request_log_analyzer/file_format/apache.rb +147 -0
  25. data/lib/request_log_analyzer/file_format/delayed_job.rb +55 -0
  26. data/lib/request_log_analyzer/file_format/merb.rb +65 -29
  27. data/lib/request_log_analyzer/file_format/mysql.rb +101 -0
  28. data/lib/request_log_analyzer/file_format/postgresql.rb +68 -0
  29. data/lib/request_log_analyzer/file_format/rack.rb +9 -0
  30. data/lib/request_log_analyzer/file_format/rails.rb +164 -78
  31. data/lib/request_log_analyzer/file_format/rails3.rb +86 -0
  32. data/lib/request_log_analyzer/file_format/rails_development.rb +12 -0
  33. data/lib/request_log_analyzer/file_format.rb +252 -58
  34. data/lib/request_log_analyzer/filter/anonymize.rb +39 -0
  35. data/lib/request_log_analyzer/filter/field.rb +19 -13
  36. data/lib/request_log_analyzer/filter/timespan.rb +25 -12
  37. data/lib/request_log_analyzer/filter.rb +30 -0
  38. data/lib/request_log_analyzer/line_definition.rb +69 -96
  39. data/lib/request_log_analyzer/log_processor.rb +31 -53
  40. data/lib/request_log_analyzer/mailer.rb +65 -0
  41. data/lib/request_log_analyzer/output/fancy_html.rb +49 -0
  42. data/lib/request_log_analyzer/output/fixed_width.rb +220 -0
  43. data/lib/request_log_analyzer/output/html.rb +187 -0
  44. data/lib/request_log_analyzer/output.rb +117 -0
  45. data/lib/request_log_analyzer/request.rb +125 -40
  46. data/lib/request_log_analyzer/source/database_loader.rb +87 -0
  47. data/lib/request_log_analyzer/source/log_parser.rb +297 -0
  48. data/lib/request_log_analyzer/source.rb +72 -0
  49. data/lib/request_log_analyzer/tracker/duration.rb +28 -64
  50. data/lib/request_log_analyzer/tracker/frequency.rb +108 -0
  51. data/lib/request_log_analyzer/tracker/hourly_spread.rb +76 -49
  52. data/lib/request_log_analyzer/tracker/numeric_value.rb +223 -0
  53. data/lib/request_log_analyzer/tracker/timespan.rb +54 -27
  54. data/lib/request_log_analyzer/tracker/traffic.rb +40 -0
  55. data/lib/request_log_analyzer/tracker.rb +101 -0
  56. data/lib/request_log_analyzer.rb +43 -13
  57. data/request-log-analyzer.gemspec +41 -0
  58. data/spec/database.yml +23 -0
  59. data/spec/fixtures/apache_combined.log +5 -0
  60. data/spec/fixtures/apache_common.log +10 -0
  61. data/spec/fixtures/decompression.log +12 -0
  62. data/spec/fixtures/decompression.log.bz2 +0 -0
  63. data/spec/fixtures/decompression.log.gz +0 -0
  64. data/spec/fixtures/decompression.log.zip +0 -0
  65. data/spec/fixtures/decompression.tar.gz +0 -0
  66. data/spec/fixtures/decompression.tgz +0 -0
  67. data/spec/fixtures/header_and_footer.log +6 -0
  68. data/spec/fixtures/merb_prefixed.log +9 -0
  69. data/spec/fixtures/mysql_slow_query.log +110 -0
  70. data/spec/fixtures/postgresql.log +2980 -0
  71. data/spec/fixtures/rails.db +0 -0
  72. data/spec/fixtures/rails_22.log +1 -1
  73. data/spec/fixtures/sinatra.log +99 -0
  74. data/spec/integration/command_line_usage_spec.rb +84 -0
  75. data/spec/integration/mailer_spec.rb +179 -0
  76. data/spec/integration/munin_plugins_rails_spec.rb +58 -0
  77. data/spec/integration/scout_spec.rb +152 -0
  78. data/spec/lib/helpers.rb +72 -0
  79. data/spec/lib/macros.rb +18 -0
  80. data/spec/lib/matchers.rb +77 -0
  81. data/spec/lib/mocks.rb +77 -0
  82. data/spec/lib/testing_format.rb +46 -0
  83. data/spec/spec_helper.rb +16 -59
  84. data/spec/unit/aggregator/database_inserter_spec.rb +93 -0
  85. data/spec/unit/aggregator/summarizer_spec.rb +26 -0
  86. data/spec/unit/controller/controller_spec.rb +41 -0
  87. data/spec/unit/controller/log_processor_spec.rb +18 -0
  88. data/spec/unit/database/base_class_spec.rb +183 -0
  89. data/spec/unit/database/connection_spec.rb +34 -0
  90. data/spec/unit/database/database_spec.rb +133 -0
  91. data/spec/unit/file_format/amazon_s3_format_spec.rb +67 -0
  92. data/spec/unit/file_format/apache_format_spec.rb +203 -0
  93. data/spec/unit/file_format/common_regular_expressions_spec.rb +53 -0
  94. data/spec/unit/file_format/delayed_job_format_spec.rb +83 -0
  95. data/spec/unit/file_format/file_format_api_spec.rb +69 -0
  96. data/spec/unit/file_format/format_autodetection_spec.rb +40 -0
  97. data/spec/unit/file_format/line_definition_spec.rb +75 -0
  98. data/spec/unit/file_format/merb_format_spec.rb +52 -0
  99. data/spec/unit/file_format/mysql_format_spec.rb +154 -0
  100. data/spec/unit/file_format/postgresql_format_spec.rb +65 -0
  101. data/spec/unit/file_format/rack_format_spec.rb +50 -0
  102. data/spec/unit/file_format/rails3_format_spec.rb +120 -0
  103. data/spec/unit/file_format/rails_format_spec.rb +180 -0
  104. data/spec/unit/filter/anonymize_filter_spec.rb +21 -0
  105. data/spec/unit/filter/field_filter_spec.rb +66 -0
  106. data/spec/unit/filter/filter_spec.rb +17 -0
  107. data/spec/unit/filter/timespan_filter_spec.rb +58 -0
  108. data/spec/unit/mailer_spec.rb +42 -0
  109. data/spec/unit/request_spec.rb +111 -0
  110. data/spec/unit/source/log_parser_spec.rb +119 -0
  111. data/spec/unit/tracker/duration_tracker_spec.rb +49 -0
  112. data/spec/unit/tracker/frequency_tracker_spec.rb +88 -0
  113. data/spec/unit/tracker/hourly_spread_spec.rb +79 -0
  114. data/spec/unit/tracker/numeric_value_tracker_spec.rb +166 -0
  115. data/spec/unit/tracker/timespan_tracker_spec.rb +73 -0
  116. data/spec/unit/tracker/tracker_api_spec.rb +125 -0
  117. data/spec/unit/tracker/traffic_tracker_spec.rb +28 -0
  118. data/tasks/github-gem.rake +290 -139
  119. data/tasks/request_log_analyzer.rake +23 -7
  120. metadata +221 -94
  121. data/DESIGN +0 -14
  122. data/HACKING +0 -7
  123. data/README.textile +0 -36
  124. data/lib/cli/bashcolorizer.rb +0 -60
  125. data/lib/request_log_analyzer/aggregator/database.rb +0 -148
  126. data/lib/request_log_analyzer/filter/base.rb +0 -29
  127. data/lib/request_log_analyzer/log_parser.rb +0 -173
  128. data/lib/request_log_analyzer/source/base.rb +0 -42
  129. data/lib/request_log_analyzer/source/log_file.rb +0 -170
  130. data/lib/request_log_analyzer/tracker/base.rb +0 -54
  131. data/lib/request_log_analyzer/tracker/category.rb +0 -71
  132. data/spec/controller_spec.rb +0 -40
  133. data/spec/database_inserter_spec.rb +0 -101
  134. data/spec/file_format_spec.rb +0 -78
  135. data/spec/file_formats/spec_format.rb +0 -26
  136. data/spec/filter_spec.rb +0 -137
  137. data/spec/line_definition_spec.rb +0 -124
  138. data/spec/log_parser_spec.rb +0 -68
  139. data/spec/log_processor_spec.rb +0 -57
  140. data/spec/merb_format_spec.rb +0 -38
  141. data/spec/rails_format_spec.rb +0 -76
  142. data/spec/request_spec.rb +0 -72
  143. data/spec/summarizer_spec.rb +0 -9
  144. data/tasks/rspec.rake +0 -6
@@ -1,173 +0,0 @@
1
- module RequestLogAnalyzer
2
-
3
- # The LogParser class reads log data from a given source and uses a file format definition
4
- # to parse all relevent information about requests from the file. A FileFormat module should
5
- # be provided that contains the definitions of the lines that occur in the log data.
6
- #
7
- # De order in which lines occur is used to combine lines to a single request. If these lines
8
- # are mixed, requests cannot be combined properly. This can be the case if data is written to
9
- # the log file simultaneously by different mongrel processes. This problem is detected by the
10
- # parser, but the requests that are mixed up cannot be parsed. It will emit warnings when this
11
- # occurs.
12
- class LogParser
13
-
14
- include RequestLogAnalyzer::FileFormat::Awareness
15
-
16
- # A hash of options
17
- attr_reader :options
18
-
19
- # The current Request object that is being parsed
20
- attr_reader :current_request
21
-
22
- # The total number of parsed lines
23
- attr_reader :parsed_lines
24
-
25
- # The total number of parsed requests.
26
- attr_reader :parsed_requests
27
-
28
- # The number of skipped requests because of date constraints
29
- attr_reader :skipped_requests
30
-
31
- # Initializes the parser instance.
32
- # It will apply the language specific FileFormat module to this instance. It will use the line
33
- # definitions in this module to parse any input.
34
- def initialize(format, options = {})
35
- @line_definitions = {}
36
- @options = options
37
- @parsed_lines = 0
38
- @parsed_requests = 0
39
- @skipped_requests = 0
40
-
41
- @current_io = nil
42
-
43
- # install the file format module (see RequestLogAnalyzer::FileFormat)
44
- # and register all the line definitions to the parser
45
- self.register_file_format(format)
46
- end
47
-
48
- # Parses a list of consequent files of the same format
49
- def parse_files(files, options = {}, &block)
50
- files.each { |file| parse_file(file, options, &block) }
51
- end
52
-
53
- # Parses a file.
54
- # Creates an IO stream for the provided file, and sends it to parse_io for further handling
55
- def parse_file(file, options = {}, &block)
56
- @progress_handler.call(:started, file) if @progress_handler
57
- File.open(file, 'r') { |f| parse_io(f, options, &block) }
58
- @progress_handler.call(:finished, file) if @progress_handler
59
- end
60
-
61
- def parse_stream(stream, options = {}, &block)
62
- parse_io(stream, options, &block)
63
- end
64
-
65
- # Finds a log line and then parses the information in the line.
66
- # Yields a hash containing the information found.
67
- # <tt>*line_types</tt> The log line types to look for (defaults to LOG_LINES.keys).
68
- # Yeilds a Hash when it encounters a chunk of information.
69
- def parse_io(io, options = {}, &block)
70
-
71
- # parse every line type by default
72
- line_types = options[:line_types] || file_format.line_definitions.keys
73
-
74
- # check whether all provided line types are valid
75
- unknown = line_types.reject { |line_type| file_format.line_definitions.has_key?(line_type) }
76
- raise "Unknown line types: #{unknown.join(', ')}" unless unknown.empty?
77
-
78
- @current_io = io
79
- @current_io.each_line do |line|
80
-
81
- @progress_handler.call(:progress, @current_io.pos) if @progress_handler && @current_io.kind_of?(File)
82
-
83
- request_data = nil
84
- line_types.each do |line_type|
85
- line_type_definition = file_format.line_definitions[line_type]
86
- break if request_data = line_type_definition.matches(line, @current_io.lineno, self)
87
- end
88
-
89
- if request_data
90
- @parsed_lines += 1
91
- update_current_request(request_data, &block)
92
- end
93
- end
94
-
95
- warn(:unfinished_request_on_eof, "End of file reached, but last request was not completed!") unless @current_request.nil?
96
-
97
- @current_io = nil
98
- end
99
-
100
- # Add a block to this method to install a progress handler while parsing
101
- def progress=(proc)
102
- @progress_handler = proc
103
- end
104
-
105
- # Add a block to this method to install a warning handler while parsing
106
- def warning=(proc)
107
- @warning_handler = proc
108
- end
109
-
110
- # This method is called by the parser if it encounteres any problems.
111
- # It will call the warning handler. The default controller will pass all warnings to every
112
- # aggregator that is registered and running
113
- def warn(type, message)
114
- @warning_handler.call(type, message, @current_io.lineno) if @warning_handler
115
- end
116
-
117
- protected
118
-
119
- # Combines the different lines of a request into a single Request object. It will start a
120
- # new request when a header line is encountered en will emit the request when a footer line
121
- # is encountered.
122
- #
123
- # - Every line that is parsed before a header line is ignored as it cannot be included in
124
- # any request. It will emit a :no_current_request warning.
125
- # - A header line that is parsed before a request is closed by a footer line, is a sign of
126
- # an unprpertly ordered file. All data that is gathered for the request until then is
127
- # discarded, the next request is ignored as well and a :unclosed_request warning is
128
- # emitted.
129
- def update_current_request(request_data, &block)
130
- if header_line?(request_data)
131
- unless @current_request.nil?
132
- if options[:assume_correct_order]
133
- handle_request(@current_request, &block)
134
- @current_request = RequestLogAnalyzer::Request.create(@file_format, request_data)
135
- else
136
- warn(:unclosed_request, "Encountered header line, but previous request was not closed!")
137
- @current_request = nil # remove all data that was parsed, skip next request as well.
138
- end
139
- else
140
- @current_request = RequestLogAnalyzer::Request.create(@file_format, request_data)
141
- end
142
- else
143
- unless @current_request.nil?
144
- @current_request << request_data
145
- if footer_line?(request_data)
146
- handle_request(@current_request, &block)
147
- @current_request = nil
148
- end
149
- else
150
- warn(:no_current_request, "Parsebale line found outside of a request!")
151
- end
152
- end
153
- end
154
-
155
- # Handles the parsed request by calling the request handler.
156
- # The default controller will send the request to every running aggegator.
157
- def handle_request(request, &block)
158
- @parsed_requests += 1
159
- accepted = block_given? ? yield(request) : true
160
- @skipped_requests += 1 if !accepted
161
- end
162
-
163
- # Checks whether a given line hash is a header line.
164
- def header_line?(hash)
165
- file_format.line_definitions[hash[:line_type]].header
166
- end
167
-
168
- # Checks whether a given line hash is a footer line.
169
- def footer_line?(hash)
170
- file_format.line_definitions[hash[:line_type]].footer
171
- end
172
- end
173
- end
@@ -1,42 +0,0 @@
1
- module RequestLogAnalyzer::Source
2
- class Base
3
-
4
- include RequestLogAnalyzer::FileFormat::Awareness
5
-
6
- # A hash of options
7
- attr_reader :options
8
-
9
- # The current Request object that is being parsed
10
- attr_reader :current_request
11
-
12
- # The total number of parsed lines
13
- attr_reader :parsed_lines
14
-
15
- # The total number of parsed requests.
16
- attr_reader :parsed_requests
17
-
18
- # The number of skipped lines because of warnings
19
- attr_reader :skipped_lines
20
-
21
- # Base source class used to filter input requests.
22
-
23
- # Initializer
24
- # <tt>format</tt> The file format
25
- # <tt>options</tt> Are passed to the filters.
26
- def initialize(format, options = {})
27
- @options = options
28
- register_file_format(format)
29
- end
30
-
31
- def prepare
32
- end
33
-
34
- def requests(&block)
35
- return true
36
- end
37
-
38
- def finalize
39
- end
40
-
41
- end
42
- end
@@ -1,170 +0,0 @@
1
- module RequestLogAnalyzer::Source
2
-
3
- # The LogParser class reads log data from a given source and uses a file format definition
4
- # to parse all relevent information about requests from the file. A FileFormat module should
5
- # be provided that contains the definitions of the lines that occur in the log data.
6
- #
7
- # De order in which lines occur is used to combine lines to a single request. If these lines
8
- # are mixed, requests cannot be combined properly. This can be the case if data is written to
9
- # the log file simultaneously by different mongrel processes. This problem is detected by the
10
- # parser, but the requests that are mixed up cannot be parsed. It will emit warnings when this
11
- # occurs.
12
- class LogFile < RequestLogAnalyzer::Source::Base
13
-
14
- attr_reader :source_files
15
-
16
- # Initializes the parser instance.
17
- # It will apply the language specific FileFormat module to this instance. It will use the line
18
- # definitions in this module to parse any input.
19
- def initialize(format, options = {})
20
- @line_definitions = {}
21
- @options = options
22
- @parsed_lines = 0
23
- @parsed_requests = 0
24
- @skipped_lines = 0
25
- @current_io = nil
26
- @source_files = options[:source_files]
27
-
28
- # install the file format module (see RequestLogAnalyzer::FileFormat)
29
- # and register all the line definitions to the parser
30
- self.register_file_format(format)
31
- end
32
-
33
- def requests(options = {}, &block)
34
-
35
- case @source_files
36
- when IO;
37
- puts "Parsing from the standard input. Press CTRL+C to finish."
38
- parse_stream(@source_files, options, &block)
39
- when String
40
- parse_file(@source_files, options, &block)
41
- when Array
42
- parse_files(@source_files, options, &block)
43
- else
44
- raise "Unknown source provided"
45
- end
46
- end
47
-
48
- # Parses a list of consequent files of the same format
49
- def parse_files(files, options = {}, &block)
50
- files.each { |file| parse_file(file, options, &block) }
51
- end
52
-
53
- # Parses a file.
54
- # Creates an IO stream for the provided file, and sends it to parse_io for further handling
55
- def parse_file(file, options = {}, &block)
56
- @progress_handler.call(:started, file) if @progress_handler
57
- File.open(file, 'r') { |f| parse_io(f, options, &block) }
58
- @progress_handler.call(:finished, file) if @progress_handler
59
- end
60
-
61
- def parse_stream(stream, options = {}, &block)
62
- parse_io(stream, options, &block)
63
- end
64
-
65
- # Finds a log line and then parses the information in the line.
66
- # Yields a hash containing the information found.
67
- # <tt>*line_types</tt> The log line types to look for (defaults to LOG_LINES.keys).
68
- # Yeilds a Hash when it encounters a chunk of information.
69
- def parse_io(io, options = {}, &block)
70
-
71
- # parse every line type by default
72
- line_types = options[:line_types] || file_format.line_definitions.keys
73
-
74
- # check whether all provided line types are valid
75
- unknown = line_types.reject { |line_type| file_format.line_definitions.has_key?(line_type) }
76
- raise "Unknown line types: #{unknown.join(', ')}" unless unknown.empty?
77
-
78
- @current_io = io
79
- @current_io.each_line do |line|
80
-
81
- @progress_handler.call(:progress, @current_io.pos) if @progress_handler && @current_io.kind_of?(File)
82
-
83
- request_data = nil
84
- line_types.each do |line_type|
85
- line_type_definition = file_format.line_definitions[line_type]
86
- break if request_data = line_type_definition.matches(line, @current_io.lineno, self)
87
- end
88
-
89
- if request_data
90
- @parsed_lines += 1
91
- update_current_request(request_data, &block)
92
- end
93
- end
94
-
95
- warn(:unfinished_request_on_eof, "End of file reached, but last request was not completed!") unless @current_request.nil?
96
-
97
- @current_io = nil
98
- end
99
-
100
- # Add a block to this method to install a progress handler while parsing
101
- def progress=(proc)
102
- @progress_handler = proc
103
- end
104
-
105
- # Add a block to this method to install a warning handler while parsing
106
- def warning=(proc)
107
- @warning_handler = proc
108
- end
109
-
110
- # This method is called by the parser if it encounteres any problems.
111
- # It will call the warning handler. The default controller will pass all warnings to every
112
- # aggregator that is registered and running
113
- def warn(type, message)
114
- @warning_handler.call(type, message, @current_io.lineno) if @warning_handler
115
- end
116
-
117
- protected
118
-
119
- # Combines the different lines of a request into a single Request object. It will start a
120
- # new request when a header line is encountered en will emit the request when a footer line
121
- # is encountered.
122
- #
123
- # - Every line that is parsed before a header line is ignored as it cannot be included in
124
- # any request. It will emit a :no_current_request warning.
125
- # - A header line that is parsed before a request is closed by a footer line, is a sign of
126
- # an unprpertly ordered file. All data that is gathered for the request until then is
127
- # discarded, the next request is ignored as well and a :unclosed_request warning is
128
- # emitted.
129
- def update_current_request(request_data, &block)
130
- if header_line?(request_data)
131
- unless @current_request.nil?
132
- if options[:assume_correct_order]
133
- @parsed_requests += 1
134
- yield @current_request
135
- @current_request = RequestLogAnalyzer::Request.create(@file_format, request_data)
136
- else
137
- @skipped_lines += 1
138
- warn(:unclosed_request, "Encountered header line, but previous request was not closed!")
139
- @current_request = nil # remove all data that was parsed, skip next request as well.
140
- end
141
- else
142
- @current_request = RequestLogAnalyzer::Request.create(@file_format, request_data)
143
- end
144
- else
145
- unless @current_request.nil?
146
- @current_request << request_data
147
- if footer_line?(request_data)
148
- @parsed_requests += 1
149
- yield @current_request
150
- @current_request = nil
151
- end
152
- else
153
- @skipped_lines += 1
154
- warn(:no_current_request, "Parsebale line found outside of a request!")
155
- end
156
- end
157
- end
158
-
159
- # Checks whether a given line hash is a header line.
160
- def header_line?(hash)
161
- file_format.line_definitions[hash[:line_type]].header
162
- end
163
-
164
- # Checks whether a given line hash is a footer line.
165
- def footer_line?(hash)
166
- file_format.line_definitions[hash[:line_type]].footer
167
- end
168
- end
169
-
170
- end
@@ -1,54 +0,0 @@
1
- module RequestLogAnalyzer
2
- module Tracker
3
-
4
- # Base tracker. All other trackers inherit from this class
5
- #
6
- # Accepts the following options:
7
- # * <tt>:line_type</tt> The line type that contains the duration field (determined by the category proc).
8
- # * <tt>:if</tt> Proc that has to return !nil for a request to be passed to the tracker.
9
- # * <tt>:output</tt> Direct output here (defaults to STDOUT)
10
- #
11
- # For example :if => lambda { |request| request[:duration] && request[:duration] > 1.0 }
12
- class Base
13
-
14
- attr_reader :options
15
-
16
- def initialize(options ={})
17
- @options = options
18
- end
19
-
20
- def prepare
21
- end
22
-
23
- def update(request)
24
- end
25
-
26
- def finalize
27
- end
28
-
29
- def should_update?(request)
30
- return false if options[:line_type] && !request.has_line_type?(options[:line_type])
31
-
32
- if options[:if].kind_of?(Symbol)
33
- return false unless request[options[:if]]
34
- elsif options[:if].respond_to?(:call)
35
- return false unless options[:if].call(request)
36
- end
37
-
38
- if options[:unless].kind_of?(Symbol)
39
- return false if request[options[:unless]]
40
- elsif options[:unless].respond_to?(:call)
41
- return false if options[:unless].call(request)
42
- end
43
-
44
- return true
45
- end
46
-
47
- def report(output=STDOUT, report_width = 80, color = false)
48
- output << self.inspect
49
- output << "\n"
50
- end
51
-
52
- end
53
- end
54
- end
@@ -1,71 +0,0 @@
1
- module RequestLogAnalyzer::Tracker
2
-
3
- # Catagorize requests.
4
- # Count and analyze requests for a specific attribute
5
- #
6
- # Accepts the following options:
7
- # * <tt>:line_type</tt> The line type that contains the duration field (determined by the category proc).
8
- # * <tt>:if</tt> Proc that has to return !nil for a request to be passed to the tracker.
9
- # * <tt>:title</tt> Title do be displayed above the report.
10
- # * <tt>:category</tt> Proc that handles the request categorization.
11
- # * <tt>:amount</tt> The amount of lines in the report
12
- #
13
- # The items in the update request hash are set during the creation of the Duration tracker.
14
- #
15
- # Example output:
16
- # HTTP methods
17
- # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
18
- # GET ┃ 22248 hits (46.2%) ┃░░░░░░░░░░░░░░░░░
19
- # PUT ┃ 13685 hits (28.4%) ┃░░░░░░░░░░░
20
- # POST ┃ 11662 hits (24.2%) ┃░░░░░░░░░
21
- # DELETE ┃ 512 hits (1.1%) ┃
22
- class Category < RequestLogAnalyzer::Tracker::Base
23
-
24
- attr_reader :categories
25
-
26
- def prepare
27
- raise "No categorizer set up for category tracker #{self.inspect}" unless options[:category]
28
- @categories = {}
29
- if options[:all_categories].kind_of?(Enumerable)
30
- options[:all_categories].each { |cat| @categories[cat] = 0 }
31
- end
32
- end
33
-
34
- def update(request)
35
- cat = options[:category].respond_to?(:call) ? options[:category].call(request) : request[options[:category]]
36
- if !cat.nil? || options[:nils]
37
- @categories[cat] ||= 0
38
- @categories[cat] += 1
39
- end
40
- end
41
-
42
- def report(output = STDOUT, report_width = 80, color = false)
43
- if options[:title]
44
- output << "\n#{options[:title]}\n"
45
- output << green(('━' * report_width), color) + "\n"
46
- end
47
-
48
- if @categories.empty?
49
- output << "None found.\n"
50
- else
51
- sorted_categories = @categories.sort { |a, b| b[1] <=> a[1] }
52
- total_hits = sorted_categories.inject(0) { |carry, item| carry + item[1] }
53
- sorted_categories = sorted_categories.slice(0...options[:amount]) if options[:amount]
54
-
55
- adjuster = color ? 33 : 24 # justifcation calcultaion is slight different when color codes are inserterted
56
- max_cat_length = [sorted_categories.map { |c| c[0].to_s.length }.max, report_width - adjuster].min
57
- sorted_categories.each do |(cat, count)|
58
- text = "%-#{max_cat_length+1}s┃%7d hits %s" % [cat.to_s[0..max_cat_length], count, (green("(%0.01f%%)", color) % [(count.to_f / total_hits) * 100])]
59
- space_left = report_width - (max_cat_length + adjuster + 3)
60
- if space_left > 3
61
- bar_chars = (space_left * (count.to_f / total_hits)).round
62
- output << "%-#{max_cat_length + adjuster}s %s%s" % [text, '┃', '░' * bar_chars] + "\n"
63
- else
64
- output << text + "\n"
65
- end
66
- end
67
- end
68
- end
69
-
70
- end
71
- end
@@ -1,40 +0,0 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
2
-
3
- describe RequestLogAnalyzer::Controller do
4
-
5
- include RequestLogAnalyzerSpecHelper
6
-
7
- # it "should include the file format module" do
8
- # controller = RequestLogAnalyzer::Controller.new(:rails)
9
- # (class << controller; self; end).ancestors.include?(RequestLogAnalyzer::FileFormat::Rails)
10
- # end
11
-
12
- it "should call the aggregators when run" do
13
- file_format = RequestLogAnalyzer::FileFormat.load(:rails)
14
- source = RequestLogAnalyzer::Source::LogFile.new(file_format, :source_files => log_fixture(:rails_1x))
15
- controller = RequestLogAnalyzer::Controller.new(source)
16
-
17
- mock_aggregator = mock('aggregator')
18
- mock_aggregator.should_receive(:prepare).once.ordered
19
- mock_aggregator.should_receive(:aggregate).with(an_instance_of(RequestLogAnalyzer::Request)).at_least(:twice).ordered
20
- mock_aggregator.should_receive(:finalize).once.ordered
21
- mock_aggregator.should_receive(:report).once.ordered
22
-
23
- another_mock_aggregator = mock('another aggregator')
24
- another_mock_aggregator.should_receive(:prepare).once.ordered
25
- another_mock_aggregator.should_receive(:aggregate).with(an_instance_of(RequestLogAnalyzer::Request)).at_least(:twice).ordered
26
- another_mock_aggregator.should_receive(:finalize).once.ordered
27
- another_mock_aggregator.should_receive(:report).once.ordered
28
-
29
-
30
- controller.aggregators << mock_aggregator << another_mock_aggregator
31
- controller.run!
32
- end
33
-
34
- it "should run well from the command line" do
35
- temp_file = "#{File.dirname(__FILE__)}/fixtures/temp.txt"
36
- system("#{File.dirname(__FILE__)}/../bin/request-log-analyzer #{log_fixture(:rails_1x)} > #{temp_file}").should be_true
37
- File.unlink(temp_file)
38
- end
39
-
40
- end
@@ -1,101 +0,0 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
2
- require 'request_log_analyzer/aggregator/database'
3
-
4
-
5
- describe RequestLogAnalyzer::Aggregator::Database, "schema creation" do
6
-
7
- TEST_DATABASE_FILE = File.dirname(__FILE__) + "/fixtures/requests.db"
8
- include RequestLogAnalyzerSpecHelper
9
-
10
- before(:each) do
11
- log_parser = RequestLogAnalyzer::LogParser.new(spec_format)
12
- @database_inserter = RequestLogAnalyzer::Aggregator::Database.new(log_parser, :database => TEST_DATABASE_FILE)
13
- end
14
-
15
- after(:each) do
16
- File.unlink(TEST_DATABASE_FILE) if File.exist?(TEST_DATABASE_FILE)
17
- end
18
-
19
- it "should create the correct tables" do
20
- ActiveRecord::Migration.should_receive(:create_table).with("warnings")
21
- ActiveRecord::Migration.should_receive(:create_table).with("requests")
22
- ActiveRecord::Migration.should_receive(:create_table).with("first_lines")
23
- ActiveRecord::Migration.should_receive(:create_table).with("test_lines")
24
- ActiveRecord::Migration.should_receive(:create_table).with("last_lines")
25
- @database_inserter.prepare
26
- end
27
-
28
- it "should create a default Request class" do
29
- @database_inserter.prepare
30
- SpecFormat::Database::Request.ancestors.should include(ActiveRecord::Base)
31
- SpecFormat::Database::Request.column_names.should include('first_lineno')
32
- SpecFormat::Database::Request.column_names.should include('last_lineno')
33
- end
34
-
35
- it "should create associations for the default Request class" do
36
- @database_inserter.prepare
37
- @request = SpecFormat::Database::Request.new
38
- @request.should respond_to(:test_lines)
39
- @request.test_lines.should
40
- end
41
-
42
- it "should create the default table names" do
43
- @database_inserter.prepare
44
- @database_inserter.file_format.line_definitions.each do |name, definition|
45
- klass = SpecFormat::Database.const_get("#{name}_line".camelize)
46
- klass.column_names.should include('id')
47
- klass.column_names.should include('lineno')
48
- klass.column_names.should include('request_id')
49
- end
50
- end
51
-
52
- it "should create the correct fields in the table" do
53
- @database_inserter.prepare
54
-
55
- SpecFormat::Database::FirstLine.column_names.should include('request_no')
56
- SpecFormat::Database::LastLine.column_names.should include('request_no')
57
- SpecFormat::Database::TestLine.column_names.should include('test_capture')
58
- end
59
-
60
- end
61
-
62
- describe RequestLogAnalyzer::Aggregator::Database, "record insertion" do
63
- include RequestLogAnalyzerSpecHelper
64
-
65
- before(:each) do
66
- log_parser = RequestLogAnalyzer::LogParser.new(spec_format)
67
- @database_inserter = RequestLogAnalyzer::Aggregator::Database.new(log_parser, :database => TEST_DATABASE_FILE)
68
- @database_inserter.prepare
69
-
70
- @incomplete_request = RequestLogAnalyzer::Request.create(spec_format, {:line_type => :first, :request_no => 564})
71
- @completed_request = RequestLogAnalyzer::Request.create(spec_format,
72
- {:line_type => :first, :request_no => 564},
73
- {:line_type => :test, :test_capture => "awesome"},
74
- {:line_type => :test, :test_capture => "indeed"},
75
- {:line_type => :last, :request_no => 564})
76
- end
77
-
78
- after(:each) do
79
- File.unlink(TEST_DATABASE_FILE) if File.exist?(TEST_DATABASE_FILE)
80
- end
81
-
82
- it "should insert a record in the request table" do
83
- SpecFormat::Database::Request.count.should == 0
84
- @database_inserter.aggregate(@incomplete_request)
85
- SpecFormat::Database::Request.count.should == 1
86
- end
87
-
88
- it "should insert records in all relevant line tables" do
89
- @database_inserter.aggregate(@completed_request)
90
- request = SpecFormat::Database::Request.first
91
- request.should have(2).test_lines
92
- request.should have(1).first_lines
93
- request.should have(1).last_lines
94
- end
95
-
96
- it "should log a warning in the warnings table" do
97
- SpecFormat::Database::Warning.should_receive(:create!).with(hash_including(:warning_type => 'test_warning'))
98
- @database_inserter.warning(:test_warning, "Testing the warning system", 12)
99
- end
100
-
101
- end