request-log-analyzer 1.2.1 → 1.2.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/.gitignore +10 -0
- data/Rakefile +3 -1
- data/lib/request_log_analyzer/aggregator/database.rb +79 -46
- data/lib/request_log_analyzer/controller.rb +12 -7
- data/lib/request_log_analyzer/file_format/merb.rb +5 -7
- data/lib/request_log_analyzer/line_definition.rb +9 -0
- data/lib/request_log_analyzer/request.rb +2 -2
- data/lib/request_log_analyzer/source/log_parser.rb +4 -0
- data/lib/request_log_analyzer.rb +1 -1
- data/request-log-analyzer.gemspec +36 -0
- data/spec/fixtures/header_and_footer.log +6 -0
- data/spec/fixtures/merb_prefixed.log +9 -0
- data/spec/integration/command_line_usage_spec.rb +0 -2
- data/spec/lib/{helper.rb → helpers.rb} +1 -3
- data/spec/lib/macros.rb +2 -0
- data/spec/lib/matchers.rb +63 -0
- data/spec/lib/mocks.rb +12 -1
- data/spec/lib/testing_format.rb +6 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +13 -4
- data/spec/unit/aggregator/database_spec.rb +234 -0
- data/spec/unit/aggregator/summarizer_spec.rb +0 -2
- data/spec/unit/controller/controller_spec.rb +0 -2
- data/spec/unit/controller/log_processor_spec.rb +0 -2
- data/spec/unit/file_format/file_format_api_spec.rb +50 -71
- data/spec/unit/file_format/line_definition_spec.rb +49 -45
- data/spec/unit/file_format/merb_format_spec.rb +20 -2
- data/spec/unit/file_format/rails_format_spec.rb +0 -2
- data/spec/unit/filter/anonymize_filter_spec.rb +0 -1
- data/spec/unit/filter/field_filter_spec.rb +0 -3
- data/spec/unit/filter/filter_spec.rb +17 -0
- data/spec/unit/filter/timespan_filter_spec.rb +0 -3
- data/spec/unit/source/log_parser_spec.rb +5 -4
- data/spec/unit/source/request_spec.rb +91 -52
- data/spec/unit/tracker/duration_tracker_spec.rb +0 -6
- data/spec/unit/tracker/frequency_tracker_spec.rb +0 -6
- data/spec/unit/tracker/hourly_spread_spec.rb +0 -4
- data/spec/unit/tracker/timespan_tracker_spec.rb +0 -4
- data/spec/unit/tracker/tracker_api_spec.rb +0 -2
- data/tasks/github-gem.rake +199 -192
- metadata +120 -92
- data/RELEASE_NOTES.rdoc +0 -10
- data/spec/unit/aggregator/database_inserter_spec.rb +0 -106
- data/tasks/rspec.rake +0 -12
data/.gitignore
ADDED
data/Rakefile
CHANGED
|
@@ -16,10 +16,13 @@ module RequestLogAnalyzer::Aggregator
|
|
|
16
16
|
# created to log all parse warnings.
|
|
17
17
|
class Database < Base
|
|
18
18
|
|
|
19
|
+
attr_reader :request_class, :request_count, :orm_module, :warning_class
|
|
20
|
+
|
|
19
21
|
# Establishes a connection to the database and creates the necessary database schema for the
|
|
20
22
|
# current file format
|
|
21
23
|
def prepare
|
|
22
|
-
|
|
24
|
+
initialize_orm_module!
|
|
25
|
+
establish_database_connection!
|
|
23
26
|
File.unlink(options[:database]) if File.exist?(options[:database]) # TODO: keep old database?
|
|
24
27
|
create_database_schema!
|
|
25
28
|
end
|
|
@@ -28,9 +31,9 @@ module RequestLogAnalyzer::Aggregator
|
|
|
28
31
|
# This will create a record in the requests table and create a record for every line that has been parsed,
|
|
29
32
|
# in which the captured values will be stored.
|
|
30
33
|
def aggregate(request)
|
|
31
|
-
@request_object =
|
|
34
|
+
@request_object = request_class.new(:first_lineno => request.first_lineno, :last_lineno => request.last_lineno)
|
|
32
35
|
request.lines.each do |line|
|
|
33
|
-
class_columns =
|
|
36
|
+
class_columns = orm_module.const_get("#{line[:line_type]}_line".classify).column_names.reject { |column| ['id'].include?(column) }
|
|
34
37
|
attributes = Hash[*line.select { |(k, v)| class_columns.include?(k.to_s) }.flatten]
|
|
35
38
|
@request_object.send("#{line[:line_type]}_lines").build(attributes)
|
|
36
39
|
end
|
|
@@ -41,13 +44,14 @@ module RequestLogAnalyzer::Aggregator
|
|
|
41
44
|
|
|
42
45
|
# Finalizes the aggregator by closing the connection to the database
|
|
43
46
|
def finalize
|
|
44
|
-
@request_count =
|
|
45
|
-
|
|
47
|
+
@request_count = orm_module::Request.count
|
|
48
|
+
remove_database_connection!
|
|
49
|
+
deinitialize_orm_module!
|
|
46
50
|
end
|
|
47
51
|
|
|
48
52
|
# Records w warining in the warnings table.
|
|
49
53
|
def warning(type, message, lineno)
|
|
50
|
-
|
|
54
|
+
warning_class.create!(:warning_type => type.to_s, :message => message, :lineno => lineno)
|
|
51
55
|
end
|
|
52
56
|
|
|
53
57
|
# Prints a short report of what has been inserted into the database
|
|
@@ -61,91 +65,119 @@ module RequestLogAnalyzer::Aggregator
|
|
|
61
65
|
output << "\n"
|
|
62
66
|
end
|
|
63
67
|
|
|
68
|
+
# Retreives the connection that is used for the database inserter
|
|
69
|
+
def connection
|
|
70
|
+
orm_module::Base.connection
|
|
71
|
+
end
|
|
72
|
+
|
|
64
73
|
protected
|
|
65
74
|
|
|
75
|
+
# Create a module and a default subclass of ActiveRecord::Base on which to establish the database connection
|
|
76
|
+
def initialize_orm_module!
|
|
77
|
+
|
|
78
|
+
# Create a Database module in the file format if it does not yet exists
|
|
79
|
+
file_format.class.const_set('Database', Module.new) unless file_format.class.const_defined?('Database')
|
|
80
|
+
@orm_module = file_format.class.const_get('Database')
|
|
81
|
+
|
|
82
|
+
# Register the base activerecord class
|
|
83
|
+
unless orm_module.const_defined?('Base')
|
|
84
|
+
orm_base_class = Class.new(ActiveRecord::Base)
|
|
85
|
+
orm_base_class.abstract_class = true
|
|
86
|
+
orm_module.const_set('Base', orm_base_class)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Deinitializes the ORM module and the ActiveRecord::Base subclass.
|
|
91
|
+
def deinitialize_orm_module!
|
|
92
|
+
file_format.class.send(:remove_const, 'Database') if file_format.class.const_defined?('Database')
|
|
93
|
+
@orm_module = nil
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Established a connection with the database for this session
|
|
97
|
+
def establish_database_connection!
|
|
98
|
+
orm_module::Base.establish_connection(:adapter => 'sqlite3', :database => options[:database])
|
|
99
|
+
#ActiveRecord::Migration.class_eval("def self.connection; #{@orm_module.to_s}::Base.connection; end ")
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def remove_database_connection!
|
|
103
|
+
#ActiveRecord::Migration.class_eval("def self.connection; ActiveRecord::Base.connection; end ")
|
|
104
|
+
orm_module::Base.remove_connection
|
|
105
|
+
end
|
|
106
|
+
|
|
66
107
|
# This function creates a database table for a given line definition.
|
|
67
108
|
# It will create a field for every capture in the line, and adds a lineno field to indicate at
|
|
68
109
|
# what line in the original file the line was found, and a request_id to link lines related
|
|
69
110
|
# to the same request. It will also create an index in the request_id field to speed up queries.
|
|
70
|
-
def create_database_table(
|
|
71
|
-
|
|
72
|
-
|
|
111
|
+
def create_database_table(definition)
|
|
112
|
+
connection.create_table("#{definition.name}_lines") do |t|
|
|
113
|
+
|
|
114
|
+
# Add default fields for every line type
|
|
73
115
|
t.column(:request_id, :integer)
|
|
74
116
|
t.column(:lineno, :integer)
|
|
117
|
+
|
|
75
118
|
definition.captures.each do |capture|
|
|
76
|
-
|
|
77
119
|
# Add a field for every capture
|
|
78
120
|
t.column(capture[:name], column_type(capture[:type]))
|
|
79
|
-
|
|
80
|
-
# If the capture provides other field as well, create them too
|
|
81
|
-
if capture[:provides].kind_of?(Hash)
|
|
82
|
-
capture[:provides].each do |field, field_type|
|
|
83
|
-
t.column(field, column_type(field_type))
|
|
84
|
-
end
|
|
85
|
-
end
|
|
121
|
+
|
|
122
|
+
# If the capture provides other field as well, create columns for them, too
|
|
123
|
+
capture[:provides].each { |field, field_type| t.column(field, column_type(field_type)) } if capture[:provides].kind_of?(Hash)
|
|
86
124
|
end
|
|
87
125
|
end
|
|
88
|
-
|
|
126
|
+
|
|
127
|
+
# Create an index on the request_id column to support faster querying
|
|
128
|
+
connection.add_index("#{definition.name}_lines", [:request_id])
|
|
89
129
|
end
|
|
90
130
|
|
|
91
131
|
# Creates an ActiveRecord class for a given line definition.
|
|
92
132
|
# A subclass of ActiveRecord::Base is created and an association with the Request class is
|
|
93
133
|
# created using belongs_to / has_many. This association will later be used to create records
|
|
94
134
|
# in the corresponding table. This table should already be created before this method is called.
|
|
95
|
-
def create_activerecord_class(
|
|
96
|
-
class_name = "#{name}_line".camelize
|
|
97
|
-
klass = Class.new(
|
|
135
|
+
def create_activerecord_class(definition)
|
|
136
|
+
class_name = "#{definition.name}_line".camelize
|
|
137
|
+
klass = Class.new(orm_module::Base)
|
|
98
138
|
klass.send(:belongs_to, :request)
|
|
99
139
|
|
|
100
|
-
definition.captures.each do |capture|
|
|
101
|
-
klass.send(:serialize, capture[:name], Hash)
|
|
140
|
+
definition.captures.select { |c| c.has_key?(:provides) }.each do |capture|
|
|
141
|
+
klass.send(:serialize, capture[:name], Hash)
|
|
102
142
|
end
|
|
103
143
|
|
|
104
|
-
|
|
105
|
-
|
|
144
|
+
orm_module.const_set(class_name, klass) unless orm_module.const_defined?(class_name)
|
|
145
|
+
request_class.send(:has_many, "#{definition.name}_lines".to_sym)
|
|
106
146
|
end
|
|
107
147
|
|
|
108
148
|
# Creates a requests table, in which a record is created for every request. It also creates an
|
|
109
149
|
# ActiveRecord::Base class to communicate with this table.
|
|
110
150
|
def create_request_table_and_class
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
t.
|
|
114
|
-
t.integer :last_lineno
|
|
151
|
+
connection.create_table("requests") do |t|
|
|
152
|
+
t.column :first_lineno, :integer
|
|
153
|
+
t.column :last_lineno, :integer
|
|
115
154
|
end
|
|
116
155
|
|
|
117
|
-
|
|
118
|
-
@request_class =
|
|
156
|
+
orm_module.const_set('Request', Class.new(orm_module::Base)) unless orm_module.const_defined?('Request')
|
|
157
|
+
@request_class = orm_module.const_get('Request')
|
|
119
158
|
end
|
|
120
159
|
|
|
121
160
|
# Creates a warnings table and a corresponding Warning class to communicate with this table using ActiveRecord.
|
|
122
161
|
def create_warning_table_and_class
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
t.
|
|
126
|
-
t.
|
|
127
|
-
t.integer :lineno
|
|
162
|
+
connection.create_table("warnings") do |t|
|
|
163
|
+
t.column :warning_type, :string, :limit => 30, :null => false
|
|
164
|
+
t.column :message, :string
|
|
165
|
+
t.column :lineno, :integer
|
|
128
166
|
end
|
|
129
167
|
|
|
130
|
-
|
|
168
|
+
orm_module.const_set('Warning', Class.new(orm_module::Base)) unless orm_module.const_defined?('Warning')
|
|
169
|
+
@warning_class = orm_module.const_get('Warning')
|
|
131
170
|
end
|
|
132
171
|
|
|
133
172
|
# Creates the database schema and related ActiveRecord::Base subclasses that correspond to the
|
|
134
173
|
# file format definition. These ORM classes will later be used to create records in the database.
|
|
135
174
|
def create_database_schema!
|
|
136
|
-
|
|
137
|
-
if file_format.class.const_defined?('Database')
|
|
138
|
-
@orm_module = file_format.class.const_get('Database')
|
|
139
|
-
else
|
|
140
|
-
@orm_module = file_format.class.const_set('Database', Module.new)
|
|
141
|
-
end
|
|
142
|
-
|
|
143
175
|
create_request_table_and_class
|
|
144
176
|
create_warning_table_and_class
|
|
145
177
|
|
|
146
178
|
file_format.line_definitions.each do |name, definition|
|
|
147
|
-
create_database_table(
|
|
148
|
-
create_activerecord_class(
|
|
179
|
+
create_database_table(definition)
|
|
180
|
+
create_activerecord_class(definition)
|
|
149
181
|
end
|
|
150
182
|
end
|
|
151
183
|
|
|
@@ -154,6 +186,7 @@ module RequestLogAnalyzer::Aggregator
|
|
|
154
186
|
def column_type(type_indicator)
|
|
155
187
|
case type_indicator
|
|
156
188
|
when :eval; :text
|
|
189
|
+
when :hash; :text
|
|
157
190
|
when :text; :text
|
|
158
191
|
when :string; :string
|
|
159
192
|
when :sec; :double
|
|
@@ -191,14 +191,11 @@ module RequestLogAnalyzer
|
|
|
191
191
|
def run!
|
|
192
192
|
|
|
193
193
|
@aggregators.each { |agg| agg.prepare }
|
|
194
|
+
install_signal_handlers
|
|
194
195
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
end
|
|
199
|
-
rescue Interrupt => e
|
|
200
|
-
handle_progress(:interrupted)
|
|
201
|
-
puts "Caught interrupt! Stopped parsing."
|
|
196
|
+
@source.each_request do |request|
|
|
197
|
+
aggregate_request(filter_request(request))
|
|
198
|
+
break if @interrupted
|
|
202
199
|
end
|
|
203
200
|
|
|
204
201
|
@aggregators.each { |agg| agg.finalize }
|
|
@@ -219,5 +216,13 @@ module RequestLogAnalyzer
|
|
|
219
216
|
end
|
|
220
217
|
end
|
|
221
218
|
|
|
219
|
+
def install_signal_handlers
|
|
220
|
+
Signal.trap("INT") do
|
|
221
|
+
handle_progress(:interrupted)
|
|
222
|
+
puts "Caught interrupt! Stopping parsing..."
|
|
223
|
+
@interrupted = true
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
222
227
|
end
|
|
223
228
|
end
|
|
@@ -16,18 +16,16 @@ module RequestLogAnalyzer::FileFormat
|
|
|
16
16
|
line.teaser = /Params/
|
|
17
17
|
line.regexp = /Params\:\ (\{.+\})/
|
|
18
18
|
line.captures << { :name => :params, :type => :eval, :provides => {
|
|
19
|
-
:namespace => :string, :controller => :string, :action => :string, :format => :string } }
|
|
19
|
+
:namespace => :string, :controller => :string, :action => :string, :format => :string, :method => :string } }
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
# ~ {:dispatch_time=>0.006117, :after_filters_time=>6.1e-05, :before_filters_time=>0.000712, :action_time=>0.005833}
|
|
23
23
|
line_definition :completed do |line|
|
|
24
24
|
line.footer = true
|
|
25
|
-
line.
|
|
26
|
-
line.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
<< { :name => :before_filters_time, :type => :duration } \
|
|
30
|
-
<< { :name => :action_time, :type => :duration }
|
|
25
|
+
line.regexp = /(\{.*\:dispatch_time\s*=>\s*\d+\.\d+.*\})/
|
|
26
|
+
line.captures << { :name => :times_hash, :type => :eval, :provides => {
|
|
27
|
+
:dispatch_time => :duration, :after_filters_time => :duration,
|
|
28
|
+
:before_filters_time => :duration, :action_time => :duration } }
|
|
31
29
|
end
|
|
32
30
|
|
|
33
31
|
REQUEST_CATEGORIZER = Proc.new do |request|
|
|
@@ -66,6 +66,8 @@ module RequestLogAnalyzer
|
|
|
66
66
|
|
|
67
67
|
alias :=~ :matches
|
|
68
68
|
|
|
69
|
+
# matches the line and converts the captured values using the request's
|
|
70
|
+
# convert_value function.
|
|
69
71
|
def match_for(line, request, lineno = nil, parser = nil)
|
|
70
72
|
if match_info = matches(line, lineno, parser)
|
|
71
73
|
convert_captured_values(match_info[:captures], request)
|
|
@@ -74,11 +76,18 @@ module RequestLogAnalyzer
|
|
|
74
76
|
end
|
|
75
77
|
end
|
|
76
78
|
|
|
79
|
+
# Updates a captures hash using the converters specified in the request
|
|
80
|
+
# and handle the :provides option in the line definition.
|
|
77
81
|
def convert_captured_values(values, request)
|
|
78
82
|
value_hash = {}
|
|
79
83
|
captures.each_with_index do |capture, index|
|
|
84
|
+
|
|
85
|
+
# convert the value using the request convert_value function
|
|
80
86
|
converted = request.convert_value(values[index], capture)
|
|
81
87
|
value_hash[capture[:name]] ||= converted
|
|
88
|
+
|
|
89
|
+
# Add items directly to the resulting hash from the converted value
|
|
90
|
+
# if it is a hash and they are set in the :provides hash for this line definition
|
|
82
91
|
if converted.kind_of?(Hash) && capture[:provides].kind_of?(Hash)
|
|
83
92
|
capture[:provides].each do |name, type|
|
|
84
93
|
value_hash[name] ||= request.convert_value(converted[name], { :type => type })
|
|
@@ -145,11 +145,11 @@ module RequestLogAnalyzer
|
|
|
145
145
|
end
|
|
146
146
|
|
|
147
147
|
def first_lineno
|
|
148
|
-
@lines.
|
|
148
|
+
@lines.map { |line| line[:lineno] }.reject { |v| v.nil? }.min
|
|
149
149
|
end
|
|
150
150
|
|
|
151
151
|
def last_lineno
|
|
152
|
-
@lines.
|
|
152
|
+
@lines.map { |line| line[:lineno] }.reject { |v| v.nil? }.max
|
|
153
153
|
end
|
|
154
154
|
end
|
|
155
155
|
end
|
|
@@ -207,6 +207,10 @@ module RequestLogAnalyzer::Source
|
|
|
207
207
|
warn(:unclosed_request, "Encountered header line (#{request_data[:line_definition].name.inspect}), but previous request was not closed!")
|
|
208
208
|
@current_request = nil # remove all data that was parsed, skip next request as well.
|
|
209
209
|
end
|
|
210
|
+
if footer_line?(request_data)
|
|
211
|
+
handle_request(@current_request, &block) # yield @current_request
|
|
212
|
+
@current_request = nil
|
|
213
|
+
end
|
|
210
214
|
else
|
|
211
215
|
@current_request = @file_format.request(request_data)
|
|
212
216
|
end
|
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 =
|
|
14
|
+
VERSION = "1.2.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.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Gem::Specification.new do |s|
|
|
2
|
+
s.name = 'request-log-analyzer'
|
|
3
|
+
s.version = "1.2.6"
|
|
4
|
+
s.date = "2009-09-02"
|
|
5
|
+
|
|
6
|
+
s.rubyforge_project = 'r-l-a'
|
|
7
|
+
|
|
8
|
+
s.bindir = 'bin'
|
|
9
|
+
s.executables = ['request-log-analyzer']
|
|
10
|
+
s.default_executable = 'request-log-analyzer'
|
|
11
|
+
|
|
12
|
+
s.summary = "A command line tool to analyze request logs for Rails, Merb and other application servers"
|
|
13
|
+
s.description = <<-eos
|
|
14
|
+
Request log analyzer's purpose is to find ot how your web application is being used and to focus your optimization efforts.
|
|
15
|
+
This tool will parse all requests in the application's log file and aggregate the information. Once it is finished parsing
|
|
16
|
+
the log file(s), it will show the requests that take op most server time using various metrics. It can also insert all
|
|
17
|
+
parsed request information into a database so you can roll your own analysis. It supports Rails- and Merb-based applications
|
|
18
|
+
out of the box, but file formats of other applications can easily be supported by supplying an easy to write log file format
|
|
19
|
+
definition.
|
|
20
|
+
eos
|
|
21
|
+
|
|
22
|
+
s.rdoc_options << '--title' << s.name << '--main' << 'README.rdoc' << '--line-numbers' << '--inline-source'
|
|
23
|
+
s.extra_rdoc_files = ['README.rdoc']
|
|
24
|
+
|
|
25
|
+
s.requirements << "To use the database inserter, ActiveRecord and an appropriate database adapter are required."
|
|
26
|
+
|
|
27
|
+
s.add_development_dependency('rspec', '>= 1.2.4')
|
|
28
|
+
s.add_development_dependency('git', '>= 1.1.0')
|
|
29
|
+
|
|
30
|
+
s.authors = ['Willem van Bergen', 'Bart ten Brinke']
|
|
31
|
+
s.email = ['willem@railsdoctors.com', 'bart@railsdoctors.com']
|
|
32
|
+
s.homepage = 'http://railsdoctors.com'
|
|
33
|
+
|
|
34
|
+
s.files = %w(spec/unit/filter/anonymize_filter_spec.rb lib/request_log_analyzer/line_definition.rb lib/request_log_analyzer/output/html.rb lib/request_log_analyzer/controller.rb spec/fixtures/rails_22_cached.log lib/request_log_analyzer/file_format/rails_development.rb spec/lib/macros.rb spec/fixtures/merb_prefixed.log tasks/request_log_analyzer.rake spec/unit/file_format/file_format_api_spec.rb spec/integration/command_line_usage_spec.rb spec/fixtures/decompression.log.bz2 lib/request_log_analyzer/log_processor.rb lib/request_log_analyzer/tracker.rb lib/request_log_analyzer/filter.rb spec/fixtures/rails_unordered.log bin/request-log-analyzer request-log-analyzer.gemspec DESIGN.rdoc spec/unit/filter/timespan_filter_spec.rb lib/request_log_analyzer/filter/field.rb lib/request_log_analyzer/tracker/frequency.rb spec/fixtures/decompression.log.gz spec/fixtures/decompression.log spec/lib/matchers.rb spec/fixtures/test_order.log lib/request_log_analyzer/output/fixed_width.rb lib/request_log_analyzer/filter/anonymize.rb spec/lib/testing_format.rb lib/request_log_analyzer/tracker/timespan.rb lib/request_log_analyzer/aggregator.rb lib/cli/progressbar.rb README.rdoc spec/fixtures/merb.log lib/request_log_analyzer/tracker/hourly_spread.rb .gitignore spec/unit/tracker/tracker_api_spec.rb spec/unit/tracker/duration_tracker_spec.rb lib/request_log_analyzer/aggregator/echo.rb spec/unit/controller/log_processor_spec.rb lib/request_log_analyzer.rb Rakefile spec/spec_helper.rb spec/unit/filter/filter_spec.rb lib/request_log_analyzer/aggregator/summarizer.rb lib/request_log_analyzer/file_format/rails.rb spec/fixtures/test_language_combined.log spec/fixtures/decompression.tar.gz spec/unit/filter/field_filter_spec.rb spec/spec.opts lib/request_log_analyzer/aggregator/database.rb lib/request_log_analyzer/filter/timespan.rb lib/request_log_analyzer/source/log_parser.rb spec/fixtures/decompression.tgz spec/unit/tracker/timespan_tracker_spec.rb spec/unit/tracker/hourly_spread_spec.rb spec/fixtures/header_and_footer.log lib/cli/tools.rb lib/request_log_analyzer/file_format/merb.rb spec/fixtures/multiple_files_1.log spec/unit/file_format/merb_format_spec.rb spec/unit/file_format/line_definition_spec.rb lib/request_log_analyzer/source.rb lib/request_log_analyzer/request.rb spec/unit/controller/controller_spec.rb lib/request_log_analyzer/output.rb spec/lib/helpers.rb spec/fixtures/rails_1x.log spec/lib/mocks.rb spec/fixtures/decompression.log.zip spec/unit/source/request_spec.rb spec/unit/source/log_parser_spec.rb spec/fixtures/test_file_format.log lib/request_log_analyzer/source/database.rb spec/unit/aggregator/database_spec.rb tasks/github-gem.rake lib/request_log_analyzer/tracker/duration.rb lib/request_log_analyzer/file_format.rb spec/unit/aggregator/summarizer_spec.rb spec/fixtures/rails_22.log spec/fixtures/multiple_files_2.log spec/fixtures/syslog_1x.log LICENSE spec/unit/tracker/frequency_tracker_spec.rb spec/unit/file_format/rails_format_spec.rb lib/cli/command_line_arguments.rb)
|
|
35
|
+
s.test_files = %w(spec/unit/filter/anonymize_filter_spec.rb spec/unit/file_format/file_format_api_spec.rb spec/integration/command_line_usage_spec.rb spec/unit/filter/timespan_filter_spec.rb spec/unit/tracker/tracker_api_spec.rb spec/unit/tracker/duration_tracker_spec.rb spec/unit/controller/log_processor_spec.rb spec/unit/filter/filter_spec.rb spec/unit/filter/field_filter_spec.rb spec/unit/tracker/timespan_tracker_spec.rb spec/unit/tracker/hourly_spread_spec.rb spec/unit/file_format/merb_format_spec.rb spec/unit/file_format/line_definition_spec.rb spec/unit/controller/controller_spec.rb spec/unit/source/request_spec.rb spec/unit/source/log_parser_spec.rb spec/unit/aggregator/database_spec.rb spec/unit/aggregator/summarizer_spec.rb spec/unit/tracker/frequency_tracker_spec.rb spec/unit/file_format/rails_format_spec.rb)
|
|
36
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Aug 31 18:35:23 typekit-web001 merb: ~
|
|
2
|
+
Aug 31 18:35:23 typekit-web001 merb: cache: [GET /] miss
|
|
3
|
+
Aug 31 18:35:24 typekit-web001 merb: ~ Started request handling: Mon Aug 31 18:35:25 +0000 2009
|
|
4
|
+
Aug 31 18:35:24 typekit-web001 merb: ~ Routed to: {"action"=>"index", "controller"=>"home"}
|
|
5
|
+
Aug 31 18:35:24 typekit-web001 merb: ~ Params: {"action"=>"index", "controller"=>"home"}
|
|
6
|
+
Aug 31 18:35:24 typekit-web001 merb: ~ In repository block default
|
|
7
|
+
Aug 31 18:35:24 typekit-web001 merb: ~ (0.000000) SELECT `id`, `created_at`, `updated_at`, `braintree_vault_id`, `cancelled_at` FROM `accounts` WHERE (`id` IN (6214)) ORDER BY `id`
|
|
8
|
+
Aug 31 18:35:24 typekit-web001 merb: ~ Redirecting to: /plans (302)
|
|
9
|
+
Aug 31 18:35:24 typekit-web001 merb: ~ {:after_filters_time=>0.0, :before_filters_time=>0.0, :dispatch_time=>0.012001, :action_time=>0.012001}
|
data/spec/lib/macros.rb
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module RequestLogAnalyzer::Spec::Matchers
|
|
2
|
+
|
|
3
|
+
class HasLineDefinition
|
|
4
|
+
|
|
5
|
+
def initialize(line_type)
|
|
6
|
+
@line_type = line_type.to_sym
|
|
7
|
+
@captures = []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def capturing(*captures)
|
|
11
|
+
@captures += captures
|
|
12
|
+
return self
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def matches?(file_format)
|
|
16
|
+
if file_format.new.line_definitions.include?(@line_type)
|
|
17
|
+
ld = file_format.new.line_definitions[@line_type]
|
|
18
|
+
@captures.all? { |c| ld.captures.include?(c) }
|
|
19
|
+
else
|
|
20
|
+
false
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class Parse
|
|
26
|
+
def initialize(line)
|
|
27
|
+
@line = line
|
|
28
|
+
@captures = nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def failure_message
|
|
32
|
+
message = "expected to parse the provided line"
|
|
33
|
+
if @found_captures
|
|
34
|
+
message << "with captures #{@captures.inspect}, but captured #{@found_captures.inspect}"
|
|
35
|
+
end
|
|
36
|
+
return message
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def capturing(*captures)
|
|
40
|
+
@captures = captures
|
|
41
|
+
return self
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def matches?(line_definition)
|
|
45
|
+
hash = line_definition.matches(@line)
|
|
46
|
+
if hash
|
|
47
|
+
@found_captures = hash[:captures].to_a
|
|
48
|
+
@captures.nil? ? true : @found_captures.eql?(@captures)
|
|
49
|
+
else
|
|
50
|
+
return false
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def have_line_definition(line_type)
|
|
56
|
+
return HasLineDefinition.new(line_type)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def parse(line)
|
|
60
|
+
Parse.new(line)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
data/spec/lib/mocks.rb
CHANGED
|
@@ -38,10 +38,21 @@ module RequestLogAnalyzer::Spec::Mocks
|
|
|
38
38
|
output.stub!(:title)
|
|
39
39
|
output.stub!(:line)
|
|
40
40
|
output.stub!(:with_style)
|
|
41
|
-
output.stub!(:table)
|
|
41
|
+
output.stub!(:table).and_yield([])
|
|
42
42
|
output.stub!(:io).and_return(mock_io)
|
|
43
43
|
return output
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
def mock_migrator
|
|
47
|
+
table_creator = mock('ActiveRecord table creator')
|
|
48
|
+
table_creator.stub!(:column)
|
|
49
|
+
|
|
50
|
+
migrator = mock('ActiveRecord::Base.connection for migrations')
|
|
51
|
+
migrator.stub!(:add_index)
|
|
52
|
+
migrator.stub!(:create_table).and_yield(table_creator).and_return(true)
|
|
53
|
+
migrator.stub!(:table_creator).and_return(table_creator)
|
|
54
|
+
return migrator
|
|
55
|
+
end
|
|
56
|
+
|
|
46
57
|
|
|
47
58
|
end
|
data/spec/lib/testing_format.rb
CHANGED
|
@@ -27,6 +27,12 @@ class TestingFormat < RequestLogAnalyzer::FileFormat::Base
|
|
|
27
27
|
line.captures = [{ :name => :request_no, :type => :integer }]
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
format_definition.combined do |line|
|
|
31
|
+
line.header = true
|
|
32
|
+
line.footer = true
|
|
33
|
+
line.regexp = /this is a header and footer line/
|
|
34
|
+
end
|
|
35
|
+
|
|
30
36
|
report do |analyze|
|
|
31
37
|
analyze.frequency :test_capture, :title => 'What is testing exactly?'
|
|
32
38
|
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
CHANGED
|
@@ -2,14 +2,23 @@ $:.reject! { |e| e.include? 'TextMate' }
|
|
|
2
2
|
$: << File.join(File.dirname(__FILE__), '..', 'lib')
|
|
3
3
|
|
|
4
4
|
require 'rubygems'
|
|
5
|
-
require 'spec'
|
|
5
|
+
require 'spec/autorun'
|
|
6
6
|
require 'request_log_analyzer'
|
|
7
7
|
|
|
8
8
|
module RequestLogAnalyzer::Spec
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
require
|
|
11
|
+
# Include all files in the spec_helper directory
|
|
12
|
+
Dir[File.dirname(__FILE__) + "/lib/**/*.rb"].each do |file|
|
|
13
|
+
require file
|
|
14
|
+
end
|
|
14
15
|
|
|
15
16
|
Dir.mkdir("#{File.dirname(__FILE__)}/../tmp") unless File.exist?("#{File.dirname(__FILE__)}/../tmp")
|
|
17
|
+
|
|
18
|
+
Spec::Runner.configure do |config|
|
|
19
|
+
config.include RequestLogAnalyzer::Spec::Matchers
|
|
20
|
+
config.include RequestLogAnalyzer::Spec::Mocks
|
|
21
|
+
config.include RequestLogAnalyzer::Spec::Helpers
|
|
22
|
+
|
|
23
|
+
config.extend RequestLogAnalyzer::Spec::Macros
|
|
24
|
+
end
|