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.
Files changed (53) hide show
  1. data/.gitignore +4 -1
  2. data/.infinity_test +8 -0
  3. data/Gemfile +2 -0
  4. data/bin/request-log-analyzer +0 -1
  5. data/lib/request_log_analyzer/aggregator/database_inserter.rb +2 -1
  6. data/lib/request_log_analyzer/aggregator.rb +5 -5
  7. data/lib/request_log_analyzer/controller.rb +0 -3
  8. data/lib/request_log_analyzer/database.rb +6 -7
  9. data/lib/request_log_analyzer/file_format/apache.rb +19 -28
  10. data/lib/request_log_analyzer/file_format/delayed_job2.rb +2 -2
  11. data/lib/request_log_analyzer/file_format/delayed_job21.rb +2 -2
  12. data/lib/request_log_analyzer/file_format/haproxy.rb +232 -0
  13. data/lib/request_log_analyzer/file_format/mysql.rb +5 -5
  14. data/lib/request_log_analyzer/file_format/rails3.rb +9 -2
  15. data/lib/request_log_analyzer/file_format/w3c.rb +56 -0
  16. data/lib/request_log_analyzer/file_format.rb +46 -15
  17. data/lib/request_log_analyzer/filter.rb +4 -5
  18. data/lib/request_log_analyzer/line_definition.rb +6 -4
  19. data/lib/request_log_analyzer/output.rb +3 -5
  20. data/lib/request_log_analyzer/request.rb +7 -0
  21. data/lib/request_log_analyzer/source/log_parser.rb +56 -4
  22. data/lib/request_log_analyzer/source.rb +3 -4
  23. data/lib/request_log_analyzer/tracker.rb +8 -8
  24. data/lib/request_log_analyzer.rb +15 -29
  25. data/request-log-analyzer.gemspec +4 -4
  26. data/spec/fixtures/mysql_slow_query.log +0 -1
  27. data/spec/integration/command_line_usage_spec.rb +0 -5
  28. data/spec/lib/helpers.rb +2 -2
  29. data/spec/lib/matchers.rb +38 -7
  30. data/spec/lib/mocks.rb +1 -5
  31. data/spec/spec_helper.rb +3 -2
  32. data/spec/unit/database/base_class_spec.rb +1 -0
  33. data/spec/unit/file_format/amazon_s3_format_spec.rb +58 -55
  34. data/spec/unit/file_format/apache_format_spec.rb +90 -161
  35. data/spec/unit/file_format/common_regular_expressions_spec.rb +51 -26
  36. data/spec/unit/file_format/delayed_job21_format_spec.rb +22 -31
  37. data/spec/unit/file_format/delayed_job2_format_spec.rb +27 -32
  38. data/spec/unit/file_format/delayed_job_format_spec.rb +44 -63
  39. data/spec/unit/file_format/haproxy_format_spec.rb +82 -0
  40. data/spec/unit/file_format/line_definition_spec.rb +26 -33
  41. data/spec/unit/file_format/merb_format_spec.rb +22 -37
  42. data/spec/unit/file_format/mysql_format_spec.rb +80 -123
  43. data/spec/unit/file_format/oink_format_spec.rb +29 -61
  44. data/spec/unit/file_format/postgresql_format_spec.rb +10 -21
  45. data/spec/unit/file_format/rack_format_spec.rb +49 -44
  46. data/spec/unit/file_format/rails3_format_spec.rb +46 -38
  47. data/spec/unit/file_format/rails_format_spec.rb +52 -68
  48. data/spec/unit/file_format/w3c_format_spec.rb +47 -0
  49. data/spec/unit/source/log_parser_spec.rb +1 -1
  50. metadata +12 -7
  51. data/lib/mixins/gets_memory_protection.rb +0 -80
  52. data/lib/request_log_analyzer/output/fancy_html.rb +0 -44
  53. 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
- before(:each) do
6
- @file_format = RequestLogAnalyzer::FileFormat.load(:amazon_s3)
7
- @log_parser = RequestLogAnalyzer::Source::LogParser.new(@file_format)
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 "should be a valid file format" do
12
- @file_format.should be_valid
13
- end
11
+ it { should have(7).report_trackers }
14
12
 
15
- it "should parse access lines and capture all of its fields" do
16
- @file_format.should have_line_definition(:access).capturing(:bucket_owner, :bucket, :timestamp, :remote_ip, :requester,
17
- :key, :operation, :total_time, :turnaround_time, :bytes_sent, :object_size, :referer, :user_agent)
18
- end
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
- it "should match the sample line" do
21
- @file_format.parse_line(@sample).should include(:line_definition, :captures)
22
- end
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
- it "should not match a nonsense line" do
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
- it "should parse and convert the sample fields correctly" do
29
- @log_parser.parse_io(StringIO.new(@sample)) do |request|
30
- request[:bucket_owner].should == '2f88111968424e6306bf4d292c0188ccb94ff9374ea2836b50a1a79f7cd656e1'
31
- request[:bucket].should == 'sample-bucket'
32
- request[:remote_ip].should == '207.171.172.6'
33
- request[:key].should == 'object.png'
34
- request[:operation].should == 'REST.GET.OBJECT'
35
- request[:requester].should == '65a011a29cdf8ec533ec3d1ccaae921c'
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
- before(:each) do
7
- @format_string = '%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i" %T'
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
- @line_definition.regexp.should be_kind_of(Regexp)
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
- @line_definition.captures.should have(12).items
14
+ line_definition.captures.should have(12).items
17
15
  end
