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,63 +1,82 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
|
-
describe RequestLogAnalyzer::FileFormat::
|
|
3
|
+
describe RequestLogAnalyzer::FileFormat::Rails3 do
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
subject { RequestLogAnalyzer::FileFormat.load(:rails3) }
|
|
6
|
+
|
|
7
|
+
it { should be_well_formed }
|
|
8
|
+
it { should have(9).report_trackers }
|
|
8
9
|
|
|
9
10
|
describe '#parse_line' do
|
|
10
|
-
before(:each) { @file_format = RequestLogAnalyzer::FileFormat.load(:rails3) }
|
|
11
11
|
|
|
12
12
|
it "should parse :started lines correctly" do
|
|
13
13
|
line = 'Started GET "/queries" for 127.0.0.1 at Thu Feb 25 16:15:18 -0800 2010'
|
|
14
|
-
|
|
14
|
+
subject.should parse_line(line).as(:started).and_capture(:method => 'GET',
|
|
15
15
|
:path => '/queries', :ip => '127.0.0.1', :timestamp => 20100225161518)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
it "should parse :started lines in Oct, Nov and Dec correctly" do
|
|
19
19
|
line = 'Started GET "/queries" for 127.0.0.1 at Thu Oct 25 16:15:18 -0800 2010'
|
|
20
|
-
|
|
20
|
+
subject.should parse_line(line).as(:started).and_capture(:method => 'GET',
|
|
21
21
|
:path => '/queries', :ip => '127.0.0.1', :timestamp => 20101025161518)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
it "should parse :started lines in Ruby 1.9.2 format correctly" do
|
|
25
25
|
line = 'Started GET "/queries" for 127.0.0.1 at 2010-10-26 02:27:15 +0000'
|
|
26
|
-
|
|
26
|
+
subject.should parse_line(line).as(:started).and_capture(:method => 'GET',
|
|
27
27
|
:path => '/queries', :ip => '127.0.0.1', :timestamp => 20101026022715)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
it "should parse :processing lines correctly" do
|
|
31
31
|
line = ' Processing by QueriesController#index as HTML'
|
|
32
|
-
|
|
32
|
+
subject.should parse_line(line).as(:processing).and_capture(
|
|
33
33
|
:controller => 'QueriesController', :action => 'index', :format => 'HTML')
|
|
34
34
|
end
|
|
35
|
+
|
|
35
36
|
it "should parse nested :processing lines correctly" do
|
|
36
37
|
line = ' Processing by Projects::QueriesController#index as HTML'
|
|
37
|
-
|
|
38
|
+
subject.should parse_line(line).as(:processing).and_capture(
|
|
38
39
|
:controller => 'Projects::QueriesController', :action => 'index', :format => 'HTML')
|
|
39
40
|
end
|
|
41
|
+
|
|
40
42
|
it "should parse :processing lines correctly with format */*" do
|
|
41
43
|
line = ' Processing by ProjectsController#avatar as */*'
|
|
42
|
-
|
|
44
|
+
subject.should parse_line(line).as(:processing).and_capture(
|
|
43
45
|
:controller => 'ProjectsController', :action => 'avatar', :format => '*/*')
|
|
44
46
|
end
|
|
47
|
+
|
|
48
|
+
it "should parse :processing lines correctly without format" do
|
|
49
|
+
line = ' Processing by ProjectsController#avatar as '
|
|
50
|
+
subject.should parse_line(line).as(:processing).and_capture(
|
|
51
|
+
:controller => 'ProjectsController', :action => 'avatar', :format => '')
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "should parse a :parameters line correctly" do
|
|
55
|
+
line = ' Parameters: {"action"=>"cached", "controller"=>"cached"}'
|
|
56
|
+
subject.should parse_line(line).as(:parameters).and_capture(:params => {:action => 'cached', :controller => 'cached'})
|
|
57
|
+
end
|
|
45
58
|
|
|
46
59
|
it "should parse :completed lines correctly" do
|
|
47
|
-
line = 'Completed 200 OK in 170ms (Views: 78.
|
|
48
|
-
|
|
49
|
-
:duration => 0.170, :status => 200)
|
|
60
|
+
line = 'Completed 200 OK in 170ms (Views: 78.0ms | ActiveRecord: 48.2ms)'
|
|
61
|
+
subject.should parse_line(line).as(:completed).and_capture(
|
|
62
|
+
:duration => 0.170, :view => 0.078, :db => 0.0482, :status => 200)
|
|
50
63
|
end
|
|
51
64
|
|
|
52
65
|
it "should parse :completed lines correctly when ActiveRecord is not mentioned" do
|
|
53
66
|
line = 'Completed 200 OK in 364ms (Views: 31.4ms)'
|
|
54
|
-
|
|
67
|
+
subject.should parse_line(line).as(:completed).and_capture(:duration => 0.364, :status => 200)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "should parse :completed lines correctly when other durations are specified" do
|
|
71
|
+
line = 'Completed 200 OK in 384ms (Views: 222.0ms | ActiveRecord: 121.0ms | Sphinx: 0.0ms)'
|
|
72
|
+
subject.should parse_line(line).as(:completed).and_capture(:duration => 0.384, :view => 0.222,
|
|
73
|
+
:db => 0.121, :status => 200)
|
|
55
74
|
end
|
|
56
75
|
|
|
57
76
|
|
|
58
77
|
it "should pase :failure lines correctly" do
|
|
59
78
|
line = "ActionView::Template::Error (undefined local variable or method `field' for #<Class>) on line #3 of /Users/willem/Code/warehouse/app/views/queries/execute.csv.erb:"
|
|
60
|
-
|
|
79
|
+
subject.should parse_line(line).as(:failure).and_capture(:line => 3,
|
|
61
80
|
:error => 'ActionView::Template::Error',
|
|
62
81
|
:message => "undefined local variable or method `field' for #<Class>",
|
|
63
82
|
:file => '/Users/willem/Code/warehouse/app/views/queries/execute.csv.erb')
|
|
@@ -65,10 +84,8 @@ describe RequestLogAnalyzer::FileFormat::Rails do
|
|
|
65
84
|
end
|
|
66
85
|
|
|
67
86
|
describe '#parse_io' do
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
end
|
|
71
|
-
|
|
87
|
+
let(:log_parser) { RequestLogAnalyzer::Source::LogParser.new(subject) }
|
|
88
|
+
|
|
72
89
|
it "should parse a successful request correctly" do
|
|
73
90
|
log = <<-EOLOG
|
|
74
91
|
Started GET "/" for 127.0.0.1 at Fri Mar 19 06:40:41 -0700 2010
|
|
@@ -79,12 +96,9 @@ describe RequestLogAnalyzer::FileFormat::Rails do
|
|
|
79
96
|
Completed 200 OK in 170ms (Views: 78.4ms | ActiveRecord: 48.2ms)
|
|
80
97
|
EOLOG
|
|
81
98
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
@log_parser.parse_io(StringIO.new(log)) do |request|
|
|
86
|
-
request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Rails3::Request) && request.completed?
|
|
87
|
-
end
|
|
99
|
+
log_parser.should_receive(:handle_request).once
|
|
100
|
+
log_parser.should_not_receive(:warn)
|
|
101
|
+
log_parser.parse_string(log)
|
|
88
102
|
end
|
|
89
103
|
|
|
90
104
|
it "should parse an unroutable request correctly" do
|
|
@@ -98,12 +112,9 @@ describe RequestLogAnalyzer::FileFormat::Rails do
|
|
|
98
112
|
|
|
99
113
|
EOLOG
|
|
100
114
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
@log_parser.parse_io(StringIO.new(log)) do |request|
|
|
105
|
-
request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Rails3::Request) && request.completed?
|
|
106
|
-
end
|
|
115
|
+
log_parser.should_receive(:handle_request).once
|
|
116
|
+
log_parser.should_not_receive(:warn)
|
|
117
|
+
log_parser.parse_string(log)
|
|
107
118
|
end
|
|
108
119
|
|
|
109
120
|
it "should parse a failing request correctly" do
|
|
@@ -130,12 +141,9 @@ describe RequestLogAnalyzer::FileFormat::Rails do
|
|
|
130
141
|
|
|
131
142
|
EOLOG
|
|
132
143
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
@log_parser.parse_io(StringIO.new(log)) do |request|
|
|
137
|
-
request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Rails3::Request) && request.completed?
|
|
138
|
-
end
|
|
144
|
+
log_parser.should_receive(:handle_request).once
|
|
145
|
+
log_parser.should_not_receive(:warn)
|
|
146
|
+
log_parser.parse_string(log)
|
|
139
147
|
end
|
|
140
148
|
end
|
|
141
149
|
end
|
|
@@ -5,186 +5,170 @@ describe RequestLogAnalyzer::FileFormat::Rails do
|
|
|
5
5
|
describe '.create' do
|
|
6
6
|
|
|
7
7
|
context 'without providing a lines argument' do
|
|
8
|
-
|
|
8
|
+
subject { RequestLogAnalyzer::FileFormat::Rails.create }
|
|
9
9
|
|
|
10
|
-
it
|
|
11
|
-
|
|
12
|
-
end
|
|
10
|
+
it { should be_well_formed }
|
|
11
|
+
it { should have(11).report_trackers }
|
|
13
12
|
|
|
14
|
-
it "should parse the production
|
|
15
|
-
|
|
16
|
-
@rails.line_definitions.should == production_rails.line_definitions
|
|
13
|
+
it "should parse the lines in the production set" do
|
|
14
|
+
subject.line_definitions.should == RequestLogAnalyzer::FileFormat.load(:rails, 'production').line_definitions
|
|
17
15
|
end
|
|
18
16
|
end
|
|
19
17
|
|
|
20
18
|
context 'using a comma separated list of lines as argument' do
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
it "should return a valid language" do
|
|
24
|
-
@rails.should be_valid
|
|
25
|
-
end
|
|
19
|
+
subject { RequestLogAnalyzer::FileFormat.load(:rails, 'minimal,failure') }
|
|
26
20
|
|
|
27
|
-
it
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
it { should be_well_formed }
|
|
22
|
+
it { should have(10).report_trackers }
|
|
23
|
+
|
|
24
|
+
it { should have_line_definition(:processing) }
|
|
25
|
+
it { should have_line_definition(:completed) }
|
|
26
|
+
it { should have_line_definition(:failure) }
|
|
30
27
|
end
|
|
31
28
|
|
|
32
29
|
RequestLogAnalyzer::FileFormat::Rails::LINE_COLLECTIONS.keys.each do |constant|
|
|
33
30
|
|
|
34
31
|
context "using the '#{constant}' line collection constant" do
|
|
35
|
-
|
|
32
|
+
subject { RequestLogAnalyzer::FileFormat.load(:rails, constant) }
|
|
36
33
|
|
|
37
|
-
it
|
|
38
|
-
|
|
39
|
-
end
|
|
34
|
+
it { should be_well_formed }
|
|
35
|
+
it { should have_at_least(9).report_trackers }
|
|
40
36
|
|
|
41
|
-
it
|
|
42
|
-
|
|
43
|
-
end
|
|
37
|
+
it { should have_line_definition(:processing) }
|
|
38
|
+
it { should have_line_definition(:completed) }
|
|
44
39
|
end
|
|
45
40
|
end
|
|
46
41
|
end
|
|
42
|
+
|
|
43
|
+
subject { RequestLogAnalyzer::FileFormat.load(:rails, :all) }
|
|
47
44
|
|
|
48
45
|
describe '#parse_line' do
|
|
49
|
-
before(:each) { @rails = RequestLogAnalyzer::FileFormat.load(:rails, :all) }
|
|
50
46
|
|
|
51
47
|
{'with prefix' => 'LINE PREFIX: ', 'without prefix' => '' }.each do |context, prefix|
|
|
52
48
|
context context do
|
|
53
49
|
it "should parse a :processing line correctly" do
|
|
54
50
|
line = prefix + 'Processing PeopleController#index (for 1.1.1.1 at 2008-08-14 21:16:30) [GET]'
|
|
55
|
-
|
|
51
|
+
subject.should parse_line(line).as(:processing).and_capture(:controller => 'PeopleController', :action => 'index', :timestamp => 20080814211630, :method => 'GET', :ip => '1.1.1.1')
|
|
56
52
|
end
|
|
57
53
|
|
|
58
54
|
it "should parse a :processing line correctly when it contains ipv6 localhost address" do
|
|
59
55
|
line = prefix + 'Processing PeopleController#index (for ::1 at 2008-08-14 21:16:30) [GET]'
|
|
60
|
-
|
|
56
|
+
subject.should parse_line(line).as(:processing).and_capture(:controller => 'PeopleController', :action => 'index', :timestamp => 20080814211630, :method => 'GET', :ip => '::1')
|
|
61
57
|
end
|
|
62
58
|
|
|
63
59
|
it "should parse a :processing line correctly when it contains ipv6 address" do
|
|
64
60
|
line = prefix + 'Processing PeopleController#index (for 3ffe:1900:4545:3:200:f8ff:fe21:67cf at 2008-08-14 21:16:30) [GET]'
|
|
65
|
-
|
|
61
|
+
subject.should parse_line(line).as(:processing).and_capture(:controller => 'PeopleController', :action => 'index', :timestamp => 20080814211630, :method => 'GET', :ip => '3ffe:1900:4545:3:200:f8ff:fe21:67cf')
|
|
66
62
|
end
|
|
67
63
|
|
|
68
64
|
it "should parse a Rails 2.1 style :completed line correctly" do
|
|
69
65
|
line = prefix + 'Completed in 0.21665 (4 reqs/sec) | Rendering: 0.00926 (4%) | DB: 0.00000 (0%) | 200 OK [http://demo.nu/employees]'
|
|
70
|
-
|
|
66
|
+
subject.should parse_line(line).as(:completed).and_capture(:duration => 0.21665, :db => 0.0, :view => 0.00926, :status => 200, :url => 'http://demo.nu/employees')
|
|
71
67
|
end
|
|
72
68
|
|
|
73
69
|
it "should parse a Rails 2.2 style :completed line correctly" do
|
|
74
70
|
line = prefix + 'Completed in 614ms (View: 120, DB: 31) | 200 OK [http://floorplanner.local/demo]'
|
|
75
|
-
|
|
71
|
+
subject.should parse_line(line).as(:completed).and_capture(:duration => 0.614, :db => 0.031, :view => 0.120, :status => 200, :url => 'http://floorplanner.local/demo')
|
|
76
72
|
end
|
|
77
73
|
|
|
78
74
|
it "should parse a Rails 2.2 style :completed line correctly when AR is disabled" do
|
|
79
75
|
line = prefix + 'Completed in 597ms (View: 298 | 200 OK [http://shapado.com]'
|
|
80
|
-
|
|
76
|
+
subject.should parse_line(line).as(:completed).and_capture(:duration => 0.597, :db => nil, :view => 0.298, :status => 200, :url => 'http://shapado.com')
|
|
81
77
|
end
|
|
82
78
|
|
|
83
79
|
it "should parse a Rails 2.2 style :completed line without view" do
|
|
84
80
|
line = prefix + "Completed in 148ms (DB: 0) | 302 Found [http://iwp-sod.hargray.org/login]"
|
|
85
|
-
|
|
81
|
+
subject.should parse_line(line).as(:completed).and_capture(:duration => 0.148, :db => 0.0, :view => nil, :status => 302, :url => 'http://iwp-sod.hargray.org/login')
|
|
86
82
|
end
|
|
87
83
|
|
|
88
84
|
it "should parse a :failure line with exception correctly" do
|
|
89
85
|
line = prefix + "NoMethodError (undefined method `update_domain_account' for nil:NilClass):"
|
|
90
|
-
|
|
86
|
+
subject.should parse_line(line).as(:failure).and_capture(:error => 'NoMethodError', :message => "undefined method `update_domain_account' for nil:NilClass")
|
|
91
87
|
end
|
|
92
88
|
|
|
93
89
|
it "should parse a :cache_hit line correctly with an filter instance reference" do
|
|
94
90
|
line = prefix + 'Filter chain halted as [#<ActionController::Filters::AroundFilter:0x2a999ad120 @identifier=nil, @kind=:filter, @options={:only=>#<Set: {"cached"}>, :if=>:not_logged_in?, :unless=>nil}, @method=#<ActionController::Caching::Actions::ActionCacheFilter:0x2a999ad620 @check=nil, @options={:store_options=>{}, :layout=>nil, :cache_path=>#<Proc:0x0000002a999b8890@/app/controllers/cached_controller.rb:8>}>>] did_not_yield.'
|
|
95
|
-
|
|
91
|
+
subject.should parse_line(line).as(:cache_hit)
|
|
96
92
|
end
|
|
97
93
|
|
|
98
94
|
it "should parse a :cache_hit line correctly with an proc instance reference" do
|
|
99
95
|
line = prefix + 'Filter chain halted as [#<ActionController::Filters::AroundFilter:0x2a9a923e38 @method=#<Proc:0x0000002a9818b3f8@/usr/local/lib/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_controller/caching/actions.rb:64>, @kind=:filter, @identifier=nil, @options={:unless=>nil, :if=>nil, :only=>#<Set: {"show"}>}>] did_not_yield.'
|
|
100
|
-
|
|
96
|
+
subject.should parse_line(line).as(:cache_hit)
|
|
101
97
|
end
|
|
102
98
|
|
|
103
99
|
|
|
104
100
|
it "should parse a :parameters line correctly" do
|
|
105
101
|
line = prefix + ' Parameters: {"action"=>"cached", "controller"=>"cached"}'
|
|
106
|
-
|
|
102
|
+
subject.should parse_line(line).as(:parameters).and_capture(:params => {:action => 'cached', :controller => 'cached'})
|
|
107
103
|
end
|
|
108
104
|
|
|
109
105
|
it "should parse a :rendered line correctly" do
|
|
110
106
|
line = prefix + 'Rendered layouts/_footer (2.9ms)'
|
|
111
|
-
|
|
107
|
+
subject.should parse_line(line).as(:rendered).and_capture(:render_file => 'layouts/_footer', :render_duration => 0.0029)
|
|
112
108
|
end
|
|
113
109
|
|
|
114
110
|
it "should parse a :query_executed line with colors" do
|
|
115
111
|
line = prefix + ' [4;36;1mUser Load (0.4ms)[0m [0;1mSELECT * FROM `users` WHERE (`users`.`id` = 18205844) [0m'
|
|
116
|
-
|
|
112
|
+
subject.should parse_line(line).as(:query_executed).and_capture(:query_class => 'User', :query_duration => 0.0004, :query_sql => 'SELECT * FROM users WHERE (users.id = :int)')
|
|
117
113
|
end
|
|
118
114
|
|
|
119
115
|
it "should parse a :query_executed line without colors" do
|
|
120
116
|
line = prefix + ' User Load (0.4ms) SELECT * FROM `users` WHERE (`users`.`id` = 18205844) '
|
|
121
|
-
|
|
117
|
+
subject.should parse_line(line).as(:query_executed).and_capture(:query_class => 'User', :query_duration => 0.0004, :query_sql => 'SELECT * FROM users WHERE (users.id = :int)')
|
|
122
118
|
end
|
|
123
119
|
|
|
124
120
|
it "should parse a :query_cached line with colors" do
|
|
125
121
|
line = prefix + ' [4;35;1mCACHE (0.0ms)[0m [0mSELECT * FROM `users` WHERE (`users`.`id` = 0) [0m'
|
|
126
|
-
|
|
122
|
+
subject.should parse_line(line).as(:query_cached).and_capture(:cached_duration => 0.0, :cached_sql => 'SELECT * FROM users WHERE (users.id = :int)')
|
|
127
123
|
end
|
|
128
124
|
|
|
129
125
|
it "should parse a :query_cached line without colors" do
|
|
130
126
|
line = prefix + ' CACHE (0.0ms) SELECT * FROM `users` WHERE (`users`.`id` = 0) '
|
|
131
|
-
|
|
127
|
+
subject.should parse_line(line).as(:query_cached).and_capture(:cached_duration => 0.0, :cached_sql => 'SELECT * FROM users WHERE (users.id = :int)')
|
|
132
128
|
end
|
|
133
129
|
|
|
134
130
|
it "should not parse an unsupported line" do
|
|
135
131
|
line = prefix + 'nonsense line that should not be parsed as anything'
|
|
136
|
-
|
|
132
|
+
subject.should_not parse_line(line)
|
|
137
133
|
end
|
|
138
134
|
end
|
|
139
135
|
end
|
|
140
136
|
end
|
|
141
137
|
|
|
142
138
|
describe '#parse_io' do
|
|
143
|
-
|
|
144
|
-
@log_parser = RequestLogAnalyzer::Source::LogParser.new(
|
|
145
|
-
RequestLogAnalyzer::FileFormat.load(:rails), :parse_strategy => 'cautious')
|
|
146
|
-
end
|
|
139
|
+
let(:log_parser) { RequestLogAnalyzer::Source::LogParser.new(subject, :parse_strategy => 'cautious') }
|
|
147
140
|
|
|
148
141
|
it "should parse a Rails 2.1 style log and find valid Rails requests without warnings" do
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
@log_parser.parse_file(log_fixture(:rails_1x)) do |request|
|
|
153
|
-
request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Rails::Request) && request.completed?
|
|
154
|
-
end
|
|
142
|
+
log_parser.should_receive(:handle_request).exactly(4).times
|
|
143
|
+
log_parser.should_not_receive(:warn)
|
|
144
|
+
log_parser.parse_file(log_fixture(:rails_1x))\
|
|
155
145
|
end
|
|
156
146
|
|
|
157
147
|
it "should parse a Rails 2.2 style log and find valid Rails requests without warnings" do
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
@log_parser.parse_file(log_fixture(:rails_22)) do |request|
|
|
162
|
-
request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Rails::Request) && request.completed?
|
|
163
|
-
end
|
|
148
|
+
log_parser.should_receive(:handle_request).once
|
|
149
|
+
log_parser.should_not_receive(:warn)
|
|
150
|
+
log_parser.parse_file(log_fixture(:rails_22))
|
|
164
151
|
end
|
|
165
152
|
|
|
166
153
|
it "should parse a Rails SyslogLogger file with prefix and find valid requests without warnings" do
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
@log_parser.parse_file(log_fixture(:syslog_1x)) do |request|
|
|
171
|
-
request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Rails::Request) && request.completed?
|
|
172
|
-
end
|
|
154
|
+
log_parser.should_receive(:handle_request).once
|
|
155
|
+
log_parser.should_not_receive(:warn)
|
|
156
|
+
log_parser.parse_file(log_fixture(:syslog_1x))
|
|
173
157
|
end
|
|
174
158
|
|
|
175
159
|
it "should parse cached requests" do
|
|
176
|
-
|
|
177
|
-
|
|
160
|
+
log_parser.should_not_receive(:warn)
|
|
161
|
+
log_parser.parse_file(log_fixture(:rails_22_cached)) do |request|
|
|
178
162
|
request.should be_completed
|
|
179
163
|
request =~ :cache_hit
|
|
180
164
|
end
|
|
181
165
|
end
|
|
182
166
|
|
|
183
167
|
it "should detect unordered requests in the logs" do
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
168
|
+
log_parser.should_not_receive(:handle_request)
|
|
169
|
+
log_parser.should_receive(:warn).with(:unclosed_request, anything).once
|
|
170
|
+
log_parser.should_receive(:warn).with(:no_current_request, anything).at_least(:twice)
|
|
171
|
+
log_parser.parse_file(log_fixture(:rails_unordered))
|
|
188
172
|
end
|
|
189
173
|
end
|
|
190
174
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe RequestLogAnalyzer::FileFormat::W3c do
|
|
4
|
+
|
|
5
|
+
subject { RequestLogAnalyzer::FileFormat.load(:w3c) }
|
|
6
|
+
|
|
7
|
+
it { should be_well_formed }
|
|
8
|
+
it { should have_line_definition(:access).capturing(:timestamp, :remote_ip, :username, :local_ip, :port,
|
|
9
|
+
:method, :path, :http_status, :bytes_sent, :bytes_received, :duration, :user_agent, :referer) }
|
|
10
|
+
|
|
11
|
+
it { should have(10).report_trackers }
|
|
12
|
+
|
|
13
|
+
let(:sample1) { '2002-05-24 20:18:01 172.224.24.114 - 206.73.118.24 80 GET /Default.htm - 200 7930 248 31 Mozilla/4.0+(compatible;+MSIE+5.01;+Windows+2000+Server) http://64.224.24.114/' }
|
|
14
|
+
let(:irrelevant) { '#Software: Microsoft Internet Information Services 6.0' }
|
|
15
|
+
|
|
16
|
+
describe '#parse_line' do
|
|
17
|
+
it { should parse_line(sample1, 'an access line').and_capture(
|
|
18
|
+
:timestamp => 20020524201801,
|
|
19
|
+
:remote_ip => "172.224.24.114",
|
|
20
|
+
:username => nil,
|
|
21
|
+
:local_ip => "206.73.118.24",
|
|
22
|
+
:port => 80,
|
|
23
|
+
:method => 'GET',
|
|
24
|
+
:path => '/Default.htm',
|
|
25
|
+
:http_status => 200,
|
|
26
|
+
:bytes_sent => 7930,
|
|
27
|
+
:bytes_received => 248,
|
|
28
|
+
:duration => 0.031,
|
|
29
|
+
:user_agent => 'Mozilla/4.0+(compatible;+MSIE+5.01;+Windows+2000+Server)',
|
|
30
|
+
:referer => 'http://64.224.24.114/')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
it { should_not parse_line(irrelevant, 'an irrelevant line') }
|
|
34
|
+
it { should_not parse_line('nonsense', 'a nonsense line') }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe '#parse_io' do
|
|
38
|
+
let(:log_parser) { RequestLogAnalyzer::Source::LogParser.new(subject) }
|
|
39
|
+
let(:snippet) { log_snippet(irrelevant, sample1, sample1) }
|
|
40
|
+
|
|
41
|
+
it "should parse a snippet successully without warnings" do
|
|
42
|
+
log_parser.should_receive(:handle_request).twice
|
|
43
|
+
log_parser.should_not_receive(:warn)
|
|
44
|
+
log_parser.parse_io(snippet)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -11,7 +11,7 @@ describe RequestLogAnalyzer::Source::LogParser, :requests do
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
it "should have a valid language" do
|
|
14
|
-
@log_parser.file_format.should
|
|
14
|
+
@log_parser.file_format.should be_well_formed
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
it "should set the :source for every parsed line" do
|
metadata
CHANGED
|
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
|
4
4
|
prerelease: false
|
|
5
5
|
segments:
|
|
6
6
|
- 1
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
version: 1.
|
|
7
|
+
- 11
|
|
8
|
+
- 0
|
|
9
|
+
version: 1.11.0
|
|
10
10
|
platform: ruby
|
|
11
11
|
authors:
|
|
12
12
|
- Willem van Bergen
|
|
@@ -15,7 +15,7 @@ autorequire:
|
|
|
15
15
|
bindir: bin
|
|
16
16
|
cert_chain: []
|
|
17
17
|
|
|
18
|
-
date: 2011-
|
|
18
|
+
date: 2011-04-27 00:00:00 -04:00
|
|
19
19
|
default_executable: request-log-analyzer
|
|
20
20
|
dependencies:
|
|
21
21
|
- !ruby/object:Gem::Dependency
|
|
@@ -83,7 +83,9 @@ extra_rdoc_files:
|
|
|
83
83
|
- README.rdoc
|
|
84
84
|
files:
|
|
85
85
|
- .gitignore
|
|
86
|
+
- .infinity_test
|
|
86
87
|
- DESIGN.rdoc
|
|
88
|
+
- Gemfile
|
|
87
89
|
- LICENSE
|
|
88
90
|
- README.rdoc
|
|
89
91
|
- Rakefile
|
|
@@ -93,7 +95,6 @@ files:
|
|
|
93
95
|
- lib/cli/database_console_init.rb
|
|
94
96
|
- lib/cli/progressbar.rb
|
|
95
97
|
- lib/cli/tools.rb
|
|
96
|
-
- lib/mixins/gets_memory_protection.rb
|
|
97
98
|
- lib/request_log_analyzer.rb
|
|
98
99
|
- lib/request_log_analyzer/aggregator.rb
|
|
99
100
|
- lib/request_log_analyzer/aggregator/database_inserter.rb
|
|
@@ -112,6 +113,7 @@ files:
|
|
|
112
113
|
- lib/request_log_analyzer/file_format/delayed_job.rb
|
|
113
114
|
- lib/request_log_analyzer/file_format/delayed_job2.rb
|
|
114
115
|
- lib/request_log_analyzer/file_format/delayed_job21.rb
|
|
116
|
+
- lib/request_log_analyzer/file_format/haproxy.rb
|
|
115
117
|
- lib/request_log_analyzer/file_format/merb.rb
|
|
116
118
|
- lib/request_log_analyzer/file_format/mysql.rb
|
|
117
119
|
- lib/request_log_analyzer/file_format/oink.rb
|
|
@@ -120,6 +122,7 @@ files:
|
|
|
120
122
|
- lib/request_log_analyzer/file_format/rails.rb
|
|
121
123
|
- lib/request_log_analyzer/file_format/rails3.rb
|
|
122
124
|
- lib/request_log_analyzer/file_format/rails_development.rb
|
|
125
|
+
- lib/request_log_analyzer/file_format/w3c.rb
|
|
123
126
|
- lib/request_log_analyzer/filter.rb
|
|
124
127
|
- lib/request_log_analyzer/filter/anonymize.rb
|
|
125
128
|
- lib/request_log_analyzer/filter/field.rb
|
|
@@ -128,12 +131,10 @@ files:
|
|
|
128
131
|
- lib/request_log_analyzer/log_processor.rb
|
|
129
132
|
- lib/request_log_analyzer/mailer.rb
|
|
130
133
|
- lib/request_log_analyzer/output.rb
|
|
131
|
-
- lib/request_log_analyzer/output/fancy_html.rb
|
|
132
134
|
- lib/request_log_analyzer/output/fixed_width.rb
|
|
133
135
|
- lib/request_log_analyzer/output/html.rb
|
|
134
136
|
- lib/request_log_analyzer/request.rb
|
|
135
137
|
- lib/request_log_analyzer/source.rb
|
|
136
|
-
- lib/request_log_analyzer/source/database_loader.rb
|
|
137
138
|
- lib/request_log_analyzer/source/log_parser.rb
|
|
138
139
|
- lib/request_log_analyzer/tracker.rb
|
|
139
140
|
- lib/request_log_analyzer/tracker/duration.rb
|
|
@@ -196,6 +197,7 @@ files:
|
|
|
196
197
|
- spec/unit/file_format/delayed_job_format_spec.rb
|
|
197
198
|
- spec/unit/file_format/file_format_api_spec.rb
|
|
198
199
|
- spec/unit/file_format/format_autodetection_spec.rb
|
|
200
|
+
- spec/unit/file_format/haproxy_format_spec.rb
|
|
199
201
|
- spec/unit/file_format/line_definition_spec.rb
|
|
200
202
|
- spec/unit/file_format/merb_format_spec.rb
|
|
201
203
|
- spec/unit/file_format/mysql_format_spec.rb
|
|
@@ -204,6 +206,7 @@ files:
|
|
|
204
206
|
- spec/unit/file_format/rack_format_spec.rb
|
|
205
207
|
- spec/unit/file_format/rails3_format_spec.rb
|
|
206
208
|
- spec/unit/file_format/rails_format_spec.rb
|
|
209
|
+
- spec/unit/file_format/w3c_format_spec.rb
|
|
207
210
|
- spec/unit/filter/anonymize_filter_spec.rb
|
|
208
211
|
- spec/unit/filter/field_filter_spec.rb
|
|
209
212
|
- spec/unit/filter/filter_spec.rb
|
|
@@ -277,6 +280,7 @@ test_files:
|
|
|
277
280
|
- spec/unit/file_format/delayed_job_format_spec.rb
|
|
278
281
|
- spec/unit/file_format/file_format_api_spec.rb
|
|
279
282
|
- spec/unit/file_format/format_autodetection_spec.rb
|
|
283
|
+
- spec/unit/file_format/haproxy_format_spec.rb
|
|
280
284
|
- spec/unit/file_format/line_definition_spec.rb
|
|
281
285
|
- spec/unit/file_format/merb_format_spec.rb
|
|
282
286
|
- spec/unit/file_format/mysql_format_spec.rb
|
|
@@ -285,6 +289,7 @@ test_files:
|
|
|
285
289
|
- spec/unit/file_format/rack_format_spec.rb
|
|
286
290
|
- spec/unit/file_format/rails3_format_spec.rb
|
|
287
291
|
- spec/unit/file_format/rails_format_spec.rb
|
|
292
|
+
- spec/unit/file_format/w3c_format_spec.rb
|
|
288
293
|
- spec/unit/filter/anonymize_filter_spec.rb
|
|
289
294
|
- spec/unit/filter/field_filter_spec.rb
|
|
290
295
|
- spec/unit/filter/filter_spec.rb
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
class File
|
|
2
|
-
alias_method :gets_original, :gets
|
|
3
|
-
# The size of the reads we will use to add to the line buffer.
|
|
4
|
-
MAX_READ_SIZE=1024*100
|
|
5
|
-
|
|
6
|
-
#
|
|
7
|
-
# This method returns the next line of the File.
|
|
8
|
-
#
|
|
9
|
-
# It works by moving the file pointer forward +MAX_READ_SIZE+ at a time,
|
|
10
|
-
# storing seen lines in <tt>@line_buffer</tt>. Once the buffer contains at
|
|
11
|
-
# least two lines (ensuring we have seen on full line) or the file pointer
|
|
12
|
-
# reaches the end of the File, the last line from the buffer is returned.
|
|
13
|
-
# When the buffer is exhausted, this will throw +nil+ (from the empty Array).
|
|
14
|
-
#
|
|
15
|
-
# Read portions of the file that do not contain the +sep_string+ are not added to
|
|
16
|
-
# the buffer. This prevents <tt>@line_buffer<tt> from growing signficantly when parsing
|
|
17
|
-
# large lines.
|
|
18
|
-
#
|
|
19
|
-
def gets(sep_string = $/)
|
|
20
|
-
@read_size ||= MAX_READ_SIZE
|
|
21
|
-
# A buffer to hold lines read, but not yet returned.
|
|
22
|
-
@line_buffer ||= Array.new
|
|
23
|
-
|
|
24
|
-
# Record where we are.
|
|
25
|
-
@current_pos ||= pos
|
|
26
|
-
|
|
27
|
-
# Last Position in the file
|
|
28
|
-
@last_pos ||= nil
|
|
29
|
-
if @last_pos.nil?
|
|
30
|
-
seek(0, IO::SEEK_END)
|
|
31
|
-
@last_pos = pos
|
|
32
|
-
seek(@current_pos, IO::SEEK_SET)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
#
|
|
36
|
-
# If we have more than one line in the buffer or we have reached the
|
|
37
|
-
# beginning of the file, send the last line in the buffer to the caller.
|
|
38
|
-
# (This may be +nil+, if the buffer has been exhausted.)
|
|
39
|
-
#
|
|
40
|
-
if @line_buffer.size > 2 or @current_pos >= @last_pos
|
|
41
|
-
self.lineno += 1
|
|
42
|
-
return @line_buffer.shift
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
sep =
|
|
46
|
-
|
|
47
|
-
chunk = String.new
|
|
48
|
-
while chunk and chunk !~ /#{sep_string}/
|
|
49
|
-
chunk = read(@read_size)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Appends new lines to the last element of the buffer
|
|
53
|
-
line_buffer_pos = @line_buffer.any? ? @line_buffer.size-1 : 0
|
|
54
|
-
|
|
55
|
-
if chunk
|
|
56
|
-
@line_buffer[line_buffer_pos] = @line_buffer[line_buffer_pos].to_s<< chunk
|
|
57
|
-
else
|
|
58
|
-
# at the end
|
|
59
|
-
return @line_buffer.shift
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
#
|
|
63
|
-
# Divide the last line of the buffer based on +sep_string+ and #flatten!
|
|
64
|
-
# those new lines into the buffer.
|
|
65
|
-
#
|
|
66
|
-
@line_buffer[line_buffer_pos] = @line_buffer[line_buffer_pos].scan(/.*?#{Regexp.escape(sep_string)}|.+/)
|
|
67
|
-
@line_buffer.flatten!
|
|
68
|
-
|
|
69
|
-
#
|
|
70
|
-
# If we made it this far, we need to read more data to try and find the
|
|
71
|
-
# end of a line or the end of the file. Move the file pointer
|
|
72
|
-
# forward a step, to give us new bytes to read.
|
|
73
|
-
#
|
|
74
|
-
@current_pos += @read_size
|
|
75
|
-
seek(@current_pos, IO::SEEK_SET)
|
|
76
|
-
|
|
77
|
-
# We have more data now, so try again to read a line...
|
|
78
|
-
gets(sep_string)
|
|
79
|
-
end
|
|
80
|
-
end
|