request-log-analyzer 1.3.5 → 1.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. data/lib/cli/database_console_init.rb +2 -1
  2. data/lib/request_log_analyzer.rb +1 -1
  3. data/lib/request_log_analyzer/aggregator.rb +1 -5
  4. data/lib/request_log_analyzer/aggregator/database_inserter.rb +4 -5
  5. data/lib/request_log_analyzer/controller.rb +10 -21
  6. data/lib/request_log_analyzer/database.rb +16 -91
  7. data/lib/request_log_analyzer/database/base.rb +4 -4
  8. data/lib/request_log_analyzer/database/request.rb +22 -0
  9. data/lib/request_log_analyzer/database/source.rb +13 -0
  10. data/lib/request_log_analyzer/database/warning.rb +14 -0
  11. data/lib/request_log_analyzer/file_format.rb +1 -13
  12. data/lib/request_log_analyzer/file_format/amazon_s3.rb +1 -2
  13. data/lib/request_log_analyzer/file_format/apache.rb +8 -10
  14. data/lib/request_log_analyzer/file_format/merb.rb +21 -5
  15. data/lib/request_log_analyzer/file_format/rails.rb +8 -14
  16. data/lib/request_log_analyzer/filter.rb +6 -10
  17. data/lib/request_log_analyzer/filter/anonymize.rb +2 -1
  18. data/lib/request_log_analyzer/log_processor.rb +6 -8
  19. data/lib/request_log_analyzer/request.rb +47 -35
  20. data/lib/request_log_analyzer/source.rb +4 -6
  21. data/lib/request_log_analyzer/source/database_loader.rb +3 -7
  22. data/lib/request_log_analyzer/source/log_parser.rb +3 -6
  23. data/lib/request_log_analyzer/tracker.rb +12 -19
  24. data/lib/request_log_analyzer/tracker/hourly_spread.rb +1 -2
  25. data/request-log-analyzer.gemspec +3 -3
  26. data/spec/database.yml +6 -0
  27. data/spec/unit/aggregator/database_inserter_spec.rb +3 -3
  28. data/spec/unit/database/base_class_spec.rb +9 -16
  29. data/spec/unit/database/database_spec.rb +9 -14
  30. data/spec/unit/tracker/tracker_api_spec.rb +111 -36
  31. metadata +7 -4
@@ -3,6 +3,7 @@ $:.unshift(File.dirname(__FILE__) + '/..')
3
3
 
4
4
  $database = RequestLogAnalyzer::Database.new(ENV['RLA_DBCONSOLE_DATABASE'])
5
5
  $database.load_database_schema!
6
+ $database.register_default_orm_classes!
6
7
 
7
8
  require 'cli/tools'
8
9
 
@@ -39,4 +40,4 @@ end
39
40
  puts "request-log-analyzer database console"
40
41
  puts "-------------------------------------"
41
42
  puts "The following ActiveRecord classes are available:"
42
- puts $database.orm_classes.join(", ")
43
+ puts $database.orm_classes.map { |k| k.name.split('::').last }.join(", ")
@@ -11,7 +11,7 @@ module RequestLogAnalyzer
11
11
 
12
12
  # The current version of request-log-analyzer.
13
13
  # This will be diplayed in output reports etc.
14
- VERSION = "1.3.5"
14
+ VERSION = "1.3.6"
15
15
 
16
16
  # Loads constants in the RequestLogAnalyzer namespace using self.load_default_class_file(base, const)
17
17
  # <tt>const</tt>:: The constant that is not yet loaded in the RequestLogAnalyzer namespace. This should be passed as a string or symbol.
@@ -8,16 +8,12 @@ module RequestLogAnalyzer::Aggregator
8
8
  # every aggregator should comply (by simply subclassing this class).
9
9
  class Base
10
10
 
11
- include RequestLogAnalyzer::FileFormat::Awareness
12
-
13
- attr_reader :options
14
- attr_reader :source
11
+ attr_reader :options, :source
15
12
 
16
13
  # Intializes a new RequestLogAnalyzer::Aggregator::Base instance
17
14
  # It will include the specific file format module.
18
15
  def initialize(source, options = {})
19
16
  @source = source
20
- self.register_file_format(source.file_format)
21
17
  @options = options
22
18
  end
23
19
 
@@ -31,7 +31,7 @@ module RequestLogAnalyzer::Aggregator
31
31
  # This will create a record in the requests table and create a record for every line that has been parsed,
