request-log-analyzer 1.7.0 → 1.10.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.
Files changed (88) hide show
  1. data/.gitignore +4 -1
  2. data/DESIGN.rdoc +0 -1
  3. data/Gemfile +2 -0
  4. data/README.rdoc +16 -5
  5. data/bin/request-log-analyzer +4 -2
  6. data/lib/cli/database_console_init.rb +1 -1
  7. data/lib/cli/tools.rb +2 -1
  8. data/lib/mixins/gets_memory_protection.rb +1 -1
  9. data/lib/request_log_analyzer/aggregator/summarizer.rb +2 -2
  10. data/lib/request_log_analyzer/aggregator.rb +3 -3
  11. data/lib/request_log_analyzer/controller.rb +42 -36
  12. data/lib/request_log_analyzer/database.rb +5 -3
  13. data/lib/request_log_analyzer/file_format/apache.rb +19 -28
  14. data/lib/request_log_analyzer/file_format/delayed_job2.rb +38 -0
  15. data/lib/request_log_analyzer/file_format/delayed_job21.rb +39 -0
  16. data/lib/request_log_analyzer/file_format/haproxy.rb +138 -0
  17. data/lib/request_log_analyzer/file_format/oink.rb +96 -0
  18. data/lib/request_log_analyzer/file_format/postgresql.rb +7 -6
  19. data/lib/request_log_analyzer/file_format/rails.rb +4 -4
  20. data/lib/request_log_analyzer/file_format/rails3.rb +34 -11
  21. data/lib/request_log_analyzer/file_format/w3c.rb +56 -0
  22. data/lib/request_log_analyzer/file_format.rb +33 -10
  23. data/lib/request_log_analyzer/filter.rb +3 -5
  24. data/lib/request_log_analyzer/output/fancy_html.rb +0 -5
  25. data/lib/request_log_analyzer/output.rb +6 -7
  26. data/lib/request_log_analyzer/request.rb +17 -3
  27. data/lib/request_log_analyzer/source/log_parser.rb +3 -1
  28. data/lib/request_log_analyzer/source.rb +2 -6
  29. data/lib/request_log_analyzer/tracker/duration.rb +7 -0
  30. data/lib/request_log_analyzer/tracker/numeric_value.rb +123 -12
  31. data/lib/request_log_analyzer/tracker/traffic.rb +7 -0
  32. data/lib/request_log_analyzer/tracker.rb +6 -5
  33. data/lib/request_log_analyzer.rb +36 -17
  34. data/request-log-analyzer.gemspec +9 -6
  35. data/spec/fixtures/oink_22.log +19 -0
  36. data/spec/fixtures/oink_22_failure.log +17 -0
  37. data/spec/fixtures/postgresql.log +29 -2980
  38. data/spec/integration/command_line_usage_spec.rb +4 -4
  39. data/spec/integration/mailer_spec.rb +20 -4
  40. data/spec/integration/munin_plugins_rails_spec.rb +1 -1
  41. data/spec/integration/scout_spec.rb +1 -1
  42. data/spec/lib/helpers.rb +2 -2
  43. data/spec/lib/macros.rb +1 -1
  44. data/spec/lib/matchers.rb +1 -1
  45. data/spec/lib/mocks.rb +1 -1
  46. data/spec/spec_helper.rb +10 -10
  47. data/spec/unit/aggregator/database_inserter_spec.rb +1 -1
  48. data/spec/unit/aggregator/summarizer_spec.rb +1 -1
  49. data/spec/unit/controller/controller_spec.rb +1 -1
  50. data/spec/unit/controller/log_processor_spec.rb +1 -1
  51. data/spec/unit/database/base_class_spec.rb +1 -1
  52. data/spec/unit/database/connection_spec.rb +1 -1
  53. data/spec/unit/database/database_spec.rb +1 -1
  54. data/spec/unit/file_format/amazon_s3_format_spec.rb +1 -1
  55. data/spec/unit/file_format/apache_format_spec.rb +18 -1
  56. data/spec/unit/file_format/common_regular_expressions_spec.rb +1 -1
  57. data/spec/unit/file_format/delayed_job21_format_spec.rb +60 -0
  58. data/spec/unit/file_format/delayed_job2_format_spec.rb +60 -0
  59. data/spec/unit/file_format/delayed_job_format_spec.rb +2 -2
  60. data/spec/unit/file_format/file_format_api_spec.rb +2 -2
  61. data/spec/unit/file_format/format_autodetection_spec.rb +7 -7
  62. data/spec/unit/file_format/haproxy_format_spec.rb +84 -0
  63. data/spec/unit/file_format/line_definition_spec.rb +1 -1
  64. data/spec/unit/file_format/merb_format_spec.rb +1 -1
  65. data/spec/unit/file_format/mysql_format_spec.rb +1 -1
  66. data/spec/unit/file_format/oink_format_spec.rb +105 -0
  67. data/spec/unit/file_format/postgresql_format_spec.rb +24 -46
  68. data/spec/unit/file_format/rack_format_spec.rb +1 -1
  69. data/spec/unit/file_format/rails3_format_spec.rb +41 -14
  70. data/spec/unit/file_format/rails_format_spec.rb +11 -1
  71. data/spec/unit/file_format/w3c_format_spec.rb +46 -0
  72. data/spec/unit/filter/anonymize_filter_spec.rb +1 -1
  73. data/spec/unit/filter/field_filter_spec.rb +1 -1
  74. data/spec/unit/filter/filter_spec.rb +1 -1
  75. data/spec/unit/filter/timespan_filter_spec.rb +1 -1
  76. data/spec/unit/mailer_spec.rb +1 -1
  77. data/spec/unit/request_spec.rb +1 -1
  78. data/spec/unit/source/log_parser_spec.rb +1 -1
  79. data/spec/unit/tracker/duration_tracker_spec.rb +4 -2
  80. data/spec/unit/tracker/frequency_tracker_spec.rb +1 -1
  81. data/spec/unit/tracker/hourly_spread_spec.rb +1 -1
  82. data/spec/unit/tracker/numeric_value_tracker_spec.rb +38 -17
  83. data/spec/unit/tracker/timespan_tracker_spec.rb +1 -1
  84. data/spec/unit/tracker/tracker_api_spec.rb +1 -1
  85. data/spec/unit/tracker/traffic_tracker_spec.rb +1 -1
  86. data/tasks/github-gem.rake +97 -60
  87. data/tasks/request_log_analyzer.rake +102 -0
  88. metadata +198 -153
