ngmoco-request-log-analyzer 1.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|