request-log-analyzer 1.0.2 → 1.6.3

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 (144) hide show
  1. data/.gitignore +10 -0
  2. data/DESIGN.rdoc +41 -0
  3. data/LICENSE +4 -4
  4. data/README.rdoc +38 -0
  5. data/Rakefile +6 -3
  6. data/bin/request-log-analyzer +70 -72
  7. data/lib/cli/command_line_arguments.rb +53 -53
  8. data/lib/cli/database_console.rb +26 -0
  9. data/lib/cli/database_console_init.rb +43 -0
  10. data/lib/cli/progressbar.rb +166 -189
  11. data/lib/cli/tools.rb +49 -0
  12. data/lib/request_log_analyzer/aggregator/database_inserter.rb +83 -0
  13. data/lib/request_log_analyzer/aggregator/echo.rb +17 -12
  14. data/lib/request_log_analyzer/aggregator/summarizer.rb +101 -63
  15. data/lib/request_log_analyzer/{aggregator/base.rb → aggregator.rb} +17 -13
  16. data/lib/request_log_analyzer/controller.rb +251 -98
  17. data/lib/request_log_analyzer/database/base.rb +114 -0
  18. data/lib/request_log_analyzer/database/connection.rb +38 -0
  19. data/lib/request_log_analyzer/database/request.rb +22 -0
  20. data/lib/request_log_analyzer/database/source.rb +13 -0
  21. data/lib/request_log_analyzer/database/warning.rb +14 -0
  22. data/lib/request_log_analyzer/database.rb +102 -0
  23. data/lib/request_log_analyzer/file_format/amazon_s3.rb +74 -0
  24. data/lib/request_log_analyzer/file_format/apache.rb +147 -0
  25. data/lib/request_log_analyzer/file_format/delayed_job.rb +55 -0
  26. data/lib/request_log_analyzer/file_format/merb.rb +65 -29
  27. data/lib/request_log_analyzer/file_format/mysql.rb +101 -0
  28. data/lib/request_log_analyzer/file_format/postgresql.rb +68 -0
  29. data/lib/request_log_analyzer/file_format/rack.rb +9 -0
  30. data/lib/request_log_analyzer/file_format/rails.rb +164 -78
  31. data/lib/request_log_analyzer/file_format/rails3.rb +86 -0
  32. data/lib/request_log_analyzer/file_format/rails_development.rb +12 -0
  33. data/lib/request_log_analyzer/file_format.rb +252 -58
  34. data/lib/request_log_analyzer/filter/anonymize.rb +39 -0
  35. data/lib/request_log_analyzer/filter/field.rb +19 -13
  36. data/lib/request_log_analyzer/filter/timespan.rb +25 -12
  37. data/lib/request_log_analyzer/filter.rb +30 -0
  38. data/lib/request_log_analyzer/line_definition.rb +69 -96
  39. data/lib/request_log_analyzer/log_processor.rb +31 -53
  40. data/lib/request_log_analyzer/mailer.rb +65 -0
  41. data/lib/request_log_analyzer/output/fancy_html.rb +49 -0
  42. data/lib/request_log_analyzer/output/fixed_width.rb +220 -0
  43. data/lib/request_log_analyzer/output/html.rb +187 -0
  44. data/lib/request_log_analyzer/output.rb +117 -0
  45. data/lib/request_log_analyzer/request.rb +125 -40
  46. data/lib/request_log_analyzer/source/database_loader.rb +87 -0
  47. data/lib/request_log_analyzer/source/log_parser.rb +297 -0
  48. data/lib/request_log_analyzer/source.rb +72 -0
  49. data/lib/request_log_analyzer/tracker/duration.rb +28 -64
  50. data/lib/request_log_analyzer/tracker/frequency.rb +108 -0
  51. data/lib/request_log_analyzer/tracker/hourly_spread.rb +76 -49
  52. data/lib/request_log_analyzer/tracker/numeric_value.rb +223 -0
  53. data/lib/request_log_analyzer/tracker/timespan.rb +54 -27
  54. data/lib/request_log_analyzer/tracker/traffic.rb +40 -0
  55. data/lib/request_log_analyzer/tracker.rb +101 -0
  56. data/lib/request_log_analyzer.rb +43 -13
  57. data/request-log-analyzer.gemspec +41 -0
  58. data/spec/database.yml +23 -0
  59. data/spec/fixtures/apache_combined.log +5 -0
  60. data/spec/fixtures/apache_common.log +10 -0
  61. data/spec/fixtures/decompression.log +12 -0
  62. data/spec/fixtures/decompression.log.bz2 +0 -0
  63. data/spec/fixtures/decompression.log.gz +0 -0
  64. data/spec/fixtures/decompression.log.zip +0 -0
  65. data/spec/fixtures/decompression.tar.gz +0 -0
  66. data/spec/fixtures/decompression.tgz +0 -0
  67. data/spec/fixtures/header_and_footer.log +6 -0
  68. data/spec/fixtures/merb_prefixed.log +9 -0
  69. data/spec/fixtures/mysql_slow_query.log +110 -0
  70. data/spec/fixtures/postgresql.log +2980 -0
  71. data/spec/fixtures/rails.db +0 -0
  72. data/spec/fixtures/rails_22.log +1 -1
  73. data/spec/fixtures/sinatra.log +99 -0
  74. data/spec/integration/command_line_usage_spec.rb +84 -0
  75. data/spec/integration/mailer_spec.rb +179 -0
  76. data/spec/integration/munin_plugins_rails_spec.rb +58 -0
  77. data/spec/integration/scout_spec.rb +152 -0
  78. data/spec/lib/helpers.rb +72 -0
  79. data/spec/lib/macros.rb +18 -0
  80. data/spec/lib/matchers.rb +77 -0
  81. data/spec/lib/mocks.rb +77 -0
  82. data/spec/lib/testing_format.rb +46 -0
  83. data/spec/spec_helper.rb +16 -59
  84. data/spec/unit/aggregator/database_inserter_spec.rb +93 -0
  85. data/spec/unit/aggregator/summarizer_spec.rb +26 -0
  86. data/spec/unit/controller/controller_spec.rb +41 -0
  87. data/spec/unit/controller/log_processor_spec.rb +18 -0
  88. data/spec/unit/database/base_class_spec.rb +183 -0
  89. data/spec/unit/database/connection_spec.rb +34 -0
  90. data/spec/unit/database/database_spec.rb +133 -0
  91. data/spec/unit/file_format/amazon_s3_format_spec.rb +67 -0
  92. data/spec/unit/file_format/apache_format_spec.rb +203 -0
  93. data/spec/unit/file_format/common_regular_expressions_spec.rb +53 -0
  94. data/spec/unit/file_format/delayed_job_format_spec.rb +83 -0
  95. data/spec/unit/file_format/file_format_api_spec.rb +69 -0
  96. data/spec/unit/file_format/format_autodetection_spec.rb +40 -0
  97. data/spec/unit/file_format/line_definition_spec.rb +75 -0
  98. data/spec/unit/file_format/merb_format_spec.rb +52 -0
  99. data/spec/unit/file_format/mysql_format_spec.rb +154 -0
  100. data/spec/unit/file_format/postgresql_format_spec.rb +65 -0
  101. data/spec/unit/file_format/rack_format_spec.rb +50 -0
  102. data/spec/unit/file_format/rails3_format_spec.rb +120 -0
  103. data/spec/unit/file_format/rails_format_spec.rb +180 -0
  104. data/spec/unit/filter/anonymize_filter_spec.rb +21 -0
  105. data/spec/unit/filter/field_filter_spec.rb +66 -0
  106. data/spec/unit/filter/filter_spec.rb +17 -0
  107. data/spec/unit/filter/timespan_filter_spec.rb +58 -0
  108. data/spec/unit/mailer_spec.rb +42 -0
  109. data/spec/unit/request_spec.rb +111 -0
  110. data/spec/unit/source/log_parser_spec.rb +119 -0
  111. data/spec/unit/tracker/duration_tracker_spec.rb +49 -0
  112. data/spec/unit/tracker/frequency_tracker_spec.rb +88 -0
  113. data/spec/unit/tracker/hourly_spread_spec.rb +79 -0
  114. data/spec/unit/tracker/numeric_value_tracker_spec.rb +166 -0
  115. data/spec/unit/tracker/timespan_tracker_spec.rb +73 -0
  116. data/spec/unit/tracker/tracker_api_spec.rb +125 -0
  117. data/spec/unit/tracker/traffic_tracker_spec.rb +28 -0
  118. data/tasks/github-gem.rake +290 -139
  119. data/tasks/request_log_analyzer.rake +23 -7
  120. metadata +221 -94
  121. data/DESIGN +0 -14
  122. data/HACKING +0 -7
  123. data/README.textile +0 -36
  124. data/lib/cli/bashcolorizer.rb +0 -60
  125. data/lib/request_log_analyzer/aggregator/database.rb +0 -148
  126. data/lib/request_log_analyzer/filter/base.rb +0 -29
  127. data/lib/request_log_analyzer/log_parser.rb +0 -173
  128. data/lib/request_log_analyzer/source/base.rb +0 -42
  129. data/lib/request_log_analyzer/source/log_file.rb +0 -170
  130. data/lib/request_log_analyzer/tracker/base.rb +0 -54
  131. data/lib/request_log_analyzer/tracker/category.rb +0 -71
  132. data/spec/controller_spec.rb +0 -40
  133. data/spec/database_inserter_spec.rb +0 -101
  134. data/spec/file_format_spec.rb +0 -78
  135. data/spec/file_formats/spec_format.rb +0 -26
  136. data/spec/filter_spec.rb +0 -137
  137. data/spec/line_definition_spec.rb +0 -124
  138. data/spec/log_parser_spec.rb +0 -68
  139. data/spec/log_processor_spec.rb +0 -57
  140. data/spec/merb_format_spec.rb +0 -38
  141. data/spec/rails_format_spec.rb +0 -76
  142. data/spec/request_spec.rb +0 -72
  143. data/spec/summarizer_spec.rb +0 -9
  144. data/tasks/rspec.rake +0 -6
