request-log-analyzer 1.9.10 → 1.11.0
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 +4 -1
- data/.infinity_test +8 -0
- data/Gemfile +2 -0
- data/bin/request-log-analyzer +0 -1
- data/lib/request_log_analyzer/aggregator/database_inserter.rb +2 -1
- data/lib/request_log_analyzer/aggregator.rb +5 -5
- data/lib/request_log_analyzer/controller.rb +0 -3
- data/lib/request_log_analyzer/database.rb +6 -7
- data/lib/request_log_analyzer/file_format/apache.rb +19 -28
- data/lib/request_log_analyzer/file_format/delayed_job2.rb +2 -2
- data/lib/request_log_analyzer/file_format/delayed_job21.rb +2 -2
- data/lib/request_log_analyzer/file_format/haproxy.rb +232 -0
- data/lib/request_log_analyzer/file_format/mysql.rb +5 -5
- data/lib/request_log_analyzer/file_format/rails3.rb +9 -2
- data/lib/request_log_analyzer/file_format/w3c.rb +56 -0
- data/lib/request_log_analyzer/file_format.rb +46 -15
- data/lib/request_log_analyzer/filter.rb +4 -5
- data/lib/request_log_analyzer/line_definition.rb +6 -4
- data/lib/request_log_analyzer/output.rb +3 -5
- data/lib/request_log_analyzer/request.rb +7 -0
- data/lib/request_log_analyzer/source/log_parser.rb +56 -4
- data/lib/request_log_analyzer/source.rb +3 -4
- data/lib/request_log_analyzer/tracker.rb +8 -8
- data/lib/request_log_analyzer.rb +15 -29
- data/request-log-analyzer.gemspec +4 -4
- data/spec/fixtures/mysql_slow_query.log +0 -1
- data/spec/integration/command_line_usage_spec.rb +0 -5
- data/spec/lib/helpers.rb +2 -2
- data/spec/lib/matchers.rb +38 -7
- data/spec/lib/mocks.rb +1 -5
- data/spec/spec_helper.rb +3 -2
- data/spec/unit/database/base_class_spec.rb +1 -0
- data/spec/unit/file_format/amazon_s3_format_spec.rb +58 -55
- data/spec/unit/file_format/apache_format_spec.rb +90 -161
- data/spec/unit/file_format/common_regular_expressions_spec.rb +51 -26
- data/spec/unit/file_format/delayed_job21_format_spec.rb +22 -31
- data/spec/unit/file_format/delayed_job2_format_spec.rb +27 -32
- data/spec/unit/file_format/delayed_job_format_spec.rb +44 -63
- data/spec/unit/file_format/haproxy_format_spec.rb +82 -0
- data/spec/unit/file_format/line_definition_spec.rb +26 -33
- data/spec/unit/file_format/merb_format_spec.rb +22 -37
- data/spec/unit/file_format/mysql_format_spec.rb +80 -123
- data/spec/unit/file_format/oink_format_spec.rb +29 -61
- data/spec/unit/file_format/postgresql_format_spec.rb +10 -21
- data/spec/unit/file_format/rack_format_spec.rb +49 -44
- data/spec/unit/file_format/rails3_format_spec.rb +46 -38
- data/spec/unit/file_format/rails_format_spec.rb +52 -68
- data/spec/unit/file_format/w3c_format_spec.rb +47 -0
- data/spec/unit/source/log_parser_spec.rb +1 -1
- metadata +12 -7
- data/lib/mixins/gets_memory_protection.rb +0 -80
- data/lib/request_log_analyzer/output/fancy_html.rb +0 -44
- data/lib/request_log_analyzer/source/database_loader.rb +0 -87
|
@@ -1,67 +1,70 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe RequestLogAnalyzer::FileFormat::AmazonS3 do
|
|
4
|
+
|
|
5
|
+
subject { RequestLogAnalyzer::FileFormat.load(:amazon_s3) }
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@sample = '2f88111968424e6306bf4d292c0188ccb94ff9374ea2836b50a1a79f7cd656e1 sample-bucket [06/Oct/2006:01:42:14 +0000] 207.171.172.6 65a011a29cdf8ec533ec3d1ccaae921c C980091AD89C936A REST.GET.OBJECT object.png "GET /sample-bucket/object.png HTTP/1.1" 200 - 1243 1243 988 987 "-" "aranhabot"'
|
|
9
|
-
end
|
|
7
|
+
it { should be_well_formed }
|
|
8
|
+
it { should have_line_definition(:access).capturing(:bucket_owner, :bucket, :timestamp, :remote_ip, :requester,
|
|
9
|
+
:key, :operation, :total_time, :turnaround_time, :bytes_sent, :object_size, :referer, :user_agent) }
|
|
10
10
|
|
|
11
|
-
it
|
|
12
|
-
@file_format.should be_valid
|
|
13
|
-
end
|
|
11
|
+
it { should have(7).report_trackers }
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
let(:sample_get) { '2f88111968424e6306bf4d292c0188ccb94ff9374ea2836b50a1a79f7cd656e1 sample-bucket [06/Oct/2006:01:42:14 +0000] 207.171.172.6 65a011a29cdf8ec533ec3d1ccaae921c C980091AD89C936A REST.GET.OBJECT object.png "GET /sample-bucket/object.png HTTP/1.1" 200 - 1243 1243 988 987 "-" "aranhabot"' }
|
|
14
|
+
let(:sample_copy) { '09216466b5571a8db0bf5abca72041fd3fc163e5eb83c51159735353ac6a2b9a testbucket [03/Mar/2010:23:04:59 +0000] 174.119.31.76 09216466b5571a8db0bf5abca72041fd3fc163e5eb83c51159735353ac6a2b9a ACCC34B843C87BC9 REST.COPY.OBJECT files/image.png "PUT /files/image.png HTTP/1.1" 200 - 234 65957 365 319 "-" "" -' }
|
|
15
|
+
|
|
16
|
+
describe '#parse_line' do
|
|
17
|
+
it { should parse_line(sample_get, 'a GET line').and_capture(
|
|
18
|
+
:bucket_owner => '2f88111968424e6306bf4d292c0188ccb94ff9374ea2836b50a1a79f7cd656e1',
|
|
19
|
+
:bucket => 'sample-bucket',
|
|
20
|
+
:timestamp => 20061006014214,
|
|
21
|
+
:remote_ip => '207.171.172.6',
|
|
22
|
+
:key => 'object.png',
|
|
23
|
+
:operation => 'REST.GET.OBJECT',
|
|
24
|
+
:requester => '65a011a29cdf8ec533ec3d1ccaae921c',
|
|
25
|
+
:request_id => 'C980091AD89C936A',
|
|
26
|
+
:request_uri => 'GET /sample-bucket/object.png HTTP/1.1',
|
|
27
|
+
:error_code => nil,
|
|
28
|
+
:http_status => 200,
|
|
29
|
+
:total_time => 0.988,
|
|
30
|
+
:turnaround_time => 0.987,
|
|
31
|
+
:bytes_sent => 1243,
|
|
32
|
+
:object_size => 1243,
|
|
33
|
+
:user_agent => 'aranhabot',
|
|
34
|
+
:referer => nil)
|
|
35
|
+
}
|
|
19
36
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
37
|
+
it { should parse_line(sample_copy, 'a COPY line').and_capture(
|
|
38
|
+
:bucket_owner => '09216466b5571a8db0bf5abca72041fd3fc163e5eb83c51159735353ac6a2b9a',
|
|
39
|
+
:bucket => 'testbucket',
|
|
40
|
+
:timestamp => 20100303230459,
|
|
41
|
+
:remote_ip => '174.119.31.76',
|
|
42
|
+
:key => 'files/image.png',
|
|
43
|
+
:operation => 'REST.COPY.OBJECT',
|
|
44
|
+
:requester => '09216466b5571a8db0bf5abca72041fd3fc163e5eb83c51159735353ac6a2b9a',
|
|
45
|
+
:request_id => 'ACCC34B843C87BC9',
|
|
46
|
+
:request_uri => 'PUT /files/image.png HTTP/1.1',
|
|
47
|
+
:error_code => nil,
|
|
48
|
+
:http_status => 200,
|
|
49
|
+
:total_time => 0.365,
|
|
50
|
+
:turnaround_time => 0.319,
|
|
51
|
+
:bytes_sent => 234,
|
|
52
|
+
:object_size => 65957,
|
|
53
|
+
:user_agent => '',
|
|
54
|
+
:referer => nil)
|
|
55
|
+
}
|
|
23
56
|
|
|
24
|
-
|
|
25
|
-
@file_format.parse_line('dsadasdas dsaadsads dsaadsads').should be_nil
|
|
57
|
+
it { should_not parse_line('nonsense', 'a nonsense line') }
|
|
26
58
|
end
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
request[:request_id].should == 'C980091AD89C936A'
|
|
37
|
-
request[:request_uri].should == 'GET /sample-bucket/object.png HTTP/1.1'
|
|
38
|
-
request[:error_code].should == nil
|
|
39
|
-
request[:http_status].should == 200
|
|
40
|
-
request[:total_time].should == 0.988
|
|
41
|
-
request[:turnaround_time].should == 0.987
|
|
42
|
-
request[:bytes_sent].should == 1243
|
|
43
|
-
request[:object_size].should == 1243
|
|
44
|
-
request[:user_agent].should == 'aranhabot'
|
|
45
|
-
request[:referer].should == nil
|
|
59
|
+
|
|
60
|
+
describe '#parse_io' do
|
|
61
|
+
let(:log_parser) { RequestLogAnalyzer::Source::LogParser.new(subject) }
|
|
62
|
+
let(:snippet) { log_snippet(sample_get, sample_copy, 'nonsense line') }
|
|
63
|
+
|
|
64
|
+
it "should parse requests correctly and not generate warnings" do
|
|
65
|
+
log_parser.should_receive(:handle_request).twice
|
|
66
|
+
log_parser.should_not_receive(:warn)
|
|
67
|
+
log_parser.parse_io(snippet)
|
|
46
68
|
end
|
|
47
69
|
end
|
|
48
|
-
|
|
49
|
-
it "should parse a COPY request correctly" do
|
|
50
|
-
line = '09216466b5571a8db0bf5abca72041fd3fc163e5eb83c51159735353ac6a2b9a testbucket [03/Mar/2010:23:04:59 +0000] 174.119.31.76 09216466b5571a8db0bf5abca72041fd3fc163e5eb83c51159735353ac6a2b9a ACCC34B843C87BC9 REST.COPY.OBJECT files/image.png "PUT /files/image.png HTTP/1.1" 200 - 234 65957 365 319 "-" "" -'
|
|
51
|
-
@file_format.should parse_line(line).as(:access).and_capture(
|
|
52
|
-
:bucket_owner => '09216466b5571a8db0bf5abca72041fd3fc163e5eb83c51159735353ac6a2b9a',
|
|
53
|
-
:bucket => 'testbucket',
|
|
54
|
-
:timestamp => 20100303230459,
|
|
55
|
-
:remote_ip => '174.119.31.76',
|
|
56
|
-
:requester => '09216466b5571a8db0bf5abca72041fd3fc163e5eb83c51159735353ac6a2b9a',
|
|
57
|
-
:key => 'files/image.png',
|
|
58
|
-
:operation => 'REST.COPY.OBJECT',
|
|
59
|
-
:total_time => 0.365,
|
|
60
|
-
:turnaround_time => 0.319,
|
|
61
|
-
:bytes_sent => 234,
|
|
62
|
-
:object_size => 65957,
|
|
63
|
-
:referer => nil,
|
|
64
|
-
:user_agent => '')
|
|
65
|
-
end
|
|
66
|
-
|
|
67
70
|
end
|
|
@@ -3,200 +3,129 @@ require 'spec_helper'
|
|
|
3
3
|
describe RequestLogAnalyzer::FileFormat::Apache do
|
|
4
4
|
|
|
5
5
|
describe '.access_line_definition' do
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@line_definition = RequestLogAnalyzer::FileFormat::Apache.access_line_definition(@format_string)
|
|
9
|
-
end
|
|
6
|
+
let(:format_string) { '%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i" %T' }
|
|
7
|
+
let(:line_definition) { RequestLogAnalyzer::FileFormat::Apache.access_line_definition(format_string) }
|
|
10
8
|
|
|
11
9
|
it "should create a Regexp to match the line" do
|
|
12
|
-
|
|
10
|
+
line_definition.regexp.should be_kind_of(Regexp)
|
|
13
11
|
end
|
|
14
12
|
|
|
15
13
|
it "should create a list of captures for the values in the lines" do
|
|
16
|
-
|
|
14
|
+
line_definition.captures.should have(12).items
|
|
17
15
|
end
|
|
18
16
|
|
|
19
17
|
it "should make it a header line" do
|
|
20
|
-
|
|
18
|
+
line_definition.should be_header
|
|
21
19
|
end
|
|
22
20
|
|
|
23
21
|
it "should make it a footer line" do
|
|
24
|
-
|
|
22
|
+
line_definition.should be_footer
|
|
25
23
|
end
|
|
26
24
|
|
|
27
25
|
it "should capture :duration" do
|
|
28
|
-
|
|
26
|
+
line_definition.captures?(:duration).should be_true
|
|
29
27
|
end
|
|
30
28
|
end
|
|
31
|
-
|
|
32
|
-
describe '.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@format = RequestLogAnalyzer::FileFormat::Apache.create(@format_string)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
it "should create the :access line definition" do
|
|
40
|
-
@format.should have_line_definition(:access).capturing(:timestamp, :remote_host, :bytes_sent, :http_method, :path, :http_version, :http_status)
|
|
29
|
+
|
|
30
|
+
describe '.access_line_definition' do
|
|
31
|
+
it "should parse values in microseconds when no argument is given to %D" do
|
|
32
|
+
format = RequestLogAnalyzer::FileFormat::Apache.create('%D')
|
|
33
|
+
format.should parse_line('12345').and_capture(:duration => 0.012345)
|
|
41
34
|
end
|
|
42
|
-
|
|
43
|
-
it "should
|
|
44
|
-
|
|
35
|
+
|
|
36
|
+
it "should parse values in microseconds when micro is given as argument to %D" do
|
|
37
|
+
format = RequestLogAnalyzer::FileFormat::Apache.create('%{micro}D')
|
|
38
|
+
format.should parse_line('12345').and_capture(:duration => 0.012345)
|
|
45
39
|
end
|
|
46
|
-
|
|
47
|
-
it "should
|
|
48
|
-
|
|
40
|
+
|
|
41
|
+
it "should parse values in microseconds when micro is given as argument to %D" do
|
|
42
|
+
format = RequestLogAnalyzer::FileFormat::Apache.create('%{milli}D')
|
|
43
|
+
format.should parse_line('12345').and_capture(:duration => 12.345)
|
|
49
44
|
end
|
|
50
45
|
end
|
|
51
46
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@log_parser = RequestLogAnalyzer::Source::LogParser.new(@file_format)
|
|
56
|
-
@sample_1 = '1.129.119.13 - - [08/Sep/2009:07:54:09 -0400] "GET /profile/18543424 HTTP/1.0" 200 8223'
|
|
57
|
-
@sample_2 = '1.82.235.29 - - [08/Sep/2009:07:54:05 -0400] "GET /gallery/fresh?page=23&per_page=16 HTTP/1.1" 200 23414'
|
|
58
|
-
@nonsense_sample = 'addasdsasadadssadasd'
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
it "should have a valid language definitions" do
|
|
62
|
-
@file_format.should be_valid
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
it "should not parse a valid access log line" do
|
|
66
|
-
@file_format.should_not parse_line(@nonsense_sample)
|
|
67
|
-
end
|
|
47
|
+
describe '.create' do
|
|
48
|
+
let(:format_string) { '%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i"' }
|
|
49
|
+
subject { RequestLogAnalyzer::FileFormat::Apache.create(format_string) }
|
|
68
50
|
|
|
69
|
-
it
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
:user => nil,
|
|
74
|
-
:timestamp => 20090908075409,
|
|
75
|
-
:http_status => 200,
|
|
76
|
-
:http_method => 'GET',
|
|
77
|
-
:http_version => '1.0',
|
|
78
|
-
:bytes_sent => 8223)
|
|
79
|
-
end
|
|
51
|
+
it { should be_well_formed }
|
|
52
|
+
it { should have_line_definition(:access).capturing(:timestamp, :remote_host, :bytes_sent, :http_method, :path, :http_version, :http_status) }
|
|
53
|
+
it { should have(8).report_trackers }
|
|
54
|
+
end
|
|
80
55
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
56
|
+
context '"Common" access log parsing' do
|
|
57
|
+
subject { RequestLogAnalyzer::FileFormat.load(:apache, :common) }
|
|
58
|
+
|
|
59
|
+
it { should be_well_formed }
|
|
60
|
+
it { should have_line_definition(:access).capturing(:remote_host, :remote_logname, :user, :timestamp, :http_status, :http_method, :http_version, :bytes_sent) }
|
|
61
|
+
it { should have(6).report_trackers }
|
|
62
|
+
|
|
63
|
+
describe '#parse_line' do
|
|
64
|
+
|
|
65
|
+
let(:sample1) { '1.129.119.13 - - [08/Sep/2009:07:54:09 -0400] "GET /profile/18543424 HTTP/1.0" 200 8223' }
|
|
66
|
+
let(:sample2) { '1.82.235.29 - - [08/Sep/2009:07:54:05 -0400] "GET /gallery/fresh?page=23&per_page=16 HTTP/1.1" 200 23414' }
|
|
67
|
+
|
|
68
|
+
it { should parse_line(sample1, 'a sample line').and_capture(
|
|
69
|
+
:remote_host => '1.129.119.13', :remote_logname => nil, :user => nil,
|
|
70
|
+
:timestamp => 20090908075409, :http_status => 200, :http_method => 'GET',
|
|
71
|
+
:http_version => '1.0', :bytes_sent => 8223)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
it { should parse_line(sample2, 'another sample line').and_capture(
|
|
75
|
+
:remote_host => '1.82.235.29', :remote_logname => nil, :user => nil,
|
|
76
|
+
:timestamp => 20090908075405, :http_status => 200, :http_method => 'GET',
|
|
77
|
+
:http_version => '1.1', :bytes_sent => 23414)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
it { should_not parse_line('nonsense', 'a nonsense line')}
|
|
91
81
|
end
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
@log_parser.should_not_receive(:warn)
|
|
82
|
+
|
|
83
|
+
describe '#parse_io' do
|
|
84
|
+
let(:log_parser) { RequestLogAnalyzer::Source::LogParser.new(subject) }
|
|
96
85
|
|
|
97
|
-
|
|
98
|
-
|
|
86
|
+
it "should parse a log snippet successfully without warnings" do
|
|
87
|
+
log_parser.should_receive(:handle_request).exactly(10).times
|
|
88
|
+
log_parser.should_not_receive(:warn)
|
|
89
|
+
log_parser.parse_file(log_fixture(:apache_common))
|
|
99
90
|
end
|
|
100
91
|
end
|
|
101
92
|
end
|
|
102
93
|
|
|
103
94
|
context '"Combined" access log parsing' do
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
it "should have a valid language definitions" do
|
|
114
|
-
@file_format.should be_valid
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
it "should not parse a valid access log line" do
|
|
118
|
-
@file_format.should_not parse_line(@nonsense_sample)
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
it "should read the correct values from a valid 404 access log line" do
|
|
122
|
-
@file_format.should parse_line(@sample_1).as(:access).and_capture(
|
|
123
|
-
:remote_host => '69.41.0.45',
|
|
124
|
-
:remote_logname => nil,
|
|
125
|
-
:user => nil,
|
|
126
|
-
:timestamp => 20090902120240,
|
|
127
|
-
:http_status => 404,
|
|
128
|
-
:http_method => 'GET',
|
|
129
|
-
:http_version => '1.1',
|
|
130
|
-
:bytes_sent => 209,
|
|
131
|
-
:referer => nil,
|
|
132
|
-
:user_agent => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows 98)')
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
it "should read the correct values from a valid 200 access log line" do
|
|
136
|
-
@file_format.should parse_line(@sample_2).as(:access).and_capture(
|
|
137
|
-
:remote_host => '10.0.1.1',
|
|
138
|
-
:remote_logname => nil,
|
|
139
|
-
:user => nil,
|
|
140
|
-
:timestamp => 20090902050833,
|
|
141
|
-
:http_status => 200,
|
|
142
|
-
:http_method => 'GET',
|
|
143
|
-
:http_version => '1.1',
|
|
144
|
-
:bytes_sent => 30,
|
|
145
|
-
:referer => nil,
|
|
146
|
-
:user_agent => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-us) AppleWebKit/531.9 (KHTML, like Gecko) Version/4.0.3 Safari/531.9')
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
it "should parse 5 request from fixture access log without warnings" do
|
|
150
|
-
request_counter.should_receive(:hit!).exactly(5).times
|
|
151
|
-
@log_parser.should_not_receive(:warn)
|
|
95
|
+
subject { RequestLogAnalyzer::FileFormat.load(:apache, :combined) }
|
|
96
|
+
|
|
97
|
+
it { should be_well_formed }
|
|
98
|
+
it { should have_line_definition(:access).capturing(:remote_host, :remote_logname, :user, :timestamp, :http_status, :http_method, :http_version, :bytes_sent, :referer, :user_agent) }
|
|
99
|
+
it { should have(8).report_trackers }
|
|
100
|
+
|
|
101
|
+
describe '#parse_line' do
|
|
102
|
+
let(:sample1) { '69.41.0.45 - - [02/Sep/2009:12:02:40 +0200] "GET //phpMyAdmin/ HTTP/1.1" 404 209 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows 98)"' }
|
|
103
|
+
let(:sample2) { '0:0:0:0:0:0:0:1 - - [02/Sep/2009:05:08:33 +0200] "GET / HTTP/1.1" 200 30 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-us) AppleWebKit/531.9 (KHTML, like Gecko) Version/4.0.3 Safari/531.9"' }
|
|
152
104
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
it "should create a kind of an Apache file format" do
|
|
169
|
-
@file_format.should be_kind_of(RequestLogAnalyzer::FileFormat::Apache)
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
it "should have a valid language definitions" do
|
|
173
|
-
@file_format.should be_valid
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
it "should parse a valid access log line with status 500" do
|
|
177
|
-
@file_format.should parse_line(@sample_1).as(:access).and_capture(
|
|
178
|
-
:remote_host => '127.0.0.1', :timestamp => 20090916074008, :user => nil,
|
|
179
|
-
:http_status => 500, :http_method => 'GET', :http_version => '1.1',
|
|
180
|
-
:duration => 0.0453, :bytes_sent => 63183, :remote_logname => nil)
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
it "should parse a valid access log line with status 200" do
|
|
184
|
-
@file_format.should parse_line(@sample_2).as(:access).and_capture(
|
|
185
|
-
:remote_host => '127.0.0.1', :timestamp => 20091001075810, :user => nil,
|
|
186
|
-
:http_status => 200, :http_method => 'GET', :http_version => '1.1',
|
|
187
|
-
:duration => 0.0045, :bytes_sent => 1, :remote_logname => nil)
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
it "should not parse an invalid access log line" do
|
|
191
|
-
@file_format.should_not parse_line(@nonsense_sample)
|
|
105
|
+
it { should parse_line(sample1, 'with IPv4 address').and_capture(
|
|
106
|
+
:remote_host => '69.41.0.45', :remote_logname => nil, :user => nil,
|
|
107
|
+
:timestamp => 20090902120240, :http_status => 404, :http_method => 'GET',
|
|
108
|
+
:http_version => '1.1', :bytes_sent => 209, :referer => nil,
|
|
109
|
+
:user_agent => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows 98)')
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
it { should parse_line(sample2, 'with IPv6 address').and_capture(
|
|
113
|
+
:remote_host => '0:0:0:0:0:0:0:1', :remote_logname => nil, :user => nil,
|
|
114
|
+
:timestamp => 20090902050833, :http_status => 200, :http_method => 'GET',
|
|
115
|
+
:http_version => '1.1', :bytes_sent => 30, :referer => nil,
|
|
116
|
+
:user_agent => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-us) AppleWebKit/531.9 (KHTML, like Gecko) Version/4.0.3 Safari/531.9')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
it { should_not parse_line('nonsense', 'a nonsense line')}
|
|
192
120
|
end
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
@log_parser.should_not_receive(:warn)
|
|
121
|
+
|
|
122
|
+
describe '#parse_io' do
|
|
123
|
+
let(:log_parser) { RequestLogAnalyzer::Source::LogParser.new(subject) }
|
|
197
124
|
|
|
198
|
-
|
|
199
|
-
|
|
125
|
+
it "should parse a log snippet successfully without warnings" do
|
|
126
|
+
log_parser.should_receive(:handle_request).exactly(5).times
|
|
127
|
+
log_parser.should_not_receive(:warn)
|
|
128
|
+
log_parser.parse_file(log_fixture(:apache_combined))
|
|
200
129
|
end
|
|
201
130
|
end
|
|
202
131
|
end
|
|
@@ -3,51 +3,76 @@ require 'spec_helper'
|
|
|
3
3
|
describe RequestLogAnalyzer::FileFormat::CommonRegularExpressions do
|
|
4
4
|
|
|
5
5
|
include RequestLogAnalyzer::FileFormat::CommonRegularExpressions
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
describe '.timestamp' do
|
|
8
|
-
|
|
9
8
|
it "should parse timestamps with a given format" do
|
|
10
|
-
timestamp('%Y-%m-%dT%H:%M:%S%z').should =~ '2009-12-03T00:12:37+0100'
|
|
11
|
-
timestamp('%Y-%m-%dT%H:%M:%S%z').should_not =~ '2009-12-03 00:12:37+0100'
|
|
12
|
-
timestamp('%Y-%m-%dT%H:%M:%S%z').should_not =~ '2009-12-03T00:12:37'
|
|
9
|
+
anchored(timestamp('%Y-%m-%dT%H:%M:%S%z')).should =~ '2009-12-03T00:12:37+0100'
|
|
10
|
+
anchored(timestamp('%Y-%m-%dT%H:%M:%S%z')).should_not =~ '2009-12-03 00:12:37+0100'
|
|
11
|
+
anchored(timestamp('%Y-%m-%dT%H:%M:%S%z')).should_not =~ '2009-12-03T00:12:37'
|
|
13
12
|
end
|
|
14
13
|
end
|
|
14
|
+
|
|
15
15
|
|
|
16
|
-
describe '.
|
|
16
|
+
describe '.hostname' do
|
|
17
|
+
it "should parse hostnames successfully" do
|
|
18
|
+
anchored(hostname).should =~ 'railsdoctors.com'
|
|
19
|
+
anchored(hostname).should =~ 'www.rails-doctors.com'
|
|
20
|
+
anchored(hostname).should =~ 'hostname.co.uk'
|
|
21
|
+
anchored(hostname).should =~ 'localhost'
|
|
17
22
|
|
|
23
|
+
anchored(hostname).should_not =~ '192.168.0.1'
|
|
24
|
+
anchored(hostname).should_not =~ '3ffe:1900:4545:3:200:f8ff:fe21:67cf'
|
|
25
|
+
anchored(hostname).should_not =~ 'railsdoctors.'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe '.ip_address' do
|
|
18
30
|
it "should parse IPv4 addresses" do
|
|
19
|
-
ip_address.should =~ '127.0.0.1'
|
|
20
|
-
ip_address.should =~ '255.255.255.255'
|
|
31
|
+
anchored(ip_address).should =~ '127.0.0.1'
|
|
32
|
+
anchored(ip_address).should =~ '255.255.255.255'
|
|
21
33
|
|
|
22
|
-
ip_address.should_not =~ '2552.2552.2552.2552'
|
|
23
|
-
ip_address.should_not =~ '127001'
|
|
24
|
-
ip_address.should_not =~ ''
|
|
25
|
-
ip_address.should_not =~ '-'
|
|
26
|
-
ip_address.should_not =~ 'sub-host.domain.tld'
|
|
34
|
+
anchored(ip_address).should_not =~ '2552.2552.2552.2552'
|
|
35
|
+
anchored(ip_address).should_not =~ '127001'
|
|
36
|
+
anchored(ip_address).should_not =~ ''
|
|
37
|
+
anchored(ip_address).should_not =~ '-'
|
|
38
|
+
anchored(ip_address).should_not =~ 'sub-host.domain.tld'
|
|
27
39
|
end
|
|
28
40
|
|
|
29
41
|
it "should pase IPv6 addresses" do
|
|
30
|
-
ip_address.should =~ '::1'
|
|
31
|
-
ip_address.should =~ '3ffe:1900:4545:3:200:f8ff:fe21:67cf'
|
|
32
|
-
ip_address.should =~ '3ffe:1900:4545:3:200:f8ff:127.0.0.1'
|
|
33
|
-
ip_address.should =~ '::3:200:f8ff:127.0.0.1'
|
|
42
|
+
anchored(ip_address).should =~ '::1'
|
|
43
|
+
anchored(ip_address).should =~ '3ffe:1900:4545:3:200:f8ff:fe21:67cf'
|
|
44
|
+
anchored(ip_address).should =~ '3ffe:1900:4545:3:200:f8ff:127.0.0.1'
|
|
45
|
+
anchored(ip_address).should =~ '::3:200:f8ff:127.0.0.1'
|
|
46
|
+
anchored(ip_address).should =~ '0:0:0:0:0:0:0:1'
|
|
34
47
|
|
|
35
|
-
ip_address.should_not =~ 'qqqq:wwww:eeee:3q:200:wf8ff:fe21:67cf'
|
|
36
|
-
ip_address.should_not =~ '3ffe44:1900f:454545:3:200:f8ff:ffff:5432'
|
|
48
|
+
anchored(ip_address).should_not =~ 'qqqq:wwww:eeee:3q:200:wf8ff:fe21:67cf'
|
|
49
|
+
anchored(ip_address).should_not =~ '3ffe44:1900f:454545:3:200:f8ff:ffff:5432'
|
|
37
50
|
end
|
|
38
51
|
|
|
39
52
|
it "should allow blank if true is given as parameter" do
|
|
40
|
-
|
|
41
|
-
|
|
53
|
+
anchored(ip_address(true)).should =~ ''
|
|
54
|
+
anchored(ip_address(true)).should_not =~ ' '
|
|
42
55
|
end
|
|
43
56
|
|
|
44
57
|
it "should allow a nil substitute if a string is given as parameter" do
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
anchored(ip_address('-')).should =~ '-'
|
|
59
|
+
anchored(ip_address('-')).should_not =~ ' -'
|
|
60
|
+
anchored(ip_address('-')).should_not =~ '--'
|
|
61
|
+
anchored(ip_address('-')).should_not =~ ''
|
|
49
62
|
end
|
|
50
|
-
|
|
51
63
|
end
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
describe '.hostname_or_ip_address' do
|
|
67
|
+
it "should parse either hostnames or ip addresses" do
|
|
68
|
+
anchored(hostname_or_ip_address).should =~ 'railsdoctors.com'
|
|
69
|
+
anchored(hostname_or_ip_address).should =~ 'hostname.co.uk'
|
|
70
|
+
anchored(hostname_or_ip_address).should =~ 'localhost'
|
|
71
|
+
anchored(hostname_or_ip_address).should =~ '192.168.0.1'
|
|
72
|
+
anchored(hostname_or_ip_address).should =~ '3ffe:1900:4545:3:200:f8ff:fe21:67cf'
|
|
73
|
+
|
|
74
|
+
anchored(hostname_or_ip_address).should_not =~ 'railsdoctors.'
|
|
75
|
+
end
|
|
76
|
+
end
|
|
52
77
|
end
|
|
53
78
|
|
|
@@ -2,42 +2,36 @@ require 'spec_helper'
|
|
|
2
2
|
|
|
3
3
|
describe RequestLogAnalyzer::FileFormat::DelayedJob do
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
subject { RequestLogAnalyzer::FileFormat.load(:delayed_job21) }
|
|
6
|
+
|
|
7
|
+
it { should be_well_formed }
|
|
8
|
+
it { should have_line_definition(:job_lock).capturing(:timestamp, :job, :host, :pid) }
|
|
9
|
+
it { should have_line_definition(:job_completed).capturing(:timestamp, :duration, :host, :pid) }
|
|
10
|
+
it { should have(4).report_trackers }
|
|
8
11
|
|
|
9
12
|
describe '#parse_line' do
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
let(:job_lock_sample1) { "2010-05-17T17:37:34+0000: [Worker(delayed_job host:hostname.co.uk pid:11888)] acquired lock on S3FileJob" }
|
|
15
|
+
let(:job_lock_sample2) { "2010-05-17T17:37:34+0000: [Worker(delayed_job.0 host:hostname.co.uk pid:11888)] acquired lock on S3FileJob" }
|
|
16
|
+
let(:job_completed_sample1) { '2010-05-17T17:37:35+0000: [Worker(delayed_job host:hostname.co.uk pid:11888)] S3FileJob completed after 1.0676' }
|
|
14
17
|
|
|
15
|
-
it
|
|
16
|
-
|
|
17
|
-
@file_format.should parse_line(line).as(:job_lock).and_capture(:timestamp => 20100517173734,
|
|
18
|
-
:job => 'S3FileJob', :host => 'hostname.co.uk', :pid => 11888)
|
|
19
|
-
end
|
|
18
|
+
it { should parse_line(job_lock_sample1, 'with a single worker').as(:job_lock).and_capture(
|
|
19
|
+
:timestamp => 20100517173734, :job => 'S3FileJob', :host => 'hostname.co.uk', :pid => 11888) }
|
|
20
20
|
|
|
21
|
-
it
|
|
22
|
-
|
|
23
|
-
@file_format.should parse_line(line).as(:job_lock).and_capture(:timestamp => 20100517173734,
|
|
24
|
-
:job => 'S3FileJob', :host => 'hostname.co.uk', :pid => 11888)
|
|
25
|
-
end
|
|
21
|
+
it { should parse_line(job_lock_sample2, 'with multiple workers').as(:job_lock).and_capture(
|
|
22
|
+
:timestamp => 20100517173734, :job => 'S3FileJob', :host => 'hostname.co.uk', :pid => 11888) }
|
|
26
23
|
|
|
27
|
-
it
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
end
|
|
24
|
+
it { should parse_line(job_completed_sample1).as(:job_completed).and_capture(
|
|
25
|
+
:timestamp => 20100517173735, :duration => 1.0676, :host => 'hostname.co.uk', :pid => 11888, :job => 'S3FileJob') }
|
|
26
|
+
|
|
27
|
+
it { should_not parse_line('nonsense', 'a nonsense line') }
|
|
32
28
|
end
|
|
33
29
|
|
|
34
30
|
describe '#parse_io' do
|
|
35
|
-
|
|
36
|
-
@log_parser = RequestLogAnalyzer::Source::LogParser.new(RequestLogAnalyzer::FileFormat.load(:delayed_job21))
|
|
37
|
-
end
|
|
31
|
+
let(:log_parser) { RequestLogAnalyzer::Source::LogParser.new(subject) }
|
|
38
32
|
|
|
39
33
|
it "should parse a batch of completed jobs without warnings" do
|
|
40
|
-
fragment = <<-EOLOG
|
|
34
|
+
fragment = log_snippet(<<-EOLOG)
|
|
41
35
|
2010-05-17T17:36:44+0000: *** Starting job worker delayed_job host:hostname.co.uk pid:11888
|
|
42
36
|
2010-05-17T17:37:34+0000: [Worker(delayed_job host:hostname.co.uk pid:11888)] acquired lock on S3FileJob
|
|
43
37
|
2010-05-17T17:37:35+0000: [Worker(delayed_job host:hostname.co.uk pid:11888)] S3FileJob completed after 1.0676
|
|
@@ -49,12 +43,9 @@ describe RequestLogAnalyzer::FileFormat::DelayedJob do
|
|
|
49
43
|
2010-05-19T11:47:26+0000: Exiting...
|
|
50
44
|
EOLOG
|
|
51
45
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@log_parser.parse_io(StringIO.new(fragment)) do |request|
|
|
56
|
-
request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::DelayedJob21::Request)
|
|
57
|
-
end
|
|
46
|
+
log_parser.should_receive(:handle_request).exactly(3).times
|
|
47
|
+
log_parser.should_not_receive(:warn)
|
|
48
|
+
log_parser.parse_io(fragment)
|
|
58
49
|
end
|
|
59
50
|
end
|
|
60
51
|
end
|