32
32
  # in which the captured values will be stored.
33
33
  def aggregate(request)
34
- @request_object = database.request_class.new(:first_lineno => request.first_lineno, :last_lineno => request.last_lineno)
34
+ @request_object = RequestLogAnalyzer::Database::Request.new(:first_lineno => request.first_lineno, :last_lineno => request.last_lineno)
35
35
  request.lines.each do |line|
36
36
  class_columns = database.get_class(line[:line_type]).column_names.reject { |column| ['id', 'source_id', 'request_id'].include?(column) }
37
37
  attributes = Hash[*line.select { |(k, v)| class_columns.include?(k.to_s) }.flatten]
@@ -45,14 +45,14 @@ module RequestLogAnalyzer::Aggregator
45
45
 
46
46
  # Finalizes the aggregator by closing the connection to the database
47
47
  def finalize
48
- @request_count = database.request_class.count
48
+ @request_count = RequestLogAnalyzer::Database::Request.count
49
49
  database.disconnect
50
50
  database.remove_orm_classes!
51
51
  end
52
52
 
53
53
  # Records w warining in the warnings table.
54
54
  def warning(type, message, lineno)
55
- database.warning_class.create!(:warning_type => type.to_s, :message => message, :lineno => lineno)
55
+ RequestLogAnalyzer::Database::Warning.create!(:warning_type => type.to_s, :message => message, :lineno => lineno)
56
56
  end
57
57
 
58
58
  # Records source changes in the sources table
@@ -60,8 +60,7 @@ module RequestLogAnalyzer::Aggregator
60
60
  if File.exist?(filename)
61
61
  case change
62
62
  when :started
63
- p database.source_class
64
- @sources[filename] = database.source_class.create!(:filename => filename)
63
+ @sources[filename] = RequestLogAnalyzer::Database::Source.create!(:filename => filename)
65
64
  when :finished
66
65
  @sources[filename].update_attributes!(:filesize => File.size(filename), :mtime => File.mtime(filename))
67
66
  end
@@ -17,14 +17,7 @@ module RequestLogAnalyzer
17
17
  # from several logrotated log files.
18
18
  class Controller
19
19
 
20
- include RequestLogAnalyzer::FileFormat::Awareness
21
-
22
- attr_reader :aggregators
23
- attr_reader :filters
24
- attr_reader :log_parser
25
- attr_reader :source
26
- attr_reader :output
27
- attr_reader :options
20
+ attr_reader :source, :filters, :aggregators, :output, :options
28
21
 
29
22
  # Builds a RequestLogAnalyzer::Controller given parsed command line arguments
30
23
  # <tt>arguments<tt> A CommandLine::Arguments hash containing parsed commandline parameters.
@@ -35,8 +28,8 @@ module RequestLogAnalyzer
35
28
  # Database command line options
36
29
  options[:database] = arguments[:database] if arguments[:database]
37
30
  options[:reset_database] = arguments[:reset_database]
38
- options[:debug] = arguments[:debug]
39
- options[:dump] = arguments[:dump]
31
+ options[:debug] = arguments[:debug]
32
+ options[:dump] = arguments[:dump]
40
33
  options[:parse_strategy] = arguments[:parse_strategy]
41
34
  options[:no_progress] = arguments[:no_progress]
42
35
 
@@ -125,17 +118,13 @@ module RequestLogAnalyzer
125
118
  @filters = []
126
119
  @output = options[:output]
127
120
 
128
- # Requester format through RequestLogAnalyzer::FileFormat and construct the parser
129
- register_file_format(@source.file_format)
130
-
131
- # Pass all warnings to every aggregator so they can do something useful with them.
132
- @source.warning = lambda { |type, message, lineno| @aggregators.each { |agg| agg.warning(type, message, lineno) } } if @source
133
-
134
- # Handle progress messagess
135
- @source.progress = lambda { |message, value| handle_progress(message, value) } if @source && !options[:no_progress]
121
+ # Register the request format for this session after checking its validity
122
+ raise "Invalid file format!" unless @source.file_format.valid?
136
123
 
137
- # Handle source change messages
138
- @source.source_changes = lambda { |change, filename| handle_source_change(change, filename) } if @source
124
+ # Install event handlers for wrnings, progress updates and source changes
125
+ @source.warning = lambda { |type, message, lineno| @aggregators.each { |agg| agg.warning(type, message, lineno) } }
126
+ @source.progress = lambda { |message, value| handle_progress(message, value) } unless options[:no_progress]
127
+ @source.source_changes = lambda { |change, filename| handle_source_change(change, filename) }
139
128
  end