data/.gitignore CHANGED
@@ -4,7 +4,10 @@ request-log-analyzer-*.gem
4
4
  requests.db
5
5
  /pkg
6
6
  /doc
7
+ .yardoc/
7
8
  /tmp
8
9
  /classes
9
10
  /files
10
- /coverage
11
+ /coverage
12
+ Gemfile.lock
13
+ /.bundle/
data/DESIGN.rdoc CHANGED
@@ -38,4 +38,3 @@ into SQLite database Y. The Summarizer will output its reports.
38
38
 
39
39
  The output is pushed to a RequestLogAnalyzer::Output object, which takes care of the output.
40
40
  It can generate either ASCII, UTF8 or even HTML output.
41
-
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/README.rdoc CHANGED
@@ -4,11 +4,11 @@ This is a simple command line tool to analyze request log files in various forma
4
4
 
5
5
  * Analyzes log files. Currently supports: Amazon S3, Apache, Delayed::Job, Merb, Mysql, PostgreSQL, Rack, Rails and more.
6
6
  * Combines multiple files and decompresses compressed files, which comes in handy if you are using logrotate.
7
- * Uses several metrics, including cumulative request time, mean request time, process blockers, database and rendering time, HTTP methods and statuses, Rails action cache statistics, etc.) (Sample output: http://wiki.github.com/wvanbergen/request-log-analyzer/sample-output)
7
+ * Uses several metrics, including cumulative request time, mean request time, process blockers, database and rendering time, HTTP methods and statuses, Rails action cache statistics, etc.) (Sample output: http://github.com/wvanbergen/request-log-analyzer/wiki/sample-output)
8
8
  * Low memory footprint and reasonably fast, so it is safe to run on a production server.
9
9
  * MIT licensed
10
10
 
11
- See the project wiki at http://wiki.github.com/wvanbergen/request-log-analyzer for documentation and additional information.
11
+ See the project wiki at http://github.com/wvanbergen/request-log-analyzer/wiki for documentation and additional information.
12
12
 
13
13
  == Installation & basic usage
14
14
 
@@ -22,7 +22,17 @@ request-log-analyzer like this:
22
22
 
23
23
  $ request-log-analyzer log/production.log
24
24
 
25
- For more details, other file formats, and available command line options, see the project's wiki at http://wiki.github.com/wvanbergen/request-log-analyzer
25
+ For more details, other file formats, and available command line options, see the project's wiki at http://github.com/wvanbergen/request-log-analyzer/wiki
26
+
27
+ == Benchmarking on different rubies
28
+
29
+ $ time ./bin/request-log-analyzer 40MBRailsFile.log
30
+
31
+ ruby-1.9.2-p0 15.19s user 0.95s system 99% cpu 16.143 total
32
+ ruby-1.8.7 25.21s user 1.02s system 99% cpu 26.238 total
33
+ jruby-1.5.3 32.64s user 4.84s system 99% cpu 37.629 total
34
+ rbx 46.60s user 2.71s system 108% cpu 45.324 total
35
+ macruby 75.00s user 20.10s system 110% cpu 1:26.13 total
26
36
 
27
37
  == Additional information
28
38
 
@@ -33,6 +43,7 @@ Do you have a rails application that is not performing as it should? If you need
33
43
  an expert to analyze your application, feel free to contact either Willem van
34
44
  Bergen (willem@railsdoctors.com) or Bart ten Brinke (bart@railsdoctors.com).
35
45
 
36
- * Project wiki at GitHub: http://wiki.github.com/wvanbergen/request-log-analyzer
46
+ * Project wiki at GitHub: http://github.com/wvanbergen/request-log-analyzer/wiki
47
+ * Issue tracker at GitHub: http://github.com/wvanbergen/request-log-analyzer/issues
37
48
  * railsdoctors homepage: http://railsdoctors.com
38
- * wvanbergen's blog posts: http://techblog.floorplanner.com/tag/request-log-analyzer
49
+ * wvanbergen's blog posts: http://techblog.floorplanner.com/tag/request-log-analyzer
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # encoding: utf-8
3
3
 
4
- $:.unshift(File.dirname(__FILE__) + '/../lib')
4
+ $:.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
5
5
  require 'request_log_analyzer'
6
6
  require 'cli/command_line_arguments'
7
7
  require 'cli/progressbar'
@@ -34,6 +34,7 @@ begin
34
34
  command_line.option(:file, :alias => :e)
35
35
  command_line.option(:mail, :alias => :m)
36
36
  command_line.option(:mailhost, :default => 'localhost')
37
+ command_line.option(:mailsubject)
37
38
  command_line.option(:parse_strategy, :default => 'assume-correct')
38
39
  command_line.option(:yaml)
39
40
  command_line.option(:dump) # To be deprecated
@@ -85,12 +86,14 @@ rescue CommandLine::Error => e
85
86
  puts " --file <filename> Redirect output to file."
86
87
  puts " --mail <emailaddress> Send report to an email address."
87
88
  puts " --mailhost <server> Use the given server as the SMTP server for sending email."
89
+ puts " --mailsubject <text> Overwrite default mailsubject."
88
90
  puts " --no-progress Hide the progress bar."
89
91
  puts " --output <format> Output format. Supports 'html' and 'fixed_width'."
90
92
  puts " --report-width <amount> Width of ASCII report. Defaults to terminal width."
91
93
  puts " --report-amount <amount> Maximum numer of results per report."
92
94
  puts " --silent Skip the commercials (includes --no-progress)."
93
95
  puts " --yaml <filename> Dump the results in YAML format in the given file."
96
+ puts " --parse-strategy <strategy> Legal values are 'assume-correct' or 'cautious'."
94
97
  puts
95
98
  puts "Examples:"
96
99
  puts " request-log-analyzer production.log"
@@ -112,7 +115,6 @@ when :console
112
115
  require 'cli/database_console'
113
116
  DatabaseConsole.new(arguments).run!
114
117
  when :strip
115
- require File.dirname(__FILE__) + '/../lib/request_log_analyzer/log_processor'
116
118
  RequestLogAnalyzer::LogProcessor.build(:strip, arguments).run!
117
119
  else
118
120
  unless arguments[:silent]
@@ -1,5 +1,5 @@
1
1
  # Setup the include path
2
- $:.unshift(File.dirname(__FILE__) + '/..')
2
+ $:.unshift(File.expand_path('..', File.dirname(__FILE__))
3
3
 
4
4
  $database = RequestLogAnalyzer::Database.new(ENV['RLA_DBCONSOLE_DATABASE'])
5
5
  $database.load_database_schema!
data/lib/cli/tools.rb CHANGED
@@ -34,7 +34,8 @@ def install_rake_tasks(install_type = :rails)
34
34
  if install_type.to_sym == :rails
35
35
  require 'ftools'
36
36
  if File.directory?('./lib/tasks/')
37
- File.copy(File.dirname(__FILE__) + '/../../tasks/request_log_analyzer.rake', './lib/tasks/request_log_analyze.rake')
37
+ task_file = File.expand_path('../../tasks/request_log_analyzer.rake', File.dirname(__FILE__))
38
+ File.copy(task_file, './lib/tasks/request_log_analyze.rake')
38
39
  puts "Installed rake tasks."
39
40
  puts "To use, run: rake rla:report"
40
41
  else
@@ -29,7 +29,7 @@ class File
29
29
  if @last_pos.nil?
30
30
  seek(0, IO::SEEK_END)
31
31
  @last_pos = pos
32
- seek(0,0)
32
+ seek(@current_pos, IO::SEEK_SET)
33
33
  end
34
34
 
35
35
  #
@@ -32,7 +32,7 @@ module RequestLogAnalyzer::Aggregator
32
32
  # <tt>optiont</tt> The options to pass to the trackers.
33
33
  def track(tracker_klass, value_field = {}, other_options = {})
34
34
  options = value_field.kind_of?(Symbol) ? other_options.merge(:value => value_field) : value_field.merge(other_options)
35
- tracker_klass = RequestLogAnalyzer::Tracker.const_get(RequestLogAnalyzer::to_camelcase(tracker_klass)) if tracker_klass.kind_of?(Symbol)
35
+ tracker_klass = RequestLogAnalyzer::Tracker.const_get(RequestLogAnalyzer.to_camelcase(tracker_klass)) if tracker_klass.kind_of?(Symbol)
36
36
  @trackers << tracker_klass.new(options)
37
37
  end
38
38
  end
@@ -85,7 +85,7 @@ module RequestLogAnalyzer::Aggregator
85
85
  trackers_export = @trackers.inject({}) do |export, tracker|
86
86
  export[tracker.title] = tracker.to_yaml_object; export
87
87
  end
88
- YAML::dump(trackers_export)
88
+ YAML.dump(trackers_export)
89
89
  end
90
90
 
91
91
  # Call report on all trackers.
@@ -1,8 +1,8 @@
1
1
  module RequestLogAnalyzer::Aggregator
2
2
 
3
- def self.const_missing(const)
4
- RequestLogAnalyzer::load_default_class_file(self, const)
5
- end
3
+ autoload :Echo, 'request_log_analyzer/aggregator/echo'
4
+ autoload :Summarizer, 'request_log_analyzer/aggregator/summarizer'
5
+ autoload :DatabaseInserter, 'request_log_analyzer/aggregator/database_inserter'
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).
@@ -20,11 +20,11 @@ module RequestLogAnalyzer
20
20
  # Builds a RequestLogAnalyzer::Controller given parsed command line arguments
21
21
  # <tt>arguments<tt> A CommandLine::Arguments hash containing parsed commandline parameters.
22
22
  def self.build_from_arguments(arguments)
23
-
24
- require 'lib/mixins/gets_memory_protection' if arguments[:gets_memory_protection]
25
-
23
+
24
+ require 'mixins/gets_memory_protection' if arguments[:gets_memory_protection]
25
+
26
26
  options = {}
27
-
27
+
28
28
  # Copy fields
29
29
  options[:database] = arguments[:database]
30
30
  options[:reset_database] = arguments[:reset_database]
@@ -33,7 +33,7 @@ module RequestLogAnalyzer
33
33
  options[:mail] = arguments[:mail]
34
34
  options[:no_progress] = arguments[:no_progress]
35
35
  options[:format] = arguments[:format]
36
- options[:output] = arguments[:output].downcase
36
+ options[:output] = arguments[:output]
37
37
  options[:file] = arguments[:file]
38
38
  options[:after] = arguments[:after]
39
39
  options[:before] = arguments[:before]
@@ -44,23 +44,25 @@ module RequestLogAnalyzer
44
44
  options[:report_width] = arguments[:report_width]
45
45
  options[:report_sort] = arguments[:report_sort]
46
46
  options[:report_amount] = arguments[:report_amount]
47
- options[:mailhost] = arguments[:mailhost]
48
- options[:silent] = arguments[:silent]
49
-
47
+ options[:mailhost] = arguments[:mailhost]
48
+ options[:mailsubject] = arguments[:mailsubject]
49
+ options[:silent] = arguments[:silent]
50
+ options[:parse_strategy] = arguments[:parse_strategy]
51
+
50
52
  # Apache format workaround
51
53
  if arguments[:rails_format]
52
54
  options[:format] = {:rails => arguments[:rails_format]}
53
55
  elsif arguments[:apache_format]
54
56
  options[:format] = {:apache => arguments[:apache_format]}
55
57
  end
56
-
58
+
57
59
  # Handle output format casing
58
60
  if options[:output].class == String
59
61
  options[:output] = 'FancyHTML' if options[:output] =~ /^fancy_?html$/i
60
62
  options[:output] = 'HTML' if options[:output] =~ /^html$/i
61
63
  options[:output] = 'FixedWidth' if options[:output] =~ /^fixed_?width$/i
62
64
  end
63
-
65
+
64
66
  # Register sources
65
67
  if arguments.parameters.length == 1
66
68
  file = arguments.parameters[0]
@@ -75,7 +77,7 @@ module RequestLogAnalyzer
75
77
  else
76
78
  options.store(:source_files, arguments.parameters)
77
79
  end
78
-
80
+
79
81
  # Guess file format
80
82
  if !options[:format] && options[:source_files]
81
83
  options[:format] = :rails # Default
@@ -89,7 +91,7 @@ module RequestLogAnalyzer
89
91
  end
90
92
  end
91
93
  end
92
-
94
+
93
95
  build(options)
94
96
  end
95
97
 
@@ -107,6 +109,7 @@ module RequestLogAnalyzer
107
109
  # * <tt>:format</tt> :rails, {:apache => 'FORMATSTRING'}, :merb, :amazon_s3, :mysql or RequestLogAnalyzer::FileFormat class. (Defaults to :rails).
108
110
  # * <tt>:mail</tt> Email the results to this email address.
109
111
  # * <tt>:mailhost</tt> Email the results to this mail server.
112
+ # * <tt>:mailsubject</tt> Email subject.
110
113
  # * <tt>:no_progress</tt> Do not display the progress bar (increases parsing speed).
111
114
  # * <tt>:output</tt> 'FixedWidth', 'HTML' or RequestLogAnalyzer::Output class. Defaults to 'FixedWidth'.
112
115
  # * <tt>:reject</tt> Reject specific {:field => :value} combination (expects a single hash).
@@ -121,7 +124,7 @@ module RequestLogAnalyzer
121
124
  # RequestLogAnalyzer::Controller.build(
122
125
  # :output => :HTML,
123
126
  # :mail => 'root@localhost',
124
- # :after => Time.now - 24*60*60,
127
+ # :after => Time.now - 24*60*60,
125
128
  # :source_files => '/var/log/passenger.log'
126
129
  # ).run!
127
130
  #
@@ -138,57 +141,60 @@ module RequestLogAnalyzer
138
141
  options[:report_sort] ||= 'sum,mean'
139
142
  options[:boring] ||= false
140
143
  options[:silent] ||= false
141
-
144
+
142
145
  options[:no_progress] = true if options[:silent]
143
-
146
+
144
147
  # Deprecation warnings
145
148
  if options[:dump]
146
149
  warn "[DEPRECATION] `:dump` is deprecated. Please use `:yaml` instead."
147
150
  options[:yaml] = options[:dump]
148
151
  end
149
-
152
+
150
153
  # Set the output class
151
154
  output_args = {}
152
155
  output_object = nil
153
156
  if options[:output].is_a?(Class)
154
157
  output_class = options[:output]
155
158
  else
156
- output_class = RequestLogAnalyzer::Output::const_get(options[:output])
159
+ output_class = RequestLogAnalyzer::Output.const_get(options[:output])
157
160
  end
158
-
161
+
159
162
  output_sort = options[:report_sort].split(',').map { |s| s.to_sym }
160
163
  output_amount = options[:report_amount] == 'all' ? :all : options[:report_amount].to_i
161
-
164
+
162
165
  if options[:file]
163
166
  output_object = %w[File StringIO].include?(options[:file].class.name) ? options[:file] : File.new(options[:file], "w+")
164
167
  output_args = {:width => 80, :color => false, :characters => :ascii, :sort => output_sort, :amount => output_amount }
165
168
  elsif options[:mail]
166
- output_object = RequestLogAnalyzer::Mailer.new(options[:mail], options[:mailhost])
169
+ output_object = RequestLogAnalyzer::Mailer.new(options[:mail], options[:mailhost], :subject => options[:mailsubject])
167
170
  output_args = {:width => 80, :color => false, :characters => :ascii, :sort => output_sort, :amount => output_amount }
168
171
  else
169
172
  output_object = STDOUT
170
173
  output_args = {:width => options[:report_width].to_i, :color => !options[:boring],
171
174
  :characters => (options[:boring] ? :ascii : :utf), :sort => output_sort, :amount => output_amount }
172
175
  end
173
-
176
+
174
177
  output_instance = output_class.new(output_object, output_args)
175
-
178
+
176
179
  # Create the controller with the correct file format
177
180
  if options[:format].kind_of?(Hash)
178
181
  file_format = RequestLogAnalyzer::FileFormat.load(options[:format].keys[0], options[:format].values[0])
179
182
  else
180
183
  file_format = RequestLogAnalyzer::FileFormat.load(options[:format])
181
184
  end
182
-
185
+
183
186
  # Kickstart the controller
184
- controller = Controller.new( RequestLogAnalyzer::Source::LogParser.new(file_format, :source_files => options[:source_files]),
185
- { :output => output_instance,
186
- :database => options[:database], # FUGLY!
187
- :yaml => options[:yaml],
188
- :reset_database => options[:reset_database],
189
- :no_progress => options[:no_progress],
190
- :silent => options[:silent],
191
- })
187
+ controller =
188
+ Controller.new(RequestLogAnalyzer::Source::LogParser.new(file_format,
189
+ :source_files => options[:source_files],
190
+ :parse_strategy => options[:parse_strategy]),
191
+ { :output => output_instance,
192
+ :database => options[:database], # FUGLY!
193
+ :yaml => options[:yaml],
194
+ :reset_database => options[:reset_database],
195
+ :no_progress => options[:no_progress],
196
+ :silent => options[:silent]
197
+ })
192
198
 
193
199
  # register filters
194
200
  if options[:after] || options[:before]
@@ -210,7 +216,7 @@ module RequestLogAnalyzer
210
216
  end
211
217
  end
212
218
 
213
- if options[:reject]
219
+ if options[:select]
214
220
  options[:select].each do |(field, value)|
215
221
  controller.add_filter(:field, :mode => :select, :field => field, :value => value)
216
222
  end
@@ -224,7 +230,7 @@ module RequestLogAnalyzer
224
230
 
225
231
  file_format.setup_environment(controller)
226
232
  return controller
227
- end
233
+ end
228
234
 
229
235
  # Builds a new Controller for the given log file format.
230
236
  # <tt>format</tt> Logfile format. Defaults to :rails
@@ -242,7 +248,7 @@ module RequestLogAnalyzer
242
248
  @filters = []
243
249
  @output = options[:output]
244
250
  @interrupted = false
245
-
251
+
246
252
  # Register the request format for this session after checking its validity
247
253
  raise "Invalid file format!" unless @source.file_format.valid?
248
254
 
@@ -281,7 +287,7 @@ module RequestLogAnalyzer
281
287
  # Adds an aggregator to the controller. The aggregator will be called for every request
282
288
  # that is parsed from the provided sources (see add_source)
283
289
  def add_aggregator(agg)
284
- agg = RequestLogAnalyzer::Aggregator.const_get(RequestLogAnalyzer::to_camelcase(agg)) if agg.kind_of?(Symbol)
290
+ agg = RequestLogAnalyzer::Aggregator.const_get(RequestLogAnalyzer.to_camelcase(agg)) if agg.kind_of?(Symbol)
285
291
  @aggregators << agg.new(@source, @options)
286
292
  end
287
293
 
@@ -289,7 +295,7 @@ module RequestLogAnalyzer
289
295
 
290
296
  # Adds a request filter to the controller.
291
297
  def add_filter(filter, filter_options = {})
292
- filter = RequestLogAnalyzer::Filter.const_get(RequestLogAnalyzer::to_camelcase(filter)) if filter.kind_of?(Symbol)
298
+ filter = RequestLogAnalyzer::Filter.const_get(RequestLogAnalyzer.to_camelcase(filter)) if filter.kind_of?(Symbol)
293
299
  @filters << filter.new(source.file_format, @options.merge(filter_options))
294
300
  end
295
301
 
@@ -3,9 +3,11 @@ require 'active_record'
3
3
 
4
4
  class RequestLogAnalyzer::Database
5
5
 
6
- def self.const_missing(const) # :nodoc:
7
- RequestLogAnalyzer::load_default_class_file(self, const)
8
- end
6
+ autoload :Connection, 'request_log_analyzer/database/connection'
7
+ autoload :Base, 'request_log_analyzer/database/base'
8
+ autoload :Request, 'request_log_analyzer/database/request'
9
+ autoload :Source, 'request_log_analyzer/database/source'
10
+ autoload :Warning, 'request_log_analyzer/database/warning'
9
11
 
10
12
  include RequestLogAnalyzer::Database::Connection
11
13
 
@@ -30,20 +30,23 @@ module RequestLogAnalyzer::FileFormat
30
30
 
31
31
  # A hash that defines how the log format directives should be parsed.
32
32
  LOG_DIRECTIVES = {
33
- '%' => { :regexp => '%', :captures => [] },
34
- 'h' => { :regexp => '([A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+)', :captures => [{:name => :remote_host, :type => :string}] },
35
- 'a' => { :regexp => "(#{ip_address})", :captures => [{:name => :remote_ip, :type => :string}] },
36
- 'b' => { :regexp => '(\d+|-)', :captures => [{:name => :bytes_sent, :type => :traffic}] },
37
- 'c' => { :regexp => '(\+|\-|\X)', :captures => [{:name => :connection_status, :type => :integer}] },
38
- 'D' => { :regexp => '(\d+|-)', :captures => [{:name => :duration, :type => :duration, :unit => :musec}] },
39
- 'l' => { :regexp => '([\w-]+)', :captures => [{:name => :remote_logname, :type => :nillable_string}] },
40
- 'T' => { :regexp => '((?:\d+(?:\.\d+))|-)', :captures => [{:name => :duration, :type => :duration, :unit => :sec}] },
41
- 't' => { :regexp => "\\[(#{APACHE_TIMESTAMP})?\\]", :captures => [{:name => :timestamp, :type => :timestamp}] },
42
- 's' => { :regexp => '(\d{3})', :captures => [{:name => :http_status, :type => :integer}] },
43
- 'u' => { :regexp => '(\w+|-)', :captures => [{:name => :user, :type => :nillable_string}] },
44
- 'U' => { :regexp => '(\/\S*)', :captures => [{:name => :path, :type => :string}] },
45
- 'r' => { :regexp => '([A-Z]+) (\S+) HTTP\/(\d+(?:\.\d+)*)', :captures => [{:name => :http_method, :type => :string},
46
- {:name => :path, :type => :path}, {:name => :http_version, :type => :string}]},
33
+ '%' => { nil => { :regexp => '%', :captures => [] } },
34
+ 'h' => { nil => { :regexp => '([A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+)', :captures => [{:name => :remote_host, :type => :string}] } },
35
+ 'a' => { nil => { :regexp => "(#{ip_address})", :captures => [{:name => :remote_ip, :type => :string}] } },
36
+ 'b' => { nil => { :regexp => '(\d+|-)', :captures => [{:name => :bytes_sent, :type => :traffic}] } },
37
+ 'c' => { nil => { :regexp => '(\+|\-|\X)', :captures => [{:name => :connection_status, :type => :integer}] } },
38
+ 'D' => { nil => { :regexp => '(\d+|-)', :captures => [ {:name => :duration, :type => :duration, :unit => :musec }] },
39
+ 'micro' => { :regexp => '(\d+|-)', :captures => [ {:name => :duration, :type => :duration, :unit => :musec }] },
40
+ 'milli' => { :regexp => '(\d+|-)', :captures => [ {:name => :duration, :type => :duration, :unit => :msec }] }
41
+ },
42
+ 'l' => { nil => { :regexp => '([\w-]+)', :captures => [{:name => :remote_logname, :type => :nillable_string}] } },
43
+ 'T' => { nil => { :regexp => '((?:\d+(?:\.\d+))|-)', :captures => [{:name => :duration, :type => :duration, :unit => :sec}] } },
44
+ 't' => { nil => { :regexp => "\\[(#{APACHE_TIMESTAMP})?\\]", :captures => [{:name => :timestamp, :type => :timestamp}] } },
45
+ 's' => { nil => { :regexp => '(\d{3})', :captures => [{:name => :http_status, :type => :integer}] } },
46
+ 'u' => { nil => { :regexp => '(\w+|-)', :captures => [{:name => :user, :type => :nillable_string}] } },
47
+ 'U' => { nil => { :regexp => '(\/\S*)', :captures => [{:name => :path, :type => :string}] } },
48
+ 'r' => { nil => { :regexp => '([A-Z]+) (\S+) HTTP\/(\d+(?:\.\d+)*)', :captures => [{:name => :http_method, :type => :string},
49
+ {:name => :path, :type => :path}, {:name => :http_version, :type => :string}]} },
47
50
  'i' => { 'Referer' => { :regexp => '(\S+)', :captures => [{:name => :referer, :type => :nillable_string}] },
48
51
  'User-agent' => { :regexp => '(.*)', :captures => [{:name => :user_agent, :type => :user_agent}] }
49
52
  }
@@ -72,14 +75,13 @@ module RequestLogAnalyzer::FileFormat
72
75
 
73
76
  if variable
74
77
  # Check if we recognize the log directive
75
- directive = LOG_DIRECTIVES[variable]
76
- directive = directive[arg] if directive && arg
78
+ directive = LOG_DIRECTIVES[variable][arg] rescue nil
77
79
 
78
80
  if directive
79
81
  line_regexp << directive[:regexp] # Parse the value of the directive
80
82
  captures += directive[:captures] # Add the directive's information to the captures
81
83
  else
82
- puts "%#{directive} log directiven not yet supported, field is ignored."
84
+ puts "Apache log directive %#{arg}#{variable} is not yet supported by RLA, the field will be ignored."
83
85
  line_regexp << '.*' # Just accept any input for this literal
84
86
  end
85
87
  end
@@ -126,22 +128,11 @@ module RequestLogAnalyzer::FileFormat
126
128
  "#{value[7,4]}#{MONTHS[value[3,3]]}#{value[0,2]}#{value[12,2]}#{value[15,2]}#{value[18,2]}".to_i
127
129
  end
128
130
 
129
- # This function can be overridden to rewrite the path for better categorization in the
130
- # reports.
131
- def convert_path(value, definition)
132
- value
133
- end
134
-
135
131
  # This function can be overridden to simplify the user agent string for better
136
132
  # categorization in the reports
137
133
  def convert_user_agent(value, definition)
138
134
  value # TODO
139
135
  end
140
-
141
- # Make sure that the string '-' is parsed as a nil value.
142
- def convert_nillable_string(value, definition)
143
- value == '-' ? nil : value
144
- end
145
136
  end
146
137
  end
147
138
  end
@@ -0,0 +1,38 @@
1
+ module RequestLogAnalyzer::FileFormat
2
+
3
+ # The DelayedJob2 file format parsed log files that are created by DelayedJob 2.0.
4
+ # By default, the log file can be found in RAILS_ROOT/log/delayed_job.log
5
+ class DelayedJob2 < Base
6
+
7
+ extend CommonRegularExpressions
8
+
9
+ line_definition :job_lock do |line|
10
+ line.header = true
11
+ line.regexp = /(#{timestamp('%Y-%m-%dT%H:%M:%S%z')}): \* \[Worker\(\S+ host:(\S+) pid:(\d+)\)\] acquired lock on (\S+)/
12
+
13
+ line.capture(:timestamp).as(:timestamp)
14
+ line.capture(:host)
15
+ line.capture(:pid).as(:integer)
16
+ line.capture(:job)
17
+ end
18
+
19
+ line_definition :job_completed do |line|
20
+ line.footer = true
21
+ line.regexp = /(#{timestamp('%Y-%m-%dT%H:%M:%S%z')}): \* \[JOB\] \S+ host:(\S+) pid:(\d+) completed after (\d+\.\d+)/
22
+ line.capture(:timestamp).as(:timestamp)
23
+ line.capture(:host)
24
+ line.capture(:pid).as(:integer)
25
+ line.capture(:duration).as(:duration, :unit => :sec)
26
+ end
27
+
28
+ report do |analyze|
29
+ analyze.timespan
30
+ analyze.hourly_spread
31
+
32
+ analyze.frequency :job, :line_type => :job_completed, :title => "Completed jobs"
33
+ #analyze.frequency :job, :if => lambda { |request| request[:attempts] == 1 }, :title => "Failed jobs"
34
+
35
+ analyze.duration :duration, :category => :job, :line_type => :job_completed, :title => "Job duration"
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,39 @@
1
+ module RequestLogAnalyzer::FileFormat
2
+
3
+ # The DelayedJob21 file format parsed log files that are created by DelayedJob 2.1 or higher.
4
+ # By default, the log file can be found in RAILS_ROOT/log/delayed_job.log
5
+ class DelayedJob21 < Base
6
+
7
+ extend CommonRegularExpressions
8
+
9
+ line_definition :job_lock do |line|
10
+ line.header = true
11
+ line.regexp = /(#{timestamp('%Y-%m-%dT%H:%M:%S%z')}): \[Worker\(\S+ host:(\S+) pid:(\d+)\)\] acquired lock on (\S+)/
12
+
13
+ line.capture(:timestamp).as(:timestamp)
14
+ line.capture(:host)
15
+ line.capture(:pid).as(:integer)
16
+ line.capture(:job)
17
+ end
18
+
19
+ line_definition :job_completed do |line|
20
+ line.footer = true
21
+ line.regexp = /(#{timestamp('%Y-%m-%dT%H:%M:%S%z')}): \[Worker\(\S+ host:(\S+) pid:(\d+)\)\] (\S+) completed after (\d+\.\d+)/
22
+ line.capture(:timestamp).as(:timestamp)
23
+ line.capture(:host)
24
+ line.capture(:pid).as(:integer)
25
+ line.capture(:job)
26
+ line.capture(:duration).as(:duration, :unit => :sec)
27
+ end
28
+
29
+ report do |analyze|
30
+ analyze.timespan
31
+ analyze.hourly_spread
32
+
33
+ analyze.frequency :job, :line_type => :job_completed, :title => "Completed jobs"
34
+ #analyze.frequency :job, :if => lambda { |request| request[:attempts] == 1 }, :title => "Failed jobs"
35
+
36
+ analyze.duration :duration, :category => :job, :line_type => :job_completed, :title => "Job duration"
37
+ end
38
+ end
39
+ end