brontes3d-production_log_analyzer 2009022403

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 (34) hide show
  1. data/History.txt +34 -0
  2. data/LICENSE.txt +27 -0
  3. data/Manifest.txt +18 -0
  4. data/README.txt +147 -0
  5. data/Rakefile +17 -0
  6. data/bin/action_errors +46 -0
  7. data/bin/action_grep +19 -0
  8. data/bin/pl_analyze +36 -0
  9. data/lib/passenger_log_per_proc.rb +55 -0
  10. data/lib/production_log/action_grep.rb +41 -0
  11. data/lib/production_log/analyzer.rb +416 -0
  12. data/lib/production_log/parser.rb +228 -0
  13. data/test/test_action_grep.rb +72 -0
  14. data/test/test_analyzer.rb +425 -0
  15. data/test/test_helper.rb +68 -0
  16. data/test/test_parser.rb +420 -0
  17. data/test/test_passenger_log_per_proc.rb +88 -0
  18. data/test/test_syslogs/test.syslog.0.14.x.log +4 -0
  19. data/test/test_syslogs/test.syslog.1.2.shortname.log +4 -0
  20. data/test/test_syslogs/test.syslog.empty.log +0 -0
  21. data/test/test_syslogs/test.syslog.log +256 -0
  22. data/test/test_vanilla/test.0.14.x.log +4 -0
  23. data/test/test_vanilla/test.1.2.shortname.log +4 -0
  24. data/test/test_vanilla/test.empty.log +0 -0
  25. data/test/test_vanilla/test.log +255 -0
  26. data/test/test_vanilla/test_log_parts/1_online1-rails-59600.log +7 -0
  27. data/test/test_vanilla/test_log_parts/2_online2-rails-59628.log +11 -0
  28. data/test/test_vanilla/test_log_parts/3_online1-rails-59628.log +9 -0
  29. data/test/test_vanilla/test_log_parts/4_online1-rails-59645.log +30 -0
  30. data/test/test_vanilla/test_log_parts/5_online1-rails-59629.log +38 -0
  31. data/test/test_vanilla/test_log_parts/6_online1-rails-60654.log +32 -0
  32. data/test/test_vanilla/test_log_parts/7_online1-rails-59627.log +70 -0
  33. data/test/test_vanilla/test_log_parts/8_online1-rails-59635.log +58 -0
  34. metadata +113 -0
