ngmoco-request-log-analyzer 1.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. data/.gitignore +10 -0
  2. data/DESIGN.rdoc +41 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +39 -0
  5. data/Rakefile +8 -0
  6. data/bin/request-log-analyzer +114 -0
  7. data/lib/cli/command_line_arguments.rb +301 -0
  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 +213 -0
  11. data/lib/cli/tools.rb +46 -0
  12. data/lib/request_log_analyzer.rb +44 -0
  13. data/lib/request_log_analyzer/aggregator.rb +49 -0
  14. data/lib/request_log_analyzer/aggregator/database_inserter.rb +83 -0
  15. data/lib/request_log_analyzer/aggregator/echo.rb +29 -0
  16. data/lib/request_log_analyzer/aggregator/summarizer.rb +175 -0
  17. data/lib/request_log_analyzer/controller.rb +332 -0
  18. data/lib/request_log_analyzer/database.rb +102 -0
  19. data/lib/request_log_analyzer/database/base.rb +115 -0
  20. data/lib/request_log_analyzer/database/connection.rb +38 -0
  21. data/lib/request_log_analyzer/database/request.rb +22 -0
  22. data/lib/request_log_analyzer/database/source.rb +13 -0
  23. data/lib/request_log_analyzer/database/warning.rb +14 -0
  24. data/lib/request_log_analyzer/file_format.rb +160 -0
  25. data/lib/request_log_analyzer/file_format/amazon_s3.rb +71 -0
  26. data/lib/request_log_analyzer/file_format/apache.rb +141 -0
  27. data/lib/request_log_analyzer/file_format/merb.rb +67 -0
  28. data/lib/request_log_analyzer/file_format/rack.rb +11 -0
  29. data/lib/request_log_analyzer/file_format/rails.rb +176 -0
  30. data/lib/request_log_analyzer/file_format/rails_development.rb +12 -0
  31. data/lib/request_log_analyzer/filter.rb +30 -0
  32. data/lib/request_log_analyzer/filter/anonymize.rb +39 -0
  33. data/lib/request_log_analyzer/filter/field.rb +42 -0
  34. data/lib/request_log_analyzer/filter/timespan.rb +45 -0
  35. data/lib/request_log_analyzer/line_definition.rb +111 -0
  36. data/lib/request_log_analyzer/log_processor.rb +99 -0
  37. data/lib/request_log_analyzer/mailer.rb +62 -0
  38. data/lib/request_log_analyzer/output.rb +113 -0
  39. data/lib/request_log_analyzer/output/fixed_width.rb +220 -0
  40. data/lib/request_log_analyzer/output/html.rb +184 -0
  41. data/lib/request_log_analyzer/request.rb +175 -0
  42. data/lib/request_log_analyzer/source.rb +72 -0
  43. data/lib/request_log_analyzer/source/database_loader.rb +87 -0
  44. data/lib/request_log_analyzer/source/log_parser.rb +274 -0
  45. data/lib/request_log_analyzer/tracker.rb +206 -0
  46. data/lib/request_log_analyzer/tracker/duration.rb +104 -0
  47. data/lib/request_log_analyzer/tracker/frequency.rb +95 -0
  48. data/lib/request_log_analyzer/tracker/hourly_spread.rb +107 -0
  49. data/lib/request_log_analyzer/tracker/timespan.rb +81 -0
  50. data/lib/request_log_analyzer/tracker/traffic.rb +106 -0
  51. data/request-log-analyzer.gemspec +40 -0
  52. data/spec/database.yml +23 -0
  53. data/spec/fixtures/apache_combined.log +5 -0
  54. data/spec/fixtures/apache_common.log +10 -0
  55. data/spec/fixtures/decompression.log +12 -0
  56. data/spec/fixtures/decompression.log.bz2 +0 -0
  57. data/spec/fixtures/decompression.log.gz +0 -0
  58. data/spec/fixtures/decompression.log.zip +0 -0
  59. data/spec/fixtures/decompression.tar.gz +0 -0
  60. data/spec/fixtures/decompression.tgz +0 -0
  61. data/spec/fixtures/header_and_footer.log +6 -0
  62. data/spec/fixtures/merb.log +84 -0
  63. data/spec/fixtures/merb_prefixed.log +9 -0
  64. data/spec/fixtures/multiple_files_1.log +5 -0
  65. data/spec/fixtures/multiple_files_2.log +2 -0
  66. data/spec/fixtures/rails.db +0 -0
  67. data/spec/fixtures/rails_1x.log +59 -0
  68. data/spec/fixtures/rails_22.log +12 -0
  69. data/spec/fixtures/rails_22_cached.log +10 -0
  70. data/spec/fixtures/rails_unordered.log +24 -0
  71. data/spec/fixtures/syslog_1x.log +5 -0
  72. data/spec/fixtures/test_file_format.log +13 -0
  73. data/spec/fixtures/test_language_combined.log +14 -0
  74. data/spec/fixtures/test_order.log +16 -0
  75. data/spec/integration/command_line_usage_spec.rb +84 -0
  76. data/spec/integration/munin_plugins_rails_spec.rb +58 -0
  77. data/spec/integration/scout_spec.rb +151 -0
  78. data/spec/lib/helpers.rb +52 -0
  79. data/spec/lib/macros.rb +18 -0
  80. data/spec/lib/matchers.rb +77 -0
  81. data/spec/lib/mocks.rb +76 -0
  82. data/spec/lib/testing_format.rb +46 -0
  83. data/spec/spec_helper.rb +24 -0
  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 +49 -0
  92. data/spec/unit/file_format/apache_format_spec.rb +203 -0
  93. data/spec/unit/file_format/file_format_api_spec.rb +69 -0
  94. data/spec/unit/file_format/line_definition_spec.rb +75 -0
  95. data/spec/unit/file_format/merb_format_spec.rb +52 -0
  96. data/spec/unit/file_format/rails_format_spec.rb +164 -0
  97. data/spec/unit/filter/anonymize_filter_spec.rb +21 -0
  98. data/spec/unit/filter/field_filter_spec.rb +66 -0
  99. data/spec/unit/filter/filter_spec.rb +17 -0
  100. data/spec/unit/filter/timespan_filter_spec.rb +58 -0
  101. data/spec/unit/mailer_spec.rb +30 -0
  102. data/spec/unit/request_spec.rb +111 -0
  103. data/spec/unit/source/log_parser_spec.rb +119 -0
  104. data/spec/unit/tracker/duration_tracker_spec.rb +130 -0
  105. data/spec/unit/tracker/frequency_tracker_spec.rb +88 -0
  106. data/spec/unit/tracker/hourly_spread_spec.rb +79 -0
  107. data/spec/unit/tracker/timespan_tracker_spec.rb +73 -0
  108. data/spec/unit/tracker/tracker_api_spec.rb +124 -0
  109. data/spec/unit/tracker/traffic_tracker_spec.rb +107 -0
  110. data/tasks/github-gem.rake +323 -0
  111. data/tasks/request_log_analyzer.rake +26 -0
  112. metadata +220 -0
