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.
- data/.gitignore +10 -0
- data/DESIGN.rdoc +41 -0
- data/LICENSE +4 -4
- data/README.rdoc +38 -0
- data/Rakefile +6 -3
- data/bin/request-log-analyzer +70 -72
- data/lib/cli/command_line_arguments.rb +53 -53
- data/lib/cli/database_console.rb +26 -0
- data/lib/cli/database_console_init.rb +43 -0
- data/lib/cli/progressbar.rb +166 -189
- data/lib/cli/tools.rb +49 -0
- data/lib/request_log_analyzer/aggregator/database_inserter.rb +83 -0
- data/lib/request_log_analyzer/aggregator/echo.rb +17 -12
- data/lib/request_log_analyzer/aggregator/summarizer.rb +101 -63
- data/lib/request_log_analyzer/{aggregator/base.rb → aggregator.rb} +17 -13
- data/lib/request_log_analyzer/controller.rb +251 -98
- data/lib/request_log_analyzer/database/base.rb +114 -0
- data/lib/request_log_analyzer/database/connection.rb +38 -0
- data/lib/request_log_analyzer/database/request.rb +22 -0
- data/lib/request_log_analyzer/database/source.rb +13 -0
- data/lib/request_log_analyzer/database/warning.rb +14 -0
- data/lib/request_log_analyzer/database.rb +102 -0
- data/lib/request_log_analyzer/file_format/amazon_s3.rb +74 -0
- data/lib/request_log_analyzer/file_format/apache.rb +147 -0
- data/lib/request_log_analyzer/file_format/delayed_job.rb +55 -0
- data/lib/request_log_analyzer/file_format/merb.rb +65 -29
- data/lib/request_log_analyzer/file_format/mysql.rb +101 -0
- data/lib/request_log_analyzer/file_format/postgresql.rb +68 -0
- data/lib/request_log_analyzer/file_format/rack.rb +9 -0
- data/lib/request_log_analyzer/file_format/rails.rb +164 -78
- data/lib/request_log_analyzer/file_format/rails3.rb +86 -0
- data/lib/request_log_analyzer/file_format/rails_development.rb +12 -0
- data/lib/request_log_analyzer/file_format.rb +252 -58
- data/lib/request_log_analyzer/filter/anonymize.rb +39 -0
- data/lib/request_log_analyzer/filter/field.rb +19 -13
- data/lib/request_log_analyzer/filter/timespan.rb +25 -12
- data/lib/request_log_analyzer/filter.rb +30 -0
- data/lib/request_log_analyzer/line_definition.rb +69 -96
- data/lib/request_log_analyzer/log_processor.rb +31 -53
- data/lib/request_log_analyzer/mailer.rb +65 -0
- data/lib/request_log_analyzer/output/fancy_html.rb +49 -0
- data/lib/request_log_analyzer/output/fixed_width.rb +220 -0
- data/lib/request_log_analyzer/output/html.rb +187 -0
- data/lib/request_log_analyzer/output.rb +117 -0
- data/lib/request_log_analyzer/request.rb +125 -40
- data/lib/request_log_analyzer/source/database_loader.rb +87 -0
- data/lib/request_log_analyzer/source/log_parser.rb +297 -0
- data/lib/request_log_analyzer/source.rb +72 -0
- data/lib/request_log_analyzer/tracker/duration.rb +28 -64
- data/lib/request_log_analyzer/tracker/frequency.rb +108 -0
- data/lib/request_log_analyzer/tracker/hourly_spread.rb +76 -49
- data/lib/request_log_analyzer/tracker/numeric_value.rb +223 -0
- data/lib/request_log_analyzer/tracker/timespan.rb +54 -27
- data/lib/request_log_analyzer/tracker/traffic.rb +40 -0
- data/lib/request_log_analyzer/tracker.rb +101 -0
- data/lib/request_log_analyzer.rb +43 -13
- data/request-log-analyzer.gemspec +41 -0
- data/spec/database.yml +23 -0
- data/spec/fixtures/apache_combined.log +5 -0
- data/spec/fixtures/apache_common.log +10 -0
- data/spec/fixtures/decompression.log +12 -0
- data/spec/fixtures/decompression.log.bz2 +0 -0
- data/spec/fixtures/decompression.log.gz +0 -0
- data/spec/fixtures/decompression.log.zip +0 -0
- data/spec/fixtures/decompression.tar.gz +0 -0
- data/spec/fixtures/decompression.tgz +0 -0
- data/spec/fixtures/header_and_footer.log +6 -0
- data/spec/fixtures/merb_prefixed.log +9 -0
- data/spec/fixtures/mysql_slow_query.log +110 -0
- data/spec/fixtures/postgresql.log +2980 -0
- data/spec/fixtures/rails.db +0 -0
- data/spec/fixtures/rails_22.log +1 -1
- data/spec/fixtures/sinatra.log +99 -0
- data/spec/integration/command_line_usage_spec.rb +84 -0
- data/spec/integration/mailer_spec.rb +179 -0
- data/spec/integration/munin_plugins_rails_spec.rb +58 -0
- data/spec/integration/scout_spec.rb +152 -0
- data/spec/lib/helpers.rb +72 -0
- data/spec/lib/macros.rb +18 -0
- data/spec/lib/matchers.rb +77 -0
- data/spec/lib/mocks.rb +77 -0
- data/spec/lib/testing_format.rb +46 -0
- data/spec/spec_helper.rb +16 -59
- data/spec/unit/aggregator/database_inserter_spec.rb +93 -0
- data/spec/unit/aggregator/summarizer_spec.rb +26 -0
- data/spec/unit/controller/controller_spec.rb +41 -0
- data/spec/unit/controller/log_processor_spec.rb +18 -0
- data/spec/unit/database/base_class_spec.rb +183 -0
- data/spec/unit/database/connection_spec.rb +34 -0
- data/spec/unit/database/database_spec.rb +133 -0
- data/spec/unit/file_format/amazon_s3_format_spec.rb +67 -0
- data/spec/unit/file_format/apache_format_spec.rb +203 -0
- data/spec/unit/file_format/common_regular_expressions_spec.rb +53 -0
- data/spec/unit/file_format/delayed_job_format_spec.rb +83 -0
- data/spec/unit/file_format/file_format_api_spec.rb +69 -0
- data/spec/unit/file_format/format_autodetection_spec.rb +40 -0
- data/spec/unit/file_format/line_definition_spec.rb +75 -0
- data/spec/unit/file_format/merb_format_spec.rb +52 -0
- data/spec/unit/file_format/mysql_format_spec.rb +154 -0
- data/spec/unit/file_format/postgresql_format_spec.rb +65 -0
- data/spec/unit/file_format/rack_format_spec.rb +50 -0
- data/spec/unit/file_format/rails3_format_spec.rb +120 -0
- data/spec/unit/file_format/rails_format_spec.rb +180 -0
- data/spec/unit/filter/anonymize_filter_spec.rb +21 -0
- data/spec/unit/filter/field_filter_spec.rb +66 -0
- data/spec/unit/filter/filter_spec.rb +17 -0
- data/spec/unit/filter/timespan_filter_spec.rb +58 -0
- data/spec/unit/mailer_spec.rb +42 -0
- data/spec/unit/request_spec.rb +111 -0
- data/spec/unit/source/log_parser_spec.rb +119 -0
- data/spec/unit/tracker/duration_tracker_spec.rb +49 -0
- data/spec/unit/tracker/frequency_tracker_spec.rb +88 -0
- data/spec/unit/tracker/hourly_spread_spec.rb +79 -0
- data/spec/unit/tracker/numeric_value_tracker_spec.rb +166 -0
- data/spec/unit/tracker/timespan_tracker_spec.rb +73 -0
- data/spec/unit/tracker/tracker_api_spec.rb +125 -0
- data/spec/unit/tracker/traffic_tracker_spec.rb +28 -0
- data/tasks/github-gem.rake +290 -139
- data/tasks/request_log_analyzer.rake +23 -7
- metadata +221 -94
- data/DESIGN +0 -14
- data/HACKING +0 -7
- data/README.textile +0 -36
- data/lib/cli/bashcolorizer.rb +0 -60
- data/lib/request_log_analyzer/aggregator/database.rb +0 -148
- data/lib/request_log_analyzer/filter/base.rb +0 -29
- data/lib/request_log_analyzer/log_parser.rb +0 -173
- data/lib/request_log_analyzer/source/base.rb +0 -42
- data/lib/request_log_analyzer/source/log_file.rb +0 -170
- data/lib/request_log_analyzer/tracker/base.rb +0 -54
- data/lib/request_log_analyzer/tracker/category.rb +0 -71
- data/spec/controller_spec.rb +0 -40
- data/spec/database_inserter_spec.rb +0 -101
- data/spec/file_format_spec.rb +0 -78
- data/spec/file_formats/spec_format.rb +0 -26
- data/spec/filter_spec.rb +0 -137
- data/spec/line_definition_spec.rb +0 -124
- data/spec/log_parser_spec.rb +0 -68
- data/spec/log_processor_spec.rb +0 -57
- data/spec/merb_format_spec.rb +0 -38
- data/spec/rails_format_spec.rb +0 -76
- data/spec/request_spec.rb +0 -72
- data/spec/summarizer_spec.rb +0 -9
- 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
|