140
129
 
141
130
  # Progress function.
@@ -176,7 +165,7 @@ module RequestLogAnalyzer
176
165
  # Adds a request filter to the controller.
177
166
  def add_filter(filter, filter_options = {})
178
167
  filter = RequestLogAnalyzer::Filter.const_get(RequestLogAnalyzer::to_camelcase(filter)) if filter.kind_of?(Symbol)
179
- @filters << filter.new(file_format, @options.merge(filter_options))
168
+ @filters << filter.new(source.file_format, @options.merge(filter_options))
180
169
  end
181
170
 
182
171
  # Push a request through the entire filterchain (@filters).
@@ -10,7 +10,7 @@ class RequestLogAnalyzer::Database
10
10
  include RequestLogAnalyzer::Database::Connection
11
11
 
12
12
  attr_accessor :file_format
13
- attr_reader :request_class, :warning_class, :source_class, :line_classes
13
+ attr_reader :line_classes
14
14
 
15
15
  def initialize(connection_identifier = nil)
16
16
  @line_classes = []
@@ -24,97 +24,17 @@ class RequestLogAnalyzer::Database
24
24
  Object.const_get("#{line_type}_line".camelize)
25
25
  end
26
26
 
27
- # Returns the Request ORM class for the current database.
28
- #
29
- # It will create the class if not previously done so. The class will
30
- # include a create_table! method the migrate the database.
31
- def request_class
32
- @request_class ||= begin
33
- klass = Class.new(RequestLogAnalyzer::Database::Base) do
34
-
35
- def lines
36
- @lines ||= begin
37
- lines = []
38
- self.class.reflections.each { |r, d| lines += self.send(r).all }
39
- lines.sort
40
- end
41
- end
42
-
43
- # Creates the requests table
44
- def self.create_table!
45
- unless database.connection.table_exists?(:requests)
46
- database.connection.create_table(:requests) do |t|
47
- t.column :first_lineno, :integer
48
- t.column :last_lineno, :integer
49
- end
50
- end
51
- end
52
- end
53
-
54
- Object.const_set('Request', klass)
55
- Object.const_get('Request')
56
- end
57
- end
58
-
59
- # Returns the Source ORM class for the current database.
60
- #
61
- # It will create the class if not previously done so. The class will
62
- # include a create_table! method the migrate the database.
63
- def source_class
64
- @source_class ||= begin
65
- klass = Class.new(RequestLogAnalyzer::Database::Base) do
66
-
67
- # Creates the sources table
68
- def self.create_table!
69
- unless database.connection.table_exists?(:sources)
70
- database.connection.create_table(:sources) do |t|
71
- t.column :filename, :string
72
- t.column :mtime, :datetime
73
- t.column :filesize, :integer
74
- end
75
- end
76
- end
77
- end
78
-
79
- Object.const_set('Source', klass)
80
- Object.const_get('Source')
81
- end
82
- end
83
-
84
-
85
- # Returns the Warning ORM class for the current database.
86
- #
87
- # It will create the class if not previously done so. The class will
88
- # include a create_table! method the migrate the database.
89
- def warning_class
90
- @warning_class ||= begin
91
- klass = Class.new(RequestLogAnalyzer::Database::Base) do
92
-
93
- # Creates the warnings table
94
- def self.create_table!
95
- unless database.connection.table_exists?(:warnings)
96
- database.connection.create_table(:warnings) do |t|
97
- t.column :warning_type, :string, :limit => 30, :null => false
98
- t.column :message, :string
99
- t.column :source_id, :integer
100
- t.column :lineno, :integer
101
- end
102
- end
103
- end
104
- end
105
-
106
- Object.const_set('Warning', klass)
107
- Object.const_get('Warning')
108
- end
27
+ def default_classes
28
+ [RequestLogAnalyzer::Database::Request, RequestLogAnalyzer::Database::Source, RequestLogAnalyzer::Database::Warning]
109
29
  end
110
30
 
111
31
  # Loads the ORM classes by inspecting the tables in the current database
112
32
  def load_database_schema!
113
33
  connection.tables.map do |table|
114
34
  case table.to_sym
