ngmoco-request-log-analyzer 1.4.2
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/DESIGN.rdoc +41 -0
- data/LICENSE +20 -0
- data/README.rdoc +39 -0
- data/Rakefile +8 -0
- data/bin/request-log-analyzer +114 -0
- data/lib/cli/command_line_arguments.rb +301 -0
- data/lib/cli/database_console.rb +26 -0
- data/lib/cli/database_console_init.rb +43 -0
- data/lib/cli/progressbar.rb +213 -0
- data/lib/cli/tools.rb +46 -0
- data/lib/request_log_analyzer.rb +44 -0
- data/lib/request_log_analyzer/aggregator.rb +49 -0
- data/lib/request_log_analyzer/aggregator/database_inserter.rb +83 -0
- data/lib/request_log_analyzer/aggregator/echo.rb +29 -0
- data/lib/request_log_analyzer/aggregator/summarizer.rb +175 -0
- data/lib/request_log_analyzer/controller.rb +332 -0
- data/lib/request_log_analyzer/database.rb +102 -0
- data/lib/request_log_analyzer/database/base.rb +115 -0
- data/lib/request_log_analyzer/database/connection.rb +38 -0
- 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 +160 -0
- data/lib/request_log_analyzer/file_format/amazon_s3.rb +71 -0
- data/lib/request_log_analyzer/file_format/apache.rb +141 -0
- data/lib/request_log_analyzer/file_format/merb.rb +67 -0
- data/lib/request_log_analyzer/file_format/rack.rb +11 -0
- data/lib/request_log_analyzer/file_format/rails.rb +176 -0
- data/lib/request_log_analyzer/file_format/rails_development.rb +12 -0
- data/lib/request_log_analyzer/filter.rb +30 -0
- data/lib/request_log_analyzer/filter/anonymize.rb +39 -0
- data/lib/request_log_analyzer/filter/field.rb +42 -0
- data/lib/request_log_analyzer/filter/timespan.rb +45 -0
- data/lib/request_log_analyzer/line_definition.rb +111 -0
- data/lib/request_log_analyzer/log_processor.rb +99 -0
- data/lib/request_log_analyzer/mailer.rb +62 -0
- data/lib/request_log_analyzer/output.rb +113 -0
- data/lib/request_log_analyzer/output/fixed_width.rb +220 -0
- data/lib/request_log_analyzer/output/html.rb +184 -0
- data/lib/request_log_analyzer/request.rb +175 -0
- data/lib/request_log_analyzer/source.rb +72 -0
- data/lib/request_log_analyzer/source/database_loader.rb +87 -0
- data/lib/request_log_analyzer/source/log_parser.rb +274 -0
- data/lib/request_log_analyzer/tracker.rb +206 -0
- data/lib/request_log_analyzer/tracker/duration.rb +104 -0
- data/lib/request_log_analyzer/tracker/frequency.rb +95 -0
- data/lib/request_log_analyzer/tracker/hourly_spread.rb +107 -0
- data/lib/request_log_analyzer/tracker/timespan.rb +81 -0
- data/lib/request_log_analyzer/tracker/traffic.rb +106 -0
- data/request-log-analyzer.gemspec +40 -0
- data/spec/database.yml +23 -0
- data/spec/fixtures/apache_combined.log +5 -0
- data/spec/fixtures/apache_common.log +10 -0
- data/spec/fixtures/decompression.log +12 -0
- data/spec/fixtures/decompression.log.bz2 +0 -0
- data/spec/fixtures/decompression.log.gz +0 -0
- data/spec/fixtures/decompression.log.zip +0 -0
- data/spec/fixtures/decompression.tar.gz +0 -0
- data/spec/fixtures/decompression.tgz +0 -0
- data/spec/fixtures/header_and_footer.log +6 -0
- data/spec/fixtures/merb.log +84 -0
- data/spec/fixtures/merb_prefixed.log +9 -0
- data/spec/fixtures/multiple_files_1.log +5 -0
- data/spec/fixtures/multiple_files_2.log +2 -0
- data/spec/fixtures/rails.db +0 -0
- data/spec/fixtures/rails_1x.log +59 -0
- data/spec/fixtures/rails_22.log +12 -0
- data/spec/fixtures/rails_22_cached.log +10 -0
- data/spec/fixtures/rails_unordered.log +24 -0
- data/spec/fixtures/syslog_1x.log +5 -0
- data/spec/fixtures/test_file_format.log +13 -0
- data/spec/fixtures/test_language_combined.log +14 -0
- data/spec/fixtures/test_order.log +16 -0
- data/spec/integration/command_line_usage_spec.rb +84 -0
- data/spec/integration/munin_plugins_rails_spec.rb +58 -0
- data/spec/integration/scout_spec.rb +151 -0
- data/spec/lib/helpers.rb +52 -0
- data/spec/lib/macros.rb +18 -0
- data/spec/lib/matchers.rb +77 -0
- data/spec/lib/mocks.rb +76 -0
- data/spec/lib/testing_format.rb +46 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/unit/aggregator/database_inserter_spec.rb +93 -0
- data/spec/unit/aggregator/summarizer_spec.rb +26 -0
- data/spec/unit/controller/controller_spec.rb +41 -0
- data/spec/unit/controller/log_processor_spec.rb +18 -0
- data/spec/unit/database/base_class_spec.rb +183 -0
- data/spec/unit/database/connection_spec.rb +34 -0
- data/spec/unit/database/database_spec.rb +133 -0
- data/spec/unit/file_format/amazon_s3_format_spec.rb +49 -0
- data/spec/unit/file_format/apache_format_spec.rb +203 -0
- data/spec/unit/file_format/file_format_api_spec.rb +69 -0
- data/spec/unit/file_format/line_definition_spec.rb +75 -0
- data/spec/unit/file_format/merb_format_spec.rb +52 -0
- data/spec/unit/file_format/rails_format_spec.rb +164 -0
- data/spec/unit/filter/anonymize_filter_spec.rb +21 -0
- data/spec/unit/filter/field_filter_spec.rb +66 -0
- data/spec/unit/filter/filter_spec.rb +17 -0
- data/spec/unit/filter/timespan_filter_spec.rb +58 -0
- data/spec/unit/mailer_spec.rb +30 -0
- data/spec/unit/request_spec.rb +111 -0
- data/spec/unit/source/log_parser_spec.rb +119 -0
- data/spec/unit/tracker/duration_tracker_spec.rb +130 -0
- data/spec/unit/tracker/frequency_tracker_spec.rb +88 -0
- data/spec/unit/tracker/hourly_spread_spec.rb +79 -0
- data/spec/unit/tracker/timespan_tracker_spec.rb +73 -0
- data/spec/unit/tracker/tracker_api_spec.rb +124 -0
- data/spec/unit/tracker/traffic_tracker_spec.rb +107 -0
- data/tasks/github-gem.rake +323 -0
- data/tasks/request_log_analyzer.rake +26 -0
- metadata +220 -0
data/spec/lib/macros.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module RequestLogAnalyzer::Spec::Macros
|
|
2
|
+
|
|
3
|
+
def test_databases
|
|
4
|
+
require 'yaml'
|
|
5
|
+
hash = YAML.load(File.read("#{File.dirname(__FILE__)}/../database.yml"))
|
|
6
|
+
hash.inject({}) { |res, (name, h)| res[name] = h.map { |(k,v)| "#{k}=#{v}" }.join(';'); res }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Create or return a new TestingFormat
|
|
10
|
+
def testing_format
|
|
11
|
+
@testing_format ||= TestingFormat.create
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def default_orm_class_names
|
|
15
|
+
['Warning', 'Request', 'Source']
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
file_format = file_format.create if file_format.kind_of?(Class)
|
|
17
|
+
if ld = file_format.line_definitions[@line_type]
|
|
18
|
+
@captures.all? { |c| ld.captures.map { |cd| cd[:name] }.include?(c) }
|
|
19
|
+
else
|
|
20
|
+
false
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class ParseLine
|
|
26
|
+
|
|
27
|
+
def initialize(line)
|
|
28
|
+
@line = line
|
|
29
|
+
@captures = {}
|
|
30
|
+
@line_type = nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def failure_message
|
|
34
|
+
@failure_message
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def as(line_type)
|
|
38
|
+
@line_type = line_type
|
|
39
|
+
return self
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def and_capture(captures)
|
|
43
|
+
@captures = captures
|
|
44
|
+
return self
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def fail(message)
|
|
48
|
+
@failure_message = message
|
|
49
|
+
return false
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def matches?(file_format)
|
|
53
|
+
if @line_hash = file_format.parse_line(@line)
|
|
54
|
+
if @line_type.nil? || @line_hash[:line_definition].name == @line_type
|
|
55
|
+
@request = file_format.request(@line_hash)
|
|
56
|
+
@captures.each do |key, value|
|
|
57
|
+
return fail("Expected line #{@line.inspect}\n to capture #{key.inspect} as #{value.inspect} but was #{@request[key].inspect}.") if @request[key] != value
|
|
58
|
+
end
|
|
59
|
+
return true
|
|
60
|
+
else
|
|
61
|
+
return fail("The line should match the #{@line_type.inspect} line definition, but matched #{@line_hash[:line_definition].name.inspect} instead.")
|
|
62
|
+
end
|
|
63
|
+
else
|
|
64
|
+
return fail("The line did not match any line definition.")
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def have_line_definition(line_type)
|
|
70
|
+
return HasLineDefinition.new(line_type)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def parse_line(line)
|
|
74
|
+
ParseLine.new(line)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
data/spec/lib/mocks.rb
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module RequestLogAnalyzer::Spec::Mocks
|
|
2
|
+
|
|
3
|
+
def mock_source
|
|
4
|
+
source = mock('RequestLogAnalyzer::Source::Base')
|
|
5
|
+
source.stub!(:file_format).and_return(testing_format)
|
|
6
|
+
source.stub!(:parsed_requests).and_return(2)
|
|
7
|
+
source.stub!(:skipped_requests).and_return(1)
|
|
8
|
+
source.stub!(:parse_lines).and_return(10)
|
|
9
|
+
|
|
10
|
+
source.stub!(:warning=)
|
|
11
|
+
source.stub!(:progress=)
|
|
12
|
+
source.stub!(:source_changes=)
|
|
13
|
+
|
|
14
|
+
source.stub!(:prepare)
|
|
15
|
+
source.stub!(:finalize)
|
|
16
|
+
|
|
17
|
+
source.stub!(:each_request).and_return do |block|
|
|
18
|
+
block.call(testing_format.request(:field => 'value1'))
|
|
19
|
+
block.call(testing_format.request(:field => 'value2'))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
return source
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def mock_io
|
|
26
|
+
mio = mock('IO')
|
|
27
|
+
mio.stub!(:print)
|
|
28
|
+
mio.stub!(:puts)
|
|
29
|
+
mio.stub!(:write)
|
|
30
|
+
return mio
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def mock_output
|
|
34
|
+
output = mock('RequestLogAnalyzer::Output::Base')
|
|
35
|
+
output.stub!(:header)
|
|
36
|
+
output.stub!(:footer)
|
|
37
|
+
output.stub!(:puts)
|
|
38
|
+
output.stub!(:<<)
|
|
39
|
+
output.stub!(:colorize).and_return("Fancy text")
|
|
40
|
+
output.stub!(:link)
|
|
41
|
+
output.stub!(:title)
|
|
42
|
+
output.stub!(:line)
|
|
43
|
+
output.stub!(:with_style)
|
|
44
|
+
output.stub!(:table).and_yield([])
|
|
45
|
+
output.stub!(:io).and_return(mock_io)
|
|
46
|
+
output.stub!(:options).and_return({})
|
|
47
|
+
output.stub!(:slice_results).and_return { |a| a }
|
|
48
|
+
return output
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def mock_database(*stubs)
|
|
52
|
+
database = mock('RequestLogAnalyzer::Database')
|
|
53
|
+
database.stub!(:connect)
|
|
54
|
+
database.stub!(:disconnect)
|
|
55
|
+
database.stub!(:connection).and_return(mock_connection)
|
|
56
|
+
stubs.each { |s| database.stub!(s)}
|
|
57
|
+
return database
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def mock_connection
|
|
61
|
+
table_creator = mock('ActiveRecord table creator')
|
|
62
|
+
table_creator.stub!(:column)
|
|
63
|
+
|
|
64
|
+
connection = mock('ActiveRecord::Base.connection')
|
|
65
|
+
connection.stub!(:add_index)
|
|
66
|
+
connection.stub!(:remove_index)
|
|
67
|
+
connection.stub!(:table_exists?).and_return(false)
|
|
68
|
+
connection.stub!(:create_table).and_yield(table_creator).and_return(true)
|
|
69
|
+
connection.stub!(:table_creator).and_return(table_creator)
|
|
70
|
+
return connection
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def request_counter
|
|
74
|
+
@request_counter ||= mock('aggregator to count request')
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Simple log file specification, used to test log parser.
|
|
2
|
+
class TestingFormat < RequestLogAnalyzer::FileFormat::Base
|
|
3
|
+
|
|
4
|
+
format_definition.first do |line|
|
|
5
|
+
line.header = true
|
|
6
|
+
line.teaser = /processing /
|
|
7
|
+
line.regexp = /processing request (\d+)/
|
|
8
|
+
line.captures = [{ :name => :request_no, :type => :integer }]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
format_definition.test do |line|
|
|
12
|
+
line.teaser = /testing /
|
|
13
|
+
line.regexp = /testing is (\w+)(?: in (\d+\.\d+)ms)?/
|
|
14
|
+
line.captures = [{ :name => :test_capture, :type => :test_type },
|
|
15
|
+
{ :name => :duration, :type => :duration, :unit => :msec }]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
format_definition.eval do |line|
|
|
19
|
+
line.regexp = /evaluation (\{.*\})/
|
|
20
|
+
line.captures = [{ :name => :evaluated, :type => :eval, :provides => { :greating => :string, :what => :string } }]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
format_definition.last do |line|
|
|
24
|
+
line.footer = true
|
|
25
|
+
line.teaser = /finishing /
|
|
26
|
+
line.regexp = /finishing request (\d+)/
|
|
27
|
+
line.captures = [{ :name => :request_no, :type => :integer }]
|
|
28
|
+
end
|
|
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
|
+
|
|
36
|
+
report do |analyze|
|
|
37
|
+
analyze.frequency :test_capture, :title => 'What is testing exactly?'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Request < RequestLogAnalyzer::Request
|
|
41
|
+
def convert_test_type(value, definition)
|
|
42
|
+
"Testing is #{value}"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
$:.reject! { |e| e.include? 'TextMate' }
|
|
2
|
+
$: << File.join(File.dirname(__FILE__), '..', 'lib')
|
|
3
|
+
|
|
4
|
+
require 'rubygems'
|
|
5
|
+
require 'spec/autorun'
|
|
6
|
+
require 'request_log_analyzer'
|
|
7
|
+
|
|
8
|
+
module RequestLogAnalyzer::Spec
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Include all files in the spec_helper directory
|
|
12
|
+
Dir[File.dirname(__FILE__) + "/lib/**/*.rb"].each do |file|
|
|
13
|
+
require file
|
|
14
|
+
end
|
|
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
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe RequestLogAnalyzer::Aggregator::DatabaseInserter do
|
|
4
|
+
|
|
5
|
+
before(:all) do
|
|
6
|
+
@log_parser = RequestLogAnalyzer::Source::LogParser.new(testing_format)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# The prepare method is called before the parsing starts. It should establish a connection
|
|
10
|
+
# to a database that is suitable for inserting requests later on.
|
|
11
|
+
describe '#prepare' do
|
|
12
|
+
|
|
13
|
+
before(:each) do
|
|
14
|
+
@database = mock_database(:create_database_schema!, :drop_database_schema!, :file_format=)
|
|
15
|
+
@database_inserter = RequestLogAnalyzer::Aggregator::DatabaseInserter.new(@log_parser)
|
|
16
|
+
RequestLogAnalyzer::Database.stub!(:new).and_return(@database)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'should establish the database connection' do
|
|
20
|
+
RequestLogAnalyzer::Database.should_receive(:new).and_return(@database)
|
|
21
|
+
@database_inserter.prepare
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "should set the file_format" do
|
|
25
|
+
@database.should_receive(:file_format=).with(testing_format)
|
|
26
|
+
@database_inserter.prepare
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'should create the database schema during preparation' do
|
|
30
|
+
@database.should_receive(:create_database_schema!)
|
|
31
|
+
@database_inserter.prepare
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'should not drop the database schema during preparation if not requested' do
|
|
35
|
+
@database.should_not_receive(:drop_database_schema!)
|
|
36
|
+
@database_inserter.prepare
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'should drop the database schema during preparation if requested' do
|
|
40
|
+
@database_inserter.options[:reset_database] = true
|
|
41
|
+
@database.should_receive(:drop_database_schema!)
|
|
42
|
+
@database_inserter.prepare
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
test_databases.each do |name, connection|
|
|
47
|
+
|
|
48
|
+
context "using a #{name} database" do
|
|
49
|
+
|
|
50
|
+
before(:each) do
|
|
51
|
+
@database_inserter = RequestLogAnalyzer::Aggregator::DatabaseInserter.new(@log_parser, :database => connection, :reset_database => true)
|
|
52
|
+
@database_inserter.prepare
|
|
53
|
+
|
|
54
|
+
@incomplete_request = testing_format.request( {:line_type => :first, :request_no => 564})
|
|
55
|
+
@completed_request = testing_format.request( {:line_type => :first, :request_no => 564},
|
|
56
|
+
{:line_type => :test, :test_capture => "awesome"},
|
|
57
|
+
{:line_type => :test, :test_capture => "indeed"},
|
|
58
|
+
{:line_type => :eval, :evaluated => { :greating => 'howdy'}, :greating => 'howdy' },
|
|
59
|
+
{:line_type => :last, :request_no => 564})
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
after(:each) do
|
|
63
|
+
@database_inserter.database.send :remove_orm_classes!
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "should insert a record in the request table" do
|
|
67
|
+
lambda {
|
|
68
|
+
@database_inserter.aggregate(@incomplete_request)
|
|
69
|
+
}.should change(RequestLogAnalyzer::Database::Request, :count).from(0).to(1)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "should insert a record in the first_lines table" do
|
|
73
|
+
lambda {
|
|
74
|
+
@database_inserter.aggregate(@incomplete_request)
|
|
75
|
+
}.should change(@database_inserter.database.get_class(:first), :count).from(0).to(1)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "should insert records in all relevant line tables" do
|
|
79
|
+
@database_inserter.aggregate(@completed_request)
|
|
80
|
+
request = RequestLogAnalyzer::Database::Request.first
|
|
81
|
+
request.should have(2).test_lines
|
|
82
|
+
request.should have(1).first_lines
|
|
83
|
+
request.should have(1).eval_lines
|
|
84
|
+
request.should have(1).last_lines
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "should log a warning in the warnings table" do
|
|
88
|
+
RequestLogAnalyzer::Database::Warning.should_receive(:create!).with(hash_including(:warning_type => 'test_warning'))
|
|
89
|
+
@database_inserter.warning(:test_warning, "Testing the warning system", 12)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe RequestLogAnalyzer::Aggregator::Summarizer do
|
|
4
|
+
|
|
5
|
+
before(:each) do
|
|
6
|
+
@summarizer = RequestLogAnalyzer::Aggregator::Summarizer.new(mock_source, :output => mock_output)
|
|
7
|
+
@summarizer.prepare
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "not raise exception when creating a report after aggregating multiple requests" do
|
|
11
|
+
@summarizer.aggregate(request(:data => 'bluh1'))
|
|
12
|
+
@summarizer.aggregate(request(:data => 'bluh2'))
|
|
13
|
+
|
|
14
|
+
lambda { @summarizer.report(mock_output) }.should_not raise_error
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "not raise exception when creating a report after aggregating a single request" do
|
|
18
|
+
@summarizer.aggregate(request(:data => 'bluh1'))
|
|
19
|
+
lambda { @summarizer.report(mock_output) }.should_not raise_error
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "not raise exception when creating a report after aggregating no requests" do
|
|
23
|
+
lambda { @summarizer.report(mock_output) }.should_not raise_error
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe RequestLogAnalyzer::Controller do
|
|
4
|
+
|
|
5
|
+
it "should use a custom output generator correctly" do
|
|
6
|
+
|
|
7
|
+
mock_output = mock('RequestLogAnalyzer::Output::Base')
|
|
8
|
+
mock_output.stub!(:io).and_return(mock_io)
|
|
9
|
+
mock_output.should_receive(:header)
|
|
10
|
+
mock_output.should_receive(:footer)
|
|
11
|
+
|
|
12
|
+
controller = RequestLogAnalyzer::Controller.new(mock_source, :output => mock_output)
|
|
13
|
+
|
|
14
|
+
controller.run!
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should call aggregators correctly when run" do
|
|
18
|
+
controller = RequestLogAnalyzer::Controller.new(mock_source, :output => mock_output)
|
|
19
|
+
|
|
20
|
+
mock_aggregator = mock('RequestLogAnalyzer::Aggregator::Base')
|
|
21
|
+
mock_aggregator.should_receive(:prepare).once.ordered
|
|
22
|
+
mock_aggregator.should_receive(:aggregate).with(an_instance_of(testing_format.request_class)).twice.ordered
|
|
23
|
+
mock_aggregator.should_receive(:finalize).once.ordered
|
|
24
|
+
mock_aggregator.should_receive(:report).once.ordered
|
|
25
|
+
|
|
26
|
+
controller.aggregators << mock_aggregator
|
|
27
|
+
controller.run!
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should call filters when run" do
|
|
31
|
+
controller = RequestLogAnalyzer::Controller.new(mock_source, :output => mock_output)
|
|
32
|
+
|
|
33
|
+
mock_filter = mock('RequestLogAnalyzer::Filter::Base')
|
|
34
|
+
mock_filter.should_receive(:filter).twice.and_return(nil)
|
|
35
|
+
controller.should_receive(:aggregate_request).twice.and_return(nil)
|
|
36
|
+
|
|
37
|
+
controller.filters << mock_filter
|
|
38
|
+
controller.run!
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
|
2
|
+
|
|
3
|
+
require 'request_log_analyzer/log_processor'
|
|
4
|
+
|
|
5
|
+
describe RequestLogAnalyzer::LogProcessor, 'stripping log files' do
|
|
6
|
+
|
|
7
|
+
before(:each) do
|
|
8
|
+
@log_stripper = RequestLogAnalyzer::LogProcessor.new(testing_format, :strip, {})
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "should remove a junk line" do
|
|
12
|
+
@log_stripper.strip_line("junk line\n").should be_empty
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should keep a teaser line intact" do
|
|
16
|
+
@log_stripper.strip_line("processing 1234\n").should be_empty
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe RequestLogAnalyzer::Database::Base do
|
|
4
|
+
|
|
5
|
+
describe '.subclass_from_line_definition' do
|
|
6
|
+
before(:all) do
|
|
7
|
+
@line_definition = RequestLogAnalyzer::LineDefinition.new(:test, { :regexp => /Testing (\w+), tries\: (\d+)/,
|
|
8
|
+
:captures => [{ :name => :what, :type => :string }, { :name => :tries, :type => :integer },
|
|
9
|
+
{ :name => :evaluated, :type => :hash, :provides => {:evaluated_field => :duration} }]})
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
before(:each) do
|
|
13
|
+
@orm_class = mock('Line ActiveRecord::Base class')
|
|
14
|
+
@orm_class.stub!(:set_table_name)
|
|
15
|
+
@orm_class.stub!(:belongs_to)
|
|
16
|
+
@orm_class.stub!(:serialize)
|
|
17
|
+
@orm_class.stub!(:line_definition=)
|
|
18
|
+
|
|
19
|
+
Class.stub!(:new).with(RequestLogAnalyzer::Database::Base).and_return(@orm_class)
|
|
20
|
+
|
|
21
|
+
RequestLogAnalyzer::Database::Request.stub!(:has_many)
|
|
22
|
+
RequestLogAnalyzer::Database::Source.stub!(:has_many)
|
|
23
|
+
|
|
24
|
+
@database = mock_database
|
|
25
|
+
RequestLogAnalyzer::Database::Base.stub!(:database).and_return(@database)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "should create a new subclass using the Base class as parent" do
|
|
29
|
+
Class.should_receive(:new).with(RequestLogAnalyzer::Database::Base).and_return(@orm_class)
|
|
30
|
+
RequestLogAnalyzer::Database::Base.subclass_from_line_definition(@line_definition)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should store the LineDefinition" do
|
|
34
|
+
@orm_class.should_receive(:line_definition=).with(@line_definition)
|
|
35
|
+
RequestLogAnalyzer::Database::Base.subclass_from_line_definition(@line_definition)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "should set the table name for the subclass" do
|
|
39
|
+
@orm_class.should_receive(:set_table_name).with('test_lines')
|
|
40
|
+
RequestLogAnalyzer::Database::Base.subclass_from_line_definition(@line_definition)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should set the :belongs_to relationship with the Request class" do
|
|
44
|
+
@orm_class.should_receive(:belongs_to).with(:request)
|
|
45
|
+
RequestLogAnalyzer::Database::Base.subclass_from_line_definition(@line_definition)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should set a :has_many relationship in the request class" do
|
|
49
|
+
RequestLogAnalyzer::Database::Request.should_receive(:has_many).with(:test_lines)
|
|
50
|
+
RequestLogAnalyzer::Database::Base.subclass_from_line_definition(@line_definition)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should set a :has_many relationship in the source class" do
|
|
54
|
+
RequestLogAnalyzer::Database::Source.should_receive(:has_many).with(:test_lines)
|
|
55
|
+
RequestLogAnalyzer::Database::Base.subclass_from_line_definition(@line_definition)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "should set the :belongs_to relationship with the Source class" do
|
|
59
|
+
@orm_class.should_receive(:belongs_to).with(:source)
|
|
60
|
+
RequestLogAnalyzer::Database::Base.subclass_from_line_definition(@line_definition)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "should serialize a complex field" do
|
|
64
|
+
@orm_class.should_receive(:serialize).with(:evaluated, Hash)
|
|
65
|
+
RequestLogAnalyzer::Database::Base.subclass_from_line_definition(@line_definition)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe '.subclass_from_table' do
|
|
71
|
+
before(:each) do
|
|
72
|
+
|
|
73
|
+
RequestLogAnalyzer::Database::Request.stub!(:has_many)
|
|
74
|
+
RequestLogAnalyzer::Database::Source.stub!(:has_many)
|
|
75
|
+
|
|
76
|
+
@database = mock_database
|
|
77
|
+
@database.connection.stub!(:table_exists?).and_return(true)
|
|
78
|
+
RequestLogAnalyzer::Database::Base.stub!(:database).and_return(@database)
|
|
79
|
+
|
|
80
|
+
@klass = mock('ActiveRecord ORM class')
|
|
81
|
+
@klass.stub!(:column_names).and_return(['id', 'request_id', 'source_id', 'lineno', 'duration'])
|
|
82
|
+
@klass.stub!(:set_table_name)
|
|
83
|
+
@klass.stub!(:belongs_to)
|
|
84
|
+
Class.stub!(:new).with(RequestLogAnalyzer::Database::Base).and_return(@klass)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "should create a new subclass using the Base class as parent" do
|
|
88
|
+
Class.should_receive(:new).with(RequestLogAnalyzer::Database::Base).and_return(@klass)
|
|
89
|
+
RequestLogAnalyzer::Database::Base.subclass_from_table('completed_lines')
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "should set the table name" do
|
|
93
|
+
@klass.should_receive(:set_table_name).with('completed_lines')
|
|
94
|
+
RequestLogAnalyzer::Database::Base.subclass_from_table('completed_lines')
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "should create the :belongs_to relation to the request class" do
|
|
98
|
+
@klass.should_receive(:belongs_to).with(:request)
|
|
99
|
+
RequestLogAnalyzer::Database::Base.subclass_from_table('completed_lines')
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "should create the :has_many relation in the request class" do
|
|
103
|
+
RequestLogAnalyzer::Database::Request.should_receive(:has_many).with(:completed_lines)
|
|
104
|
+
RequestLogAnalyzer::Database::Base.subclass_from_table('completed_lines')
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "should create the :belongs_to relation to the source class" do
|
|
108
|
+
@klass.should_receive(:belongs_to).with(:source)
|
|
109
|
+
RequestLogAnalyzer::Database::Base.subclass_from_table('completed_lines')
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it "should create the :has_many relation in the request class" do
|
|
113
|
+
RequestLogAnalyzer::Database::Source.should_receive(:has_many).with(:completed_lines)
|
|
114
|
+
RequestLogAnalyzer::Database::Base.subclass_from_table('completed_lines')
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe '#create_table' do
|
|
120
|
+
|
|
121
|
+
before(:all) do
|
|
122
|
+
@line_definition = RequestLogAnalyzer::LineDefinition.new(:test, { :regexp => /Testing (\w+), tries\: (\d+)/,
|
|
123
|
+
:captures => [{ :name => :what, :type => :string }, { :name => :tries, :type => :integer },
|
|
124
|
+
{ :name => :evaluated, :type => :hash, :provides => {:evaluated_field => :duration} }]})
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
before(:each) do
|
|
128
|
+
@database = RequestLogAnalyzer::Database.new
|
|
129
|
+
@database.stub!(:connection).and_return(mock_connection)
|
|
130
|
+
@klass = @database.load_activerecord_class(@line_definition)
|
|
131
|
+
@klass.stub!(:table_exists?).and_return(false)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
after(:each) do
|
|
135
|
+
@klass.drop_table!
|
|
136
|
+
@database.remove_orm_classes!
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it "should call create_table with the correct table name" do
|
|
140
|
+
@database.connection.should_receive(:create_table).with(:test_lines)
|
|
141
|
+
@klass.create_table!
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it "should not create a table based on the line type name if it already exists" do
|
|
145
|
+
@klass.stub!(:table_exists?).and_return(true)
|
|
146
|
+
@database.connection.should_not_receive(:create_table).with(:test_lines)
|
|
147
|
+
@klass.create_table!
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it "should create an index on the request_id field" do
|
|
151
|
+
@database.connection.should_receive(:add_index).with(:test_lines, [:request_id])
|
|
152
|
+
@klass.create_table!
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it "should create an index on the source_id field" do
|
|
156
|
+
@database.connection.should_receive(:add_index).with(:test_lines, [:source_id])
|
|
157
|
+
@klass.create_table!
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "should create a request_id field to link the requests together" do
|
|
161
|
+
@database.connection.table_creator.should_receive(:column).with(:request_id, :integer)
|
|
162
|
+
@klass.create_table!
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "should create a lineno field to save the location of the line in the original file" do
|
|
166
|
+
@database.connection.table_creator.should_receive(:column).with(:lineno, :integer)
|
|
167
|
+
@klass.create_table!
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it "should create a field of the correct type for every defined capture field" do
|
|
171
|
+
@database.connection.table_creator.should_receive(:column).with(:what, :string)
|
|
172
|
+
@database.connection.table_creator.should_receive(:column).with(:tries, :integer)
|
|
173
|
+
@database.connection.table_creator.should_receive(:column).with(:evaluated, :text)
|
|
174
|
+
@klass.create_table!
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "should create a field of the correct type for every provided field" do
|
|
178
|
+
@database.connection.table_creator.should_receive(:column).with(:evaluated_field, :double)
|
|
179
|
+
@klass.create_table!
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|