@@ -0,0 +1,30 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::Mailer, 'mailer' do
4
+
5
+ it "should store printed data" do
6
+ @mailer = RequestLogAnalyzer::Mailer.new('alfa@beta.com', 'localhost', :debug => true)
7
+
8
+ @mailer << 'test1'
9
+ @mailer.puts 'test2'
10
+
11
+ @mailer.data.should eql(['test1', 'test2'])
12
+ end
13
+
14
+ it "should send mail" do
15
+ @mailer = RequestLogAnalyzer::Mailer.new('alfa@beta.com', 'localhost', :debug => true)
16
+
17
+ @mailer << 'test1'
18
+ @mailer.puts 'test2'
19
+
20
+ mail = @mailer.mail
21
+
22
+ mail[0].should include("contact@railsdoctors.com")
23
+ mail[0].should include("test1")
24
+ mail[0].should include("test2")
25
+
26
+ mail[1].should include("contact@railsdoctors.com")
27
+ mail[2].should include("alfa@beta.com")
28
+ end
29
+
30
+ 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), 'r')
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,130 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::Tracker::Duration do
4
+
5
+ context 'using a static category' do
6
+
7
+ before(:each) do
8
+ @tracker = RequestLogAnalyzer::Tracker::Duration.new(:duration => :duration, :category => :category)
9
+ @tracker.prepare
10
+ end
11
+
12
+ it "should create a category for every request using the category field" do
13
+ @tracker.update(request(:category => 'a', :duration => 0.2))
14
+ @tracker.categories.keys.should include('a')
15
+ end
16
+
17
+ it "should register a request as hit in the right category" do
18
+ @tracker.update(request(:category => 'a', :duration => 0.2))
19
+ @tracker.update(request(:category => 'b', :duration => 0.3))
20
+ @tracker.update(request(:category => 'b', :duration => 0.4))
21
+
22
+ @tracker.hits('a').should == 1
23
+ @tracker.hits('b').should == 2
24
+ end
25
+ end
26
+
27
+ context 'using a dynamic category' do
28
+ before(:each) do
29
+ @categorizer = Proc.new { |request| request[:duration] > 0.2 ? 'slow' : 'fast' }
30
+ @tracker = RequestLogAnalyzer::Tracker::Duration.new(:duration => :duration, :category => @categorizer)
31
+ @tracker.prepare
32
+ end
33
+
34
+ it "should use the categorizer to determine the right category" do
35
+ @tracker.update(request(:category => 'a', :duration => 0.2))
36
+ @tracker.update(request(:category => 'b', :duration => 0.3))
37
+ @tracker.update(request(:category => 'b', :duration => 0.4))
38
+
39
+ @tracker.hits('fast').should == 1
40
+ @tracker.hits('slow').should == 2
41
+ end
42
+ end
43
+
44
+ describe '#update' do
45
+
46
+ before(:each) do
47
+ @tracker = RequestLogAnalyzer::Tracker::Duration.new(:duration => :duration, :category => :category)
48
+ @tracker.prepare
49
+
50
+ @tracker.update(request(:category => 'a', :duration => 0.4))
51
+ @tracker.update(request(:category => 'a', :duration => 0.2))
52
+ @tracker.update(request(:category => 'a', :duration => 0.3))
53
+ end
54
+
55
+ it "should sum of the durations for a category correctly" do
56
+ @tracker.sum('a').should be_close(0.9, 0.000001)
57
+ end
58
+
59
+ it "should overall sum of the durations correctly" do
60
+ @tracker.sum_overall.should be_close(0.9, 0.000001)
61
+ end
62
+
63
+ it "should keep track of the minimum and maximum duration" do
64
+ @tracker.min('a').should == 0.2
65
+ @tracker.max('a').should == 0.4
66
+ end
67
+
68
+ it "should calculate the mean duration correctly" do
69
+ @tracker.mean('a').should be_close(0.3, 0.000001)
70
+ end
71
+
72
+ it "should calculate the overall mean duration correctly" do
73
+ @tracker.mean_overall.should be_close(0.3, 0.000001)
74
+ end
75
+
76
+ it "should calculate the duration variance correctly" do
77
+ @tracker.variance('a').should be_close(0.01, 0.000001)
78
+ end
79
+
80
+ it "should calculate the duration standard deviation correctly" do
81
+ @tracker.stddev('a').should be_close(0.1, 0.000001)
82
+ end
83
+ end
84
+
85
+ describe '#report' do
86
+
87
+ before(:each) do
88
+ @tracker = RequestLogAnalyzer::Tracker::Duration.new(:category => :category, :duration => :duration)
89
+ @tracker.prepare
90
+ end
91
+
92
+ it "should generate a report without errors when one category is present" do
93
+ @tracker.update(request(:category => 'a', :duration => 0.2))
94
+ lambda { @tracker.report(mock_output) }.should_not raise_error
95
+ end
96
+
97
+ it "should generate a report without errors when no category is present" do
98
+ lambda { @tracker.report(mock_output) }.should_not raise_error
99
+ end
100
+
101
+ it "should generate a report without errors when multiple categories are present" do
102
+ @tracker.update(request(:category => 'a', :duration => 0.2))
103
+ @tracker.update(request(:category => 'b', :duration => 0.2))
104
+ lambda { @tracker.report(mock_output) }.should_not raise_error
105
+ end
106
+
107
+ it "should generate a YAML output" do
108
+ @tracker.update(request(:category => 'a', :duration => 0.2))
109
+ @tracker.update(request(:category => 'b', :duration => 0.2))
110
+ @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}}
111
+ end
112
+ end
113
+
114
+ describe '#display_value' do
115
+ before(:each) { @tracker = RequestLogAnalyzer::Tracker::Duration.new(:category => :category, :duration => :duration) }
116
+
117
+ it "should only display seconds when time < 60" do
118
+ @tracker.display_value(33.12).should == '33.12s'
119
+ end
120
+
121
+ it "should display minutes and wholeseconds when time > 60" do
122
+ @tracker.display_value(63.12).should == '1m03s'
123
+ end
124
+
125
+ it "should display minutes and wholeseconds when time > 60" do
126
+ @tracker.display_value(3601.12).should == '1h00m01s'
127
+ end
128
+
129
+ end
130
+ end
@@ -0,0 +1,88 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::Tracker::Frequency do
4
+
5
+ context 'static category' do
6
+ before(:each) do
7
+ @tracker = RequestLogAnalyzer::Tracker::Frequency.new(:category => :category)
8
+ @tracker.prepare
9
+ end
10
+
11
+ it "should register a request in the right category" do
12
+ @tracker.update(request(:category => 'a', :blah => 0.2))
13
+ @tracker.categories.should include('a')
14
+ end
15
+
16
+ it "should register a request in the right category" do
17
+ @tracker.update(request(:category => 'a', :blah => 0.2))
18
+ @tracker.update(request(:category => 'b', :blah => 0.2))
19
+ @tracker.update(request(:category => 'b', :blah => 0.2))
20
+
21
+ @tracker.frequency('a').should == 1
22
+ @tracker.frequency('b').should == 2
23
+ @tracker.overall_frequency.should == 3
24
+ end
25
+
26
+ it "should sort correctly by frequency" do
27
+ @tracker.update(request(:category => 'a', :blah => 0.2))
28
+ @tracker.update(request(:category => 'b', :blah => 0.2))
29
+ @tracker.update(request(:category => 'b', :blah => 0.2))
30
+
31
+ @tracker.sorted_by_frequency.should == [['b', 2], ['a', 1]]
32
+ end
33
+ end
34
+
35
+
36
+ context 'dynamic category' do
37
+ before(:each) do
38
+ @categorizer = Proc.new { |request| request[:duration] > 0.2 ? 'slow' : 'fast' }
39
+ @tracker = RequestLogAnalyzer::Tracker::Frequency.new(:category => @categorizer)
40
+ @tracker.prepare
41
+ end
42
+
43
+ it "should use the categorizer to determine the right category" do
44
+ @tracker.update(request(:category => 'a', :duration => 0.2))
45
+ @tracker.update(request(:category => 'b', :duration => 0.3))
46
+ @tracker.update(request(:category => 'b', :duration => 0.4))
47
+
48
+ @tracker.frequency('fast').should == 1
49
+ @tracker.frequency('slow').should == 2
50
+ @tracker.frequency('moderate').should == 0
51
+ end
52
+ end
53
+
54
+ describe '#report' do
55
+ before(:each) do
56
+ @tracker = RequestLogAnalyzer::Tracker::Frequency.new(:category => :category)
57
+ @tracker.prepare
58
+ end
59
+
60
+ it "should generate a report without errors when one category is present" do
61
+ @tracker.update(request(:category => 'a', :blah => 0.2))
62
+ lambda { @tracker.report(mock_output) }.should_not raise_error
63
+ end
64
+
65
+ it "should generate a report without errors when no category is present" do
66
+ lambda { @tracker.report(mock_output) }.should_not raise_error
67
+ end
68
+
69
+ it "should generate a report without errors when multiple categories are present" do
70
+ @tracker.update(request(:category => 'a', :blah => 0.2))
71
+ @tracker.update(request(:category => 'b', :blah => 0.2))
72
+ lambda { @tracker.report(mock_output) }.should_not raise_error
73
+ end
74
+ end
75
+
76
+ describe '#to_yaml_object' do
77
+ before(:each) do
78
+ @tracker = RequestLogAnalyzer::Tracker::Frequency.new(:category => :category)
79
+ @tracker.prepare
80
+ end
81
+
82
+ it "should generate a YAML output" do
83
+ @tracker.update(request(:category => 'a', :blah => 0.2))
84
+ @tracker.update(request(:category => 'b', :blah => 0.2))
85
+ @tracker.to_yaml_object.should == { "a" => 1, "b" => 1 }
86
+ end
87
+ end
88
+ end