115
- when :warnings then warning_class
116
- when :sources then source_class
117
- when :requests then request_class
35
+ when :warnings then RequestLogAnalyzer::Database::Warning
36
+ when :sources then RequestLogAnalyzer::Database::Source
37
+ when :requests then RequestLogAnalyzer::Database::Request
118
38
  else load_activerecord_class(table)
119
39
  end
120
40
  end
@@ -122,7 +42,7 @@ class RequestLogAnalyzer::Database
122
42
 
123
43
  # Returns an array of all the ActiveRecord-bases ORM classes for this database
124
44
  def orm_classes
125
- [warning_class, request_class, source_class] + line_classes
45
+ default_classes + line_classes
126
46
  end
127
47
 
128
48
  # Loads an ActiveRecord-based class that correspond to the given parameter, which can either be
@@ -146,8 +66,6 @@ class RequestLogAnalyzer::Database
146
66
 
147
67
  def fileformat_classes
148
68
  raise "No file_format provided!" unless file_format
149
-
150
- default_classes = [request_class, source_class, warning_class]
151
69
  line_classes = file_format.line_definitions.map { |(name, definition)| load_activerecord_class(definition) }
152
70
  return default_classes + line_classes
153
71
  end
@@ -164,12 +82,19 @@ class RequestLogAnalyzer::Database
164
82
  remove_orm_classes!
165
83
  end
166
84
 
85
+ # Registers the default ORM classes in the default namespace
86
+ def register_default_orm_classes!
87
+ Object.const_set('Request', RequestLogAnalyzer::Database::Request)
88
+ Object.const_set('Source', RequestLogAnalyzer::Database::Source)
89
+ Object.const_set('Warning', RequestLogAnalyzer::Database::Warning)
90
+ end
91
+
167
92
  # Unregisters every ORM class constant
168
93
  def remove_orm_classes!
169
94
  orm_classes.each do |klass|
170
95
  if klass.respond_to?(:name) && !klass.name.blank?
171
- # klass_base_name = klass.name.split('::').last
172
- Object.send(:remove_const, klass.name) if Object.const_defined?(klass.name)
96
+ klass_name = klass.name.split('::').last
97
+ Object.send(:remove_const, klass_name) if Object.const_defined?(klass_name)
173
98
  end
174
99
  end
175
100
  end
@@ -32,8 +32,8 @@ class RequestLogAnalyzer::Database::Base < ActiveRecord::Base
32
32
  klass.send(:serialize, capture[:name], Hash)
33
33
  end
34
34
 
35
- database.request_class.has_many "#{definition.name}_lines".to_sym
36
- database.source_class.has_many "#{definition.name}_lines".to_sym
35
+ RequestLogAnalyzer::Database::Request.has_many "#{definition.name}_lines".to_sym
36
+ RequestLogAnalyzer::Database::Source.has_many "#{definition.name}_lines".to_sym
37
37
 
38
38
  return klass
39
39
  end
@@ -46,12 +46,12 @@ class RequestLogAnalyzer::Database::Base < ActiveRecord::Base
46
46
 
47
47
  if klass.column_names.include?('request_id')
48
48
  klass.belongs_to :request
49
- database.request_class.has_many table.to_sym
49
+ RequestLogAnalyzer::Database::Request.has_many table.to_sym
50
50
  end
51
51
 
52
52
  if klass.column_names.include?('source_id')
53
53
  klass.belongs_to :source
54
- database.source_class.has_many table.to_sym
54
+ RequestLogAnalyzer::Database::Source.has_many table.to_sym
55
55
  end
56
56
 
57
57
  return klass
