request-log-analyzer 1.3.5 → 1.3.6
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/lib/cli/database_console_init.rb +2 -1
- data/lib/request_log_analyzer.rb +1 -1
- data/lib/request_log_analyzer/aggregator.rb +1 -5
- data/lib/request_log_analyzer/aggregator/database_inserter.rb +4 -5
- data/lib/request_log_analyzer/controller.rb +10 -21
- data/lib/request_log_analyzer/database.rb +16 -91
- data/lib/request_log_analyzer/database/base.rb +4 -4
- 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/file_format.rb +1 -13
- data/lib/request_log_analyzer/file_format/amazon_s3.rb +1 -2
- data/lib/request_log_analyzer/file_format/apache.rb +8 -10
- data/lib/request_log_analyzer/file_format/merb.rb +21 -5
- data/lib/request_log_analyzer/file_format/rails.rb +8 -14
- data/lib/request_log_analyzer/filter.rb +6 -10
- data/lib/request_log_analyzer/filter/anonymize.rb +2 -1
- data/lib/request_log_analyzer/log_processor.rb +6 -8
- data/lib/request_log_analyzer/request.rb +47 -35
- data/lib/request_log_analyzer/source.rb +4 -6
- data/lib/request_log_analyzer/source/database_loader.rb +3 -7
- data/lib/request_log_analyzer/source/log_parser.rb +3 -6
- data/lib/request_log_analyzer/tracker.rb +12 -19
- data/lib/request_log_analyzer/tracker/hourly_spread.rb +1 -2
- data/request-log-analyzer.gemspec +3 -3
- data/spec/database.yml +6 -0
- data/spec/unit/aggregator/database_inserter_spec.rb +3 -3
- data/spec/unit/database/base_class_spec.rb +9 -16
- data/spec/unit/database/database_spec.rb +9 -14
- data/spec/unit/tracker/tracker_api_spec.rb +111 -36
- 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(", ")
|
data/lib/request_log_analyzer.rb
CHANGED
@@ -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.
|
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
|
-
|
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 =
|
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 =
|
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
|
-
|
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
|
-
|
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
|
-
|
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]
|
39
|
-
options[: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
|
-
#
|
129
|
-
|
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
|
-
#
|
138
|
-
@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 :
|
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
|
-
|
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
|
116
|
-
when :sources then
|
117
|
-
when :requests then
|
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
|
-
|
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
|
-
|
172
|
-
Object.send(:remove_const,
|
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
|
-
|
36
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 =>
|
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
|
-
|
101
|
-
|
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
|
-
|
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
|
-
|
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
|