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,75 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::LineDefinition do
4
+
5
+ before(:each) do
6
+ @line_definition = RequestLogAnalyzer::LineDefinition.new(:test, {
7
+ :teaser => /Testing /,
8
+ :regexp => /Testing (\w+), tries\: (\d+)/,
9
+ :captures => [{ :name => :what, :type => :string }, { :name => :tries, :type => :integer }]
10
+ })
11
+ end
12
+
13
+ describe '#matches' do
14
+
15
+ it "should return false on an unmatching line" do
16
+ @line_definition.matches("nonmatching").should be_false
17
+ end
18
+
19
+ it "should return false when only the teaser matches" do
20
+ @line_definition.matches("Testing LineDefinition").should be_false
21
+ end
22
+
23
+ it "should parse a line and capture the expected values" do
24
+ @line_definition.matches("Testing LineDefinition, tries: 123").should == {:line_definition => @line_definition, :captures => ['LineDefinition', '123'] }
25
+ end
26
+
27
+ it "should know which names it can capture" do
28
+ @line_definition.captures?(:what).should be_true
29
+ @line_definition.captures?(:tries).should be_true
30
+ @line_definition.captures?(:bogus).should be_false
31
+ end
32
+
33
+ end
34
+
35
+ describe '#convert_captured_values' do
36
+
37
+ before(:each) do
38
+ @request = mock('request')
39
+ @request.stub!(:convert_value).and_return('foo')
40
+ end
41
+
42
+ it "should call convert_value for every captured value" do
43
+ @request.should_receive(:convert_value).twice
44
+ @line_definition.convert_captured_values(['test', '123'], @request)
45
+ end
46
+
47
+ it "should set the converted values" do
48
+ @line_definition.convert_captured_values(['test', '123'], @request).should == {:what => 'foo', :tries => 'foo'}
49
+ end
50
+
51
+ context 'when using :provides option' do
52
+ before(:each) do
53
+ @ld = RequestLogAnalyzer::LineDefinition.new(:test, :regexp => /Hash\: (\{.+\})/,
54
+ :captures => [{ :name => :hash, :type => :hash, :provides => {:bar => :string}}])
55
+
56
+ @request = mock('request')
57
+
58
+ @request.stub!(:convert_value).with("{:bar=>'baz'}", anything).and_return(:bar => 'baz')
59
+ @request.stub!(:convert_value).with('baz', anything).and_return('foo')
60
+ end
61
+
62
+ it "should call Request#convert_value for the initial hash and the value in the hash" do
63
+ @request.should_receive(:convert_value).with("{:bar=>'baz'}", anything).and_return(:bar => 'baz')
64
+ @request.should_receive(:convert_value).with("baz", anything)
65
+ @ld.convert_captured_values(["{:bar=>'baz'}"], @request)
66
+ end
67
+
68
+ it "should set the provides fields" do
69
+ # The captures field must be set and converted as well
70
+ @ld.convert_captured_values(["{:bar=>'baz'}"], @request)[:bar].should eql('foo')
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,52 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::FileFormat::Merb do
4
+
5
+ it "should be a valid file format" do
6
+ RequestLogAnalyzer::FileFormat.load(:merb).should be_valid
7
+ end
8
+
9
+ describe '#parse_line' do
10
+ before(:each) do
11
+ @file_format = RequestLogAnalyzer::FileFormat.load(:merb)
12
+ end
13
+
14
+ it "should parse a :started line correctly" do
15
+ line = '~ Started request handling: Fri Aug 29 11:10:23 +0200 2008'
16
+ @file_format.should parse_line(line).as(:started).and_capture(:timestamp => 20080829111023)
17
+ end
18
+
19
+ it "should parse a prefixed :started line correctly" do
20
+ line = '~ Aug 31 18:35:24 typekit-web001 merb: ~ Started request handling: Mon Aug 31 18:35:25 +0000 2009'
21
+ @file_format.should parse_line(line).as(:started).and_capture(:timestamp => 20090831183525)
22
+ end
23
+
24
+ it "should parse a :params line correctly" do
25
+ line = '~ Params: {"_method"=>"delete", "authenticity_token"=>"[FILTERED]", "action"=>"delete", "controller"=>"session"}'
26
+ @file_format.should parse_line(line).as(:params).and_capture(:controller => 'session', :action => 'delete', :namespace => nil)
27
+ end
28
+
29
+ it "should parse a :completed line correctly" do
30
+ line = '~ {:dispatch_time=>0.006117, :after_filters_time=>6.1e-05, :before_filters_time=>0.000712, :action_time=>0.005833}'
31
+ @file_format.should parse_line(line).as(:completed).and_capture(:dispatch_time => 0.006117,
32
+ :before_filters_time => 0.000712, :action_time => 0.005833, :after_filters_time => 6.1e-05)
33
+ end
34
+ end
35
+
36
+ describe '#parse_io' do
37
+ before(:each) do
38
+ @log_parser = RequestLogAnalyzer::Source::LogParser.new(RequestLogAnalyzer::FileFormat.load(:merb))
39
+ end
40
+
41
+ it "should parse a stream and find valid Merb requests" do
42
+ @log_parser.parse_file(log_fixture(:merb)) do |request|
43
+ request.should be_kind_of(RequestLogAnalyzer::FileFormat::Merb::Request)
44
+ end
45
+ end
46
+
47
+ it "should find 11 completed requests" do
48
+ @log_parser.should_receive(:handle_request).exactly(11).times
49
+ @log_parser.parse_file(log_fixture(:merb))
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,154 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::FileFormat::Mysql do
4
+
5
+ it "should be a valid file format" do
6
+ RequestLogAnalyzer::FileFormat.load(:mysql).should be_valid
7
+ end
8
+
9
+ describe '#parse_line' do
10
+ before(:each) do
11
+ @file_format = RequestLogAnalyzer::FileFormat.load(:mysql)
12
+ end
13
+
14
+ it "should parse a :time line correctly" do
15
+ line = '# Time: 091112 8:13:56'
16
+ @file_format.should parse_line(line).as(:time).and_capture(:timestamp => 20091112081356)
17
+ end
18
+
19
+ it "should parse a :user_host line correctly with IP present" do
20
+ line = '# User@Host: admin[admin] @ db1 [10.0.0.1]'
21
+ @file_format.should parse_line(line).as(:user_host).and_capture(:user => "admin", :host => 'db1', :ip => '10.0.0.1')
22
+ end
23
+
24
+ it "should parse a :user_host line correctly without a host" do
25
+ line = '# User@Host: admin[admin] @ [10.0.0.1]'
26
+ @file_format.should parse_line(line).as(:user_host).and_capture(:user => "admin", :host => '', :ip => '10.0.0.1')
27
+ end
28
+
29
+ it "should parse a :user_host line correctly with IP absent" do
30
+ line = '# User@Host: root[root] @ localhost []'
31
+ @file_format.should parse_line(line).as(:user_host).and_capture(:user => "root", :host => 'localhost', :ip => "")
32
+ end
33
+
34
+ it "should parse a :query_statistics line" do
35
+ line = '# Query_time: 10 Lock_time: 0 Rows_sent: 1191307 Rows_examined: 1191307'
36
+ @file_format.should parse_line(line).as(:query_statistics).and_capture(:query_time => 10.0,
37
+ :lock_time => 0.0, :rows_sent => 1191307, :rows_examined => 1191307)
38
+ end
39
+
40
+ it "should parse a :query_statistics line with floating point durations" do
41
+ line = '# Query_time: 10.00000 Lock_time: 0.00000 Rows_sent: 1191307 Rows_examined: 1191307'
42
+ @file_format.should parse_line(line).as(:query_statistics).and_capture(:query_time => 10.0,
43
+ :lock_time => 0.0, :rows_sent => 1191307, :rows_examined => 1191307)
44
+ end
45
+
46
+ it "should parse a :query_part line" do
47
+ line = ' AND clients.index > 0'
48
+ @file_format.should parse_line(line).as(:query_part).and_capture(:query_fragment => line)
49
+ end
50
+
51
+ it "should parse a final :query line" do
52
+ line = 'SELECT /*!40001 SQL_NO_CACHE */ * FROM `events`; '
53
+ @file_format.should parse_line(line).as(:query).and_capture(:query =>
54
+ 'SELECT /*!:int SQL_NO_CACHE */ * FROM events')
55
+ end
56
+
57
+ it "should parse a :use_database line" do
58
+ line = 'use db;'
59
+ @file_format.should parse_line(line).as(:use_database).and_capture(:database => 'db')
60
+ end
61
+
62
+ it "should not parse a SET timestamp line" do
63
+ line = 'SET timestamp=1250651725;'
64
+ @file_format.should_not parse_line(line)
65
+ end
66
+
67
+ it "should not parse a SET insert_id line" do
68
+ line = 'SET insert_id=1250651725;'
69
+ @file_format.should_not parse_line(line)
70
+ end
71
+
72
+ it "should not parse a SET timestamp, insert_id line" do
73
+ line = 'SET timestamp=1250651725, insert_id=45674;'
74
+ @file_format.should_not parse_line(line)
75
+ end
76
+
77
+
78
+ end
79
+
80
+ describe '#parse_io' do
81
+ before(:each) do
82
+ @log_parser = RequestLogAnalyzer::Source::LogParser.new(RequestLogAnalyzer::FileFormat.load(:mysql))
83
+ end
84
+
85
+ it "should parse a single line query entry correctly" do
86
+ fixture = <<EOS
87
+ # Time: 091112 18:13:56
88
+ # User@Host: admin[admin] @ db1 [10.0.0.1]
89
+ # Query_time: 10 Lock_time: 0 Rows_sent: 1191307 Rows_examined: 1191307
90
+ SELECT /*!40001 SQL_NO_CACHE */ * FROM `events`;
91
+ EOS
92
+ @log_parser.parse_io(StringIO.new(fixture)) do |request|
93
+ request.should be_kind_of(RequestLogAnalyzer::FileFormat::Mysql::Request)
94
+ request[:query].should == 'SELECT /*!:int SQL_NO_CACHE */ * FROM events'
95
+ end
96
+ end
97
+
98
+ it "should parse a multiline query entry correctly" do
99
+ fixture = <<EOS
100
+ # Time: 091112 18:13:56
101
+ # User@Host: admin[admin] @ db1 [10.0.0.1]
102
+ # Query_time: 10 Lock_time: 0 Rows_sent: 1191307 Rows_examined: 1191307
103
+ SELECT * FROM `clients` WHERE (1=1
104
+ AND clients.valid_from < '2009-12-05' AND (clients.valid_to IS NULL or clients.valid_to > '2009-11-20')
105
+ AND clients.index > 0
106
+ ) AND (clients.deleted_at IS NULL);
107
+ EOS
108
+ @log_parser.parse_io(StringIO.new(fixture)) do |request|
109
+ request.should be_kind_of(RequestLogAnalyzer::FileFormat::Mysql::Request)
110
+ request[:query].should == "SELECT * FROM clients WHERE (:int=:int AND clients.valid_from < :date AND (clients.valid_to IS NULL or clients.valid_to > :date) AND clients.index > :int ) AND (clients.deleted_at IS NULL)"
111
+ end
112
+ end
113
+
114
+ it "should parse a request without timestamp correctly" do
115
+ fixture = <<EOS
116
+ # User@Host: admin[admin] @ db1 [10.0.0.1]
117
+ # Query_time: 10 Lock_time: 0 Rows_sent: 1191307 Rows_examined: 1191307
118
+ SELECT /*!40001 SQL_NO_CACHE */ * FROM `events`;
119
+ EOS
120
+ request_counter.should_receive(:hit!).once
121
+ @log_parser.should_not_receive(:warn)
122
+
123
+ @log_parser.parse_io(StringIO.new(fixture)) do |request|
124
+ request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Mysql::Request) && request.completed?
125
+ end
126
+ end
127
+
128
+ it "should parse a query with context information correctly" do
129
+ fixture = <<EOS
130
+ # Time: 091112 18:13:56
131
+ # User@Host: admin[admin] @ db1 [10.0.0.1]
132
+ # Query_time: 10 Lock_time: 0 Rows_sent: 1191307 Rows_examined: 1191307
133
+ use database_name;
134
+ SET timestamp=4324342342423, insert_id = 224253443;
135
+ SELECT /*!40001 SQL_NO_CACHE */ * FROM `events`;
136
+ EOS
137
+
138
+ request_counter.should_receive(:hit!).once
139
+ @log_parser.should_not_receive(:warn)
140
+
141
+ @log_parser.parse_io(StringIO.new(fixture)) do |request|
142
+ request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Mysql::Request) && request.completed?
143
+ request[:query].should == 'SELECT /*!:int SQL_NO_CACHE */ * FROM events'
144
+ end
145
+
146
+ end
147
+
148
+
149
+ it "should find 26 completed sloq query entries" do
150
+ @log_parser.should_receive(:handle_request).exactly(26).times
151
+ @log_parser.parse_file(log_fixture(:mysql_slow_query))
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,65 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::FileFormat::Postgresql do
4
+
5
+ it "should be a valid file format" do
6
+ RequestLogAnalyzer::FileFormat.load(:Postgresql).should be_valid
7
+ end
8
+
9
+ describe '#parse_line' do
10
+ before(:each) do
11
+ @file_format = RequestLogAnalyzer::FileFormat.load(:Postgresql)
12
+ end
13
+
14
+ it "should parse a :query line correctly" do
15
+ line = '2004-05-07 11:58:36 LOG: query: SELECT plugin_id, plugin_name FROM plugins'
16
+ @file_format.should parse_line(line).as(:query).and_capture(:timestamp => 20040507115836, :query_fragment => 'SELECT plugin_id, plugin_name FROM plugins')
17
+ end
18
+
19
+ it "should parse a :query_fragment line correctly" do
20
+ line = ' groups.type_id,users.user_name,users.realname,'
21
+ @file_format.should parse_line(line).as(:query_fragment).and_capture(:query_fragment => "groups.type_id,users.user_name,users.realname,")
22
+ end
23
+
24
+ it "should parse a :duration line correctly" do
25
+ line = '2004-05-07 11:58:36 LOG: duration: 0.002612 sec'
26
+ @file_format.should parse_line(line).as(:duration).and_capture(:query_time => 0.002612)
27
+ end
28
+ end
29
+
30
+ describe '#parse_io' do
31
+ before(:each) do
32
+ @log_parser = RequestLogAnalyzer::Source::LogParser.new(RequestLogAnalyzer::FileFormat.load(:postgresql))
33
+ end
34
+
35
+ it "should parse a multiline query entry correctly" do
36
+ fixture = <<EOS
37
+ 2004-05-07 11:58:22 LOG: query: SELECT groups.group_name,groups.unix_group_name,
38
+ groups.type_id,users.user_name,users.realname,
39
+ news_bytes.forum_id,news_bytes.summary,news_bytes.post_date,news_bytes.details
40
+ FROM users,news_bytes,groups
41
+ WHERE news_bytes.group_id='98' AND news_bytes.is_approved <> '4'
42
+ AND users.user_id=news_bytes.submitted_by
43
+ AND news_bytes.group_id=groups.group_id
44
+ ORDER BY post_date DESC LIMIT 10 OFFSET 0
45
+ 2004-05-07 11:58:22 LOG: duration: 0.002680 sec
46
+ EOS
47
+ @log_parser.parse_io(StringIO.new(fixture)) do |request|
48
+ request.should be_kind_of(RequestLogAnalyzer::FileFormat::Postgresql::Request)
49
+ request[:query].should == "SELECT groups.group_name,groups.unix_group_name, groups.type_id,users.user_name,users.realname, news_bytes.forum_id,news_bytes.summary,news_bytes.post_date,news_bytes.details FROM users,news_bytes,groups WHERE news_bytes.group_id=:string AND news_bytes.is_approved <> :string AND users.user_id=news_bytes.submitted_by AND news_bytes.group_id=groups.group_id ORDER BY post_date DESC LIMIT :int OFFSET :int"
50
+ end
51
+ end
52
+
53
+ it "should parse a dualline query entry correctly" do
54
+ fixture = <<EOS
55
+ 2004-05-07 11:58:36 LOG: query: SELECT type, count FROM project_sums_agg WHERE group_id=59
56
+ 2004-05-07 11:58:36 LOG: duration: 0.001197 sec
57
+ EOS
58
+ @log_parser.parse_io(StringIO.new(fixture)) do |request|
59
+ request.should be_kind_of(RequestLogAnalyzer::FileFormat::Postgresql::Request)
60
+ request[:query].should == "SELECT type, count FROM project_sums_agg WHERE group_id=:int"
61
+ end
62
+ end
63
+ end
64
+ end
65
+
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::FileFormat::Rack do
4
+
5
+ context '"Sinatra" access log parsing' do
6
+
7
+ before(:each) do
8
+ @file_format = RequestLogAnalyzer::FileFormat.load(:rack)
9
+ @log_parser = RequestLogAnalyzer::Source::LogParser.new(@file_format)
10
+ @sample = '127.0.0.1 - - [23/Nov/2009 21:47:47] "GET /css/stylesheet.css HTTP/1.1" 200 3782 0.0024'
11
+ end
12
+
13
+ it "should be a valid file format" do
14
+ @file_format.should be_valid
15
+ end
16
+
17
+ it "should parse access lines and capture all of its fields" do
18
+ @file_format.should have_line_definition(:access).capturing(:remote_host, :timestamp, :http_method, :path, :http_version,
19
+ :http_status, :bytes_sent, :duration)
20
+ end
21
+
22
+ it "should match the sample line" do
23
+ @file_format.parse_line(@sample).should include(:line_definition, :captures)
24
+ end
25
+
26
+ it "should not match a nonsense line" do
27
+ @file_format.parse_line('== Sinatra/0.9.4 has taken the stage on 4567 for development with backup from Mongrel').should be_nil
28
+ end
29
+
30
+ it "should parse the sample fields correctly" do
31
+ match = @file_format.parse_line(@sample)
32
+ match.should_not be_nil
33
+ end
34
+
35
+ it "should parse and convert the sample fields correctly" do
36
+ @log_parser.parse_io(StringIO.new(@sample)) do |request|
37
+ request[:remote_host].should == '127.0.0.1'
38
+ request[:timestamp].should == 20091123214747
39
+ request[:http_method].should == 'GET'
40
+ request[:path].should == '/css/stylesheet.css'
41
+ request[:http_version].should == '1.1'
42
+ request[:http_status].should == 200
43
+ request[:bytes_sent].should == 3782
44
+ request[:duration].should == 0.0024
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,120 @@
1
+ require 'spec_helper'
2
+
3
+ describe RequestLogAnalyzer::FileFormat::Rails do
4
+
5
+ it "should be a valid file format" do
6
+ RequestLogAnalyzer::FileFormat.load(:rails3).should be_valid
7
+ end
8
+
9
+ describe '#parse_line' do
10
+ before(:each) { @file_format = RequestLogAnalyzer::FileFormat.load(:rails3) }
11
+
12
+ it "should parse :started lines correctly" do
13
+ line = 'Started GET "/queries" for 127.0.0.1 at 2010-02-25 16:15:18'
14
+ @file_format.should parse_line(line).as(:started).and_capture(:method => 'GET',
15
+ :path => '/queries', :ip => '127.0.0.1', :timestamp => 20100225161518)
16
+ end
17
+
18
+ it "should parse :processing lines correctly" do
19
+ line = ' Processing by QueriesController#index as HTML'
20
+ @file_format.should parse_line(line).as(:processing).and_capture(
21
+ :controller => 'QueriesController', :action => 'index', :format => 'HTML')
22
+ end
23
+
24
+
25
+ # it "should parse beta :completed lines correctly" do
26
+ # line = 'Completed in 9ms (Views: 4.9ms | ActiveRecord: 0.5ms) with 200'
27
+ # @file_format.should parse_line(line).as(:completed).and_capture(
28
+ # :duration => 0.009, :status => 200)
29
+ # end
30
+
31
+ it "should parse :completed lines correctly" do
32
+ line = 'Completed 200 OK in 170ms (Views: 78.4ms | ActiveRecord: 48.2ms)'
33
+ @file_format.should parse_line(line).as(:completed).and_capture(
34
+ :duration => 0.170, :status => 200)
35
+ end
36
+
37
+ it "should pase :failure lines correctly" do
38
+ 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:"
39
+ @file_format.should parse_line(line).as(:failure).and_capture(:line => 3,
40
+ :error => 'ActionView::Template::Error',
41
+ :message => "undefined local variable or method `field' for #<Class>",
42
+ :file => '/Users/willem/Code/warehouse/app/views/queries/execute.csv.erb')
43
+ end
44
+ end
45
+
46
+ describe '#parse_io' do
47
+ before(:each) do
48
+ @log_parser = RequestLogAnalyzer::Source::LogParser.new(RequestLogAnalyzer::FileFormat.load(:rails3))
49
+ end
50
+
51
+ it "should parse a successful request correctly" do
52
+ log = <<-EOLOG
53
+ Started GET "/" for 127.0.0.1 at 2010-03-19 06:40:41
54
+ Processing by QueriesController#index as HTML
55
+ SQL (16.3ms) SHOW TABLES
56
+ Query Load (32.0ms) SELECT `queries`.* FROM `queries`
57
+ Rendered queries/index.html.erb within layouts/default (40.9ms)
58
+ Completed 200 OK in 170ms (Views: 78.4ms | ActiveRecord: 48.2ms)
59
+ EOLOG
60
+
61
+ request_counter.should_receive(:hit!).once
62
+ @log_parser.should_not_receive(:warn)
63
+
64
+ @log_parser.parse_io(StringIO.new(log)) do |request|
65
+ request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Rails3::Request) && request.completed?
66
+ end
67
+ end
68
+
69
+ it "should parse an unroutable request correctly" do
70
+ log = <<-EOLOG
71
+ Started GET "/404" for 127.0.0.1 at 2010-03-19 06:40:57
72
+
73
+ ActionController::RoutingError (No route matches "/404"):
74
+
75
+
76
+ Rendered /Users/rails/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb within rescues/layout (1.0ms)
77
+
78
+ EOLOG
79
+
80
+ request_counter.should_receive(:hit!).once
81
+ @log_parser.should_not_receive(:warn)
82
+
83
+ @log_parser.parse_io(StringIO.new(log)) do |request|
84
+ request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Rails3::Request) && request.completed?
85
+ end
86
+ end
87
+
88
+ it "should parse a failing request correctly" do
89
+ log = <<-EOLOG
90
+ Started POST "/queries/397638749/execute.csv" for 127.0.0.1 at 2010-03-01 18:44:33
91
+ Processing by QueriesController#execute as CSV
92
+ Parameters: {"commit"=>"Run query", "authenticity_token"=>"pz9WcxkcrlG/43eg6BgSAnJL7yIsaffuHbYxPHUsUzQ=", "id"=>"397638749"}
93
+
94
+ ActionView::Template::Error (undefined local variable or method `field' for #<Class>) on line #3 of /Users/application/app/views/queries/execute.csv.erb:
95
+ 1: <%=raw @result.fields.map { |f| f.humanize.to_json }.join(',') %>
96
+ 2: <% @result.each do |record| %>
97
+ 3: <%=raw @result.fields.map { |f| record[field].to_s }.join(",") %>
98
+ 4: <% end %>
99
+
100
+ app/views/queries/execute.csv.erb:3:in `_render_template__652100315_2182241460_0'
101
+ app/views/queries/execute.csv.erb:3:in `map'
102
+ app/views/queries/execute.csv.erb:3:in `_render_template__652100315_2182241460_0'
103
+ app/views/queries/execute.csv.erb:2:in `_render_template__652100315_2182241460_0'
104
+ app/controllers/queries_controller.rb:34:in `execute'
105
+
106
+ Rendered /rails/actionpack-3.0.0.beta/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.0ms)
107
+ Rendered /rails/actionpack-3.0.0.beta/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (9.7ms)
108
+ Rendered /rails/actionpack-3.0.0.beta/lib/action_dispatch/middleware/templates/rescues/template_error.erb within /Users/willem/.rvm/gems/ruby-1.8.7-p248/gems/actionpack-3.0.0.beta/lib/action_dispatch/middleware/templates/rescues/layout.erb (20.4ms)
109
+
110
+ EOLOG
111
+
112
+ request_counter.should_receive(:hit!).once
113
+ @log_parser.should_not_receive(:warn)
114
+
115
+ @log_parser.parse_io(StringIO.new(log)) do |request|
116
+ request_counter.hit! if request.kind_of?(RequestLogAnalyzer::FileFormat::Rails3::Request) && request.completed?
117
+ end
118
+ end
119
+ end
120
+ end