data/History.txt ADDED
@@ -0,0 +1,34 @@
1
+ = 1.5.0
2
+
3
+ * Fixed empty log bug. Patch by Tim Lucas.
4
+ * Fixed bug where sometimes lines would be logged before the
5
+ Processing line. Patch by Geoff Grosenbach.
6
+
7
+ = 1.4.0
8
+
9
+ * Switched to Hoe
10
+ * Allowed action_errors to suppress routing errors with > 3 occurances
11
+ * action_grep now works correctly with components
12
+ * pl_analyze now works correctly with components
13
+ * Added action_errors to extract error counts from logs
14
+ * Retabbed to match the rest of the world
15
+
16
+ = 1.3.0
17
+
18
+ * Added action_grep
19
+ * Added support for newer log format
20
+
21
+ = 1.2.0
22
+
23
+ * pl_analyze calculates per-action statistics
24
+ * pl_analyze can send an email with its output
25
+
26
+ = 1.1.0
27
+
28
+ * RDoc
29
+ * Other various fixes lost to time.
30
+
31
+ = 1.0.0
32
+
33
+ * Birthday!
34
+
data/LICENSE.txt ADDED
@@ -0,0 +1,27 @@
1
+ Copyright 2005, 2007 Eric Hodel, The Robot Co-op. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright
8
+ notice, this list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright
10
+ notice, this list of conditions and the following disclaimer in the
11
+ documentation and/or other materials provided with the distribution.
12
+ 3. Neither the names of the authors nor the names of their contributors
13
+ may be used to endorse or promote products derived from this software
14
+ without specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
17
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
20
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
22
+ OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
data/Manifest.txt ADDED
@@ -0,0 +1,18 @@
1
+ History.txt
2
+ LICENSE.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ bin/action_errors
7
+ bin/action_grep
8
+ bin/pl_analyze
9
+ lib/production_log/action_grep.rb
10
+ lib/production_log/analyzer.rb
11
+ lib/production_log/parser.rb
12
+ test/test.syslog.0.14.x.log
13
+ test/test.syslog.1.2.shortname.log
14
+ test/test.syslog.empty.log
15
+ test/test.syslog.log
16
+ test/test_action_grep.rb
17
+ test/test_analyzer.rb
18
+ test/test_parser.rb
data/README.txt ADDED
@@ -0,0 +1,147 @@
1
+ = production_log_analyzer
2
+
3
+ production_log_analyzer lets you find out which actions on a Rails
4
+ site are slowing you down.
5
+
6
+ http://seattlerb.rubyforge.org/production_log_analyzer
7
+
8
+ http://rubyforge.org/projects/seattlerb
9
+
10
+ Bug reports:
11
+
12
+ http://rubyforge.org/tracker/?func=add&group_id=1513&atid=5921
13
+
14
+ == About
15
+
16
+ production_log_analyzer provides three tools to analyze log files
17
+ created by SyslogLogger. pl_analyze for getting daily reports,
18
+ action_grep for pulling log lines for a single action and
19
+ action_errors to summarize errors with counts.
20
+
21
+ The analyzer currently requires the use of SyslogLogger because the
22
+ default Logger doesn't give any way to associate lines logged to a
23
+ request.
24
+
25
+ The PL Analyzer also includes action_grep which lets you grab lines from a log
26
+ that only match a single action.
27
+
28
+ action_grep RssController#uber /var/log/production.log
29
+
30
+ == Installing
31
+
32
+ sudo gem install production_log_analyzer
33
+
34
+ === Setup
35
+
36
+ First:
37
+
38
+ Set up SyslogLogger according to the instructions here:
39
+
40
+ http://seattlerb.rubyforge.org/SyslogLogger/
41
+
42
+ Then:
43
+
44
+ Set up a cronjob (or something like that) to run log files through pl_analyze.
45
+
46
+ == Using pl_analyze
47
+
48
+ To run pl_analyze simply give it the name of a log file to analyze.
49
+
50
+ pl_analyze /var/log/production.log
51
+
52
+ If you want, you can run it from a cron something like this:
53
+
54
+ /usr/bin/gzip -dc /var/log/production.log.0.gz | /usr/local/bin/pl_analyze /dev/stdin
55
+
56
+ Or, have pl_analyze email you (which is preferred, because tabs get preserved):
57
+
58
+ /usr/bin/gzip -dc /var/log/production.log.0.gz | /usr/local/bin/pl_analyze /dev/stdin -e devnull@robotcoop.com -s "pl_analyze for `date -v-1d "+%D"`"
59
+
60
+ In the future, pl_analyze will be able to read from STDIN.
61
+
62
+ == Sample output
63
+
64
+ Request Times Summary: Count Avg Std Dev Min Max
65
+ ALL REQUESTS: 11 0.576 0.508 0.000 1.470
66
+
67
+ ThingsController#view: 3 0.716 0.387 0.396 1.260
68
+ TeamsController#progress: 2 0.841 0.629 0.212 1.470
69
+ RssController#uber: 2 0.035 0.000 0.035 0.035
70
+ PeopleController#progress: 2 0.489 0.489 0.000 0.977
71
+ PeopleController#view: 2 0.731 0.371 0.360 1.102
72
+
73
+ Average Request Time: 0.634
74
+ Request Time Std Dev: 0.498
75
+
76
+ Slowest Request Times:
77
+ TeamsController#progress took 1.470s
78
+ ThingsController#view took 1.260s
79
+ PeopleController#view took 1.102s
80
+ PeopleController#progress took 0.977s
81
+ ThingsController#view took 0.492s
82
+ ThingsController#view took 0.396s
83
+ PeopleController#view took 0.360s
84
+ TeamsController#progress took 0.212s
85
+ RssController#uber took 0.035s
86
+ RssController#uber took 0.035s
87
+
88
+ ------------------------------------------------------------------------
89
+
90
+ DB Times Summary: Count Avg Std Dev Min Max
91
+ ALL REQUESTS: 11 0.366 0.393 0.000 1.144
92
+
93
+ ThingsController#view: 3 0.403 0.362 0.122 0.914
94
+ TeamsController#progress: 2 0.646 0.497 0.149 1.144
95
+ RssController#uber: 2 0.008 0.000 0.008 0.008
96
+ PeopleController#progress: 2 0.415 0.415 0.000 0.830
97
+ PeopleController#view: 2 0.338 0.149 0.189 0.486
98
+
99
+ Average DB Time: 0.402
100
+ DB Time Std Dev: 0.394
101
+
102
+ Slowest Total DB Times:
103
+ TeamsController#progress took 1.144s
104
+ ThingsController#view took 0.914s
105
+ PeopleController#progress took 0.830s
106
+ PeopleController#view took 0.486s
107
+ PeopleController#view took 0.189s
108
+ ThingsController#view took 0.173s
109
+ TeamsController#progress took 0.149s
110
+ ThingsController#view took 0.122s
111
+ RssController#uber took 0.008s
112
+ RssController#uber took 0.008s
113
+
114
+ ------------------------------------------------------------------------
115
+
116
+ Render Times Summary: Count Avg Std Dev Min Max
117
+ ALL REQUESTS: 11 0.219 0.253 0.000 0.695
118
+
119
+ ThingsController#view: 3 0.270 0.171 0.108 0.506
120
+ TeamsController#progress: 2 0.000 0.000 0.000 0.000
121
+ RssController#uber: 2 0.012 0.000 0.012 0.012
122
+ PeopleController#progress: 2 0.302 0.302 0.000 0.604
123
+ PeopleController#view: 2 0.487 0.209 0.278 0.695
124
+
125
+ Average Render Time: 0.302
126
+ Render Time Std Dev: 0.251
127
+
128
+ Slowest Total Render Times:
129
+ PeopleController#view took 0.695s
130
+ PeopleController#progress took 0.604s
131
+ ThingsController#view took 0.506s
132
+ PeopleController#view took 0.278s
133
+ ThingsController#view took 0.197s
134
+ ThingsController#view took 0.108s
135
+ RssController#uber took 0.012s
136
+ RssController#uber took 0.012s
137
+ TeamsController#progress took 0.000s
138
+ TeamsController#progress took 0.000s
139
+
140
+ == What's missing
141
+
142
+ * More reports
143
+ * Command line arguments including:
144
+ * Help
145
+ * What type of log file you've got (if somebody sends patches with tests)
146
+ * Read from STDIN
147
+
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ require 'hoe'
2
+
3
+ $:.unshift './lib'
4
+ require 'production_log/analyzer'
5
+
6
+ Hoe.new 'production_log_analyzer', '2009022403' do |p|
7
+ p.summary = p.paragraphs_of('README.txt', 1).join ' '
8
+ p.description = p.paragraphs_of('README.txt', 7).join ' '
9
+ p.author = 'Eric Hodel'
10
+ p.email = 'drbrain@segment7.net'
11
+ p.url = p.paragraphs_of('README.txt', 2).join ' '
12
+
13
+ p.rubyforge_name = 'seattlerb'
14
+
15
+ p.extra_deps << ['rails_analyzer_tools', '>= 1.4.0']
16
+ end
17
+
data/bin/action_errors ADDED
@@ -0,0 +1,46 @@
1
+ #!/usr/local/bin/ruby -ws
2
+
3
+ $h ||= false
4
+ $r ||= false
5
+ $o ||= false
6
+
7
+ $r = $r ? ($r.to_i rescue false) : false
8
+
9
+ if $h then
10
+ $stderr.puts "Usage: #{$0} [-r=N] LOGFILE"
11
+ $stderr.puts "\t-r=N\tShow routing errors with N or more occurances"
12
+ $stderr.puts "\t-o\tShow errors with one occurance"
13
+ exit
14
+ end
15
+
16
+ errors = {}
17
+ counts = Hash.new 0
18
+
19
+ ARGF.each_line do |line|
20
+ line =~ /\]: (.*?) (.*)/
21
+ next if $1.nil?
22
+ msg = $1
23
+ trace = $2
24
+ key = msg.gsub(/\d/, '#')
25
+ counts[key] += 1
26
+ next if counts[key] > 1
27
+ trace = trace.split(' ')[0..-2].map { |l| l.strip }.join("\n\t")
28
+ error = "#{msg}\n\t#{trace}"
29
+ errors[key] = error
30
+ end
31
+
32
+ counts.sort_by { |_,c| -c }.each do |key, count|
33
+ next if count == 1 and not $o
34
+ error = errors[key]
35
+
36
+ if error =~ /^ActionController::RoutingError/ then
37
+ next unless $r
38
+ next if $r and count < $r
39
+ end
40
+
41
+ puts "count: #{count}"
42
+ puts "{{{"
43
+ puts error
44
+ puts "}}}"
45
+ end
46
+
data/bin/action_grep ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ require 'production_log/action_grep'
4
+
5
+ action_name = ARGV.shift
6
+ file_name = ARGV.shift
7
+
8
+ if action_name.nil? or file_name.nil? then
9
+ puts "Usage: #{$0} action_name file_name"
10
+ exit 1
11
+ end
12
+
13
+ begin
14
+ ActionGrep.grep action_name, file_name
15
+ rescue ArgumentError => e
16
+ puts e
17
+ exit 1
18
+ end
19
+
data/bin/pl_analyze ADDED
@@ -0,0 +1,36 @@
1
+ #!/bin/env ruby -w
2
+
3
+ $:.unshift "#{File.dirname(__FILE__)}/../lib/"
4
+ require "production_log/analyzer"
5
+
6
+ file_name = ARGV.shift
7
+
8
+ if file_name.nil? then
9
+ puts "Usage: #{$0} file_name [-e email_recipient [-s subject]] [count]"
10
+ exit 1
11
+ end
12
+
13
+ email_recipient = nil
14
+ subject = nil
15
+
16
+ if ARGV.first == '-e' then
17
+ ARGV.shift # -e
18
+ email_recipient = ARGV.shift
19
+ end
20
+
21
+ if email_recipient and ARGV.first == '-s' then
22
+ ARGV.shift # -s
23
+ subject = ARGV.shift
24
+ end
25
+
26
+ count = ARGV.shift
27
+ count = count.nil? ? 10 : Integer(count)
28
+
29
+ if email_recipient.nil? then
30
+ analyzer = Analyzer.new file_name
31
+ analyzer.process
32
+ puts analyzer.report(count)
33
+ else
34
+ Analyzer.email file_name, email_recipient, subject, count
35
+ end
36
+
@@ -0,0 +1,55 @@
1
+ class PassengerLogPerProc
2
+
3
+ cattr_accessor :log_path_prefix
4
+
5
+ def self.logger_mutex
6
+ @@logger_mutex ||= Mutex.new
7
+ end
8
+
9
+ # with log_path_with_prefix like {RAILS_ROOT}/log/passenger/{RAILS_ENV}
10
+ # you will get logs like {RAILS_ROOT}/log/passenger/{RAILS_ENV}_{Pid}.log
11
+ def self.enable(log_path_with_prefix)
12
+ PassengerLogPerProc.log_path_prefix = log_path_with_prefix
13
+ PhusionPassenger::Rack::RequestHandler.class_eval do
14
+ cattr_accessor :process_logger
15
+
16
+ def process_request_with_extra_logging(env, input, output)
17
+ unless self.class.process_logger
18
+ PhusionPassenger::Rack::RequestHandler.process_logger = ActiveSupport::BufferedLogger.new(PassengerLogPerProc.log_path_prefix + "_#{Process.pid}" + ".log")
19
+
20
+ PhusionPassenger::Rack::RequestHandler.process_logger.debug("\nPassenger logging started")
21
+
22
+ RAILS_DEFAULT_LOGGER.instance_eval do
23
+ class << self
24
+ def add(severity, message = nil, progname = nil, &block)
25
+ to_return = super
26
+ if to_return
27
+ Thread.current[:passenger_logs] ||= []
28
+ Thread.current[:passenger_logs] << [severity, to_return.strip]
29
+ end
30
+ to_return
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ Thread.current[:passenger_logs] ||= []
37
+ start_time = Time.now
38
+ process_request_without_extra_logging(env, input, output)
39
+ ensure
40
+ PassengerLogPerProc.logger_mutex.synchronize do
41
+ PhusionPassenger::Rack::RequestHandler.process_logger.info("RECEIVE_REQUEST (#{Process.pid}) #{env['REQUEST_METHOD']} #{env['REQUEST_URI']} #{start_time}")
42
+ Thread.current[:passenger_logs].each do |to_log|
43
+ PhusionPassenger::Rack::RequestHandler.process_logger.add(to_log[0], to_log[1])
44
+ end
45
+ PhusionPassenger::Rack::RequestHandler.process_logger.info("#{Time.now} (#{Process.pid}) RESPONSE_SENT")
46
+ PhusionPassenger::Rack::RequestHandler.process_logger.info("\n")
47
+ Thread.current[:passenger_logs] = []
48
+ end
49
+ end
50
+
51
+ alias_method_chain :process_request, :extra_logging
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,41 @@
1
+ module ActionGrep; end
2
+
3
+ class << ActionGrep
4
+
5
+ def grep(action_name, file_name)
6
+ unless action_name =~ /\A([A-Z][A-Za-z\d]*)(?:#([A-Za-z]\w*))?\Z/ then
7
+ raise ArgumentError, "Invalid action name #{action_name} expected something like SomeController#action"
8
+ end
9
+
10
+ unless File.file? file_name and File.readable? file_name then
11
+ raise ArgumentError, "Unable to read #{file_name}"
12
+ end
13
+
14
+ buckets = Hash.new { |h,k| h[k] = [] }
15
+ comp_count = Hash.new 0
16
+
17
+ File.open file_name do |fp|
18
+ LogParser.detect_mode(fp)
19
+ fp.each_line do |line|
20
+ bucket, data = LogParser.extract_bucket_and_data(line)
21
+ next if !bucket
22
+
23
+ buckets[bucket] << line
24
+
25
+ case data
26
+ when /^Start rendering component / then
27
+ comp_count[bucket] += 1
28
+ when /^End of component rendering$/ then
29
+ comp_count[bucket] -= 1
30
+ when /^Completed/ then
31
+ next unless comp_count[bucket] == 0
32
+ action = buckets.delete bucket
33
+ next unless action.any? { |l| l =~ /Processing #{action_name}/ }
34
+ puts action.join
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+