18
16
 
19
17
  it "should make it a header line" do
20
- @line_definition.should be_header
18
+ line_definition.should be_header
21
19
  end
22
20
 
23
21
  it "should make it a footer line" do
24
- @line_definition.should be_footer
22
+ line_definition.should be_footer
25
23
  end
26
24
 
27
25
  it "should capture :duration" do
28
- @line_definition.captures?(:duration).should be_true
26
+ line_definition.captures?(:duration).should be_true
29
27
  end
30
28
  end
31
-
32
- describe '.create' do
33
-
34
- before(:each) do
35
- @format_string = '%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i"'
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 be a valid file format" do
44
- @format.should be_valid
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 setup report trackers" do
48
- @format.report_trackers.should_not be_empty
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
- context '"Common" access log parsing' do
53
- before(:all) do
54
- @file_format = RequestLogAnalyzer::FileFormat.load(:apache, :common)
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 "should read the correct values from a valid HTTP/1.0 access log line" do
70
- @file_format.should parse_line(@sample_1).as(:access).and_capture(
71
- :remote_host => '1.129.119.13',
72
- :remote_logname => nil,
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
- it "should read the correct values from a valid 200 access log line" do
82
- @file_format.should parse_line(@sample_2).as(:access).and_capture(
83
- :remote_host => '1.82.235.29',
84
- :remote_logname => nil,
85
- :user => nil,
86
- :timestamp => 20090908075405,
87
- :http_status => 200,
88
- :http_method => 'GET',
89
- :http_version => '1.1',
90
- :bytes_sent => 23414)
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
- it "should parse 10 request from fixture access log without warnings" do
94
- request_counter.should_receive(:hit!).exactly(10).times
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
- @log_parser.parse_file(log_fixture(:apache_common)) do |request|
98
- request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Apache::Request)
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
- before(:all) do
106
- @file_format = RequestLogAnalyzer::FileFormat.load(:apache, :combined)
107
- @log_parser = RequestLogAnalyzer::Source::LogParser.new(@file_format)
108
- @sample_1 = '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)"'
109
- @sample_2 = '10.0.1.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"'
110
- @nonsense_sample = 'addasdsasadadssadasd'
111
- end
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
- @log_parser.parse_file(log_fixture(:apache_combined)) do |request|
154
- request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Apache::Request)
155
- end
156
- end
157
- end
158
-
159
- context '"Rack" access log parser' do
160
- before(:each) do
161
- @file_format = RequestLogAnalyzer::FileFormat.load(:rack)
162
- @log_parser = RequestLogAnalyzer::Source::LogParser.new(@file_format)
163
- @sample_1 = '127.0.0.1 - - [16/Sep/2009 07:40:08] "GET /favicon.ico HTTP/1.1" 500 63183 0.0453'
164
- @sample_2 = '127.0.0.1 - - [01/Oct/2009 07:58:10] "GET / HTTP/1.1" 200 1 0.0045'
165
- @nonsense_sample = 'addasdsasadadssadasd'
166
- end
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
- it "should parse 2 Apache requests from a sample without warnings" do
195
- request_counter.should_receive(:hit!).twice
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
- @log_parser.parse_io(log_stream(@sample_1, @nonsense_sample, @sample_2)) do |request|
199
- request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Apache::Request)
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 '.ip_address' do
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
- /^#{ip_address(true)}$/.should =~ ''
41
- /^#{ip_address(true)}$/.should_not =~ ' '
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
- /^#{ip_address('-')}$/.should =~ '-'
46
- /^#{ip_address('-')}$/.should_not =~ ' -'
47
- /^#{ip_address('-')}$/.should_not =~ '--'
48
- /^#{ip_address('-')}$/.should_not =~ ''
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
- it "should be a valid file format" do
6
- RequestLogAnalyzer::FileFormat.load(:delayed_job).should be_valid
7
- end
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
- before(:each) do
12
- @file_format = RequestLogAnalyzer::FileFormat.load(:delayed_job21)
13
- end
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 "should parse a :job_lock line correctly" do
16
- line = "2010-05-17T17:37:34+0000: [Worker(delayed_job host:hostname.co.uk pid:11888)] acquired lock on S3FileJob"
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 "should parse a :job_lock line correctly when the worker is one of many" do
22
- line = "2010-05-17T17:37:34+0000: [Worker(delayed_job.0 host:hostname.co.uk pid:11888)] acquired lock on S3FileJob"
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 "should parse a :job_completed line correctly" do
28
- line = '2010-05-17T17:37:35+0000: [Worker(delayed_job host:hostname.co.uk pid:11888)] S3FileJob completed after 1.0676'
29
- @file_format.should parse_line(line).as(:job_completed).and_capture(:timestamp => 20100517173735,
30
- :duration => 1.0676, :host => 'hostname.co.uk', :pid => 11888, :job => 'S3FileJob')
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
- before(:each) do
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
- request_counter.should_receive(:hit!).exactly(3).times
53
- @log_parser.should_not_receive(:warn)
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