@@ -0,0 +1,22 @@
1
+ class RequestLogAnalyzer::Database::Request < RequestLogAnalyzer::Database::Base
2
+
3
+ # Returns an array of all the Line objects of this request in the correct order.
4
+ def lines
5
+ @lines ||= begin
6
+ lines = []
7
+ self.class.reflections.each { |r, d| lines += self.send(r).all }
8
+ lines.sort
9
+ end
10
+ end
11
+
12
+ # Creates the table to store requests in.
13
+ def self.create_table!
14
+ unless database.connection.table_exists?(:requests)
15
+ database.connection.create_table(:requests) do |t|
16
+ t.column :first_lineno, :integer
17
+ t.column :last_lineno, :integer
18
+ end
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,13 @@
1
+ class RequestLogAnalyzer::Database::Source < RequestLogAnalyzer::Database::Base
2
+
3
+ def self.create_table!
4
+ unless database.connection.table_exists?(:sources)
5
+ database.connection.create_table(:sources) do |t|
6
+ t.column :filename, :string
7
+ t.column :mtime, :datetime
8
+ t.column :filesize, :integer
9
+ end
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,14 @@
1
+ class RequestLogAnalyzer::Database::Warning < RequestLogAnalyzer::Database::Base
2
+
3
+ def self.create_table!
4
+ unless database.connection.table_exists?(:warnings)
5
+ database.connection.create_table(:warnings) do |t|
6
+ t.column :warning_type, :string, :limit => 30, :null => false
7
+ t.column :message, :string
8
+ t.column :source_id, :integer
9
+ t.column :lineno, :integer
10
+ end
11
+ end
12
+ end
13
+
14
+ end
@@ -42,19 +42,7 @@ module RequestLogAnalyzer::FileFormat
42
42
  raise "Invalid FileFormat class" unless klass.kind_of?(Class) && klass.ancestors.include?(RequestLogAnalyzer::FileFormat::Base)
43
43
 
44
44
  @current_file_format = klass.create(*args) # return an instance of the class
45
- end
46
-
47
- # Makes classes aware of a file format by registering the file_format variable
48
- module Awareness
49
-
50
- def self.included(base)
51
- base.send(:attr_reader, :file_format)
52
- end
53
-
54
- def register_file_format(format)
55
- @file_format = format
56
- end
57
- end
45
+ end
58
46
 
59
47
  # Base class for all log file format definitions. This class provides functions for subclasses to
60
48
  # define their LineDefinitions and to define a summary report.
@@ -48,8 +48,7 @@ module RequestLogAnalyzer::FileFormat
48
48
  # Do not use DateTime.parse, but parse the timestamp ourselves to return a integer
49
49
  # to speed up parsing.
50
50
  def convert_timestamp(value, definition)
51
- d = /^(\d{2})\/([A-Za-z]{3})\/(\d{4}).(\d{2}):(\d{2}):(\d{2})/.match(value).captures
52
- "#{d[2]}#{MONTHS[d[1]]}#{d[0]}#{d[3]}#{d[4]}#{d[5]}".to_i
51
+ "#{value[7,4]}#{MONTHS[value[3,3]]}#{value[0,2]}#{value[12,2]}#{value[15,2]}#{value[18,2]}".to_i
53
52
  end
54
53
 
55
54
  # Make sure that the string '-' is parsed as a nil value.
@@ -92,18 +92,13 @@ module RequestLogAnalyzer::FileFormat
92
92
 
93
93
  analyze.frequency :category => :http_method, :amount => 20, :title => "HTTP methods" if line_definition.captures?(:http_method)
94
94
  analyze.frequency :category => :http_status, :amount => 20, :title => "HTTP statuses" if line_definition.captures?(:http_status)
95
- analyze.frequency :category => :path, :amount => 20, :title => "Most popular URIs" if line_definition.captures?(:path)
95
+ analyze.frequency :category => lambda { |r| r.category }, :amount => 20, :title => "Most popular URIs" if line_definition.captures?(:path)
96
96
 
97
97
  analyze.frequency :category => :user_agent, :amount => 20, :title => "User agents" if line_definition.captures?(:user_agent)
98
98
  analyze.frequency :category => :referer, :amount => 20, :title => "Referers" if line_definition.captures?(:referer)
99
99
 
100
- if line_definition.captures?(:path) && line_definition.captures?(:duration)
101
- analyze.duration :duration => :duration, :category => :path , :title => 'Request duration'
102
- end
103
-
104
- if line_definition.captures?(:path) && line_definition.captures?(:bytes_sent)
105
- analyze.traffic :traffic => :bytes_sent, :category => :path , :title => 'Traffic'
106
- end
100
+ analyze.duration :duration => :duration, :category => lambda { |r| r.category }, :title => 'Request duration' if line_definition.captures?(:duration)
101
+ analyze.traffic :traffic => :bytes_sent, :category => lambda { |r| r.category }, :title => 'Traffic' if line_definition.captures?(:bytes_sent)
107
102
 
108
103
  return analyze.trackers
109
104
  end
@@ -111,14 +106,17 @@ module RequestLogAnalyzer::FileFormat
111
106
  # Define a custom Request class for the Apache file format to speed up timestamp handling.
112
107
  class Request < RequestLogAnalyzer::Request