@@ -0,0 +1,180 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::FileFormat::Rails do
4
+
5
+ describe '.create' do
6
+
7
+ context 'without providing a lines argument' do
8
+ before(:each) { @rails = RequestLogAnalyzer::FileFormat.load(:rails) }
9
+
10
+ it "should create a valid file format" do
11
+ @rails.should be_valid
12
+ end
13
+
14
+ it "should parse the production lines" do
15
+ production_rails = RequestLogAnalyzer::FileFormat.load(:rails, 'production')
16
+ @rails.line_definitions.should == production_rails.line_definitions
17
+ end
18
+ end
19
+
20
+ context 'using a comma separated list of lines as argument' do
21
+ before(:each) { @rails = RequestLogAnalyzer::FileFormat.load(:rails, 'minimal,failure') }
22
+
23
+ it "should return a valid language" do
24
+ @rails.should be_valid
25
+ end
26
+
27
+ it "should at least parse :processing and :completed lines" do
28
+ @rails.line_definitions.should include(:processing, :completed, :failure)
29
+ end
30
+ end
31
+
32
+ RequestLogAnalyzer::FileFormat::Rails::LINE_COLLECTIONS.keys.each do |constant|
33
+
34
+ context "using the '#{constant}' line collection constant" do
35
+ before(:each) { @rails = RequestLogAnalyzer::FileFormat.load(:rails, constant) }
36
+
37
+ it "should return a valid language" do
38
+ @rails.should be_valid
39
+ end
40
+
41
+ it "should at least parse :processing and :completed lines" do
42
+ @rails.line_definitions.should include(:processing, :completed)
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ describe '#parse_line' do
49
+ before(:each) { @rails = RequestLogAnalyzer::FileFormat.load(:rails, :all) }
50
+
51
+ {'with prefix' => 'LINE PREFIX: ', 'without prefix' => '' }.each do |context, prefix|
52
+ context context do
53
+ it "should parse a :processing line correctly" do
54
+ line = prefix + 'Processing PeopleController#index (for 1.1.1.1 at 2008-08-14 21:16:30) [GET]'
55
+ @rails.should parse_line(line).as(:processing).and_capture(:controller => 'PeopleController', :action => 'index', :timestamp => 20080814211630, :method => 'GET', :ip => '1.1.1.1')
56
+ end
57
+
58
+ it "should parse a :processing line correctly when it contains ipv6 localhost address" do
59
+ line = prefix + 'Processing PeopleController#index (for ::1 at 2008-08-14 21:16:30) [GET]'
60
+ @rails.should parse_line(line).as(:processing).and_capture(:controller => 'PeopleController', :action => 'index', :timestamp => 20080814211630, :method => 'GET', :ip => '::1')
61
+ end
62
+
63
+ it "should parse a :processing line correctly when it contains ipv6 address" do
64
+ line = prefix + 'Processing PeopleController#index (for 3ffe:1900:4545:3:200:f8ff:fe21:67cf at 2008-08-14 21:16:30) [GET]'
65
+ @rails.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
+ end
67
+
68
+ it "should parse a Rails 2.1 style :completed line correctly" do
69
+ line = prefix + 'Completed in 0.21665 (4 reqs/sec) | Rendering: 0.00926 (4%) | DB: 0.00000 (0%) | 200 OK [http://demo.nu/employees]'
70
+ @rails.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
+ end
72
+
73
+ it "should parse a Rails 2.2 style :completed line correctly" do
74
+ line = prefix + 'Completed in 614ms (View: 120, DB: 31) | 200 OK [http://floorplanner.local/demo]'
75
+ @rails.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
+ end
77
+
78
+ it "should parse a :failure line with exception correctly" do
79
+ line = prefix + "NoMethodError (undefined method `update_domain_account' for nil:NilClass):"
80
+ @rails.should parse_line(line).as(:failure).and_capture(:error => 'NoMethodError', :message => "undefined method `update_domain_account' for nil:NilClass")
81
+ end
82
+
83
+ it "should parse a :cache_hit line correctly with an filter instance reference" do
84
+ 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.'
85
+ @rails.should parse_line(line).as(:cache_hit)
86
+ end
87
+
88
+ it "should parse a :cache_hit line correctly with an proc instance reference" do
89
+ 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.'
90
+ @rails.should parse_line(line).as(:cache_hit)
91
+ end
92
+
93
+
94
+ it "should parse a :parameters line correctly" do
95
+ line = prefix + ' Parameters: {"action"=>"cached", "controller"=>"cached"}'
96
+ @rails.should parse_line(line).as(:parameters).and_capture(:params => {:action => 'cached', :controller => 'cached'})
97
+ end
98
+
99
+ it "should parse a :rendered line correctly" do
100
+ line = prefix + 'Rendered layouts/_footer (2.9ms)'
101
+ @rails.should parse_line(line).as(:rendered).and_capture(:render_file => 'layouts/_footer', :render_duration => 0.0029)
102
+ end
103
+
104
+ it "should parse a :query_executed line with colors" do
105
+ line = prefix + ' User Load (0.4ms) SELECT * FROM `users` WHERE (`users`.`id` = 18205844) '
106
+ @rails.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)')
107
+ end
108
+
109
+ it "should parse a :query_executed line without colors" do
110
+ line = prefix + ' User Load (0.4ms) SELECT * FROM `users` WHERE (`users`.`id` = 18205844) '
111
+ @rails.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)')
112
+ end
113
+
114
+ it "should parse a :query_cached line with colors" do
115
+ line = prefix + ' CACHE (0.0ms) SELECT * FROM `users` WHERE (`users`.`id` = 0) '
116
+ @rails.should parse_line(line).as(:query_cached).and_capture(:cached_duration => 0.0, :cached_sql => 'SELECT * FROM users WHERE (users.id = :int)')
117
+ end
118
+
119
+ it "should parse a :query_cached line without colors" do
120
+ line = prefix + ' CACHE (0.0ms) SELECT * FROM `users` WHERE (`users`.`id` = 0) '
121
+ @rails.should parse_line(line).as(:query_cached).and_capture(:cached_duration => 0.0, :cached_sql => 'SELECT * FROM users WHERE (users.id = :int)')
122
+ end
123
+
124
+ it "should not parse an unsupported line" do
125
+ line = prefix + 'nonsense line that should not be parsed as anything'
126
+ @rails.should_not parse_line(line)
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ describe '#parse_io' do
133
+ before(:each) do
134
+ @log_parser = RequestLogAnalyzer::Source::LogParser.new(
135
+ RequestLogAnalyzer::FileFormat.load(:rails), :parse_strategy => 'cautious')
136
+ end
137
+
138
+ it "should parse a Rails 2.1 style log and find valid Rails requests without warnings" do
139
+ request_counter.should_receive(:hit!).exactly(4).times
140
+ @log_parser.should_not_receive(:warn)
141
+
142
+ @log_parser.parse_file(log_fixture(:rails_1x)) do |request|
143
+ request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Rails::Request) && request.completed?
144
+ end
145
+ end
146
+
147
+ it "should parse a Rails 2.2 style log and find valid Rails requests without warnings" do
148
+ request_counter.should_receive(:hit!).once
149
+ @log_parser.should_not_receive(:warn)
150
+
151
+ @log_parser.parse_file(log_fixture(:rails_22)) do |request|
152
+ request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Rails::Request) && request.completed?
153
+ end
154
+ end
155
+
156
+ it "should parse a Rails SyslogLogger file with prefix and find valid requests without warnings" do
157
+ request_counter.should_receive(:hit!).once
158
+ @log_parser.should_not_receive(:warn)
159
+
160
+ @log_parser.parse_file(log_fixture(:syslog_1x)) do |request|
161
+ request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Rails::Request) && request.completed?
162
+ end
163
+ end
164
+
165
+ it "should parse cached requests" do
166
+ @log_parser.should_not_receive(:warn)
167
+ @log_parser.parse_file(log_fixture(:rails_22_cached)) do |request|
168
+ request.should be_completed
169
+ request =~ :cache_hit
170
+ end
171
+ end
172
+
173
+ it "should detect unordered requests in the logs" do
174
+ @log_parser.should_not_receive(:handle_request)
175
+ @log_parser.should_receive(:warn).with(:unclosed_request, anything).once
176
+ @log_parser.should_receive(:warn).with(:no_current_request, anything).twice
177
+ @log_parser.parse_file(log_fixture(:rails_unordered))
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,21 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::Filter::Anonymize, 'anonymize request' do
4
+
5
+ before(:each) do
6
+ @filter = RequestLogAnalyzer::Filter::Anonymize.new(testing_format)
7
+ end
8
+
9
+ it "should anonimize ip" do
10
+ @filter.filter(request(:ip => '123.123.123.123'))[:ip].should_not eql('123.123.123.123')
11
+ end
12
+
13
+ it "should anonimize url" do
14
+ @filter.filter(request(:url => 'https://test.mysite.com/employees'))[:url].should eql('http://example.com/employees')
15
+ end
16
+
17
+ it "should fuzz durations" do
18
+ @filter.filter(request(:duration => 100))[:duration].should_not eql(100)
19
+ end
20
+
21
+ end
@@ -0,0 +1,66 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::Filter::Field, 'string in accept mode' do
4
+
5
+ before(:each) do
6
+ @filter = RequestLogAnalyzer::Filter::Field.new(testing_format, :field => :test, :value => 'test', :mode => :select)
7
+ end
8
+
9
+ it "should reject a request if the field value does not match" do
10
+ @filter.filter(request(:test => 'not test')).should be_nil
11
+ end
12
+
13
+ it "should reject a request if the field name does not match" do
14
+ @filter.filter(request(:testing => 'test')).should be_nil
15
+ end
16
+
17
+ it "should accept a request if the both name and value match" do
18
+ @filter.filter(request(:test => 'test')).should_not be_nil
19
+ end
20
+
21
+ it "should accept a request if the value is not the first value" do
22
+ @filter.filter(request([{:test => 'ignore'}, {:test => 'test'}])).should_not be_nil
23
+ end
24
+ end
25
+
26
+ describe RequestLogAnalyzer::Filter::Field, 'string in reject mode' do
27
+
28
+ before(:each) do
29
+ @filter = RequestLogAnalyzer::Filter::Field.new(testing_format, :field => :test, :value => 'test', :mode => :reject)
30
+ end
31
+
32
+ it "should accept a request if the field value does not match" do
33
+ @filter.filter(request(:test => 'not test')).should_not be_nil
34
+ end
35
+
36
+ it "should accept a request if the field name does not match" do
37
+ @filter.filter(request(:testing => 'test')).should_not be_nil
38
+ end
39
+
40
+ it "should reject a request if the both name and value match" do
41
+ @filter.filter(request(:test => 'test')).should be_nil
42
+ end
43
+
44
+ it "should reject a request if the value is not the first value" do
45
+ @filter.filter(request([{:test => 'ignore'}, {:test => 'test'}])).should be_nil
46
+ end
47
+ end
48
+
49
+ describe RequestLogAnalyzer::Filter::Field, 'regexp in accept mode' do
50
+
51
+ before(:each) do
52
+ @filter = RequestLogAnalyzer::Filter::Field.new(testing_format, :field => :test, :value => '/test/', :mode => :select)
53
+ end
54
+
55
+ it "should reject a request if the field value does not match" do
56
+ @filter.filter(request(:test => 'a working test')).should_not be_nil
57
+ end
58
+
59
+ it "should reject a request if the field name does not match" do
60
+ @filter.filter(request(:testing => 'test')).should be_nil
61
+ end
62
+
63
+ it "should accept a request if the value is not the first value" do
64
+ @filter.filter(request([{:test => 'ignore'}, {:test => 'testing 123'}])).should_not be_nil
65
+ end
66
+ end
@@ -0,0 +1,17 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::Filter::Base, 'base filter' do
4
+
5
+ before(:each) do
6
+ @filter = RequestLogAnalyzer::Filter::Base.new(testing_format)
7
+ end
8
+
9
+ it "should return everything" do
10
+ @filter.filter(request(:ip => '123.123.123.123'))[:ip].should eql('123.123.123.123')
11
+ end
12
+
13
+ it "should return nil on nil request" do
14
+ @filter.filter(nil).should be_nil
15
+ end
16
+
17
+ end
@@ -0,0 +1,58 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::Filter::Timespan, 'both before and after' do
4
+
5
+ before(:each) do
6
+ @filter = RequestLogAnalyzer::Filter::Timespan.new(testing_format, :after => DateTime.parse('2009-01-01'), :before => DateTime.parse('2009-02-02'))
7
+ end
8
+
9
+ it "should reject a request before the after date" do
10
+ @filter.filter(request(:timestamp => 20081212000000)).should be_nil
11
+ end
12
+
13
+ it "should reject a request after the before date" do
14
+ @filter.filter(request(:timestamp => 20090303000000)).should be_nil
15
+ end
16
+
17
+ it "should accept a request between the after and before dates" do
18
+ @filter.filter(request(:timestamp => 20090102000000)).should_not be_nil
19
+ end
20
+ end
21
+
22
+ describe RequestLogAnalyzer::Filter::Timespan, 'only before' do
23
+
24
+ before(:each) do
25
+ @filter = RequestLogAnalyzer::Filter::Timespan.new(testing_format, :before => DateTime.parse('2009-02-02'))
26
+ end
27
+
28
+ it "should accept a request before the after date" do
29
+ @filter.filter(request(:timestamp => 20081212000000)).should_not be_nil
30
+ end
31
+
32
+ it "should reject a request after the before date" do
33
+ @filter.filter(request(:timestamp => 20090303000000)).should be_nil
34
+ end
35
+
36
+ it "should accept a request between the after and before dates" do
37
+ @filter.filter(request(:timestamp => 20090102000000)).should_not be_nil
38
+ end
39
+ end
40
+
41
+ describe RequestLogAnalyzer::Filter::Timespan, 'only after' do
42
+
43
+ before(:each) do
44
+ @filter = RequestLogAnalyzer::Filter::Timespan.new(testing_format, :after => DateTime.parse('2009-01-01'))
45
+ end
46
+
47
+ it "should reject a request before the after date" do
48
+ @filter.filter(request(:timestamp => 20081212000000)).should be_nil
49
+ end
50
+
51
+ it "should accept a request after the before date" do
52
+ @filter.filter(request(:timestamp => 20090303000000)).should_not be_nil
53
+ end
54
+
55
+ it "should accept a request between the after and before dates" do
56
+ @filter.filter(request(:timestamp => 20090102000000)).should_not be_nil
57
+ end
58
+ end
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::Mailer, 'mailer' do
4
+
5
+ it "should initialize correctly" do
6
+ @mailer = RequestLogAnalyzer::Mailer.new('alfa@beta.com', 'localhost', :debug => true)
7
+ @mailer.host.should eql("localhost")
8
+ @mailer.port.should eql(25)
9
+ end
10
+
11
+ it "should allow alternate port settings" do
12
+ @mailer = RequestLogAnalyzer::Mailer.new('alfa@beta.com', 'localhost:2525', :debug => true)
13
+ @mailer.host.should eql("localhost")
14
+ @mailer.port.should eql("2525")
15
+ end
16
+
17
+ it "should store printed data" do
18
+ @mailer = RequestLogAnalyzer::Mailer.new('alfa@beta.com', 'localhost', :debug => true)
19
+
20
+ @mailer << 'test1'
21
+ @mailer.puts 'test2'
22
+
23
+ @mailer.data.should eql(['test1', 'test2'])
24
+ end
25
+
26
+ it "should send mail" do
27
+ @mailer = RequestLogAnalyzer::Mailer.new('alfa@beta.com', 'localhost', :debug => true)
28
+
29
+ @mailer << 'test1'
30
+ @mailer.puts 'test2'
31
+
32
+ mail = @mailer.mail
33
+
34
+ mail[0].should include("contact@railsdoctors.com")
35
+ mail[0].should include("test1")
36
+ mail[0].should include("test2")
37
+
38
+ mail[1].should include("contact@railsdoctors.com")
39
+ mail[2].should include("alfa@beta.com")
40
+ end
41
+
42
+ end
@@ -0,0 +1,111 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::Request do
4
+
5
+ before(:each) do
6
+ @request = testing_format.request
7
+ end
8
+
9
+ it "should be empty without any captured lines in it" do
10
+ @request.should be_empty
11
+ end
12
+
13
+ context :incomplete do
14
+
15
+ before(:each) do
16
+ @request << { :line_type => :test, :lineno => 1, :test_capture => 'awesome!' }
17
+ end
18
+
19
+ it "should be single if only one line has been added" do
20
+ @request.should_not be_empty
21
+ end
22
+
23
+ it "should not be a completed request" do
24
+ @request.should_not be_completed
25
+ end
26
+
27
+ it "should take the line type of the first line as global line_type" do
28
+ @request.lines[0][:line_type].should == :test
29
+ @request.should =~ :test
30
+ end
31
+
32
+ it "should return the first field value" do
33
+ @request[:test_capture].should == 'awesome!'
34
+ end
35
+
36
+ it "should return nil if no such field is present" do
37
+ @request[:nonexisting].should be_nil
38
+ end
39
+ end
40
+
41
+ context :completed do
42
+
43
+ before(:each) do
44
+ @request << { :line_type => :first, :lineno => 1, :name => 'first line!' }
45
+ @request << { :line_type => :test, :lineno => 4, :test_capture => 'testing' }
46
+ @request << { :line_type => :test, :lineno => 7, :test_capture => 'testing some more' }
47
+ @request << { :line_type => :last, :lineno => 10, :time => 0.03 }
48
+ end
49
+
50
+ it "should not be empty when multiple liness are added" do
51
+ @request.should_not be_empty
52
+ end
53
+
54
+ it "should be a completed request" do
55
+ @request.should be_completed
56
+ end
57
+
58
+ it "should recognize all line types" do
59
+ [:first, :test, :last].each { |type| @request.should =~ type }
60
+ end
61
+
62
+ it "should detect the correct field value" do
63
+ @request[:name].should == 'first line!'
64
+ @request[:time].should == 0.03
65
+ end
66
+
67
+ it "should detect the first matching field value" do
68
+ @request.first(:test_capture).should == 'testing'
69
+ end
70
+
71
+ it "should detect the every matching field value" do
72
+ @request.every(:test_capture).should == ['testing', "testing some more"]
73
+ end
74
+
75
+ it "should set the first_lineno for a request to the lowest lineno encountered" do
76
+ @request.first_lineno.should eql(1)
77
+ end
78
+
79
+ it "should set the first_lineno for a request if a line with a lower lineno is added" do
80
+ @request << { :line_type => :test, :lineno => 0 }
81
+ @request.first_lineno.should eql(0)
82
+ end
83
+
84
+ it "should set the last_lineno for a request to the highest encountered lineno" do
85
+ @request.last_lineno.should eql(10)
86
+ end
87
+
88
+ it "should not set the last_lineno for a request if a line with a lower lineno is added" do
89
+ @request << { :line_type => :test, :lineno => 7 }
90
+ @request.last_lineno.should eql(10)
91
+ end
92
+
93
+ it "should not have a timestamp if no such field is captured" do
94
+ @request.timestamp.should be_nil
95
+ end
96
+
97
+ it "should set return a timestamp field if such a field is captured" do
98
+ @request << { :line_type => :first, :lineno => 1, :name => 'first line!', :timestamp => Time.now}
99
+ @request.timestamp.should_not be_nil
100
+ end
101
+ end
102
+
103
+ context 'single line' do
104
+ # combined is both a header and a footer line
105
+ before(:each) { @request << { :line_type => :combined, :lineno => 1 } }
106
+
107
+ it "should be a completed request if the line is both header and footer" do
108
+ @request.should be_completed
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,119 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::Source::LogParser, :requests do
4
+
5
+ before(:each) do
6
+ @log_parser = RequestLogAnalyzer::Source::LogParser.new(testing_format)
7
+ end
8
+
9
+ it "should have multiple line definitions" do
10
+ @log_parser.file_format.line_definitions.length.should >= 2
11
+ end
12
+
13
+ it "should have a valid language" do
14
+ @log_parser.file_format.should be_valid
15
+ end
16
+
17
+ it "should set the :source for every parsed line" do
18
+ @log_parser.parse_file(log_fixture(:rails_22)) do |request|
19
+ request.lines.all? { |line| line[:source] == log_fixture(:rails_22) }.should be_true
20
+ end
21
+ end
22
+
23
+ it "should set the :lineno for every parsed line" do
24
+ @log_parser.parse_file(log_fixture(:rails_22)) do |request|
25
+ request.lines.all? { |line| line.has_key?(:lineno) }.should be_true
26
+ end
27
+ end
28
+
29
+ it "should parse more lines than requests" do
30
+ @log_parser.should_receive(:handle_request).with(an_instance_of(TestingFormat::Request)).twice
31
+ @log_parser.parse_file(log_fixture(:test_language_combined))
32
+ @log_parser.parsed_lines.should > 2
33
+ end
34
+
35
+ it "should parse requests spanned over multiple files" do
36
+ @log_parser.should_receive(:handle_request).with(an_instance_of(TestingFormat::Request)).once
37
+ @log_parser.parse_files([log_fixture(:multiple_files_1), log_fixture(:multiple_files_2)])
38
+ end
39
+
40
+ it "should parse all request values when spanned over multiple files" do
41
+ @log_parser.parse_files([log_fixture(:multiple_files_1), log_fixture(:multiple_files_2)]) do |request|
42
+ request.lines.should have(4).items
43
+ request[:request_no].should == 1
44
+ request[:test_capture].should == "Testing is amazing" # Note the custom converter
45
+ end
46
+ end
47
+
48
+ it "should parse a stream and find valid requests" do
49
+ io = File.new(log_fixture(:test_file_format), 'rb')
50
+ @log_parser.parse_io(io) do |request|
51
+ request.should be_kind_of(RequestLogAnalyzer::Request)
52
+ request.should =~ :test
53
+ request[:test_capture].should_not be_nil
54
+ end
55
+ io.close
56
+ end
57
+
58
+ it "should parse a request that only consists of one line" do
59
+ @log_parser.parse_file(log_fixture(:header_and_footer))
60
+ @log_parser.parsed_requests.should == 2
61
+ end
62
+ end
63
+
64
+ describe RequestLogAnalyzer::Source::LogParser, :warnings do
65
+
66
+ before(:each) do
67
+ @log_parser = RequestLogAnalyzer::Source::LogParser.new(testing_format, :parse_strategy => 'cautious')
68
+ end
69
+
70
+ it "should warn about teaser matching problems" do
71
+ @log_parser.should_receive(:warn).with(:teaser_check_failed, anything).exactly(5).times
72
+ @log_parser.parse_file(log_fixture(:test_file_format))
73
+ end
74
+
75
+ it "should warn about unmatching request headers and footers" do
76
+ @log_parser.should_receive(:warn).with(:unclosed_request, anything).at_least(1).times
77
+ @log_parser.should_receive(:warn).with(:no_current_request, anything).at_least(1).times
78
+ @log_parser.should_not_receive(:handle_request)
79
+ @log_parser.parse_file(log_fixture(:test_order))
80
+ end
81
+ end
82
+
83
+ describe RequestLogAnalyzer::Source::LogParser, :decompression do
84
+
85
+ before(:each) do
86
+ @log_parser = RequestLogAnalyzer::Source::LogParser.new(RequestLogAnalyzer::FileFormat::Rails.create)
87
+ end
88
+
89
+ it "should parse a rails gzipped log file" do
90
+ @log_parser.should_receive(:handle_request).once
91
+ @log_parser.parse_file(log_fixture(:decompression, "log.gz"))
92
+ @log_parser.parsed_lines.should > 0
93
+ end
94
+
95
+ it "should parse a rails tar gzipped log folder" do
96
+ @log_parser.should_receive(:handle_request).twice
97
+ @log_parser.parse_file(log_fixture(:decompression, "tar.gz"))
98
+ @log_parser.parsed_lines.should > 1
99
+ end
100
+
101
+ it "should parse a rails tar gzipped log folder" do
102
+ @log_parser.should_receive(:handle_request).twice
103
+ @log_parser.parse_file(log_fixture(:decompression, "tgz"))
104
+ @log_parser.parsed_lines.should > 1
105
+ end
106
+
107
+ it "should parse a rails bz2 zipped log file" do
108
+ @log_parser.should_receive(:handle_request).once
109
+ @log_parser.parse_file(log_fixture(:decompression, "log.bz2"))
110
+ @log_parser.parsed_lines.should > 0
111
+ end
112
+
113
+ it "should parse a rails zipped log file" do
114
+ @log_parser.should_receive(:handle_request).once
115
+ @log_parser.parse_file(log_fixture(:decompression, "log.zip"))
116
+ @log_parser.parsed_lines.should > 0
117
+ end
118
+
119
+ end
@@ -0,0 +1,49 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::Tracker::Duration do
4
+
5
+ describe '#report' do
6
+
7
+ before(:each) do
8
+ @tracker = RequestLogAnalyzer::Tracker::Duration.new(:category => :category, :value => :duration)
9
+ @tracker.prepare
10
+ end
11
+
12
+ it "should generate a report without errors when one category is present" do
13
+ @tracker.update(request(:category => 'a', :duration => 0.2))
14
+ lambda { @tracker.report(mock_output) }.should_not raise_error
15
+ end
16
+
17
+ it "should generate a report without errors when no category is present" do
18
+ lambda { @tracker.report(mock_output) }.should_not raise_error
19
+ end
20
+
21
+ it "should generate a report without errors when multiple categories are present" do
22
+ @tracker.update(request(:category => 'a', :duration => 0.2))
23
+ @tracker.update(request(:category => 'b', :duration => 0.2))
24
+ lambda { @tracker.report(mock_output) }.should_not raise_error
25
+ end
26
+
27
+ it "should generate a YAML output" do
28
+ @tracker.update(request(:category => 'a', :duration => 0.2))
29
+ @tracker.update(request(:category => 'b', :duration => 0.2))
30
+ @tracker.to_yaml_object.should == {"a"=>{:hits=>1, :min=>0.2, :mean=>0.2, :max=>0.2, :sum_of_squares=>0.0, :sum=>0.2}, "b"=>{:hits=>1, :min=>0.2, :mean=>0.2, :max=>0.2, :sum_of_squares=>0.0, :sum=>0.2}}
31
+ end
32
+ end
33
+
34
+ describe '#display_value' do
35
+ before(:each) { @tracker = RequestLogAnalyzer::Tracker::Duration.new(:category => :category, :value => :duration) }
36
+
37
+ it "should only display seconds when time < 60" do
38
+ @tracker.display_value(33.12).should == '33.12s'
39
+ end
40
+
41
+ it "should display minutes and wholeseconds when time > 60" do
42
+ @tracker.display_value(63.12).should == '1m03s'
43
+ end
44
+
45
+ it "should display minutes and wholeseconds when time > 60" do
46
+ @tracker.display_value(3601.12).should == '1h00m01s'
47
+ end
48
+ end
49
+ end