request-log-analyzer 1.13.1 → 1.13.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/bin/console +17 -0
- data/lib/cli/command_line_arguments.rb +29 -36
- data/lib/cli/database_console.rb +1 -3
- data/lib/cli/database_console_init.rb +11 -11
- data/lib/cli/progressbar.rb +30 -32
- data/lib/cli/tools.rb +20 -23
- data/lib/request_log_analyzer.rb +8 -8
- data/lib/request_log_analyzer/aggregator.rb +4 -7
- data/lib/request_log_analyzer/aggregator/database_inserter.rb +10 -13
- data/lib/request_log_analyzer/aggregator/echo.rb +5 -7
- data/lib/request_log_analyzer/aggregator/summarizer.rb +15 -18
- data/lib/request_log_analyzer/class_level_inheritable_attributes.rb +23 -0
- data/lib/request_log_analyzer/controller.rb +36 -42
- data/lib/request_log_analyzer/database.rb +4 -6
- data/lib/request_log_analyzer/database/base.rb +39 -41
- data/lib/request_log_analyzer/database/connection.rb +8 -10
- data/lib/request_log_analyzer/database/request.rb +1 -3
- data/lib/request_log_analyzer/database/source.rb +0 -2
- data/lib/request_log_analyzer/database/warning.rb +4 -6
- data/lib/request_log_analyzer/file_format.rb +46 -49
- data/lib/request_log_analyzer/file_format/amazon_s3.rb +15 -19
- data/lib/request_log_analyzer/file_format/apache.rb +42 -45
- data/lib/request_log_analyzer/file_format/delayed_job.rb +13 -15
- data/lib/request_log_analyzer/file_format/delayed_job2.rb +9 -11
- data/lib/request_log_analyzer/file_format/delayed_job21.rb +9 -11
- data/lib/request_log_analyzer/file_format/delayed_job3.rb +5 -8
- data/lib/request_log_analyzer/file_format/delayed_job4.rb +5 -8
- data/lib/request_log_analyzer/file_format/haproxy.rb +44 -48
- data/lib/request_log_analyzer/file_format/merb.rb +13 -17
- data/lib/request_log_analyzer/file_format/mysql.rb +21 -25
- data/lib/request_log_analyzer/file_format/nginx.rb +0 -2
- data/lib/request_log_analyzer/file_format/oink.rb +30 -31
- data/lib/request_log_analyzer/file_format/postgresql.rb +11 -15
- data/lib/request_log_analyzer/file_format/rack.rb +0 -2
- data/lib/request_log_analyzer/file_format/rails.rb +100 -104
- data/lib/request_log_analyzer/file_format/rails3.rb +19 -23
- data/lib/request_log_analyzer/file_format/rails_development.rb +0 -1
- data/lib/request_log_analyzer/file_format/w3c.rb +16 -18
- data/lib/request_log_analyzer/filter.rb +0 -2
- data/lib/request_log_analyzer/filter/anonymize.rb +4 -7
- data/lib/request_log_analyzer/filter/field.rb +3 -6
- data/lib/request_log_analyzer/filter/timespan.rb +2 -6
- data/lib/request_log_analyzer/line_definition.rb +16 -19
- data/lib/request_log_analyzer/log_processor.rb +10 -14
- data/lib/request_log_analyzer/mailer.rb +9 -12
- data/lib/request_log_analyzer/output.rb +12 -14
- data/lib/request_log_analyzer/output/fixed_width.rb +21 -28
- data/lib/request_log_analyzer/output/html.rb +11 -14
- data/lib/request_log_analyzer/request.rb +53 -33
- data/lib/request_log_analyzer/source.rb +2 -5
- data/lib/request_log_analyzer/source/log_parser.rb +9 -16
- data/lib/request_log_analyzer/tracker.rb +10 -12
- data/lib/request_log_analyzer/tracker/duration.rb +4 -6
- data/lib/request_log_analyzer/tracker/frequency.rb +9 -11
- data/lib/request_log_analyzer/tracker/hourly_spread.rb +8 -11
- data/lib/request_log_analyzer/tracker/numeric_value.rb +40 -44
- data/lib/request_log_analyzer/tracker/timespan.rb +5 -8
- data/lib/request_log_analyzer/tracker/traffic.rb +8 -10
- data/lib/request_log_analyzer/version.rb +1 -1
- data/request-log-analyzer.gemspec +6 -6
- data/spec/integration/command_line_usage_spec.rb +33 -33
- data/spec/integration/mailer_spec.rb +181 -185
- data/spec/integration/munin_plugins_rails_spec.rb +20 -20
- data/spec/integration/scout_spec.rb +40 -41
- data/spec/lib/helpers.rb +8 -9
- data/spec/lib/macros.rb +2 -4
- data/spec/lib/matchers.rb +20 -25
- data/spec/lib/mocks.rb +10 -11
- data/spec/lib/testing_format.rb +8 -10
- data/spec/spec_helper.rb +5 -1
- data/spec/unit/aggregator/database_inserter_spec.rb +23 -23
- data/spec/unit/aggregator/summarizer_spec.rb +7 -7
- data/spec/unit/controller/controller_spec.rb +14 -14
- data/spec/unit/controller/log_processor_spec.rb +3 -3
- data/spec/unit/database/base_class_spec.rb +36 -37
- data/spec/unit/database/connection_spec.rb +10 -10
- data/spec/unit/database/database_spec.rb +11 -11
- data/spec/unit/file_format/amazon_s3_format_spec.rb +66 -62
- data/spec/unit/file_format/apache_format_spec.rb +57 -52
- data/spec/unit/file_format/common_regular_expressions_spec.rb +18 -21
- data/spec/unit/file_format/delayed_job21_format_spec.rb +22 -16
- data/spec/unit/file_format/delayed_job2_format_spec.rb +22 -16
- data/spec/unit/file_format/delayed_job3_format_spec.rb +14 -10
- data/spec/unit/file_format/delayed_job4_format_spec.rb +14 -10
- data/spec/unit/file_format/delayed_job_format_spec.rb +12 -12
- data/spec/unit/file_format/file_format_api_spec.rb +19 -19
- data/spec/unit/file_format/format_autodetection_spec.rb +7 -7
- data/spec/unit/file_format/haproxy_format_spec.rb +53 -49
- data/spec/unit/file_format/inheritance_spec.rb +13 -0
- data/spec/unit/file_format/line_definition_spec.rb +35 -33
- data/spec/unit/file_format/merb_format_spec.rb +13 -11
- data/spec/unit/file_format/mysql_format_spec.rb +24 -24
- data/spec/unit/file_format/oink_format_spec.rb +29 -29
- data/spec/unit/file_format/postgresql_format_spec.rb +9 -9
- data/spec/unit/file_format/rack_format_spec.rb +36 -31
- data/spec/unit/file_format/rails3_format_spec.rb +46 -46
- data/spec/unit/file_format/rails_format_spec.rb +52 -53
- data/spec/unit/file_format/w3c_format_spec.rb +27 -24
- data/spec/unit/filter/anonymize_filter_spec.rb +7 -7
- data/spec/unit/filter/field_filter_spec.rb +26 -26
- data/spec/unit/filter/filter_spec.rb +4 -4
- data/spec/unit/filter/timespan_filter_spec.rb +22 -22
- data/spec/unit/mailer_spec.rb +21 -21
- data/spec/unit/request_spec.rb +29 -29
- data/spec/unit/source/log_parser_spec.rb +5 -5
- data/spec/unit/tracker/duration_tracker_spec.rb +23 -23
- data/spec/unit/tracker/frequency_tracker_spec.rb +29 -30
- data/spec/unit/tracker/hourly_spread_spec.rb +35 -35
- data/spec/unit/tracker/numeric_value_tracker_spec.rb +71 -72
- data/spec/unit/tracker/timespan_tracker_spec.rb +31 -31
- data/spec/unit/tracker/tracker_api_spec.rb +43 -44
- data/spec/unit/tracker/traffic_tracker_spec.rb +7 -7
- metadata +38 -35
@@ -1,5 +1,4 @@
|
|
1
1
|
module RequestLogAnalyzer::Tracker
|
2
|
-
|
3
2
|
# Analyze the average and total traffic of requests
|
4
3
|
#
|
5
4
|
# === Options
|
@@ -10,16 +9,15 @@ module RequestLogAnalyzer::Tracker
|
|
10
9
|
# * <tt>:title</tt> Title do be displayed above the report
|
11
10
|
# * <tt>:unless</tt> Handle request if this proc is false for the handled request.
|
12
11
|
class Traffic < NumericValue
|
13
|
-
|
14
12
|
# Check if duration and catagory option have been received,
|
15
13
|
def prepare
|
16
14
|
options[:value] = options[:traffic] if options[:traffic]
|
17
15
|
options[:total] = true
|
18
16
|
super
|
19
|
-
|
17
|
+
|
20
18
|
@number_of_buckets = options[:number_of_buckets] || 1000
|
21
19
|
@min_bucket_value = options[:min_bucket_value] ? options[:min_bucket_value].to_f : 1
|
22
|
-
@max_bucket_value = options[:max_bucket_value] ? options[:max_bucket_value].to_f :
|
20
|
+
@max_bucket_value = options[:max_bucket_value] ? options[:max_bucket_value].to_f : 1_000_000_000_000
|
23
21
|
|
24
22
|
# precalculate the bucket size
|
25
23
|
@bucket_size = (Math.log(@max_bucket_value) - Math.log(@min_bucket_value)) / @number_of_buckets.to_f
|
@@ -27,15 +25,15 @@ module RequestLogAnalyzer::Tracker
|
|
27
25
|
|
28
26
|
# Formats the traffic number using x B/kB/MB/GB etc notation
|
29
27
|
def display_value(bytes)
|
30
|
-
return
|
31
|
-
return
|
32
|
-
|
28
|
+
return '-' if bytes.nil?
|
29
|
+
return '0 B' if bytes.zero?
|
30
|
+
|
33
31
|
case [Math.log10(bytes.abs).floor, 0].max
|
34
32
|
when 0...4 then '%d B' % bytes
|
35
33
|
when 4...7 then '%d kB' % (bytes / 1000)
|
36
|
-
when 7...10 then '%d MB' % (bytes /
|
37
|
-
when 10...13 then '%d GB' % (bytes /
|
38
|
-
else '%d TB' % (bytes /
|
34
|
+
when 7...10 then '%d MB' % (bytes / 1_000_000)
|
35
|
+
when 10...13 then '%d GB' % (bytes / 1_000_000_000)
|
36
|
+
else '%d TB' % (bytes / 1_000_000_000_000)
|
39
37
|
end
|
40
38
|
end
|
41
39
|
|
@@ -17,11 +17,11 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.summary = "A command line tool to analyze request logs for Apache, Rails, Merb, MySQL and other web application servers"
|
18
18
|
gem.description = <<-eos
|
19
19
|
Request log analyzer's purpose is to find out how your web application is being used, how it performs and to
|
20
|
-
focus your optimization efforts. This tool will parse all requests in the application's log file and aggregate the
|
21
|
-
information. Once it is finished parsing the log file(s), it will show the requests that take op most server time
|
20
|
+
focus your optimization efforts. This tool will parse all requests in the application's log file and aggregate the
|
21
|
+
information. Once it is finished parsing the log file(s), it will show the requests that take op most server time
|
22
22
|
using various metrics. It can also insert all parsed request information into a database so you can roll your own
|
23
|
-
analysis. It supports Rails-, Merb- and Rack-based applications logs, Apache and Amazon S3 access logs and MySQL
|
24
|
-
slow query logs out of the box, but file formats of other applications can easily be supported by supplying an
|
23
|
+
analysis. It supports Rails-, Merb- and Rack-based applications logs, Apache and Amazon S3 access logs and MySQL
|
24
|
+
slow query logs out of the box, but file formats of other applications can easily be supported by supplying an
|
25
25
|
easy to write log file format definition.
|
26
26
|
eos
|
27
27
|
|
@@ -32,7 +32,7 @@ Gem::Specification.new do |gem|
|
|
32
32
|
gem.required_ruby_version = '>= 1.9.3'
|
33
33
|
|
34
34
|
gem.add_development_dependency('rake')
|
35
|
-
gem.add_development_dependency('rspec', '~>
|
35
|
+
gem.add_development_dependency('rspec', '~> 3')
|
36
36
|
gem.add_development_dependency('activerecord')
|
37
37
|
if defined?(JRUBY_VERSION)
|
38
38
|
gem.add_development_dependency('jdbc-sqlite3')
|
@@ -46,7 +46,7 @@ Gem::Specification.new do |gem|
|
|
46
46
|
gem.add_development_dependency('mysql2')
|
47
47
|
gem.add_development_dependency('pg')
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
gem.files = `git ls-files`.split($/)
|
51
51
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
52
52
|
|
@@ -10,85 +10,85 @@ describe RequestLogAnalyzer, 'running from command line' do
|
|
10
10
|
cleanup_temp_files!
|
11
11
|
end
|
12
12
|
|
13
|
-
it
|
13
|
+
it 'should find 4 requests in default mode' do
|
14
14
|
output = run("#{log_fixture(:rails_1x)}")
|
15
|
-
output.any? { |line| /^Parsed requests\:\s*4\s/ =~ line }.should
|
15
|
+
output.any? { |line| /^Parsed requests\:\s*4\s/ =~ line }.should == true
|
16
16
|
end
|
17
17
|
|
18
|
-
it
|
18
|
+
it 'should find 2 requests when parsing a compressed file' do
|
19
19
|
output = run("#{log_fixture(:decompression, :tgz)}")
|
20
|
-
output.any? { |line| /^Parsed requests\:\s*2\s/ =~ line }.should
|
20
|
+
output.any? { |line| /^Parsed requests\:\s*2\s/ =~ line }.should == true
|
21
21
|
end
|
22
22
|
|
23
|
-
it
|
23
|
+
it 'should skip 1 requests with a --select option' do
|
24
24
|
output = run("#{log_fixture(:rails_1x)} --select controller PeopleController")
|
25
|
-
output.any? { |line| /^Skipped requests\:\s*1\s/ =~ line }.should
|
25
|
+
output.any? { |line| /^Skipped requests\:\s*1\s/ =~ line }.should == true
|
26
26
|
end
|
27
27
|
|
28
|
-
it
|
28
|
+
it 'should skip 3 requests with a --reject option' do
|
29
29
|
output = run("#{log_fixture(:rails_1x)} --reject controller PeopleController")
|
30
|
-
output.any? { |line| /^Skipped requests\:\s*3\s/ =~ line }.should
|
30
|
+
output.any? { |line| /^Skipped requests\:\s*3\s/ =~ line }.should == true
|
31
31
|
end
|
32
32
|
|
33
|
-
it
|
33
|
+
it 'should not write output with the --silent option' do
|
34
34
|
output = run("#{log_fixture(:rails_1x)} --silent --file #{temp_output_file(:report)}")
|
35
35
|
output.should be_empty
|
36
|
-
File.exist?(temp_output_file(:report)).should
|
36
|
+
File.exist?(temp_output_file(:report)).should == true
|
37
37
|
end
|
38
38
|
|
39
|
-
it
|
39
|
+
it 'should write output to a file with the --file option' do
|
40
40
|
run("#{log_fixture(:rails_1x)} --file #{temp_output_file(:report)}")
|
41
|
-
File.exist?(temp_output_file(:report)).should
|
41
|
+
File.exist?(temp_output_file(:report)).should == true
|
42
42
|
end
|
43
43
|
|
44
|
-
it
|
44
|
+
it 'should write only ASCII characters to a file with the --file option' do
|
45
45
|
run("#{log_fixture(:rails_1x)} --file #{temp_output_file(:report)}")
|
46
46
|
/^[\x00-\x7F]*$/.match(File.read(temp_output_file(:report))).should_not be_nil
|
47
47
|
end
|
48
48
|
|
49
|
-
it
|
49
|
+
it 'should write HTML if --output HTML is provided' do
|
50
50
|
output = run("#{log_fixture(:rails_1x)} --output HTML")
|
51
|
-
output.any? { |line| /<html[^>]*>/ =~ line}.should
|
51
|
+
output.any? { |line| /<html[^>]*>/ =~ line }.should == true
|
52
52
|
end
|
53
53
|
|
54
|
-
it
|
54
|
+
it 'should run with the --database option' do
|
55
55
|
run("#{log_fixture(:rails_1x)} --database #{temp_output_file(:database)}")
|
56
|
-
File.exist?(temp_output_file(:database)).should
|
56
|
+
File.exist?(temp_output_file(:database)).should == true
|
57
57
|
end
|
58
58
|
|
59
|
-
it
|
59
|
+
it 'should use no colors in the report with the --boring option' do
|
60
60
|
output = run("#{log_fixture(:rails_1x)} --boring --format rails")
|
61
|
-
output.any? { |line| /\e/ =~ line }.should
|
61
|
+
output.any? { |line| /\e/ =~ line }.should == false
|
62
62
|
end
|
63
63
|
|
64
|
-
it
|
64
|
+
it 'should use only ASCII characters in the report with the --boring option' do
|
65
65
|
output = run("#{log_fixture(:rails_1x)} --boring")
|
66
|
-
output.all? { |line| /^[\x00-\x7F]*$/ =~ line }.should
|
66
|
+
output.all? { |line| /^[\x00-\x7F]*$/ =~ line }.should == true
|
67
67
|
end
|
68
68
|
|
69
|
-
it
|
69
|
+
it 'should parse a Merb file if --format merb is set' do
|
70
70
|
output = run("#{log_fixture(:merb)} --format merb")
|
71
|
-
output.any? { |line| /Parsed requests\:\s*11/ =~ line }.should
|
71
|
+
output.any? { |line| /Parsed requests\:\s*11/ =~ line }.should == true
|
72
72
|
end
|
73
73
|
|
74
|
-
it
|
74
|
+
it 'should parse a Apache access log file if --apache-format is set' do
|
75
75
|
output = run("#{log_fixture(:apache_combined)} --apache-format combined")
|
76
|
-
output.any? { |line| /Parsed requests\:\s*5/ =~ line }.should
|
76
|
+
output.any? { |line| /Parsed requests\:\s*5/ =~ line }.should == true
|
77
77
|
end
|
78
78
|
|
79
|
-
it
|
79
|
+
it 'should dump the results to a YAML file' do
|
80
80
|
run("#{log_fixture(:rails_1x)} --yaml #{temp_output_file(:yaml)}")
|
81
|
-
File.exist?(temp_output_file(:yaml)).should
|
82
|
-
YAML.load(File.read(temp_output_file(:yaml))).should
|
81
|
+
File.exist?(temp_output_file(:yaml)).should == true
|
82
|
+
YAML.load(File.read(temp_output_file(:yaml))).length.should be >= 1
|
83
83
|
end
|
84
84
|
|
85
|
-
it
|
85
|
+
it 'should parse 4 requests from the standard input' do
|
86
86
|
output = run("--format rails - < #{log_fixture(:rails_1x)}")
|
87
|
-
output.any? { |line| /^Parsed requests\:\s*4\s/ =~ line }.should
|
87
|
+
output.any? { |line| /^Parsed requests\:\s*4\s/ =~ line }.should == true
|
88
88
|
end
|
89
89
|
|
90
|
-
it
|
91
|
-
output = run("#{log_directory_fixture(
|
92
|
-
output.any? { |line| /^Parsed requests:\s*8\s/ =~ line }.should
|
90
|
+
it 'should accept a directory as a commandline option' do
|
91
|
+
output = run("#{log_directory_fixture('s3_logs')} --format amazon_s3")
|
92
|
+
output.any? { |line| /^Parsed requests:\s*8\s/ =~ line }.should == true
|
93
93
|
end
|
94
94
|
end
|
@@ -1,230 +1,226 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'socket'
|
3
3
|
|
4
|
-
unless defined?(JRUBY_VERSION) #No Fork on JRUBY
|
4
|
+
unless defined?(JRUBY_VERSION) # No Fork on JRUBY
|
5
5
|
|
6
|
-
describe RequestLogAnalyzer, 'running mailer integration' do
|
6
|
+
describe RequestLogAnalyzer, 'running mailer integration' do
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
before(:each) do
|
9
|
+
@log_file = temp_output_file('mailtrap.log')
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
Process.fork do
|
12
|
+
Mailtrap.new('localhost', 2525, true, @log_file)
|
13
|
+
Process.exit! # Do not call rspec after exit hook!
|
14
|
+
end
|
15
|
+
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
after(:each) do
|
18
|
+
cleanup_temp_files!
|
19
|
+
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
21
|
+
it 'should send plaintext mail' do
|
22
|
+
RequestLogAnalyzer::Controller.build(
|
23
|
+
mail: 'root@localhost',
|
24
|
+
mailhost: 'localhost:2525',
|
25
|
+
source_files: log_fixture(:rails_1x),
|
26
|
+
format: RequestLogAnalyzer::FileFormat::Rails,
|
27
|
+
no_progress: true
|
28
|
+
).run!
|
29
|
+
|
30
|
+
Process.wait # Wait for mailer to complete
|
31
|
+
|
32
|
+
find_string_in_file('From: <contact@railsdoctors.com>', @log_file).should_not be_nil
|
33
|
+
find_string_in_file('To: <root@localhost>', @log_file).should_not be_nil
|
34
|
+
find_string_in_file('From: Request-log-analyzer reporter <contact@railsdoctors.com>', @log_file).should_not be_nil
|
35
|
+
find_string_in_file('Subject: Request log analyzer report - generated on', @log_file).should_not be_nil
|
36
|
+
find_string_in_file('Request summary', @log_file).should_not be_nil
|
37
|
+
find_string_in_file('PeopleControll | 1 | 40ms | 40ms | 0ms | 40ms | 40ms | 40ms-41ms', @log_file).should_not be_nil
|
38
|
+
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
40
|
+
it 'should allow a custom mail subject' do
|
41
|
+
RequestLogAnalyzer::Controller.build(
|
42
|
+
mail: 'root@localhost',
|
43
|
+
mailhost: 'localhost:2525',
|
44
|
+
mailsubject: 'TESTSUBJECT',
|
45
|
+
source_files: log_fixture(:rails_1x),
|
46
|
+
format: RequestLogAnalyzer::FileFormat::Rails,
|
47
|
+
no_progress: true
|
48
|
+
).run!
|
49
49
|
|
50
|
-
|
50
|
+
Process.wait # Wait for mailer to complete
|
51
51
|
|
52
|
-
|
53
|
-
|
52
|
+
find_string_in_file('Subject: TESTSUBJECT', @log_file).should_not be_nil
|
53
|
+
end
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
55
|
+
it 'should allow a custom mail sender address' do
|
56
|
+
RequestLogAnalyzer::Controller.build(
|
57
|
+
mail: 'root@localhost',
|
58
|
+
mailhost: 'localhost:2525',
|
59
|
+
mailfrom: 'customaddr@example.com',
|
60
|
+
source_files: log_fixture(:rails_1x),
|
61
|
+
format: RequestLogAnalyzer::FileFormat::Rails,
|
62
|
+
no_progress: true
|
63
|
+
).run!
|
64
64
|
|
65
|
-
|
65
|
+
Process.wait # Wait for mailer to complete
|
66
66
|
|
67
|
-
|
68
|
-
|
67
|
+
find_string_in_file('From: Request-log-analyzer reporter <customaddr@example.com>', @log_file).should_not be_nil
|
68
|
+
end
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
70
|
+
it 'should allow a custom mail sender name' do
|
71
|
+
RequestLogAnalyzer::Controller.build(
|
72
|
+
mail: 'root@localhost',
|
73
|
+
mailhost: 'localhost:2525',
|
74
|
+
mailfrom_name: 'Custom Name',
|
75
|
+
source_files: log_fixture(:rails_1x),
|
76
|
+
format: RequestLogAnalyzer::FileFormat::Rails,
|
77
|
+
no_progress: true
|
78
|
+
).run!
|
79
79
|
|
80
|
-
|
80
|
+
Process.wait # Wait for mailer to complete
|
81
81
|
|
82
|
-
|
83
|
-
|
82
|
+
find_string_in_file('From: Custom Name <contact@railsdoctors.com>', @log_file).should_not be_nil
|
83
|
+
end
|
84
84
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
85
|
+
it 'should send html mail' do
|
86
|
+
RequestLogAnalyzer::Controller.build(
|
87
|
+
output: 'HTML',
|
88
|
+
mail: 'root@localhost',
|
89
|
+
mailhost: 'localhost:2525',
|
90
|
+
source_files: log_fixture(:rails_1x),
|
91
|
+
format: RequestLogAnalyzer::FileFormat::Rails,
|
92
|
+
no_progress: true
|
93
|
+
).run!
|
94
|
+
|
95
|
+
Process.wait # Wait for mailer to complete
|
96
|
+
|
97
|
+
find_string_in_file('From: <contact@railsdoctors.com>', @log_file).should_not be_nil
|
98
|
+
find_string_in_file('To: <root@localhost>', @log_file).should_not be_nil
|
99
|
+
find_string_in_file('From: Request-log-analyzer reporter <contact@railsdoctors.com>', @log_file).should_not be_nil
|
100
|
+
find_string_in_file('<h1>Request-log-analyzer summary report</h1>', @log_file).should_not be_nil
|
101
|
+
find_string_in_file('<td class="alt">287ms-296ms</td></tr><tr><td>DashboardController#index.html [GET]</td>', @log_file).should_not be_nil
|
102
|
+
end
|
102
103
|
end
|
103
|
-
end
|
104
|
-
|
105
104
|
|
105
|
+
# Mailtrap
|
106
|
+
# by Matt Mower <self@mattmower.com>
|
107
|
+
# http://matt.blogs.it/
|
108
|
+
#
|
109
|
+
# Included in RLA because original mailtrap puts anoying stuff when called
|
110
|
+
# through ruby.
|
111
|
+
#
|
112
|
+
# Mailtrap creates a TCP server that listens on a specified port for SMTP
|
113
|
+
# clients. Accepts the connection and talks just enough of the SMTP protocol
|
114
|
+
# for them to deliver a message which it writes to disk.
|
115
|
+
#
|
116
|
+
class Mailtrap
|
117
|
+
VERSION = '0.2.1'
|
118
|
+
|
119
|
+
# Create a new Mailtrap on the specified host:port. If once it true it
|
120
|
+
# will listen for one message then exit. Specify the msgdir where messages
|
121
|
+
# are written.
|
122
|
+
def initialize(host, port, once, msgfile)
|
123
|
+
@host = host
|
124
|
+
@port = port
|
125
|
+
@once = once
|
126
|
+
@msgfile = msgfile
|
127
|
+
|
128
|
+
File.open(@msgfile, 'a') do |file|
|
129
|
+
file.puts "\n* Mailtrap started at #{@host}:#{port}\n"
|
130
|
+
end
|
106
131
|
|
107
|
-
|
108
|
-
|
109
|
-
# http://matt.blogs.it/
|
110
|
-
#
|
111
|
-
# Included in RLA because original mailtrap puts anoying stuff when called
|
112
|
-
# through ruby.
|
113
|
-
#
|
114
|
-
# Mailtrap creates a TCP server that listens on a specified port for SMTP
|
115
|
-
# clients. Accepts the connection and talks just enough of the SMTP protocol
|
116
|
-
# for them to deliver a message which it writes to disk.
|
117
|
-
#
|
118
|
-
class Mailtrap
|
119
|
-
VERSION = '0.2.1'
|
120
|
-
|
121
|
-
# Create a new Mailtrap on the specified host:port. If once it true it
|
122
|
-
# will listen for one message then exit. Specify the msgdir where messages
|
123
|
-
# are written.
|
124
|
-
def initialize( host, port, once, msgfile )
|
125
|
-
@host = host
|
126
|
-
@port = port
|
127
|
-
@once = once
|
128
|
-
@msgfile = msgfile
|
129
|
-
|
130
|
-
File.open( @msgfile, "a" ) do |file|
|
131
|
-
file.puts "\n* Mailtrap started at #{@host}:#{port}\n"
|
132
|
+
service = TCPServer.new(@host, @port)
|
133
|
+
accept(service)
|
132
134
|
end
|
133
135
|
|
134
|
-
|
135
|
-
accept(
|
136
|
-
|
136
|
+
# Service one or more SMTP client connections
|
137
|
+
def accept(service)
|
138
|
+
while session = service.accept
|
137
139
|
|
138
|
-
|
139
|
-
|
140
|
-
|
140
|
+
class << session
|
141
|
+
def get_line
|
142
|
+
line = gets
|
143
|
+
line.chomp! unless line.nil?
|
144
|
+
line
|
145
|
+
end
|
146
|
+
end
|
141
147
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
line.chomp! unless line.nil?
|
146
|
-
line
|
148
|
+
begin
|
149
|
+
serve(session)
|
150
|
+
rescue => e
|
147
151
|
end
|
148
|
-
end
|
149
152
|
|
150
|
-
|
151
|
-
serve( session )
|
152
|
-
rescue Exception => e
|
153
|
+
break if @once
|
153
154
|
end
|
154
|
-
|
155
|
-
break if @once
|
156
155
|
end
|
157
|
-
end
|
158
156
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
157
|
+
# Write a plain text dump of the incoming email to a text
|
158
|
+
# file. The file will be in the @msgdir folder and will
|
159
|
+
# be called smtp0001.msg, smtp0002.msg, and so on.
|
160
|
+
def write(from, to_list, message)
|
161
|
+
# Strip SMTP commands from To: and From:
|
162
|
+
from.gsub!(/MAIL FROM:\s*/, '')
|
163
|
+
to_list = to_list.map { |to| to.gsub(/RCPT TO:\s*/, '') }
|
164
|
+
|
165
|
+
# Append to the end of the messages file
|
166
|
+
File.open(@msgfile, 'a') do |file|
|
167
|
+
file.puts '* Message begins'
|
168
|
+
file.puts "From: #{from}"
|
169
|
+
file.puts "To: #{to_list.join(', ')}"
|
170
|
+
file.puts 'Body:'
|
171
|
+
file.puts message
|
172
|
+
file.puts "\n* Message ends"
|
173
|
+
end
|
176
174
|
end
|
177
175
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
connection.puts( "220 #{@host} MailTrap ready ESTMP" )
|
184
|
-
helo = connection.get_line # whoever they are
|
176
|
+
# Talk pidgeon-SMTP to the client to get them to hand over the message
|
177
|
+
# and go away.
|
178
|
+
def serve(connection)
|
179
|
+
connection.puts("220 #{@host} MailTrap ready ESTMP")
|
180
|
+
helo = connection.get_line # whoever they are
|
185
181
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
182
|
+
if helo =~ /^EHLO\s+/
|
183
|
+
connection.puts "250-#{@host} offers just ONE extension my pretty"
|
184
|
+
connection.puts '250 HELP'
|
185
|
+
end
|
190
186
|
|
191
|
-
|
192
|
-
|
193
|
-
|
187
|
+
# Accept MAIL FROM:
|
188
|
+
from = connection.get_line
|
189
|
+
connection.puts('250 OK')
|
194
190
|
|
195
|
-
|
191
|
+
to_list = []
|
196
192
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
193
|
+
# Accept RCPT TO: until we see DATA
|
194
|
+
loop do
|
195
|
+
to = connection.get_line
|
196
|
+
break if to.nil?
|
201
197
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
198
|
+
if to =~ /^DATA/
|
199
|
+
connection.puts('354 Start your message')
|
200
|
+
break
|
201
|
+
else
|
202
|
+
to_list << to
|
203
|
+
connection.puts('250 OK')
|
204
|
+
end
|
208
205
|
end
|
209
|
-
end
|
210
206
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
207
|
+
# Capture the message body terminated by <CR>.<CR>
|
208
|
+
lines = []
|
209
|
+
loop do
|
210
|
+
line = connection.get_line
|
211
|
+
break if line.nil? || line == '.'
|
212
|
+
lines << line
|
213
|
+
end
|
218
214
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
215
|
+
# We expect the client will go away now
|
216
|
+
connection.puts('250 OK')
|
217
|
+
connection.gets # Quit
|
218
|
+
connection.puts '221 Seeya'
|
219
|
+
connection.close
|
224
220
|
|
225
|
-
|
221
|
+
write(from, to_list, lines.join("\n"))
|
222
|
+
end
|
226
223
|
end
|
227
|
-
end
|
228
224
|
|
229
225
|
else
|
230
226
|
p 'Skipping mailer integration specs, because of missing Process.fork()'
|