113
108
 
109
+ def category
110
+ first(:path)
111
+ end
112
+
114
113
  MONTHS = {'Jan' => '01', 'Feb' => '02', 'Mar' => '03', 'Apr' => '04', 'May' => '05', 'Jun' => '06',
115
114
  'Jul' => '07', 'Aug' => '08', 'Sep' => '09', 'Oct' => '10', 'Nov' => '11', 'Dec' => '12' }
116
115
 
117
116
  # Do not use DateTime.parse, but parse the timestamp ourselves to return a integer
118
117
  # to speed up parsing.
119
118
  def convert_timestamp(value, definition)
120
- d = /^(\d{2})\/([A-Za-z]{3})\/(\d{4}).(\d{2}):(\d{2}):(\d{2})/.match(value).captures
121
- "#{d[2]}#{MONTHS[d[1]]}#{d[0]}#{d[3]}#{d[4]}#{d[5]}".to_i
119
+ "#{value[7,4]}#{MONTHS[value[3,3]]}#{value[0,2]}#{value[12,2]}#{value[15,2]}#{value[18,2]}".to_i
122
120
  end
123
121
 
124
122
  # This function can be overridden to rewrite the path for better categorization in the
@@ -1,11 +1,14 @@
1
1
  module RequestLogAnalyzer::FileFormat
2
2
 
3
+ # The Merb file format parses the request header with the timestamp, the params line
4
+ # with the most important request information and the durations line which contains
5
+ # the different request durations that can be used for analysis.
3
6
  class Merb < Base
4
7
 
5
8
  # ~ Started request handling: Fri Aug 29 11:10:23 +0200 2008
6
9
  line_definition :started do |line|
7
10
  line.header = true
8
- line.teaser = /Started/
11
+ # line.teaser = /Started/
9
12
  line.regexp = /Started request handling\:\ (.+)/
10
13
  line.captures << { :name => :timestamp, :type => :timestamp }
11
14
  end
@@ -13,7 +16,7 @@ module RequestLogAnalyzer::FileFormat
13
16
  # ~ Params: {"action"=>"create", "controller"=>"session"}
14
17
  # ~ Params: {"_method"=>"delete", "authenticity_token"=>"[FILTERED]", "action"=>"d}
15
18
  line_definition :params do |line|
16
- line.teaser = /Params/
19
+ # line.teaser = /Params/
17
20
  line.regexp = /Params\:\ (\{.+\})/
18
21
  line.captures << { :name => :params, :type => :eval, :provides => {
19
22
  :namespace => :string, :controller => :string, :action => :string, :format => :string, :method => :string } }
@@ -36,15 +39,28 @@ module RequestLogAnalyzer::FileFormat
36
39
  end
37
40
 
38
41
  report do |analyze|
39
- analyze.timespan :line_type => :started
42
+
43
+ analyze.timespan
44
+ analyze.hourly_spread
45
+
40
46
  analyze.frequency :category => REQUEST_CATEGORIZER, :amount => 20, :title => "Top 20 by hits"
41
- analyze.hourly_spread :line_type => :started
42
47
  analyze.duration :dispatch_time, :category => REQUEST_CATEGORIZER, :title => 'Request dispatch duration'
48
+
43
49
  # analyze.duration :action_time, :category => REQUEST_CATEGORIZER, :title => 'Request action duration'
44
50
  # analyze.duration :after_filters_time, :category => REQUEST_CATEGORIZER, :title => 'Request after_filter duration'
45
51
  # analyze.duration :before_filters_time, :category => REQUEST_CATEGORIZER, :title => 'Request before_filter duration'
46
52
  end
47
-
53
+
54
+ class Request < RequestLogAnalyzer::Request
55
+
56
+ MONTHS = {'Jan' => '01', 'Feb' => '02', 'Mar' => '03', 'Apr' => '04', 'May' => '05', 'Jun' => '06',
57
+ 'Jul' => '07', 'Aug' => '08', 'Sep' => '09', 'Oct' => '10', 'Nov' => '11', 'Dec' => '12' }
58
+
59
+ # Speed up timestamp conversion
60
+ def convert_timestamp(value, definition)
61
+ "#{value[26,4]}#{MONTHS[value[4,3]]}#{value[8,2]}#{value[11,2]}#{value[14,2]}#{value[17,2]}".to_i
62
+ end
63
+ end
48
64
  end
49
65
 
50
66
  end