diff -Nur ../production_log_analyzer-1.5.0/bin/pl_analyze ./bin/pl_analyze
--- ../production_log_analyzer-1.5.0/bin/pl_analyze	2007-06-15 10:19:53.000000000 +0200
+++ ./bin/pl_analyze	2007-06-14 18:36:50.000000000 +0200
@@ -1,35 +1,56 @@
 #!/usr/local/bin/ruby -w
 
 require 'production_log/analyzer'
+require 'production_log/email'
 
-file_name = ARGV.shift
-
-if file_name.nil? then
-  puts "Usage: #{$0} file_name [-e email_recipient [-s subject]] [count]"
-  exit 1
-end
-
-email_recipient = nil
+log_files = []
+email_addresses = []
+count = 15
+progress = false
 subject = nil
-
-if ARGV.first == '-e' then
-  ARGV.shift # -e
-  email_recipient = ARGV.shift
+ignore_missing_domain = false
+log_format = nil
+special_controllers = []
+
+while !ARGV.empty?
+  arg = ARGV.shift
+  case arg
+  when /^(--format|-f)$/
+    log_format = ARGV.shift.to_sym
+  when /^(--email|-e)$/
+    email_addresses << ARGV.shift
+  when /^(--subject|-s)$/
+    subject = ARGV.shift
+  when /^(--count|-s)$/
+    count = ARGV.shift.to_i
+  when /^(--progress|-p)$/
+    progress = true
+  when /^(--ignore-missing-domain|-i)$/
+    ignore_missing_domain = true
+  when /^(--special-controller|-c)$/
+    special_controllers << ARGV.shift
+  else
+    log_files << arg
+  end
 end
 
-if email_recipient and ARGV.first == '-s' then
-  ARGV.shift # -s
-  subject = ARGV.shift
-end
-
-count = ARGV.shift
-count = count.nil? ? 10 : Integer(count)
-
-if email_recipient.nil? then
-  analyzer = Analyzer.new file_name
-  analyzer.process
-  puts analyzer.report(count)
+if log_files.empty? 
+  puts "Usage: #{$0} [--format|-f autodetect|rails|syslog] [--email|-e email_address] [--subject|-s email_subject] [--count|-c num_entries] [--progress|-p] [--ignore-missing-domain|-i] [--special-controller|-c] log_files"
 else
-  Analyzer.email file_name, email_recipient, subject, count
+  analyzer = Analyzer.new count
+  analyzer.ignore_missing_domain = ignore_missing_domain
+
+  log_files.each do |log_file|
+    analyzer.process log_file, progress, log_format
+  end
+
+  report = analyzer.report special_controllers
+  puts report
+
+  if !email_addresses.empty?
+    body = email report, email_addresses, subject
+    send_mail body
+  end
 end
 
+
diff -Nur ../production_log_analyzer-1.5.0/History.txt ./History.txt
--- ../production_log_analyzer-1.5.0/History.txt	2007-06-15 10:19:53.000000000 +0200
+++ ./History.txt	2007-06-14 18:43:46.000000000 +0200
@@ -1,3 +1,15 @@
+= 1.5.4
+
+* Support for rails logs
+* Analyzing multiple logs at the same time
+* List of most frequently encountered http codes
+* Better command line handling
+* Optional progress bar while parsing
+* Optional ignoring of http:// URLs without domain
+* Requests per second, time of first and last request logged
+* Separate statistic for additional controllers
+* Remove dependency on rails_analyzer_tools
+
 = 1.5.0
 
 * Fixed empty log bug.  Patch by Tim Lucas.
diff -Nur ../production_log_analyzer-1.5.0/lib/production_log/analyzer.rb ./lib/production_log/analyzer.rb
--- ../production_log_analyzer-1.5.0/lib/production_log/analyzer.rb	2007-06-15 10:19:53.000000000 +0200
+++ ./lib/production_log/analyzer.rb	2007-06-07 18:34:17.000000000 +0200
@@ -113,11 +113,6 @@
 class Analyzer
 
   ##
-  # The version of the production log analyzer you are using.
-
-  VERSION = '1.5.0'
-
-  ##
   # The logfile being read by the Analyzer.
 
   attr_reader :logfile_name
@@ -138,59 +133,51 @@
   attr_reader :render_times
 
   ##
-  # Generates and sends an email report with lots of fun stuff in it.  This
-  # way, Mail.app will behave when given tabs.
-
-  def self.email(file_name, recipient, subject, count = 10)
-    analyzer = self.new file_name
-    analyzer.process
-    body = analyzer.report count
-
-    email = self.envelope(recipient, subject)
-    email << nil
-    email << "<pre>#{body}</pre>"
-    email = email.join($/) << $/
-
-    return email if $TESTING
-
-    IO.popen("/usr/sbin/sendmail -i -t", "w+") do |sm|
-      sm.print email
-      sm.flush
-    end
-  end
+  # An Array of all the http status codes for the log file
 
-  def self.envelope(recipient, subject = nil) # :nodoc:
-    envelope = {}
-    envelope['To'] = recipient
-    envelope['Subject'] = subject || "pl_analyze"
-    envelope['Content-Type'] = "text/html"
+  attr_reader :http_statuses
+  
+  ##
+  # Ignore URL http:// as this can be used by monitoring apps to check if the application is working
 
-    return envelope.map { |(k,v)| "#{k}: #{v}" }
-  end
+  attr_accessor :ignore_missing_domain
 
   ##
-  # Creates a new Analyzer that will read data from +logfile_name+.
+  # Creates a new Analyzer that finds the most relevant +count+ requests.
 
-  def initialize(logfile_name)
-    @logfile_name  = logfile_name
+  def initialize(count = 20)
     @request_times = Hash.new { |h,k| h[k] = [] }
     @db_times      = Hash.new { |h,k| h[k] = [] }
     @render_times  = Hash.new { |h,k| h[k] = [] }
+    @http_statuses = Hash.new { |h,k| h[k] = [] }
+    @times         = Hash.new { |h,k| h[k] = [] }
+    @urls          = Hash.new { |h,k| h[k] = [] }
+
+    @analyzed_log_files = []
+
+    @count = count
+    @ignore_missing_domain = false
   end
 
   ##
-  # Processes the log file collecting statistics from each found LogEntry.
-
-  def process
-    File.open @logfile_name do |fp|
-      LogParser.parse fp do |entry|
-        entry_page = entry.page
-        next if entry_page.nil?
-        @request_times[entry_page] << entry.request_time
-        @db_times[entry_page] << entry.db_time
-        @render_times[entry_page] << entry.render_time
-      end
+  # Processes the log files collecting statistics from each found LogEntry.
+
+  def process( log_file, progress = false, log_format = :autodetect )
+    Log::Parser.parse_file(log_file, progress, log_format) do |entry|
+      entry_page = entry.page
+      next if entry_page.nil?
+      next if @ignore_missing_domain and entry.url == "http://"
+      @request_times[entry_page] << entry.request_time
+      @db_times[entry_page] << entry.db_time
+      @render_times[entry_page] << entry.render_time
+      @http_statuses[entry_page] << entry.http_status
+      @times[entry_page] << entry.time
+      @urls[entry_page] << entry.url
     end
+
+    @analyzed_log_files << log_file
+
+    return self
   end
 
   ##
@@ -210,8 +197,8 @@
   ##
   # The +limit+ slowest total request times.
 
-  def slowest_request_times(limit = 10)
-    return slowest_times(@request_times, limit)
+  def slowest_request_times
+    return slowest_times(@request_times)
   end
 
   ##
@@ -231,8 +218,8 @@
   ##
   # The +limit+ slowest total database times.
 
-  def slowest_db_times(limit = 10)
-    return slowest_times(@db_times, limit)
+  def slowest_db_times
+    return slowest_times(@db_times)
   end
 
   ##
@@ -252,8 +239,8 @@
   ##
   # The +limit+ slowest total render times for all requests.
 
-  def slowest_render_times(limit = 10)
-    return slowest_times(@render_times, limit)
+  def slowest_render_times
+    return slowest_times(@render_times)
   end
 
   ##
@@ -278,17 +265,84 @@
   end
 
   ##
-  # Builds a report containing +count+ slow items.
+  # A list of count/min/max/avg/std dev for request times 
+  # for a certain controller.
+
+  def controller_times_summary controller
+    controller_times = @request_times.dup.delete_if{ |k, v| !(k =~ Regexp.new("^#{controller}#")) }
+
+    return "Controller '#{controller}' had no requests" if controller_times.empty?
+    return summarize("#{controller}", controller_times)
+  end
+
+  ##
+  # List the http status codes and their names of the 
+  # requests analyzed, sorted by number encountered.
+
+  def request_statuses_summary
+    text = []
+    text << "HTTP Status Codes Summary"
+    columns = ['Code', "HTTP\t", 'Number', 'Fra']
+    text << columns.join("\t")
+
+    number = @http_statuses.values.flatten.size
+
+    counter = Hash.new
+
+    @http_statuses.each do |entry_page, statuses|
+      statuses.compact!
+      statuses.each do |status|
+        counter[status] = (counter[status] || 0) + 1
+      end
+    end
+
+    require 'net/http'
+    counter.sort{|a,b| a[1]<=>b[1]}.reverse.each do |count|
+      # get HTTP status code name from ruby lib for better readability
+      http_code = Net::HTTPResponse::CODE_TO_OBJ[count[0].to_s].to_s.slice(9,20) || ""
+      line =  [count[0], (http_code.length > 7) ? http_code : "#{http_code}\t", count[1], '%0.2f%' % (count[1].to_f * 100 / number)]
+      text << line.join("\t")
+    end
+
+    return text.join("\n")
+  end
+
+    
+  ##
+  # Builds a report.
 
-  def report(count)
+  def report special_controllers = []
     return "No requests to analyze" if request_times.empty?
 
     text = []
 
+    text << "Log files analyzed:"
+    @analyzed_log_files.each{ |f| text << f}
+    text << nil
+
+    text << "URL http:// ignored!" << nil if @ignore_missing_domain
+
+    first = @times.values.flatten.min
+    last = @times.values.flatten.max
+    text << "First request:         #{first}"
+    text << "Last request:          #{last}"
+    req_per_sec = @times.values.flatten.size.to_f / (last - first)
+    text << "Requests per second/minute/hour:   #{'%0.2f' % req_per_sec}/#{'%0.2f' % (req_per_sec * 60)}/#{'%0.2f' % (req_per_sec * 3600)}"
+
+    text << nil << "-" * 72 << nil
+
+    text << request_statuses_summary
+
+    text << nil << "-" * 72 << nil
+
+    special_controllers.each do |controller|
+      text << controller_times_summary(controller) << nil << '-' * 72 << nil
+    end
+
     text << request_times_summary
     text << nil
     text << "Slowest Request Times:"
-    slowest_request_times(count).each do |time, name|
+    slowest_request_times.each do |time, name|
       text << "\t#{name} took #{'%0.3f' % time}s"
     end
     text << nil
@@ -298,7 +352,7 @@
     text << db_times_summary
     text << nil
     text << "Slowest Total DB Times:"
-    slowest_db_times(count).each do |time, name|
+    slowest_db_times.each do |time, name|
       text << "\t#{name} took #{'%0.3f' % time}s"
     end
     text << nil
@@ -308,7 +362,7 @@
     text << render_times_summary
     text << nil
     text << "Slowest Total Render Times:"
-    slowest_render_times(count).each do |time, name|
+    slowest_render_times.each do |time, name|
       text << "\t#{name} took #{'%0.3f' % time}s"
     end
     text << nil
@@ -323,32 +377,37 @@
     list = []
 
     # header
-    record = [pad_request_name("#{title} Summary"), 'Count', 'Avg', 'Std Dev',
+    record = [pad_request_name("#{title} Summary"), 'Count', 'Fra', 'Avg', 'Std Dev',
               'Min', 'Max']
     list << record.join("\t")
 
     # all requests
     times = records.values.flatten
+    num_records = times.size
     record = [times.average, times.standard_deviation, times.min, times.max]
     record.map! { |v| "%0.3f" % v }
-    record.unshift [pad_request_name('ALL REQUESTS'), times.size]
+    record.unshift [pad_request_name('ALL REQUESTS'), times.size, "100%"]
     list << record.join("\t")
 
     # spacer
     list << nil
 
-    records.sort_by { |k,v| v.size}.reverse_each do |req, times|
+    sorted_records = records.sort_by{ |k,v| v.size}.reverse[0..@count-1]
+
+    sorted_records.each do |req, times|
       record = [times.average, times.standard_deviation, times.min, times.max]
       record.map! { |v| "%0.3f" % v }
-      record.unshift ["#{pad_request_name req}", times.size]
+      record.unshift ["#{pad_request_name req}", times.size, "%0.1f%" % (times.size.to_f * 100 / num_records)]
       list << record.join("\t")
     end
 
+    list << "... [#{records.size - sorted_records.size} not shown]" if sorted_records.size < records.size
+
     return list.join("\n")
   end
 
-  def slowest_times(records, limit) # :nodoc:
-    slowest_times = SlowestTimes.new limit
+  def slowest_times(records) # :nodoc:
+    slowest_times = SlowestTimes.new @count
 
     records.each do |name, times|
       times.each do |time|
diff -Nur ../production_log_analyzer-1.5.0/lib/production_log/email.rb ./lib/production_log/email.rb
--- ../production_log_analyzer-1.5.0/lib/production_log/email.rb	1970-01-01 01:00:00.000000000 +0100
+++ ./lib/production_log/email.rb	2007-06-06 12:15:27.000000000 +0200
@@ -0,0 +1,21 @@
+def email report, addresses, subject = nil
+  email = envelope(addresses, subject).map { |(k,v)| "#{k}: #{v}" }
+  email << nil
+  email << "<pre>#{report}</pre>"
+  email = email.join($/) << $/
+end
+
+def envelope addresses, subject = nil
+  envelope = {}
+  envelope['To'] = (addresses.class == Array) ? addresses.join(', ') : addresses
+  envelope['Subject'] = subject || $0
+  envelope['Content-Type'] = "text/html"
+  envelope
+end
+
+def send_mail email
+  IO.popen("/usr/sbin/sendmail -i -t", "w+") do |sm|
+    sm.print email
+    sm.flush
+  end
+end
diff -Nur ../production_log_analyzer-1.5.0/lib/production_log/parser.rb ./lib/production_log/parser.rb
--- ../production_log_analyzer-1.5.0/lib/production_log/parser.rb	2007-06-15 10:19:53.000000000 +0200
+++ ./lib/production_log/parser.rb	2007-06-08 12:58:22.000000000 +0200
@@ -1,13 +1,14 @@
 ##
-# LogParser parses a Syslog log file looking for lines logged by the 'rails'
-# program.  A typical log line looks like this:
+# Log::Parser parses a log file looking for lines logged by the 'rails'
+# program.  A typical syslog log line looks like this:
 #
 #   Mar  7 00:00:20 online1 rails[59600]: Person Load (0.001884)   SELECT * FROM people WHERE id = 10519 LIMIT 1
 #
-# LogParser does not work with Rails' default logger because there is no way
-# to group all the log output of a single request.  You must use SyslogLogger.
+# Log::Parser now also works with native rails logs. However, as there is 
+# no way to group log records whose lines are mixed up, it assumes 
+# that this is not the case and that all requests are logged in sequence.
 
-module LogParser
+module Log
 
   ##
   # LogEntry contains a summary of log data for a single request.
@@ -51,6 +52,16 @@
     attr_reader :db_time
 
     ##
+    # Http return status code
+    
+    attr_reader :http_status
+
+    ##
+    # URL served by this request
+
+    attr_reader :url
+
+    ##
     # Creates a new LogEntry from the log data in +entry+.
 
     def initialize(entry)
@@ -62,6 +73,8 @@
       @render_time = 0
       @db_time = 0
       @in_component = 0
+      @http_status = nil
+      @url = nil
 
       parse entry
     end
@@ -80,19 +93,23 @@
           next if @in_component > 0
           @page = $1
           @ip   = $2
-          @time = $3
-        when /^Completed in ([\S]+) .+ Rendering: ([\S]+) .+ DB: ([\S]+)/ then
+          @time = Time.parse($3)
+        when /^Completed in ([\S]+) .+ Rendering: ([\S]+) .+ DB: ([\S]+)( .+ ([\d]{3,3}) \D*\[(\S*).*\])?/ then
           next if @in_component > 0
           @request_time = $1.to_f
           @render_time = $2.to_f
           @db_time = $3.to_f
-        when /^Completed in ([\S]+) .+ DB: ([\S]+)/ then # Redirect
+          @http_status = $5.to_i if $5
+          @url = $6 if $6
+        when /^Completed in ([\S]+) .+ DB: ([\S]+)( .+ ([\d]{3,3}) \D*\[(\S*).*\])?/ then # Redirect
           next if @in_component > 0
           @request_time = $1.to_f
           @render_time = 0
           @db_time = $2.to_f
-        when /(.+?) \(([^)]+)\)   / then
-          @queries << [$1, $2.to_f]
+          @http_status = $4.to_i if $4
+          @url = $5 if $5
+#        when /(.+?) \(([^)]+)\)   / then # commmented out as parsing speed can suffer a lot by this
+#          @queries << [$1, $2.to_f]
         when /^Start rendering component / then
           @in_component += 1
         when /^End of component rendering$/ then
@@ -117,42 +134,151 @@
 
   end
 
-  ##
-  # Parses IO stream +stream+, creating a LogEntry for each recognizable log
-  # entry.
-  #
-  # Log entries are recognised as starting with Processing, continuing with
-  # the same process id through Completed.
-
-  def self.parse(stream) # :yields: log_entry
-    buckets = Hash.new { |h,k| h[k] = [] }
-    comp_count = Hash.new 0
+  class Parser
+
+    def initialize
+      @buckets = Hash.new { |h,k| h[k] = [] }
+      @comp_count = Hash.new 0
+
+      @entry = []
+    end
+
+    ##
+    # Parse log lines, using format +log_format+. Expects +log+ to respond to each_line
+
+    def self.parse(log, log_format)
+      parser = Parser.new
+
+      case log_format
+      when :rails
+        log.each_line do |line|
+          parser.parse_rails(line) do |entry|
+            yield entry
+          end
+        end
+      when :syslog
+        log.each_line do |line|
+          parser.parse_syslog(line) do |entry|
+            yield entry
+          end
+        end
+      end
+    end
+
+    ##
+    # Parse one log file. If +progress+ is set, display progress information (this 
+    # requires the 'progressbar' gem to be installed). Log format may be :rails or 
+    # :syslog; if no format is specified, autodetection is used.
+
+    def self.parse_file(log_file, progress = false, log_format = :autodetect)
+      begin
+        log_format = detect_log_format log_file if log_format == :autodetect or 
+                                                   log_format.nil?
+      rescue RuntimeError
+        return        
+      end
+
+      parser = Parser.new
+      
+      if progress
+        num_lines = File.size(log_file) / 84
+        require 'progressbar'
+        pbar = ProgressBar.new("parsing", num_lines)
+      end
+      
+      case log_format
+      when :rails
+        File.open(log_file).each_line do |line|
+          parser.parse_rails(line) do |entry|
+            yield entry
+          end
+          pbar.inc if progress
+        end
+      when :syslog
+        File.open(log_file).each_line do |line|
+          parser.parse_syslog(line) do |entry|
+            yield entry
+          end
+          pbar.inc if progress
+        end
+        parser.yield_incomplete_buckets do |entry|
+          yield entry
+        end
+      end
+      pbar.finish if progress
+    end
+
+    ##
+    # Parse one syslog log line. Log entries are recognised as 
+    # starting with Processing, continuing with the same process id 
+    # through Completed.
 
-    stream.each_line do |line|
+    def parse_syslog(line) # :yields: log_entry
+     
       line =~ / ([^ ]+) ([^ ]+)\[(\d+)\]: (.*)/
-      next if $2.nil? or $2 == 'newsyslog'
+      return if $2.nil? or $2 == 'newsyslog'
+
       bucket = [$1, $2, $3].join '-'
       data = $4
 
-      buckets[bucket] << data
+      @buckets[bucket] << data
 
       case data
       when /^Start rendering component / then
-        comp_count[bucket] += 1
+        @comp_count[bucket] += 1
       when /^End of component rendering$/ then
-        comp_count[bucket] -= 1
+        @comp_count[bucket] -= 1
       when /^Completed/ then
-        next unless comp_count[bucket] == 0
-        entry = buckets.delete bucket
-        next unless entry.any? { |l| l =~ /^Processing/ }
+        return unless @comp_count[bucket] == 0
+        entry =  @buckets.delete bucket
+        return unless entry.any? { |l| l =~ /^Processing/ }
         yield LogEntry.new(entry)
+      when /^ActionController::RoutingError/
+        @buckets.delete bucket
       end
     end
 
-    buckets.each do |bucket, data|
-      yield LogEntry.new(data)
+    def yield_incomplete_buckets #:nodoc:
+      @buckets.each do |bucket, data|
+        yield LogEntry.new(data)
+      end
     end
-  end
 
+    ##
+    # Parse one rails log line.
+
+    def parse_rails(line) # :yields: log_entry
+      return unless line =~ /[^ ]/ # no empty lines
+
+      @entry << line
+
+      case line
+      when /^Completed/
+        yield LogEntry.new(@entry)
+        @entry = []
+      when /^ActionController::RoutingError/
+        @entry = []
+      end
+    end
+
+    ##
+    # Detect the log format of the given +log_file+. Returns 
+    # :rails or :syslog if detected correcty.
+
+    def self.detect_log_format log_file
+      File.open(log_file) do |file|
+        15.times do
+          case file.gets
+          when /^Processing /
+            return :rails
+          when / ([^ ]+) ([^ ]+)\[(\d+)\]: (.*)/
+            return :syslog
+          end
+        end
+      end
+      raise "Unable to determine log format for #{log_file}"
+    end
+
+  end
 end
 
diff -Nur ../production_log_analyzer-1.5.0/Manifest.txt ./Manifest.txt
--- ../production_log_analyzer-1.5.0/Manifest.txt	2007-06-15 10:19:53.000000000 +0200
+++ ./Manifest.txt	2007-06-14 18:32:10.000000000 +0200
@@ -9,10 +9,15 @@
 lib/production_log/action_grep.rb
 lib/production_log/analyzer.rb
 lib/production_log/parser.rb
+lib/production_log/email.rb
 test/test.syslog.0.14.x.log
 test/test.syslog.1.2.shortname.log
-test/test.syslog.empty.log
+test/test.empty.log
 test/test.syslog.log
 test/test_action_grep.rb
 test/test_analyzer.rb
+test/analyzer_report.txt
 test/test_parser.rb
+test/test.railslog.log
+test/test.railslog.route_error.log
+test/test_email.rb
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/bin/action_errors ./pkg/production_log_analyzer-1.5.4/bin/action_errors
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/bin/action_errors	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/bin/action_errors	2007-05-16 23:37:35.000000000 +0200
@@ -0,0 +1,46 @@
+#!/usr/local/bin/ruby -ws
+
+$h ||= false
+$r ||= false
+$o ||= false
+
+$r = $r ? ($r.to_i rescue false) : false
+
+if $h then
+  $stderr.puts "Usage: #{$0} [-r=N] LOGFILE"
+  $stderr.puts "\t-r=N\tShow routing errors with N or more occurances"
+  $stderr.puts "\t-o\tShow errors with one occurance"
+  exit
+end
+
+errors = {}
+counts = Hash.new 0
+
+ARGF.each_line do |line|
+  line =~ /\]: (.*?)     (.*)/
+  next if $1.nil?
+  msg = $1
+  trace = $2
+  key = msg.gsub(/\d/, '#')
+  counts[key] += 1
+  next if counts[key] > 1
+  trace = trace.split('     ')[0..-2].map { |l| l.strip }.join("\n\t")
+  error = "#{msg}\n\t#{trace}"
+  errors[key] = error
+end
+
+counts.sort_by { |_,c| -c }.each do |key, count|
+  next if count == 1 and not $o
+  error = errors[key]
+
+  if error =~ /^ActionController::RoutingError/ then
+    next unless $r
+    next if $r and count < $r
+  end
+
+  puts "count: #{count}"
+  puts "{{{"
+  puts error
+  puts "}}}"
+end
+
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/bin/action_grep ./pkg/production_log_analyzer-1.5.4/bin/action_grep
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/bin/action_grep	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/bin/action_grep	2007-05-16 23:37:35.000000000 +0200
@@ -0,0 +1,19 @@
+#!/usr/local/bin/ruby -w
+
+require 'production_log/action_grep'
+
+action_name = ARGV.shift
+file_name = ARGV.shift
+
+if action_name.nil? or file_name.nil? then
+  puts "Usage: #{$0} action_name file_name"
+  exit 1
+end
+
+begin
+  ActionGrep.grep action_name, file_name
+rescue ArgumentError => e
+  puts e
+  exit 1
+end
+
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/bin/pl_analyze ./pkg/production_log_analyzer-1.5.4/bin/pl_analyze
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/bin/pl_analyze	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/bin/pl_analyze	2007-06-14 18:36:50.000000000 +0200
@@ -0,0 +1,56 @@
+#!/usr/local/bin/ruby -w
+
+require 'production_log/analyzer'
+require 'production_log/email'
+
+log_files = []
+email_addresses = []
+count = 15
+progress = false
+subject = nil
+ignore_missing_domain = false
+log_format = nil
+special_controllers = []
+
+while !ARGV.empty?
+  arg = ARGV.shift
+  case arg
+  when /^(--format|-f)$/
+    log_format = ARGV.shift.to_sym
+  when /^(--email|-e)$/
+    email_addresses << ARGV.shift
+  when /^(--subject|-s)$/
+    subject = ARGV.shift
+  when /^(--count|-s)$/
+    count = ARGV.shift.to_i
+  when /^(--progress|-p)$/
+    progress = true
+  when /^(--ignore-missing-domain|-i)$/
+    ignore_missing_domain = true
+  when /^(--special-controller|-c)$/
+    special_controllers << ARGV.shift
+  else
+    log_files << arg
+  end
+end
+
+if log_files.empty? 
+  puts "Usage: #{$0} [--format|-f autodetect|rails|syslog] [--email|-e email_address] [--subject|-s email_subject] [--count|-c num_entries] [--progress|-p] [--ignore-missing-domain|-i] [--special-controller|-c] log_files"
+else
+  analyzer = Analyzer.new count
+  analyzer.ignore_missing_domain = ignore_missing_domain
+
+  log_files.each do |log_file|
+    analyzer.process log_file, progress, log_format
+  end
+
+  report = analyzer.report special_controllers
+  puts report
+
+  if !email_addresses.empty?
+    body = email report, email_addresses, subject
+    send_mail body
+  end
+end
+
+
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/History.txt ./pkg/production_log_analyzer-1.5.4/History.txt
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/History.txt	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/History.txt	2007-06-14 18:43:46.000000000 +0200
@@ -0,0 +1,46 @@
+= 1.5.4
+
+* Support for rails logs
+* Analyzing multiple logs at the same time
+* List of most frequently encountered http codes
+* Better command line handling
+* Optional progress bar while parsing
+* Optional ignoring of http:// URLs without domain
+* Requests per second, time of first and last request logged
+* Separate statistic for additional controllers
+* Remove dependency on rails_analyzer_tools
+
+= 1.5.0
+
+* Fixed empty log bug.  Patch by Tim Lucas.
+* Fixed bug where sometimes lines would be logged before the
+  Processing line.  Patch by Geoff Grosenbach.
+
+= 1.4.0
+
+* Switched to Hoe
+* Allowed action_errors to suppress routing errors with > 3 occurances
+* action_grep now works correctly with components
+* pl_analyze now works correctly with components
+* Added action_errors to extract error counts from logs
+* Retabbed to match the rest of the world
+
+= 1.3.0
+
+* Added action_grep
+* Added support for newer log format
+
+= 1.2.0
+
+* pl_analyze calculates per-action statistics
+* pl_analyze can send an email with its output
+
+= 1.1.0
+
+* RDoc
+* Other various fixes lost to time.
+
+= 1.0.0
+
+* Birthday!
+
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/lib/production_log/action_grep.rb ./pkg/production_log_analyzer-1.5.4/lib/production_log/action_grep.rb
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/lib/production_log/action_grep.rb	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/lib/production_log/action_grep.rb	2007-05-16 23:37:35.000000000 +0200
@@ -0,0 +1,42 @@
+module ActionGrep; end
+
+class << ActionGrep
+
+  def grep(action_name, file_name)
+    unless action_name =~ /\A([A-Z][A-Za-z\d]*)(?:#([A-Za-z]\w*))?\Z/ then
+      raise ArgumentError, "Invalid action name #{action_name} expected something like SomeController#action"
+    end
+
+    unless File.file? file_name and File.readable? file_name then
+      raise ArgumentError, "Unable to read #{file_name}"
+    end
+
+    buckets = Hash.new { |h,k| h[k] = [] }
+    comp_count = Hash.new 0
+
+    File.open file_name do |fp|
+      fp.each_line do |line|
+        line =~ / ([^ ]+) ([^ ]+)\[(\d+)\]: (.*)/
+        next if $2.nil? or $2 == 'newsyslog'
+        bucket = [$1, $2, $3].join '-'
+        data = $4
+
+        buckets[bucket] << line
+
+        case data
+        when /^Start rendering component / then
+          comp_count[bucket] += 1
+        when /^End of component rendering$/ then
+          comp_count[bucket] -= 1
+        when /^Completed/ then
+          next unless comp_count[bucket] == 0
+          action = buckets.delete bucket
+          next unless action.any? { |l| l =~ /: Processing #{action_name}/ }
+          puts action.join
+        end
+      end
+    end
+  end
+
+end
+
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/lib/production_log/analyzer.rb ./pkg/production_log_analyzer-1.5.4/lib/production_log/analyzer.rb
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/lib/production_log/analyzer.rb	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/lib/production_log/analyzer.rb	2007-06-07 18:34:17.000000000 +0200
@@ -0,0 +1,455 @@
+$TESTING = false unless defined? $TESTING
+
+require 'production_log/parser'
+
+module Enumerable
+
+  ##
+  # Sum of all the elements of the Enumerable
+
+  def sum
+    return self.inject(0) { |acc, i| acc + i }
+  end
+
+  ##
+  # Average of all the elements of the Enumerable
+  #
+  # The Enumerable must respond to #length
+
+  def average
+    return self.sum / self.length.to_f
+  end
+
+  ##
+  # Sample variance of all the elements of the Enumerable
+  #
+  # The Enumerable must respond to #length
+
+  def sample_variance
+    avg = self.average
+    sum = self.inject(0) { |acc, i| acc + (i - avg) ** 2 }
+    return (1 / self.length.to_f * sum)
+  end
+
+  ##
+  # Standard deviation of all the elements of the Enumerable
+  #
+  # The Enumerable must respond to #length
+
+  def standard_deviation
+    return Math.sqrt(self.sample_variance)
+  end
+
+end
+
+##
+# A list that only stores +limit+ items.
+
+class SizedList < Array
+
+  ##
+  # Creates a new SizedList that can hold up to +limit+ items.  Whenever
+  # adding a new item to the SizedList would make the list larger than
+  # +limit+, +delete_block+ is called.
+  #
+  # +delete_block+ is passed the list and the item being added.
+  # +delete_block+ must take action to remove an item and return true or
+  # return false if the item should not be added to the list.
+
+  def initialize(limit, &delete_block)
+    @limit = limit
+    @delete_block = delete_block
+  end
+
+  ##
+  # Attempts to add +obj+ to the list.
+
+  def <<(obj)
+    return super if self.length < @limit
+    return super if @delete_block.call self, obj
+  end
+
+end
+
+##
+# Stores +limit+ time/object pairs, keeping only the largest +limit+ items.
+#
+# Sample usage:
+#
+#   l = SlowestTimes.new 5
+#   
+#   l << [Time.now, 'one']
+#   l << [Time.now, 'two']
+#   l << [Time.now, 'three']
+#   l << [Time.now, 'four']
+#   l << [Time.now, 'five']
+#   l << [Time.now, 'six']
+#
+#   p l.map { |i| i.last }
+
+class SlowestTimes < SizedList
+
+  ##
+  # Creates a new SlowestTimes SizedList that holds only +limit+ time/object
+  # pairs.
+
+  def initialize(limit)
+    super limit do |arr, new_item|
+      fastest_time = arr.sort_by { |time, name| time }.first
+      if fastest_time.first < new_item.first then
+        arr.delete_at index(fastest_time)
+        true
+      else
+        false
+      end
+    end
+  end
+
+end
+
+##
+# Calculates statistics for production logs.
+
+class Analyzer
+
+  ##
+  # The logfile being read by the Analyzer.
+
+  attr_reader :logfile_name
+
+  ##
+  # An Array of all the request total times for the log file.
+
+  attr_reader :request_times
+
+  ##
+  # An Array of all the request database times for the log file.
+
+  attr_reader :db_times
+
+  ##
+  # An Array of all the request render times for the log file.
+
+  attr_reader :render_times
+
+  ##
+  # An Array of all the http status codes for the log file
+
+  attr_reader :http_statuses
+  
+  ##
+  # Ignore URL http:// as this can be used by monitoring apps to check if the application is working
+
+  attr_accessor :ignore_missing_domain
+
+  ##
+  # Creates a new Analyzer that finds the most relevant +count+ requests.
+
+  def initialize(count = 20)
+    @request_times = Hash.new { |h,k| h[k] = [] }
+    @db_times      = Hash.new { |h,k| h[k] = [] }
+    @render_times  = Hash.new { |h,k| h[k] = [] }
+    @http_statuses = Hash.new { |h,k| h[k] = [] }
+    @times         = Hash.new { |h,k| h[k] = [] }
+    @urls          = Hash.new { |h,k| h[k] = [] }
+
+    @analyzed_log_files = []
+
+    @count = count
+    @ignore_missing_domain = false
+  end
+
+  ##
+  # Processes the log files collecting statistics from each found LogEntry.
+
+  def process( log_file, progress = false, log_format = :autodetect )
+    Log::Parser.parse_file(log_file, progress, log_format) do |entry|
+      entry_page = entry.page
+      next if entry_page.nil?
+      next if @ignore_missing_domain and entry.url == "http://"
+      @request_times[entry_page] << entry.request_time
+      @db_times[entry_page] << entry.db_time
+      @render_times[entry_page] << entry.render_time
+      @http_statuses[entry_page] << entry.http_status
+      @times[entry_page] << entry.time
+      @urls[entry_page] << entry.url
+    end
+
+    @analyzed_log_files << log_file
+
+    return self
+  end
+
+  ##
+  # The average total request time for all requests.
+
+  def average_request_time
+    return time_average(@request_times)
+  end
+
+  ##
+  # The standard deviation of the total request time for all requests.
+
+  def request_time_std_dev
+    return time_std_dev(@request_times)
+  end
+
+  ##
+  # The +limit+ slowest total request times.
+
+  def slowest_request_times
+    return slowest_times(@request_times)
+  end
+
+  ##
+  # The average total database time for all requests.
+
+  def average_db_time
+    return time_average(@db_times)
+  end
+
+  ##
+  # The standard deviation of the total database time for all requests.
+
+  def db_time_std_dev
+    return time_std_dev(@db_times)
+  end
+
+  ##
+  # The +limit+ slowest total database times.
+
+  def slowest_db_times
+    return slowest_times(@db_times)
+  end
+
+  ##
+  # The average total render time for all requests.
+
+  def average_render_time
+    return time_average(@render_times)
+  end
+
+  ##
+  # The standard deviation of the total render time for all requests.
+
+  def render_time_std_dev
+    return time_std_dev(@render_times)
+  end
+
+  ##
+  # The +limit+ slowest total render times for all requests.
+
+  def slowest_render_times
+    return slowest_times(@render_times)
+  end
+
+  ##
+  # A list of count/min/max/avg/std dev for request times.
+
+  def request_times_summary
+    return summarize("Request Times", @request_times)
+  end
+
+  ##
+  # A list of count/min/max/avg/std dev for database times.
+
+  def db_times_summary
+    return summarize("DB Times", @db_times)
+  end
+
+  ##
+  # A list of count/min/max/avg/std dev for request times.
+
+  def render_times_summary
+    return summarize("Render Times", @render_times)
+  end
+
+  ##
+  # A list of count/min/max/avg/std dev for request times 
+  # for a certain controller.
+
+  def controller_times_summary controller
+    controller_times = @request_times.dup.delete_if{ |k, v| !(k =~ Regexp.new("^#{controller}#")) }
+
+    return "Controller '#{controller}' had no requests" if controller_times.empty?
+    return summarize("#{controller}", controller_times)
+  end
+
+  ##
+  # List the http status codes and their names of the 
+  # requests analyzed, sorted by number encountered.
+
+  def request_statuses_summary
+    text = []
+    text << "HTTP Status Codes Summary"
+    columns = ['Code', "HTTP\t", 'Number', 'Fra']
+    text << columns.join("\t")
+
+    number = @http_statuses.values.flatten.size
+
+    counter = Hash.new
+
+    @http_statuses.each do |entry_page, statuses|
+      statuses.compact!
+      statuses.each do |status|
+        counter[status] = (counter[status] || 0) + 1
+      end
+    end
+
+    require 'net/http'
+    counter.sort{|a,b| a[1]<=>b[1]}.reverse.each do |count|
+      # get HTTP status code name from ruby lib for better readability
+      http_code = Net::HTTPResponse::CODE_TO_OBJ[count[0].to_s].to_s.slice(9,20) || ""
+      line =  [count[0], (http_code.length > 7) ? http_code : "#{http_code}\t", count[1], '%0.2f%' % (count[1].to_f * 100 / number)]
+      text << line.join("\t")
+    end
+
+    return text.join("\n")
+  end
+
+    
+  ##
+  # Builds a report.
+
+  def report special_controllers = []
+    return "No requests to analyze" if request_times.empty?
+
+    text = []
+
+    text << "Log files analyzed:"
+    @analyzed_log_files.each{ |f| text << f}
+    text << nil
+
+    text << "URL http:// ignored!" << nil if @ignore_missing_domain
+
+    first = @times.values.flatten.min
+    last = @times.values.flatten.max
+    text << "First request:         #{first}"
+    text << "Last request:          #{last}"
+    req_per_sec = @times.values.flatten.size.to_f / (last - first)
+    text << "Requests per second/minute/hour:   #{'%0.2f' % req_per_sec}/#{'%0.2f' % (req_per_sec * 60)}/#{'%0.2f' % (req_per_sec * 3600)}"
+
+    text << nil << "-" * 72 << nil
+
+    text << request_statuses_summary
+
+    text << nil << "-" * 72 << nil
+
+    special_controllers.each do |controller|
+      text << controller_times_summary(controller) << nil << '-' * 72 << nil
+    end
+
+    text << request_times_summary
+    text << nil
+    text << "Slowest Request Times:"
+    slowest_request_times.each do |time, name|
+      text << "\t#{name} took #{'%0.3f' % time}s"
+    end
+    text << nil
+    text << "-" * 72
+    text << nil
+
+    text << db_times_summary
+    text << nil
+    text << "Slowest Total DB Times:"
+    slowest_db_times.each do |time, name|
+      text << "\t#{name} took #{'%0.3f' % time}s"
+    end
+    text << nil
+    text << "-" * 72
+    text << nil
+
+    text << render_times_summary
+    text << nil
+    text << "Slowest Total Render Times:"
+    slowest_render_times.each do |time, name|
+      text << "\t#{name} took #{'%0.3f' % time}s"
+    end
+    text << nil
+
+    return text.join($/)
+  end
+
+  private unless $TESTING
+
+  def summarize(title, records) # :nodoc:
+    record = nil
+    list = []
+
+    # header
+    record = [pad_request_name("#{title} Summary"), 'Count', 'Fra', 'Avg', 'Std Dev',
+              'Min', 'Max']
+    list << record.join("\t")
+
+    # all requests
+    times = records.values.flatten
+    num_records = times.size
+    record = [times.average, times.standard_deviation, times.min, times.max]
+    record.map! { |v| "%0.3f" % v }
+    record.unshift [pad_request_name('ALL REQUESTS'), times.size, "100%"]
+    list << record.join("\t")
+
+    # spacer
+    list << nil
+
+    sorted_records = records.sort_by{ |k,v| v.size}.reverse[0..@count-1]
+
+    sorted_records.each do |req, times|
+      record = [times.average, times.standard_deviation, times.min, times.max]
+      record.map! { |v| "%0.3f" % v }
+      record.unshift ["#{pad_request_name req}", times.size, "%0.1f%" % (times.size.to_f * 100 / num_records)]
+      list << record.join("\t")
+    end
+
+    list << "... [#{records.size - sorted_records.size} not shown]" if sorted_records.size < records.size
+
+    return list.join("\n")
+  end
+
+  def slowest_times(records) # :nodoc:
+    slowest_times = SlowestTimes.new @count
+
+    records.each do |name, times|
+      times.each do |time|
+        slowest_times << [time, name]
+      end
+    end
+
+    return slowest_times.sort_by { |time, name| time }.reverse
+  end
+
+  def time_average(records) # :nodoc:
+    times = records.values.flatten
+    times.delete 0
+    return times.average
+  end
+
+  def time_std_dev(records) # :nodoc:
+    times = records.values.flatten
+    times.delete 0
+    return times.standard_deviation
+  end
+
+  def longest_request_name # :nodoc:
+    return @longest_req if defined? @longest_req
+
+    names = @request_times.keys.map do |name|
+      (name||'Unknown').length + 1 # + : - HACK where does nil come from?
+    end
+
+    @longest_req = names.max
+
+    @longest_req = 'Unknown'.length + 1 if @longest_req.nil?
+
+    return @longest_req
+  end
+
+  def pad_request_name(name) # :nodoc:
+    name = (name||'Unknown') + ':' # HACK where does nil come from?
+    padding_width = longest_request_name - name.length
+    padding_width = 0 if padding_width < 0
+    name += (' ' * padding_width)
+  end
+
+end
+
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/lib/production_log/email.rb ./pkg/production_log_analyzer-1.5.4/lib/production_log/email.rb
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/lib/production_log/email.rb	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/lib/production_log/email.rb	2007-06-06 12:15:27.000000000 +0200
@@ -0,0 +1,21 @@
+def email report, addresses, subject = nil
+  email = envelope(addresses, subject).map { |(k,v)| "#{k}: #{v}" }
+  email << nil
+  email << "<pre>#{report}</pre>"
+  email = email.join($/) << $/
+end
+
+def envelope addresses, subject = nil
+  envelope = {}
+  envelope['To'] = (addresses.class == Array) ? addresses.join(', ') : addresses
+  envelope['Subject'] = subject || $0
+  envelope['Content-Type'] = "text/html"
+  envelope
+end
+
+def send_mail email
+  IO.popen("/usr/sbin/sendmail -i -t", "w+") do |sm|
+    sm.print email
+    sm.flush
+  end
+end
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/lib/production_log/parser.rb ./pkg/production_log_analyzer-1.5.4/lib/production_log/parser.rb
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/lib/production_log/parser.rb	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/lib/production_log/parser.rb	2007-06-08 12:58:22.000000000 +0200
@@ -0,0 +1,284 @@
+##
+# Log::Parser parses a log file looking for lines logged by the 'rails'
+# program.  A typical syslog log line looks like this:
+#
+#   Mar  7 00:00:20 online1 rails[59600]: Person Load (0.001884)   SELECT * FROM people WHERE id = 10519 LIMIT 1
+#
+# Log::Parser now also works with native rails logs. However, as there is 
+# no way to group log records whose lines are mixed up, it assumes 
+# that this is not the case and that all requests are logged in sequence.
+
+module Log
+
+  ##
+  # LogEntry contains a summary of log data for a single request.
+
+  class LogEntry
+
+    ##
+    # Controller and action for this request
+
+    attr_reader :page
+
+    ##
+    # Requesting IP
+
+    attr_reader :ip
+
+    ##
+    # Time the request was made
+
+    attr_reader :time
+
+    ##
+    # Array of SQL queries containing query type and time taken.  The
+    # complete text of the SQL query is not saved to reduct memory usage.
+
+    attr_reader :queries
+
+    ##
+    # Total request time, including database, render and other.
+
+    attr_reader :request_time
+
+    ##
+    # Total render time.
+
+    attr_reader :render_time
+
+    ##
+    # Total database time
+
+    attr_reader :db_time
+
+    ##
+    # Http return status code
+    
+    attr_reader :http_status
+
+    ##
+    # URL served by this request
+
+    attr_reader :url
+
+    ##
+    # Creates a new LogEntry from the log data in +entry+.
+
+    def initialize(entry)
+      @page = nil
+      @ip = nil
+      @time = nil
+      @queries = []
+      @request_time = 0
+      @render_time = 0
+      @db_time = 0
+      @in_component = 0
+      @http_status = nil
+      @url = nil
+
+      parse entry
+    end
+
+    ##
+    # Extracts log data from +entry+, which is an Array of lines from the
+    # same request.
+
+    def parse(entry)
+      entry.each do |line|
+        case line
+        when /^Parameters/, /^Cookie set/, /^Rendering/,
+          /^Redirected/ then
+          # nothing
+        when /^Processing ([\S]+) \(for (.+) at (.*)\)/ then
+          next if @in_component > 0
+          @page = $1
+          @ip   = $2
+          @time = Time.parse($3)
+        when /^Completed in ([\S]+) .+ Rendering: ([\S]+) .+ DB: ([\S]+)( .+ ([\d]{3,3}) \D*\[(\S*).*\])?/ then
+          next if @in_component > 0
+          @request_time = $1.to_f
+          @render_time = $2.to_f
+          @db_time = $3.to_f
+          @http_status = $5.to_i if $5
+          @url = $6 if $6
+        when /^Completed in ([\S]+) .+ DB: ([\S]+)( .+ ([\d]{3,3}) \D*\[(\S*).*\])?/ then # Redirect
+          next if @in_component > 0
+          @request_time = $1.to_f
+          @render_time = 0
+          @db_time = $2.to_f
+          @http_status = $4.to_i if $4
+          @url = $5 if $5
+#        when /(.+?) \(([^)]+)\)   / then # commmented out as parsing speed can suffer a lot by this
+#          @queries << [$1, $2.to_f]
+        when /^Start rendering component / then
+          @in_component += 1
+        when /^End of component rendering$/ then
+          @in_component -= 1
+        when /^Fragment hit: / then
+        else # noop
+#          raise "Can't handle #{line.inspect}" if $TESTING
+        end
+      end
+    end
+
+    def ==(other) # :nodoc:
+      other.class == self.class and
+      other.page == self.page and
+      other.ip == self.ip and
+      other.time == self.time and
+      other.queries == self.queries and
+      other.request_time == self.request_time and
+      other.render_time == self.render_time and
+      other.db_time == self.db_time
+    end
+
+  end
+
+  class Parser
+
+    def initialize
+      @buckets = Hash.new { |h,k| h[k] = [] }
+      @comp_count = Hash.new 0
+
+      @entry = []
+    end
+
+    ##
+    # Parse log lines, using format +log_format+. Expects +log+ to respond to each_line
+
+    def self.parse(log, log_format)
+      parser = Parser.new
+
+      case log_format
+      when :rails
+        log.each_line do |line|
+          parser.parse_rails(line) do |entry|
+            yield entry
+          end
+        end
+      when :syslog
+        log.each_line do |line|
+          parser.parse_syslog(line) do |entry|
+            yield entry
+          end
+        end
+      end
+    end
+
+    ##
+    # Parse one log file. If +progress+ is set, display progress information (this 
+    # requires the 'progressbar' gem to be installed). Log format may be :rails or 
+    # :syslog; if no format is specified, autodetection is used.
+
+    def self.parse_file(log_file, progress = false, log_format = :autodetect)
+      begin
+        log_format = detect_log_format log_file if log_format == :autodetect or 
+                                                   log_format.nil?
+      rescue RuntimeError
+        return        
+      end
+
+      parser = Parser.new
+      
+      if progress
+        num_lines = File.size(log_file) / 84
+        require 'progressbar'
+        pbar = ProgressBar.new("parsing", num_lines)
+      end
+      
+      case log_format
+      when :rails
+        File.open(log_file).each_line do |line|
+          parser.parse_rails(line) do |entry|
+            yield entry
+          end
+          pbar.inc if progress
+        end
+      when :syslog
+        File.open(log_file).each_line do |line|
+          parser.parse_syslog(line) do |entry|
+            yield entry
+          end
+          pbar.inc if progress
+        end
+        parser.yield_incomplete_buckets do |entry|
+          yield entry
+        end
+      end
+      pbar.finish if progress
+    end
+
+    ##
+    # Parse one syslog log line. Log entries are recognised as 
+    # starting with Processing, continuing with the same process id 
+    # through Completed.
+
+    def parse_syslog(line) # :yields: log_entry
+     
+      line =~ / ([^ ]+) ([^ ]+)\[(\d+)\]: (.*)/
+      return if $2.nil? or $2 == 'newsyslog'
+
+      bucket = [$1, $2, $3].join '-'
+      data = $4
+
+      @buckets[bucket] << data
+
+      case data
+      when /^Start rendering component / then
+        @comp_count[bucket] += 1
+      when /^End of component rendering$/ then
+        @comp_count[bucket] -= 1
+      when /^Completed/ then
+        return unless @comp_count[bucket] == 0
+        entry =  @buckets.delete bucket
+        return unless entry.any? { |l| l =~ /^Processing/ }
+        yield LogEntry.new(entry)
+      when /^ActionController::RoutingError/
+        @buckets.delete bucket
+      end
+    end
+
+    def yield_incomplete_buckets #:nodoc:
+      @buckets.each do |bucket, data|
+        yield LogEntry.new(data)
+      end
+    end
+
+    ##
+    # Parse one rails log line.
+
+    def parse_rails(line) # :yields: log_entry
+      return unless line =~ /[^ ]/ # no empty lines
+
+      @entry << line
+
+      case line
+      when /^Completed/
+        yield LogEntry.new(@entry)
+        @entry = []
+      when /^ActionController::RoutingError/
+        @entry = []
+      end
+    end
+
+    ##
+    # Detect the log format of the given +log_file+. Returns 
+    # :rails or :syslog if detected correcty.
+
+    def self.detect_log_format log_file
+      File.open(log_file) do |file|
+        15.times do
+          case file.gets
+          when /^Processing /
+            return :rails
+          when / ([^ ]+) ([^ ]+)\[(\d+)\]: (.*)/
+            return :syslog
+          end
+        end
+      end
+      raise "Unable to determine log format for #{log_file}"
+    end
+
+  end
+end
+
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/LICENSE.txt ./pkg/production_log_analyzer-1.5.4/LICENSE.txt
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/LICENSE.txt	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/LICENSE.txt	2007-05-16 23:37:35.000000000 +0200
@@ -0,0 +1,27 @@
+Copyright 2005, 2007 Eric Hodel, The Robot Co-op.  All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the names of the authors nor the names of their contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/Manifest.txt ./pkg/production_log_analyzer-1.5.4/Manifest.txt
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/Manifest.txt	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/Manifest.txt	2007-06-14 18:32:10.000000000 +0200
@@ -0,0 +1,23 @@
+History.txt
+LICENSE.txt
+Manifest.txt
+README.txt
+Rakefile
+bin/action_errors
+bin/action_grep
+bin/pl_analyze
+lib/production_log/action_grep.rb
+lib/production_log/analyzer.rb
+lib/production_log/parser.rb
+lib/production_log/email.rb
+test/test.syslog.0.14.x.log
+test/test.syslog.1.2.shortname.log
+test/test.empty.log
+test/test.syslog.log
+test/test_action_grep.rb
+test/test_analyzer.rb
+test/analyzer_report.txt
+test/test_parser.rb
+test/test.railslog.log
+test/test.railslog.route_error.log
+test/test_email.rb
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/Rakefile ./pkg/production_log_analyzer-1.5.4/Rakefile
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/Rakefile	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/Rakefile	2007-06-14 18:41:12.000000000 +0200
@@ -0,0 +1,15 @@
+require 'hoe'
+
+$:.unshift './lib'
+require 'production_log/analyzer'
+
+Hoe.new 'production_log_analyzer', '1.5.4' do |p|
+  p.summary = p.paragraphs_of('README.txt', 1).join ' '
+  p.description = p.paragraphs_of('README.txt', 7).join ' '
+  p.author = 'Eric Hodel'
+  p.email = 'drbrain@segment7.net'
+  p.url = p.paragraphs_of('README.txt', 2).join ' '
+
+  p.rubyforge_name = 'seattlerb'
+end
+
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/README.txt ./pkg/production_log_analyzer-1.5.4/README.txt
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/README.txt	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/README.txt	2007-06-05 17:43:36.000000000 +0200
@@ -0,0 +1,156 @@
+= production_log_analyzer
+
+production_log_analyzer lets you find out which actions on a Rails
+site are used most and which ones are slowing your server down.
+
+http://seattlerb.rubyforge.org/production_log_analyzer
+
+http://rubyforge.org/projects/seattlerb
+
+Bug reports:
+
+http://rubyforge.org/tracker/?func=add&group_id=1513&atid=5921
+
+== About
+
+production_log_analyzer provides three tools to analyze log files
+created by rails applications.  pl_analyze can be used for getting daily reports,
+action_grep for pulling log lines for a single action and
+action_errors to summarize errors with counts.
+
+The PL Analyzer also includes action_grep which lets you grab lines from a log
+that only match a single action.
+
+  action_grep RssController#uber /var/log/production.log
+
+The log formats syslog and rails native are supported. PL Analyzer will by 
+default try to detect the log file format automatically.
+
+Warning: as of now, action_grep and action_errors don't support the 
+rails log format
+
+== Installing
+
+  sudo gem install production_log_analyzer
+
+=== Setup
+
+You might want to use syslog for logging your production rails app as that 
+offers you features such as remote logging. Instructions to set up SyslogLogger 
+with rails are provided here:
+
+http://seattlerb.rubyforge.org/SyslogLogger/
+
+Then:
+
+Set up a cronjob (or something like that) to run log files through pl_analyze.
+
+== Using pl_analyze
+
+pl_analyze offers a number of command line options to change its behaviour. To 
+see a list of options, simply execute pl_analyze.
+
+Basic use:
+
+Run pl_analyze with the name of one or more log files to analyze.
+
+  pl_analyze /var/log/production.log
+
+To see a progress bar while parsing, use the option --progress (this requires the 
+gem 'progressbar' to be installed).
+
+  pl_analyze --progress /var/log/production.log
+
+If you want, you can run it from a cron something like this:
+
+  /usr/bin/gzip -dc /var/log/production.log.0.gz | /usr/local/bin/pl_analyze /dev/stdin
+
+Or, have pl_analyze email you (which is preferred, because tabs get preserved):
+
+  /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"`"
+
+In the future, pl_analyze will be able to read from STDIN.
+
+== Sample output
+
+  Request Times Summary:          Count   Avg     Std Dev Min     Max
+  ALL REQUESTS:                   11      0.576   0.508   0.000   1.470
+  
+  ThingsController#view:          3       0.716   0.387   0.396   1.260
+  TeamsController#progress:       2       0.841   0.629   0.212   1.470
+  RssController#uber:             2       0.035   0.000   0.035   0.035
+  PeopleController#progress:      2       0.489   0.489   0.000   0.977
+  PeopleController#view:          2       0.731   0.371   0.360   1.102
+  
+  Average Request Time: 0.634
+  Request Time Std Dev: 0.498
+  
+  Slowest Request Times:
+          TeamsController#progress took 1.470s
+          ThingsController#view took 1.260s
+          PeopleController#view took 1.102s
+          PeopleController#progress took 0.977s
+          ThingsController#view took 0.492s
+          ThingsController#view took 0.396s
+          PeopleController#view took 0.360s
+          TeamsController#progress took 0.212s
+          RssController#uber took 0.035s
+          RssController#uber took 0.035s
+  
+  ------------------------------------------------------------------------
+  
+  DB Times Summary:               Count   Avg     Std Dev Min     Max
+  ALL REQUESTS:                   11      0.366   0.393   0.000   1.144
+  
+  ThingsController#view:          3       0.403   0.362   0.122   0.914
+  TeamsController#progress:       2       0.646   0.497   0.149   1.144
+  RssController#uber:             2       0.008   0.000   0.008   0.008
+  PeopleController#progress:      2       0.415   0.415   0.000   0.830
+  PeopleController#view:          2       0.338   0.149   0.189   0.486
+  
+  Average DB Time: 0.402
+  DB Time Std Dev: 0.394
+  
+  Slowest Total DB Times:
+          TeamsController#progress took 1.144s
+          ThingsController#view took 0.914s
+          PeopleController#progress took 0.830s
+          PeopleController#view took 0.486s
+          PeopleController#view took 0.189s
+          ThingsController#view took 0.173s
+          TeamsController#progress took 0.149s
+          ThingsController#view took 0.122s
+          RssController#uber took 0.008s
+          RssController#uber took 0.008s
+  
+  ------------------------------------------------------------------------
+  
+  Render Times Summary:           Count   Avg     Std Dev Min     Max
+  ALL REQUESTS:                   11      0.219   0.253   0.000   0.695
+  
+  ThingsController#view:          3       0.270   0.171   0.108   0.506
+  TeamsController#progress:       2       0.000   0.000   0.000   0.000
+  RssController#uber:             2       0.012   0.000   0.012   0.012
+  PeopleController#progress:      2       0.302   0.302   0.000   0.604
+  PeopleController#view:          2       0.487   0.209   0.278   0.695
+  
+  Average Render Time: 0.302
+  Render Time Std Dev: 0.251
+  
+  Slowest Total Render Times:
+          PeopleController#view took 0.695s
+          PeopleController#progress took 0.604s
+          ThingsController#view took 0.506s
+          PeopleController#view took 0.278s
+          ThingsController#view took 0.197s
+          ThingsController#view took 0.108s
+          RssController#uber took 0.012s
+          RssController#uber took 0.012s
+          TeamsController#progress took 0.000s
+          TeamsController#progress took 0.000s
+
+== What's missing
+
+* More reports
+* Read from STDIN
+
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/analyzer_report.txt ./pkg/production_log_analyzer-1.5.4/test/analyzer_report.txt
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/analyzer_report.txt	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/test/analyzer_report.txt	2007-06-06 10:14:10.000000000 +0200
@@ -0,0 +1,56 @@
+Log files analyzed:
+test/test.syslog.log
+
+First request:         Mon Mar 07 07:00:25 +0100 2005
+Last request:          Mon Mar 07 07:00:32 +0100 2005
+Requests per second/minute/hour:   1.57/94.29/5657.14
+
+------------------------------------------------------------------------
+
+HTTP Status Codes Summary
+Code	HTTP		Number	Fra
+
+------------------------------------------------------------------------
+
+Request Times Summary:    	Count	Fra	Avg	Std Dev	Min	Max
+ALL REQUESTS:             	11	100%	0.576	0.508	0.000	1.470
+
+ThingsController#view:    	3	27.3%	0.716	0.387	0.396	1.260
+TeamsController#progress: 	2	18.2%	0.841	0.629	0.212	1.470
+RssController#uber:       	2	18.2%	0.035	0.000	0.035	0.035
+... [2 not shown]
+
+Slowest Request Times:
+	TeamsController#progress took 1.470s
+	ThingsController#view took 1.260s
+	PeopleController#view took 1.102s
+
+------------------------------------------------------------------------
+
+DB Times Summary:         	Count	Fra	Avg	Std Dev	Min	Max
+ALL REQUESTS:             	11	100%	0.366	0.393	0.000	1.144
+
+ThingsController#view:    	3	27.3%	0.403	0.362	0.122	0.914
+TeamsController#progress: 	2	18.2%	0.646	0.497	0.149	1.144
+RssController#uber:       	2	18.2%	0.008	0.000	0.008	0.008
+... [2 not shown]
+
+Slowest Total DB Times:
+	TeamsController#progress took 1.144s
+	ThingsController#view took 0.914s
+	PeopleController#progress took 0.830s
+
+------------------------------------------------------------------------
+
+Render Times Summary:     	Count	Fra	Avg	Std Dev	Min	Max
+ALL REQUESTS:             	11	100%	0.219	0.253	0.000	0.695
+
+ThingsController#view:    	3	27.3%	0.270	0.171	0.108	0.506
+TeamsController#progress: 	2	18.2%	0.000	0.000	0.000	0.000
+RssController#uber:       	2	18.2%	0.012	0.000	0.012	0.012
+... [2 not shown]
+
+Slowest Total Render Times:
+	PeopleController#view took 0.695s
+	PeopleController#progress took 0.604s
+	ThingsController#view took 0.506s
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test_action_grep.rb ./pkg/production_log_analyzer-1.5.4/test/test_action_grep.rb
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test_action_grep.rb	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/test/test_action_grep.rb	2007-05-16 23:37:36.000000000 +0200
@@ -0,0 +1,68 @@
+#!/usr/local/bin/ruby -w
+
+require 'stringio'
+require 'tempfile'
+require 'test/unit'
+
+require 'production_log/action_grep'
+
+class TestActionGrep < Test::Unit::TestCase
+
+  def setup
+    @syslog_file_name = File.expand_path(File.join(File.dirname(__FILE__),
+                                                   'test.syslog.log'))
+  end
+
+  def test_module_grep
+    old_stdout = $stdout.dup
+    stdout = StringIO.new
+    $stdout = stdout
+
+    ActionGrep.grep 'RssController', @syslog_file_name
+
+    stdout.rewind
+
+    lines = stdout.readlines
+
+    assert_equal 19, lines.length
+
+  ensure
+    $stdout = old_stdout
+  end
+
+  def test_module_grep_arguments
+    file = Tempfile.new File.basename(__FILE__)
+
+    assert_raises ArgumentError do
+      ActionGrep.grep 'Foo_Controller', '/tmp/no_such_file/no_really/'
+    end
+
+    assert_raises ArgumentError do
+      ActionGrep.grep 'FooController#5', '/tmp/no_such_file/no_really/'
+    end
+
+    assert_raises ArgumentError do
+      ActionGrep.grep '5', '/tmp/no_such_file/no_really/'
+    end
+
+    assert_raises ArgumentError do
+      ActionGrep.grep 'FooController', '/tmp/no_such_file/no_really'
+    end
+
+    assert_nothing_raised do
+      ActionGrep.grep 'FooController', file.path
+      ActionGrep.grep 'FooController5', file.path
+      ActionGrep.grep 'FooController#action', file.path
+      ActionGrep.grep 'FooController#action_thingy', file.path
+      ActionGrep.grep 'FooController#action_thingy5', file.path
+      ActionGrep.grep 'FooController5#action', file.path
+      ActionGrep.grep 'FooController5#action_thingy', file.path
+      ActionGrep.grep 'FooController5#action_thingy5', file.path
+    end
+
+  ensure
+    file.close
+  end
+
+end
+
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test_analyzer.rb ./pkg/production_log_analyzer-1.5.4/test/test_analyzer.rb
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test_analyzer.rb	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/test/test_analyzer.rb	2007-06-14 18:23:50.000000000 +0200
@@ -0,0 +1,305 @@
+#!/usr/local/bin/ruby -w
+
+$TESTING = true
+
+require 'test/unit'
+
+require 'production_log/analyzer'
+
+# hack String so the tests don't fail because there are tabs/spaces in the way
+class String
+  def ==(string)
+    return false if !string.respond_to?(:to_s)
+    return (self.gsub(/\s+/, ' ') <=> string.gsub(/\s+/, ' ')) == 0
+  end
+end
+
+class TestEnumerable < Test::Unit::TestCase
+  def test_sum
+    assert_equal 45, (1..9).sum
+  end
+
+  def test_average
+    # Ranges don't have a length
+    assert_in_delta 5.0, (1..9).to_a.average, 0.01
+  end
+
+  def test_sample_variance
+    assert_in_delta 6.6666, (1..9).to_a.sample_variance, 0.0001
+  end
+
+  def test_standard_deviation
+    assert_in_delta 2.5819, (1..9).to_a.standard_deviation, 0.0001
+  end
+end
+
+class TestSizedList < Test::Unit::TestCase
+
+  def setup
+    @list = SizedList.new 10 do |arr,|
+      arr.delete_at 0
+    true
+    end
+  end
+
+  def test_append
+    assert_equal [], @list.entries
+
+    (1..10).each { |i| @list << i }
+    assert_equal 10, @list.length
+    assert_equal((1..10).to_a, @list.entries)
+
+    @list << 11
+    assert_equal 10, @list.length
+    assert_equal((2..11).to_a, @list.entries)
+  end
+
+end
+
+class TestSlowestTimes < Test::Unit::TestCase
+
+  def setup
+    @list = SlowestTimes.new 10
+  end
+
+  def test_that_it_works
+    expected = []
+
+    10.downto(1) do |i|
+      @list << [i, nil]
+      expected << [i, nil]
+    end
+
+    assert_equal expected, @list.entries
+
+    @list << [11, nil]
+    expected.pop
+    expected.push [11, nil]
+
+    assert_equal 10, @list.length
+    assert_equal expected, @list.entries
+
+    @list << [0, nil]
+
+    assert_equal expected, @list.entries
+  end
+
+end
+
+class TestAnalyzer < Test::Unit::TestCase
+
+  def setup
+    @analyzer = Analyzer.new 3
+    @analyzer.process 'test/test.syslog.log'
+  end
+
+  def test_average_db_time
+    assert_in_delta 0.4023761, @analyzer.average_db_time, 0.0000001
+  end
+
+  def test_average_render_time
+    assert_in_delta 0.3015584, @analyzer.average_render_time, 0.0000001
+  end
+
+  def test_average_request_time
+    assert_in_delta 0.6338176, @analyzer.average_request_time, 0.0000001
+  end
+
+  def test_db_time_std_dev
+    assert_in_delta 0.3941380, @analyzer.db_time_std_dev, 0.0000001
+  end
+
+  def test_db_times_summary
+    expected = <<EOF.strip
+DB Times Summary:               Count   Fra     Avg     Std Dev Min     Max
+ALL REQUESTS:                   11      100%    0.366   0.393   0.000   1.144
+
+ThingsController#view:          3       27.3%   0.403   0.362   0.122   0.914
+TeamsController#progress:       2       18.2%   0.646   0.497   0.149   1.144
+RssController#uber:             2       18.2%   0.008   0.000   0.008   0.008
+... [2 not shown]
+EOF
+
+    assert_equal expected, @analyzer.db_times_summary
+  end
+
+  def test_empty_syslog
+    analyzer = Analyzer.new
+    assert_nothing_raised do
+      analyzer.process 'test/test.empty.log'
+      analyzer.report
+    end
+    assert_equal "No requests to analyze", analyzer.report
+  end
+
+  def test_longest_request_name
+    assert_equal false, @analyzer.instance_variables.include?('@longest_req')
+
+    request_times = {
+      "ThingsController#view"     => [0],
+      "TeamsController#progress"  => [1],
+      "RssController#uber"        => [0],
+      "PeopleController#progress" => [0],
+      nil                         => [0],
+    }
+
+    @analyzer.instance_variable_set('@request_times', request_times)
+
+    assert_equal 26, @analyzer.longest_request_name
+  end
+
+  def test_pad_request_name
+    assert_equal 26, @analyzer.longest_request_name
+    assert_equal("PeopleController#view:    ",
+                 @analyzer.pad_request_name("PeopleController#view"))
+  end
+
+  def test_pad_request_name_nil
+    assert_equal 26, @analyzer.longest_request_name
+    assert_equal("Unknown:                  ",
+                 @analyzer.pad_request_name(nil))
+  end
+
+  def test_pad_request_name_short
+    analyzer = Analyzer.new
+    analyzer.process 'test/test.syslog.1.2.shortname.log'
+    longer_request_name_value = " " * (analyzer.longest_request_name + 1)
+    assert_nothing_raised do
+      analyzer.pad_request_name(longer_request_name_value)
+    end
+    assert_equal longer_request_name_value + ":", analyzer.pad_request_name(longer_request_name_value)
+  end
+
+  def test_process
+    expected_request_times = {
+      "PeopleController#view"     => [1.102098, 0.36021],
+      "ThingsController#view"     => [0.396183, 0.49176, 1.259728],
+      "TeamsController#progress"  => [1.469788, 0.211973],
+      "RssController#uber"        => [0.034519, 0.034519],
+      "PeopleController#progress" => [0.977398, 0]
+    }
+    assert_equal expected_request_times, @analyzer.request_times
+
+    expected_db_times = {
+      "PeopleController#view"     => [0.486258, 0.189119],
+      "ThingsController#view"     => [0.122158, 0.172767, 0.914192],
+      "TeamsController#progress"  => [1.143577, 0.149357],
+      "RssController#uber"        => [0.007962, 0.007962],
+      "PeopleController#progress" => [0.830409, 0]
+    }
+    assert_equal expected_db_times, @analyzer.db_times
+
+    expected_render_times = {
+      "PeopleController#view"     => [0.695476, 0.277921],
+      "ThingsController#view"     => [0.107987, 0.197126, 0.505973],
+      "TeamsController#progress"  => [0, 0],
+      "RssController#uber"        => [0.01177, 0.01177],
+      "PeopleController#progress" => [0.604444, 0]
+    }
+    assert_equal expected_render_times, @analyzer.render_times
+  end
+
+  def test_render_time_std_dev
+    assert_in_delta 0.2513925, @analyzer.render_time_std_dev, 0.0000001
+  end
+
+  def test_render_times_summary
+    expected = <<EOF.strip
+Render Times Summary:           Count   Fra     Avg     Std Dev Min     Max
+ALL REQUESTS:                   11      100%    0.219   0.253   0.000   0.695
+
+ThingsController#view:          3       27.3%   0.270   0.171   0.108   0.506
+TeamsController#progress:       2       18.2%   0.000   0.000   0.000   0.000
+RssController#uber:             2       18.2%   0.012   0.000   0.012   0.012
+... [2 not shown]
+EOF
+
+    assert_equal expected, @analyzer.render_times_summary
+  end
+
+  def test_report
+    assert_equal File.open("test/analyzer_report.txt").read, @analyzer.report
+  end
+
+  def test_request_time_std_dev
+    assert_in_delta 0.4975667, @analyzer.request_time_std_dev, 0.0000001
+  end
+
+  def test_request_times_summary
+    expected = <<EOF.strip
+Request Times Summary:          Count   Fra     Avg     Std Dev Min     Max
+ALL REQUESTS:                   11      100%    0.576   0.508   0.000   1.470
+
+ThingsController#view:          3       27.3%   0.716   0.387   0.396   1.260
+TeamsController#progress:       2       18.2%   0.841   0.629   0.212   1.470
+RssController#uber:             2       18.2%   0.035   0.000   0.035   0.035
+... [2 not shown]
+EOF
+
+    assert_equal expected, @analyzer.request_times_summary
+  end
+
+  def test_slowest_db_times
+    times = @analyzer.slowest_db_times
+    assert_equal 3, times.length
+    expected = [
+      [1.143577, "TeamsController#progress"],
+      [0.914192, "ThingsController#view"],
+      [0.830409, "PeopleController#progress"]
+    ]
+    assert_equal expected, times
+  end
+
+  def test_slowest_request_times
+    times = @analyzer.slowest_request_times
+    assert_equal 3, times.length
+    expected = [
+      [1.469788, "TeamsController#progress"],
+      [1.259728, "ThingsController#view"],
+      [1.102098, "PeopleController#view"]
+    ]
+    assert_equal expected, times
+  end
+
+  def test_slowest_render_times
+    times = @analyzer.slowest_render_times
+    assert_equal 3, times.length
+    expected = [
+      [0.695476, "PeopleController#view"],
+      [0.604444, "PeopleController#progress"],
+      [0.505973, "ThingsController#view"]
+    ]
+    assert_equal expected, times
+  end
+
+  def test_http_statuses
+    statuses = Analyzer.new.process('test/test.railslog.log').http_statuses
+    assert_equal 3, statuses.values.flatten.size
+    expected = {"MessageController#index"=>[302], "AccountController#login"=>[302, 302]}
+    assert_equal expected, statuses
+  end
+
+  def test_request_statuses_summary
+    statuses_summary = Analyzer.new.process('test/test.railslog.log').request_statuses_summary
+    expected = <<EOF.strip
+HTTP Status Codes Summary
+Code    HTTP            Number  Fra
+302     Found           3       100.00%
+EOF
+    
+    assert_equal expected, statuses_summary    
+  end
+
+  def test_controller_times_summary
+    summary = Analyzer.new.process('test/test.syslog.log').controller_times_summary('TeamsController')
+    expected = <<EOF.strip
+TeamsController Summary:        Count   Fra     Avg     Std Dev Min     Max
+ALL REQUESTS:                   2       100%    0.841   0.629   0.212   1.470
+
+TeamsController#progress:       2       100.0%  0.841   0.629   0.212   1.470
+EOF
+
+    assert_equal expected, summary
+  end
+end
+
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test_email.rb ./pkg/production_log_analyzer-1.5.4/test/test_email.rb
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test_email.rb	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/test/test_email.rb	2007-06-06 12:18:50.000000000 +0200
@@ -0,0 +1,71 @@
+require 'production_log/email'
+
+class TestEmail < Test::Unit::TestCase
+  def test_email
+    report = Analyzer.new(1).process('test/test.syslog.log').report
+    email = email report, 'test@test.com', 'pl_analyze'
+    expected = <<EOF
+Subject: pl_analyze
+To: test@test.com
+Content-Type: text/html
+
+<pre>Log files analyzed:
+test/test.syslog.log
+
+First request:         Mon Mar 07 07:00:25 +0100 2005
+Last request:          Mon Mar 07 07:00:32 +0100 2005
+Requests per second/minute/hour:   1.57/94.29/5657.14
+
+------------------------------------------------------------------------
+
+HTTP Status Codes Summary
+Code  HTTP    Number  Fra
+
+------------------------------------------------------------------------
+
+Request Times Summary:      Count Fra Avg Std Dev Min Max
+ALL REQUESTS:               11  100%  0.576 0.508 0.000 1.470
+
+ThingsController#view:      3 27.3% 0.716 0.387 0.396 1.260
+... [4 not shown]
+
+Slowest Request Times:
+  TeamsController#progress took 1.470s
+
+  ------------------------------------------------------------------------
+
+  DB Times Summary:           Count Fra Avg Std Dev Min Max
+  ALL REQUESTS:               11  100%  0.366 0.393 0.000 1.144
+
+  ThingsController#view:      3 27.3% 0.403 0.362 0.122 0.914
+  ... [4 not shown]
+
+  Slowest Total DB Times:
+    TeamsController#progress took 1.144s
+
+    ------------------------------------------------------------------------
+
+    Render Times Summary:       Count Fra Avg Std Dev Min Max
+    ALL REQUESTS:               11  100%  0.219 0.253 0.000 0.695
+
+    ThingsController#view:      3 27.3% 0.270 0.171 0.108 0.506
+    ... [4 not shown]
+
+    Slowest Total Render Times:
+      PeopleController#view took 0.695s
+      </pre>
+EOF
+
+    assert_equal expected, email
+  end
+
+  def test_envelope
+    expected = {
+      'Subject' => 'pl_analyze',
+      'To' => 'test@test.com',
+      'Content-Type' => 'text/html' }
+    envelope = envelope 'test@test.com', 'pl_analyze'
+
+    assert_equal expected, envelope
+  end
+end
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.empty.log ./pkg/production_log_analyzer-1.5.4/test/test.empty.log
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.empty.log	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/test/test.empty.log	2007-06-15 10:24:45.000000000 +0200
@@ -0,0 +1 @@
+<dummy line>
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test_parser.rb ./pkg/production_log_analyzer-1.5.4/test/test_parser.rb
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test_parser.rb	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/test/test_parser.rb	2007-06-14 18:24:42.000000000 +0200
@@ -0,0 +1,327 @@
+#!/usr/local/bin/ruby -w
+
+$TESTING = true
+
+require 'tempfile'
+require 'test/unit'
+require 'stringio'
+require 'time'
+
+require 'production_log/parser'
+
+class TestLogEntry < Test::Unit::TestCase
+
+  def setup
+    request = <<EOF
+Processing TwinklerController#index (for 81.109.96.173 at Wed Dec 01 16:01:56 CST 2004)
+Parameters: {\"action\"=>\"index\", \"controller\"=>\"twinkler\"}
+Browser Load First (0.001114)   SELECT * FROM browsers WHERE ubid = 'ixsXHgUo7U9PJGgBzr7e9ocaDOc=' LIMIT 1
+Goal Count (0.001762)   SELECT COUNT(*) FROM goals WHERE browser_id = '96181' and is_active = 1 
+Rendering twinkler/index within layouts/default
+Rendering layouts/default (200 OK)
+Completed in 0.616122 (1 reqs/sec) | Rendering: 0.242475 (39%) | DB: 0.002876 (0%)
+EOF
+    @entry = Log::LogEntry.new request.split("\n")
+  end
+
+  def test_parse
+    request = <<EOF
+Processing RssController#uber (for 67.18.200.5 at Mon Mar 07 00:00:25 CST 2005)
+Parameters: {:id=>"author", :"rss/uber/author.html/uber/author"=>nil, :action=>"uber", :username=>"looch", :controller=>"rss"}
+Cookie set: auth=dc%2FGUP20BwziF%2BApGecc0pXB0PF0obi55az63ubAFtsnOOdJPkhfJH2U09yuzQD3WtdmWnydLzFcRA78kwi7Gw%3D%3D; path=/; expires=Thu, 05 Mar 2015 06:00:25 GMT
+Cookie set: ubid=kF05DqFH%2F9hRCOxTz%2Bfb8Q7UV%2FI%3D; path=/; expires=Thu, 05 Mar 2015 06:00:25 GMT
+Browser Load (0.003963)   SELECT * FROM browsers WHERE ubid = 'kF05DqFH/9hRCOxTz+fb8Q7UV/I=' LIMIT 1
+Person Load (0.002445)   SELECT * FROM people WHERE username = 'looch' AND active = '1' LIMIT 1
+ProfileImage Load (0.001554)   SELECT * FROM profile_images WHERE id = 2782 LIMIT 1
+Rendering rss/rss2.0 (200 OK)
+Completed in 0.034519 (28 reqs/sec) | Rendering: 0.011770 (34%) | DB: 0.007962 (23%)
+EOF
+    request = request.split "\n"
+
+    entry = Log::LogEntry.new request
+
+    assert_kind_of Log::LogEntry, entry
+    assert_equal "RssController#uber", entry.page
+#    assert_equal 3, entry.queries.length
+#    assert_equal ['Browser Load', 0.003963], entry.queries.first
+    assert_equal 0.034519, entry.request_time
+  end
+
+  def test_page
+    assert_equal "TwinklerController#index", @entry.page
+  end
+
+  def test_ip
+    assert_equal "81.109.96.173", @entry.ip
+  end
+
+  def test_time
+    assert_equal "Wed Dec 01 23:01:56 +0100 2004", @entry.time.to_s
+  end
+
+#  def test_queries
+#    expected = []
+#    expected << ["Browser Load First", 0.001114]
+#    expected << ["Goal Count", 0.001762]
+#    assert_equal expected, @entry.queries
+#  end
+
+  def test_request_time
+    assert_equal 0.616122, @entry.request_time
+
+    @entry = Log::LogEntry.new "Processing TwinklerController#add_thing (for 144.164.232.114 at Wed Dec 01 16:01:56 CST 2004)
+Completed in 0.261485 (3 reqs/sec) | DB: 0.009325 (3%)"
+
+    assert_equal 0.261485, @entry.request_time
+  end
+
+  def test_render_time
+    assert_equal 0.242475, @entry.render_time
+
+    @entry = Log::LogEntry.new "Processing TwinklerController#add_thing (for 144.164.232.114 at Wed Dec 01 16:01:56 CST 2004)
+Completed in 0.261485 (3 reqs/sec) | DB: 0.009325 (3%)"
+
+    assert_equal 0, @entry.render_time
+  end
+
+  def test_db_time
+    assert_equal 0.002876, @entry.db_time
+  end
+
+  def test_http_status
+    @entry_404 = Log::LogEntry.new "Completed in 0.01285 (77 reqs/sec) | Rendering: 0.00519 (40%) | DB: 0.00308 (23%) | 404 [http:// /]"
+    assert_equal 404, @entry_404.http_status
+
+    @entry_302 = Log::LogEntry.new "Completed in 0.04697 (21 reqs/sec) | DB: 0.01132 (24%) | 302 Found [http:// /]"
+    assert_equal 302, @entry_302.http_status
+
+    @entry_200 = Log::LogEntry.new "Completed in 0.04368 (22 reqs/sec) | Rendering: 0.00510 (11%) | DB: 0.00000 (0%) | 200 OK [http:// /]"
+    assert_equal 200, @entry_200.http_status
+  end
+
+  def test_url
+    entry = Log::LogEntry.new "Completed in 0.01285 (77 reqs/sec) | Rendering: 0.00519 (40%) | DB: 0.00308 (23%) | 404 [http://example.org/test/]"
+    assert_equal "http://example.org/test/", entry.url
+  end
+end
+
+class TestSyslogParser < Test::Unit::TestCase
+  include Log
+
+  def test_class_parse_syslog
+    log = StringIO.new <<-EOF
+Mar  7 00:00:25 online1 rails[59628]: Processing RssController#uber (for 67.18.200.5 at Mon Mar 07 00:00:25 CST 2005)
+Mar  7 00:00:25 online1 rails[59628]: Parameters: {:id=>"author", :"rss/uber/author.html/uber/author"=>nil, :action=>"uber", :username=>"looch", :controller=>"rss"}
+Mar  7 00:00:25 online1 rails[59628]: Cookie set: auth=dc%2FGUP20BwziF%2BApGecc0pXB0PF0obi55az63ubAFtsnOOdJPkhfJH2U09yuzQD3WtdmWnydLzFcRA78kwi7Gw%3D%3D; path=/; expires=Thu, 05 Mar 2015 06:00:25 GMT
+Mar  7 00:00:25 online1 rails[59628]: Cookie set: ubid=kF05DqFH%2F9hRCOxTz%2Bfb8Q7UV%2FI%3D; path=/; expires=Thu, 05 Mar 2015 06:00:25 GMT
+Mar  7 00:00:25 online1 rails[59628]: Browser Load (0.003963)   SELECT * FROM browsers WHERE ubid = 'kF05DqFH/9hRCOxTz+fb8Q7UV/I=' LIMIT 1
+Mar  7 00:00:25 online1 rails[59628]: Person Load (0.002445)   SELECT * FROM people WHERE username = 'looch' AND active = '1' LIMIT 1
+Mar  7 00:00:25 online1 rails[59628]: ProfileImage Load (0.001554)   SELECT * FROM profile_images WHERE id = 2782 LIMIT 1
+Mar  7 00:00:25 online1 rails[59628]: Rendering rss/rss2.0 (200 OK)
+Mar  7 00:00:25 online1 rails[59628]: Completed in 0.034519 (28 reqs/sec) | Rendering: 0.011770 (34%) | DB: 0.007962 (23%)
+        EOF
+
+    entries = []
+    
+    Parser.parse(log, :syslog) do |entry|
+      entries << entry
+    end
+
+    assert_equal 1, entries.length
+    assert_equal 'RssController#uber', entries.first.page
+  end
+
+  def test_class_parse_syslog_components
+    log = StringIO.new <<-EOF
+Jul 11 10:05:20 www rails[61243]: Processing ChatroomsController#launch (for 213.152.37.169 at Mon Jul 11 10:05:20 CDT 2005)
+Jul 11 10:05:20 www rails[61243]: Start rendering component ({:action=>"online_count", :controller=>"members"}):
+Jul 11 10:05:20 www rails[34216]: Processing ChatroomsController#launch (for 213.152.37.169 at Mon Jul 11 10:05:20 CDT 2005)
+Jul 11 10:05:20 www rails[34216]: Start rendering component ({:action=>"online_count", :controller=>"members"}):
+Jul 11 10:05:20 www rails[34216]: Processing MembersController#online_count (for 213.152.37.169 at Mon Jul 11 10:05:20 CDT 2005)
+Jul 11 10:05:20 www rails[34216]: Completed in 0.00741 (135 reqs/sec) | DB: 0.00320 (43%)
+Jul 11 10:05:20 www rails[34216]: End of component rendering
+Jul 11 10:05:28 www rails[34216]: Completed in 8.65005 (0 reqs/sec) | Rendering: 8.64820 (99%) | DB: 0.00000 (0%)
+Jul 11 10:05:20 www rails[34216]: Processing ChatroomsController#launch (for 213.152.37.169 at Mon Jul 11 10:05:20 CDT 2005)
+Jul 11 10:05:20 www rails[34216]: Start rendering component ({:action=>"online_count", :controller=>"members"}):
+Jul 11 10:05:20 www rails[34216]: Processing MembersController#online_count (for 213.152.37.169 at Mon Jul 11 10:05:20 CDT 2005)
+Jul 11 10:05:20 www rails[34216]: Completed in 0.00741 (135 reqs/sec) | DB: 0.00320 (43%)
+Jul 11 10:05:20 www rails[34216]: End of component rendering
+Jul 11 10:05:28 www rails[34216]: Completed in 8.65005 (0 reqs/sec) | Rendering: 8.64820 (99%) | DB: 0.00000 (0%)
+Jul 11 10:05:20 www rails[61243]: Processing MembersController#online_count (for 213.152.37.169 at Mon Jul 11 10:05:20 CDT 2005)
+Jul 11 10:05:20 www rails[61243]: Completed in 0.00741 (135 reqs/sec) | DB: 0.00320 (43%)
+Jul 11 10:05:20 www rails[61243]: End of component rendering
+Jul 11 10:05:28 www rails[61243]: Completed in 8.65005 (0 reqs/sec) | Rendering: 8.64820 (99%) | DB: 0.00000 (0%)
+        EOF
+
+    entries = []
+    Parser.parse(log, :syslog) { |entry| entries << entry }
+
+    assert_equal 3, entries.length
+    assert_equal 'ChatroomsController#launch', entries.first.page
+    assert_equal 8.65005, entries.first.request_time
+  end
+
+  def test_class_parse_syslog_entries_with_pre_processing_garbage
+    log = StringIO.new <<-EOF
+Jan 03 12:51:34 duo2 rails[4347]: [4;36;1mFont Load (0.000475)[0m   [0;1mSELECT * FROM fonts ORDER BY name [0m
+Jan 03 12:51:34 duo2 rails[4347]: Processing StylesheetsController#show (for 127.0.0.1 at 2007-01-03 12:51:34) [GET]
+Jan 03 12:51:34 duo2 rails[4347]: Parameters: {"action"=>"show", "id"=>"1", "controller"=>"stylesheets"}
+Jan 03 12:51:34 duo2 rails[4347]: [4;35;1mNewspaper Load (0.000970)[0m   [0mSELECT newspapers.* FROM newspapers INNER JOIN users ON newspapers.editor_in_chief = users.id WHERE (users.login = 'geoff') LIMIT 1[0m
+Jan 03 12:51:34 duo2 rails[4347]: [4;36;1mLayout Load (0.000501)[0m   [0;1mSELECT * FROM layouts WHERE (layouts.id = 1) LIMIT 1[0m
+Jan 03 12:51:34 duo2 rails[4347]: Completed in 0.00807 (123 reqs/sec) | Rendering: 0.00006 (0%) | DB: 0.00195 (24%) | 200 OK [http://geoff.localhost.com/stylesheets/show/1/styles.css]
+    EOF
+
+    entries = []
+    Parser.parse(log, :syslog) { |entry| entries << entry }
+
+    assert_equal 1, entries.length, "Number of entries was incorrect"
+    assert_equal 'StylesheetsController#show', entries.first.page
+    assert_equal 0.00807, entries.first.request_time
+  end
+
+  def test_class_parse_syslog_rails_engines_plugin
+    log = StringIO.new <<-EOF
+Jan 03 12:24:21 duo2 rails[4277]: Trying to start engine 'login_engine' from '/Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine'
+Jan 03 12:24:21 duo2 rails[4277]: adding /Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine/lib/login_engine to the load path
+Jan 03 12:24:21 duo2 rails[4277]: adding /Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine/app/views/user_notify to the load path
+Jan 03 12:24:21 duo2 rails[4277]: adding /Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine/app/views/user to the load path
+Jan 03 12:24:21 duo2 rails[4277]: adding /Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine/app/views to the load path
+Jan 03 12:24:21 duo2 rails[4277]: adding /Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine/app/models to the load path
+Jan 03 12:24:21 duo2 rails[4277]: adding /Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine/app/helpers to the load path
+Jan 03 12:24:21 duo2 rails[4277]: adding /Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine/app/controllers to the load path
+Jan 03 12:24:21 duo2 rails[4277]: Attempting to copy public engine files from '/Users/topfunky/web/rails/repos/roughunderbelly/config/../vendor/plugins/login_engine/public'
+Jan 03 12:24:21 duo2 rails[4277]: source dirs: ["/Users/topfunky/web/rails/repos/roughunderbelly/config/../vendor/plugins/login_engine/public/stylesheets"]
+Jan 03 12:24:22 duo2 rails[4277]: finally loading from application: 'exception_notifier.rb'
+Jan 03 12:24:22 duo2 rails[4277]: requiring file 'exception_notifier_helper'
+Jan 03 12:24:22 duo2 rails[4277]: checking 'login_engine' for /Users/topfunky/web/rails/repos/roughunderbelly/config/../vendor/plugins/login_engine/exception_notifier_helper.rb
+Jan 03 12:24:22 duo2 rails[4277]: finally loading from application: 'exception_notifier_helper.rb'
+Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: '/Users/topfunky/web/rails/repos/roughunderbelly/config/../app/controllers/application.rb'
+Jan 03 12:24:23 duo2 rails[4277]: requiring file 'application_helper'
+Jan 03 12:24:23 duo2 rails[4277]: checking 'login_engine' for /Users/topfunky/web/rails/repos/roughunderbelly/config/../vendor/plugins/login_engine/application_helper.rb
+Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'application_helper.rb'
+Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'exception_notifiable.rb'
+Jan 03 12:24:23 duo2 rails[4277]: requiring file 'user_helper'
+Jan 03 12:24:23 duo2 rails[4277]: checking 'login_engine' for /Users/topfunky/web/rails/repos/roughunderbelly/config/../vendor/plugins/login_engine/user_helper.rb
+Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'user_helper.rb'
+Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'user.rb'
+Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'task.rb'
+Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'client.rb'
+Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'email.rb'
+Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'worth.rb'
+Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'column_pref.rb'
+Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'timer.rb'
+Jan 03 12:24:23 duo2 rails[4277]: requiring file '/Users/topfunky/web/rails/repos/roughunderbelly/config/../app/controllers/tasks_controller.rb'
+Jan 03 12:24:23 duo2 rails[4277]: detected RAILS_ROOT, rewriting to 'app/controllers/tasks_controller.rb'
+Jan 03 12:24:23 duo2 rails[4277]: checking 'login_engine' for /Users/topfunky/web/rails/repos/roughunderbelly/config/../vendor/plugins/login_engine/app/controllers/tasks_controller.rb
+Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: '/Users/topfunky/web/rails/repos/roughunderbelly/config/../app/controllers/tasks_controller.rb'
+Jan 03 12:24:23 duo2 rails[4277]: requiring file 'tasks_helper'
+Jan 03 12:24:23 duo2 rails[4277]: checking 'login_engine' for /Users/topfunky/web/rails/repos/roughunderbelly/config/../vendor/plugins/login_engine/tasks_helper.rb
+Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'tasks_helper.rb'
+Jan 03 12:24:23 duo2 rails[4277]: requiring file 'sparklines_helper'
+Jan 03 12:24:23 duo2 rails[4277]: checking 'login_engine' for /Users/topfunky/web/rails/repos/roughunderbelly/config/../vendor/plugins/login_engine/sparklines_helper.rb
+Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'sparklines_helper.rb'
+Jan 03 12:24:24 duo2 rails[4277]: [4;36;1mSQL (0.000072)[0m   [0;1mBEGIN[0m
+Jan 03 12:24:24 duo2 rails[4277]: [4;35;1mSQL (0.000240)[0m   [0mINSERT INTO sessions (`updated_at`, `session_id`, `data`) VALUES('2007-01-03 20:24:24', 'bdbb75323d5da69f707d5576e907706e', 'BAh7AA==\n')[0m
+Jan 03 12:24:24 duo2 rails[4277]: [4;36;1mSQL (0.000400)[0m   [0;1mCOMMIT[0m
+Jan 03 12:24:24 duo2 rails[4277]: Processing TasksController#index (for 127.0.0.1 at 2007-01-03 12:24:24) [GET]
+Jan 03 12:24:24 duo2 rails[4277]: Parameters: {"action"=>"index", "controller"=>"tasks"}
+Jan 03 12:24:24 duo2 rails[4277]: Redirected to http://localhost:3000/tasks/list
+Jan 03 12:24:24 duo2 rails[4277]: Completed in 0.00112 (896 reqs/sec) | DB: 0.00071 (63%) | 302 Found [http://localhost/]
+    EOF
+
+    entries = []
+
+    Parser.parse(log, :syslog) do |entry|
+      entries << entry
+    end
+
+    assert_equal 1, entries.length, "The number of entries was incorrect"
+    assert_equal 'TasksController#index', entries.first.page
+    assert_equal 0.00112, entries.first.request_time
+  end
+
+  def test_class_parse_syslog_multi
+    entries = []
+    parser = Parser.new
+    Parser.parse_file('test/test.syslog.log', false, :syslog) do |entry|
+      entries << entry
+    end
+
+    assert_equal 12, entries.length
+    assert_equal 'RssController#uber', entries.first.page
+
+    redirect = entries[5]
+    assert_equal 'TeamsController#progress', redirect.page
+    assert_equal 0, redirect.render_time
+
+    last = entries.last
+    assert_equal 'PeopleController#progress', last.page
+    assert_equal 0, last.request_time
+  end
+
+  def test_class_parse_syslog_0_14_x
+    entries = []
+    parser = Parser.new
+    File.open 'test/test.syslog.0.14.x.log' do |fp|
+      parser.parse_syslog fp do |entry|
+        entries << entry
+      end
+    end
+  end
+end
+
+class TestLogParser < Test::Unit::TestCase
+  include Log
+  def test_class_detect_log_format
+    assert_equal :rails, Parser.detect_log_format('test/test.railslog.log')
+    assert_equal :syslog, Parser.detect_log_format('test/test.syslog.log')
+
+    assert_raise RuntimeError do
+      Parser.detect_log_format 'test/test.empty.log'
+    end
+  end
+
+  def test_class_parse_file_detects_format_correcly
+    entries = []
+    Parser.parse_file 'test/test.railslog.log' do |entry|
+      entries << entry
+    end
+
+    assert_equal 3, entries.length
+    assert_equal 'MessageController#index', entries.first.page
+
+    entries = []
+    Parser.parse_file 'test/test.syslog.log' do |entry|
+      entries << entry
+    end
+
+    assert_equal 12, entries.length
+    assert_equal 'RssController#uber', entries.first.page
+  end
+
+  def test_parsing_routing_error_does_not_yield_entry
+    entries = []
+    Parser.parse_file 'test/test.railslog.route_error.log' do |entry|
+      entries << entry
+    end
+
+    assert_equal 2, entries.size
+  end
+end
+  
+class TestRailslogParser < Test::Unit::TestCase
+  include Log
+  def test_class_parse_multi
+    entries = []
+    parser = Parser.new
+    File.open('test/test.railslog.log').each_line do |line|
+      parser.parse_rails line do |entry|
+        entries << entry
+      end
+    end
+
+    assert_equal 3, entries.length
+    assert_equal 'MessageController#index', entries.first.page
+  end
+end
+   
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.railslog.log ./pkg/production_log_analyzer-1.5.4/test/test.railslog.log
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.railslog.log	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/test/test.railslog.log	2007-06-14 18:21:58.000000000 +0200
@@ -0,0 +1,33 @@
+
+Processing MessageController#index (for 127.0.0.1 at 2007-01-28 18:00:30) [GET]
+   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
+   Parameters: {"action"=>"index", "controller"=>"message"}
+   ^[[4;35;1mUser Load (0.001405)^[[0m   ^[[0mSELECT * FROM users WHERE (users.`id` = 1) LIMIT 1^[[0m
+Redirected to http://localhost:3000/account/login
+Filter chain halted as [#<ActionController::Filters::ClassMethods::SymbolFilter:0x6a7938 @filter=:login_required>] returned false.
+Completed in 0.00431 (232 reqs/sec) | DB: 0.00527 (122%) | 302 Found [http://localhost/]
+ApplicationController: missing default helper module ApplicationHelper
+  ^[[4;36;1mUser Columns (0.004048)^[[0m   ^[[0;1mSHOW FIELDS FROM users^[[0m
+
+
+Processing AccountController#login (for 127.0.0.1 at 2007-01-28 18:00:30) [GET]
+   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
+   Parameters: {"action"=>"index", "controller"=>"message"}
+   ^[[4;35;1mUser Load (0.001405)^[[0m   ^[[0mSELECT * FROM users WHERE (users.`id` = 1) LIMIT 1^[[0m
+Redirected to http://localhost:3000/account/login
+Filter chain halted as [#<ActionController::Filters::ClassMethods::SymbolFilter:0x6a7938 @filter=:login_required>] returned false.
+Completed in 0.00431 (232 reqs/sec) | DB: 0.00527 (122%) | 302 Found [http://localhost/]
+ApplicationController: missing default helper module ApplicationHelper
+  ^[[4;36;1mUser Columns (0.004048)^[[0m   ^[[0;1mSHOW FIELDS FROM users^[[0m
+
+
+Processing AccountController#login (for 127.0.0.1 at 2007-01-28 18:00:31) [GET]
+   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
+   Parameters: {"action"=>"index", "controller"=>"message"}
+   ^[[4;35;1mUser Load (0.001405)^[[0m   ^[[0mSELECT * FROM users WHERE (users.`id` = 1) LIMIT 1^[[0m
+Redirected to http://localhost:3000/account/login
+Filter chain halted as [#<ActionController::Filters::ClassMethods::SymbolFilter:0x6a7938 @filter=:login_required>] returned false.
+Completed in 0.00431 (232 reqs/sec) | DB: 0.00527 (122%) | 302 Found [http://localhost/]
+ApplicationController: missing default helper module ApplicationHelper
+  ^[[4;36;1mUser Columns (0.004048)^[[0m   ^[[0;1mSHOW FIELDS FROM users^[[0m
+
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.railslog.route_error.log ./pkg/production_log_analyzer-1.5.4/test/test.railslog.route_error.log
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.railslog.route_error.log	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/test/test.railslog.route_error.log	2007-06-14 18:30:55.000000000 +0200
@@ -0,0 +1,74 @@
+
+
+Processing MessageController#index (for 127.0.0.1 at 2007-01-28 18:00:30) [GET]
+   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
+   Parameters: {"action"=>"index", "controller"=>"message"}
+   ^[[4;35;1mUser Load (0.001405)^[[0m   ^[[0mSELECT * FROM users WHERE (users.`id` = 1) LIMIT 1^[[0m
+Redirected to http://localhost:3000/account/login
+Filter chain halted as [#<ActionController::Filters::ClassMethods::SymbolFilter:0x6a7938 @filter=:login_required>] returned false.
+Completed in 0.00431 (232 reqs/sec) | DB: 0.00527 (122%) | 302 Found [http://localhost/]
+
+Processing ApplicationController#index (for 127.0.0.1 at 2007-01-28 18:38:27) [GET]
+   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
+   Parameters: {}
+
+
+ActionController::RoutingError (no route found to match "/messages" with {:method=>:get}):
+     /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/routing.rb:1266:in `recognize_path'
+     /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/routing.rb:1256:in `recognize'
+     /opt/local/lib/ruby/gems/1.8/gems/rails-1.2.1/lib/dispatcher.rb:40:in `dispatch'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/rails.rb:78:in `process'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/rails.rb:76:in `process'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:618:in `process_client'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:617:in `process_client'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:736:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:736:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:720:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/configurator.rb:271:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/configurator.rb:270:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/bin/mongrel_rails:127:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/command.rb:211:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/bin/mongrel_rails:243
+     /opt/local/bin/mongrel_rails:18
+
+
+Rendering /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/templates/rescues/layout.rhtml (404 Page Not Found)
+ApplicationController: missing default helper module ApplicationHelper
+
+
+Processing ApplicationController#index (for 127.0.0.1 at 2007-01-28 18:38:27) [GET]
+   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
+   Parameters: {}
+
+
+ActionController::RoutingError (no route found to match "/messages" with {:method=>:get}):
+     /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/routing.rb:1266:in `recognize_path'
+     /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/routing.rb:1256:in `recognize'
+     /opt/local/lib/ruby/gems/1.8/gems/rails-1.2.1/lib/dispatcher.rb:40:in `dispatch'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/rails.rb:78:in `process'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/rails.rb:76:in `process'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:618:in `process_client'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:617:in `process_client'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:736:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:736:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:720:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/configurator.rb:271:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/configurator.rb:270:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/bin/mongrel_rails:127:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/command.rb:211:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/bin/mongrel_rails:243
+     /opt/local/bin/mongrel_rails:18
+
+
+Rendering /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/templates/rescues/layout.rhtml (404 Page Not Found)
+ApplicationController: missing default helper module ApplicationHelper
+
+
+Processing MessageController#index (for 127.0.0.1 at 2007-01-28 18:00:30) [GET]
+   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
+   Parameters: {"action"=>"index", "controller"=>"message"}
+   ^[[4;35;1mUser Load (0.001405)^[[0m   ^[[0mSELECT * FROM users WHERE (users.`id` = 1) LIMIT 1^[[0m
+Redirected to http://localhost:3000/account/login
+Filter chain halted as [#<ActionController::Filters::ClassMethods::SymbolFilter:0x6a7938 @filter=:login_required>] returned false.
+Completed in 0.00431 (232 reqs/sec) | DB: 0.00527 (122%) | 302 Found [http://localhost/]
+
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.syslog.0.14.x.log ./pkg/production_log_analyzer-1.5.4/test/test.syslog.0.14.x.log
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.syslog.0.14.x.log	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/test/test.syslog.0.14.x.log	2007-05-16 23:37:36.000000000 +0200
@@ -0,0 +1,5 @@
+//src/production_log_analyzer/dev/test/test.syslog.0.14.x.log#1 - add change 3135 (text)
+Nov  7 12:14:02 192.168.1.71 43things[24950]: Processing RssController#entries (for 10.43.199.12 at 2005-11-07 12:14:02) [GET]
+Nov  7 12:14:02 192.168.1.71 43things[24950]: Parameters: {"rss/entries/goal.html/entries/goal"=>nil, "action"=>"entries", "id"=>"goal", "controller"=>"rss", "goal_id"=>"86381"}
+Nov  7 12:14:02 192.168.1.71 43things[24950]: Rendering rss/rss2.0
+Nov  7 12:14:02 192.168.1.71 43things[24950]: Completed in 0.00860 (116 reqs/sec) | DB: 0.00328 (38%) | 200 OK [http://www.43things.com/rss/entries/goal?goal_id=86381]
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.syslog.1.2.shortname.log ./pkg/production_log_analyzer-1.5.4/test/test.syslog.1.2.shortname.log
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.syslog.1.2.shortname.log	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/test/test.syslog.1.2.shortname.log	2007-05-16 23:37:36.000000000 +0200
@@ -0,0 +1,5 @@
+//src/production_log_analyzer/dev/test/test.syslog.1.2.shortname.log#1 - add change 3135 (text)
+Jan 17 16:19:22 tlucas rails[2265]: Processing Short#a (for 127.0.0.1 at 2007-01-17 16:19:22) [GET]
+Jan 17 16:19:22 tlucas rails[2265]: Parameters: {"action"=>"a", "controller"=>"short"}
+Jan 17 16:19:22 tlucas rails[2265]: Rendering short/a
+Jan 17 16:19:22 tlucas rails[2265]: Completed in 0.02666 (37 reqs/sec) | Rendering: 0.02261 (84%) | DB: 0.00222 (8%) | 200 OK [http://127.0.0.1/short]
diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.syslog.log ./pkg/production_log_analyzer-1.5.4/test/test.syslog.log
--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.syslog.log	1970-01-01 01:00:00.000000000 +0100
+++ ./pkg/production_log_analyzer-1.5.4/test/test.syslog.log	2007-05-16 23:37:36.000000000 +0200
@@ -0,0 +1,258 @@
+//src/production_log_analyzer/dev/test/test.syslog.log#1 - add change 3135 (text)
+Mar  7 00:00:00 online1 newsyslog[61307]: logfile turned over
+Mar  7 00:00:20 online1 rails[59600]: Goal Load (0.002112)   SELECT g.*, gs.score as score FROM goals g, goal_similarities gs WHERE g.id = gs.similar_goal_id AND g.num_active_people > 0 AND gs.goal_similarity_type_id = 1 AND gs.goal_id = 59133 ORDER BY score DESC LIMIT 3
+Mar  7 00:00:20 online1 rails[59600]: Tag Load (0.001527)   SELECT tags.*, count(*) as num_goals FROM tags_teams, tags, teams WHERE tags_teams.tag_id = tags.id AND tags_teams.team_id = teams.id AND teams.num_members > 0 AND teams.goal_id = 59133 GROUP BY tags.id ORDER BY num_goals DESC LIMIT 5
+Mar  7 00:00:20 online1 rails[59600]: Rendering things/view within layouts/default
+Mar  7 00:00:20 online1 rails[59600]: Person Load (0.001884)   SELECT * FROM people WHERE id = 10519 LIMIT 1
+Mar  7 00:00:20 online1 rails[59600]: Person Load (0.001159)   SELECT * FROM people WHERE id = 10519 LIMIT 1
+Mar  7 00:00:20 online1 rails[59600]: Rendering layouts/default (200 OK)
+Mar  7 00:00:20 online1 rails[59600]: Completed in 0.300741 (3 reqs/sec) | Rendering: 0.049924 (16%) | DB: 0.092428 (30%)
+Mar  7 00:00:25 online2 rails[59628]: -> vsize: 83628032 rssize: 79933440 runtime: 1151489566)
+Mar  7 00:00:25 online2 rails[59628]: Processing RssController#uber (for 67.18.200.5 at Mon Mar 07 00:00:25 CST 2005)
+Mar  7 00:00:25 online2 rails[59628]: Parameters: {:id=>"author", :"rss/uber/author.html/uber/author"=>nil, :action=>"uber", :username=>"looch", :controller=>"rss"}
+Mar  7 00:00:25 online2 rails[59628]: Cookie set: auth=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:25 GMT
+Mar  7 00:00:25 online2 rails[59628]: Cookie set: ubid=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:25 GMT
+Mar  7 00:00:25 online2 rails[59628]: Browser Load (0.003963)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
+Mar  7 00:00:25 online2 rails[59628]: Person Load (0.002445)   SELECT * FROM people WHERE username = 'looch' AND active = '1' LIMIT 1
+Mar  7 00:00:25 online2 rails[59628]: ProfileImage Load (0.001554)   SELECT * FROM profile_images WHERE id = 2782 LIMIT 1
+Mar  7 00:00:25 online2 rails[59628]: Rendering rss/rss2.0 (200 OK)
+Mar  7 00:00:25 online1 rails[59628]: Processing RssController#uber (for 67.18.200.5 at Mon Mar 07 00:00:25 CST 2005)
+Mar  7 00:00:25 online1 rails[59628]: Parameters: {:id=>"author", :"rss/uber/author.html/uber/author"=>nil, :action=>"uber", :username=>"looch", :controller=>"rss"}
+Mar  7 00:00:25 online1 rails[59628]: Cookie set: auth=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:25 GMT
+Mar  7 00:00:25 online1 rails[59628]: Cookie set: ubid=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:25 GMT
+Mar  7 00:00:25 online1 rails[59628]: Browser Load (0.003963)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
+Mar  7 00:00:25 online1 rails[59628]: Person Load (0.002445)   SELECT * FROM people WHERE username = 'looch' AND active = '1' LIMIT 1
+Mar  7 00:00:25 online1 rails[59628]: ProfileImage Load (0.001554)   SELECT * FROM profile_images WHERE id = 2782 LIMIT 1
+Mar  7 00:00:25 online1 rails[59628]: Rendering rss/rss2.0 (200 OK)
+Mar  7 00:00:25 online1 rails[59628]: Completed in 0.034519 (28 reqs/sec) | Rendering: 0.011770 (34%) | DB: 0.007962 (23%)
+Mar  7 00:00:25 online2 rails[59628]: Completed in 0.034519 (28 reqs/sec) | Rendering: 0.011770 (34%) | DB: 0.007962 (23%)
+Mar  7 00:00:25 online2 rails[59628]: <- vsize: 83628032 rssize: 79933440 runtime: 1151487080
+Mar  7 00:00:27 online1 rails[59645]: Processing ThingsController#view (for 67.18.200.5 at Mon Mar 07 00:00:27 CST 2005)
+Mar  7 00:00:27 online1 rails[59645]: Parameters: {:id=>"11891", :"things/view/11891.html/view/11891"=>nil, :action=>"view", :controller=>"things"}
+Mar  7 00:00:27 online1 rails[59645]: Cookie set: auth=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:27 GMT
+Mar  7 00:00:27 online1 rails[59645]: Cookie set: ubid=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:27 GMT
+Mar  7 00:00:27 online1 rails[59645]: Browser Load (0.001425)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
+Mar  7 00:00:27 online1 rails[59645]: Goal Load (0.001303)   SELECT * FROM goals WHERE id = '11891' LIMIT 1
+Mar  7 00:00:27 online1 rails[59645]: TeamMember Load (0.002593)   SELECT * FROM team_members WHERE goal_id = 11891 AND active_member = 1 AND goal_is_complete = 1 AND goal_is_complete = '1' ORDER BY num_entries DESC, updated_date DESC LIMIT 3
+Mar  7 00:00:27 online1 rails[59645]: TeamMember Load (0.070738)   SELECT * FROM team_members WHERE goal_id = 11891 AND active_member = 1 AND goal_is_complete = 0 AND goal_is_complete = '0' ORDER BY num_entries DESC, updated_date DESC LIMIT 10
+Mar  7 00:00:27 online1 rails[59645]: Entry Load (0.003074)   SELECT e.* FROM entries e, teams t WHERE e.team_id = t.id AND t.goal_id = 11891 AND e.active = 1 ORDER BY e.id DESC LIMIT 10
+Mar  7 00:00:27 online1 rails[59645]: Entry Count (0.000956)   SELECT count(*) as num_entries FROM entries e, teams t WHERE e.team_id = t.id AND t.goal_id = 11891 AND e.active = 1 AND e.active = 1 
+Mar  7 00:00:27 online1 rails[59645]: TeamMember Count (0.000993)   SELECT count(*) as count FROM team_members WHERE worth_it = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 11891
+Mar  7 00:00:27 online1 rails[59645]: TeamMember Count (0.000878)   SELECT count(*) as count FROM team_members WHERE (worth_it <> 1 or worth_it is null) AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 11891
+Mar  7 00:00:27 online1 rails[59645]: TeamMember Count (0.000878)   SELECT count(*) as count FROM team_members WHERE help_others = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 11891
+Mar  7 00:00:27 online1 rails[59645]: Goal Load (0.002212)   SELECT g.*, gs.score as score FROM goals g, goal_similarities gs WHERE g.id = gs.similar_goal_id AND g.num_active_people > 0 AND gs.goal_similarity_type_id = 1 AND gs.goal_id = 11891 ORDER BY score DESC LIMIT 3
+Mar  7 00:00:27 online1 rails[59645]: Tag Load (0.002522)   SELECT tags.*, count(*) as num_goals FROM tags_teams, tags, teams WHERE tags_teams.tag_id = tags.id AND tags_teams.team_id = teams.id AND teams.num_members > 0 AND teams.goal_id = 11891 GROUP BY tags.id ORDER BY num_goals DESC LIMIT 5
+Mar  7 00:00:27 online1 rails[59645]: Rendering things/view within layouts/default
+Mar  7 00:00:27 online1 rails[59645]: Person Load (0.018285)   SELECT * FROM people WHERE id = 2526 LIMIT 1
+Mar  7 00:00:27 online1 rails[59645]: Person Load (0.001736)   SELECT * FROM people WHERE id = 6627 LIMIT 1
+Mar  7 00:00:27 online1 rails[59645]: Person Load (0.001611)   SELECT * FROM people WHERE id = 11889 LIMIT 1
+Mar  7 00:00:27 online1 rails[59645]: Person Load (0.001879)   SELECT * FROM people WHERE id = 12472 LIMIT 1
+Mar  7 00:00:27 online1 rails[59645]: ProfileImage Load (0.001176)   SELECT * FROM profile_images WHERE id = 6024 LIMIT 1
+Mar  7 00:00:27 online1 rails[59645]: City Load (0.001226)   SELECT * FROM cities WHERE id = 43 LIMIT 1
+Mar  7 00:00:27 online1 rails[59645]: Person Load (0.001712)   SELECT * FROM people WHERE id = 2576 LIMIT 1
+Mar  7 00:00:27 online1 rails[59645]: ProfileImage Load (0.001464)   SELECT * FROM profile_images WHERE id = 1579 LIMIT 1
+Mar  7 00:00:27 online1 rails[59645]: City Load (0.001098)   SELECT * FROM cities WHERE id = 409 LIMIT 1
+Mar  7 00:00:27 online1 rails[59645]: Person Load (0.001300)   SELECT * FROM people WHERE id = 2526 LIMIT 1
+Mar  7 00:00:27 online1 rails[59645]: ProfileImage Load (0.001536)   SELECT * FROM profile_images WHERE id = 1234 LIMIT 1
+Mar  7 00:00:27 online1 rails[59645]: Person Load (0.001562)   SELECT * FROM people WHERE id = 6627 LIMIT 1
+Mar  7 00:00:27 online1 rails[59645]: Rendering layouts/default (200 OK)
+Mar  7 00:00:27 online1 rails[59645]: Completed in 0.396183 (2 reqs/sec) | Rendering: 0.107987 (27%) | DB: 0.122158 (30%)
+Mar  7 00:00:28 online1 rails[59629]: Processing PeopleController#view (for 67.18.200.5 at Mon Mar 07 00:00:28 CST 2005)
+Mar  7 00:00:28 online1 rails[59629]: Parameters: {:id=>"denstat", :action=>"view", :controller=>"people", :"people/view/denstat.html/view/denstat"=>nil}
+Mar  7 00:00:28 online1 rails[59629]: Cookie set: auth=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:28 GMT
+Mar  7 00:00:28 online1 rails[59629]: Cookie set: ubid=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:28 GMT
+Mar  7 00:00:28 online1 rails[59629]: Browser Load (0.003869)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
+Mar  7 00:00:28 online1 rails[59629]: Person Load (0.088823)   SELECT * FROM people WHERE username = 'denstat' AND active = '1' LIMIT 1
+Mar  7 00:00:28 online1 rails[59629]: ProfileImage Load (0.001174)   SELECT * FROM profile_images WHERE id = 2031 LIMIT 1
+Mar  7 00:00:28 online1 rails[59629]: TeamMember Load (0.004332)   SELECT * FROM team_members WHERE person_id = 2693 AND active_member = '1' 
+Mar  7 00:00:28 online1 rails[59629]: Goal Load (0.010591)   SELECT g.* FROM goals g, team_members tm WHERE g.id = tm.goal_id AND tm.person_id = 2693 AND tm.active_member <> 0
+Mar  7 00:00:28 online1 rails[59629]: Team Load (0.005076)   SELECT t.* FROM teams t, team_members tm WHERE t.id = tm.team_id AND goal_is_complete = 0 AND active_member != 0 AND tm.person_id = 2693 ORDER BY sort_order, tm.joined_date
+Mar  7 00:00:28 online1 rails[59629]: Team Load (0.003954)   SELECT t.* FROM teams t, team_members tm WHERE t.id = tm.team_id AND goal_is_complete = '1' AND active_member != '0' AND tm.person_id = 2693 ORDER BY tm.updated_date DESC LIMIT 10 
+Mar  7 00:00:28 online1 rails[59629]: Entry Load (0.010843)   SELECT e.* FROM entries e, team_members tm, (SELECT team_id, max(id) recent_id FROM entries WHERE active <> 0 AND person_id = 2693 GROUP BY team_id) recent_entries WHERE e.id = recent_entries.recent_id AND tm.person_id = e.person_id AND tm.team_id = e.team_id AND tm.active_member <> 0
+Mar  7 00:00:28 online1 rails[59629]: Rendering people/view within layouts/default
+Mar  7 00:00:28 online1 rails[59629]: ProfileImage Load (0.001088)   SELECT * FROM profile_images WHERE id = 2031 LIMIT 1
+Mar  7 00:00:28 online1 rails[59629]: City Load (0.000689)   SELECT * FROM cities WHERE id = 1170 LIMIT 1
+Mar  7 00:00:28 online1 rails[59629]: Team Load (0.001622)   SELECT * FROM teams WHERE id = 132991 LIMIT 1
+Mar  7 00:00:28 online1 rails[59629]: Team Load (0.001059)   SELECT * FROM teams WHERE id = 132980 LIMIT 1
+Mar  7 00:00:28 online1 rails[59629]: Team Load (0.001117)   SELECT * FROM teams WHERE id = 129534 LIMIT 1
+Mar  7 00:00:28 online1 rails[59629]: Team Load (0.001104)   SELECT * FROM teams WHERE id = 126799 LIMIT 1
+Mar  7 00:00:28 online1 rails[59629]: Team Load (0.015325)   SELECT * FROM teams WHERE id = 124130 LIMIT 1
+Mar  7 00:00:28 online1 rails[59629]: Team Load (0.001067)   SELECT * FROM teams WHERE id = 121991 LIMIT 1
+Mar  7 00:00:29 online1 rails[59629]: Team Load (0.001169)   SELECT * FROM teams WHERE id = 121978 LIMIT 1
+Mar  7 00:00:29 online1 rails[59629]: Team Load (0.001264)   SELECT * FROM teams WHERE id = 121976 LIMIT 1
+Mar  7 00:00:29 online1 rails[59629]: Team Load (0.001131)   SELECT * FROM teams WHERE id = 121971 LIMIT 1
+Mar  7 00:00:29 online1 rails[59629]: Team Load (0.000970)   SELECT * FROM teams WHERE id = 121967 LIMIT 1
+Mar  7 00:00:29 online1 rails[59629]: Team Load (0.001507)   SELECT * FROM teams WHERE id = 121868 LIMIT 1
+Mar  7 00:00:29 online1 rails[59629]: Team Load (0.075690)   SELECT * FROM teams WHERE id = 69373 LIMIT 1
+Mar  7 00:00:29 online1 rails[60654]: Processing TeamsController#progress (for 220.255.60.111; 165.21.154.137 at Mon Mar 07 00:00:29 CST 2005)
+Mar  7 00:00:29 online1 rails[60654]: Parameters: {:id=>"35926", :action=>"progress", :"teams/progress/35926.html/progress/35926"=>nil, :controller=>"teams"}
+Mar  7 00:00:29 online1 rails[59627]: Processing ThingsController#view (for 67.18.200.5 at Mon Mar 07 00:00:29 CST 2005)
+Mar  7 00:00:29 online1 rails[59627]: Parameters: {:id=>"19986", :action=>"view", :"things/view/19986.html/view/19986"=>nil, :controller=>"things"}
+Mar  7 00:00:29 online1 rails[59627]: Cookie set: auth=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:29 GMT
+Mar  7 00:00:29 online1 rails[59627]: Cookie set: ubid=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:29 GMT
+Mar  7 00:00:29 online1 rails[60654]: Browser Load (0.204679)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
+Mar  7 00:00:29 online1 rails[59627]: Browser Load (0.028144)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
+Mar  7 00:00:29 online1 rails[59629]: Team Load (0.201582)   SELECT * FROM teams WHERE id = 69343 LIMIT 1
+Mar  7 00:00:29 online1 rails[59629]: Team Load (0.001294)   SELECT * FROM teams WHERE id = 65942 LIMIT 1
+Mar  7 00:00:29 online1 rails[59627]: Goal Load (0.001371)   SELECT * FROM goals WHERE id = '19986' LIMIT 1
+Mar  7 00:00:29 online1 rails[59629]: Team Load (0.013834)   SELECT * FROM teams WHERE id = 65932 LIMIT 1
+Mar  7 00:00:29 online1 rails[59627]: TeamMember Load (0.014705)   SELECT * FROM team_members WHERE goal_id = 19986 AND active_member = 1 AND goal_is_complete = 1 AND goal_is_complete = '1' ORDER BY num_entries DESC, updated_date DESC LIMIT 3
+Mar  7 00:00:29 online1 rails[59629]: Team Load (0.001222)   SELECT * FROM teams WHERE id = 27367 LIMIT 1
+Mar  7 00:00:29 online1 rails[59627]: TeamMember Load (0.002019)   SELECT * FROM team_members WHERE goal_id = 19986 AND active_member = 1 AND goal_is_complete = 0 AND goal_is_complete = '0' ORDER BY num_entries DESC, updated_date DESC LIMIT 10
+Mar  7 00:00:29 online1 rails[59629]: Team Load (0.004668)   SELECT * FROM teams WHERE id = 42378 LIMIT 1
+Mar  7 00:00:29 online1 rails[59627]: Entry Load (0.005880)   SELECT e.* FROM entries e, teams t WHERE e.team_id = t.id AND t.goal_id = 19986 AND e.active = 1 ORDER BY e.id DESC LIMIT 10
+Mar  7 00:00:29 online1 rails[59629]: Team Load (0.001271)   SELECT * FROM teams WHERE id = 27314 LIMIT 1
+Mar  7 00:00:29 online1 rails[59627]: Entry Count (0.001063)   SELECT count(*) as num_entries FROM entries e, teams t WHERE e.team_id = t.id AND t.goal_id = 19986 AND e.active = 1 AND e.active = 1 
+Mar  7 00:00:29 online1 rails[59629]: Team Load (0.009752)   SELECT * FROM teams WHERE id = 27306 LIMIT 1
+Mar  7 00:00:29 online1 rails[59627]: TeamMember Count (0.015829)   SELECT count(*) as count FROM team_members WHERE worth_it = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 19986
+Mar  7 00:00:29 online1 rails[59627]: TeamMember Count (0.000884)   SELECT count(*) as count FROM team_members WHERE (worth_it <> 1 or worth_it is null) AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 19986
+Mar  7 00:00:29 online1 rails[59627]: TeamMember Count (0.000809)   SELECT count(*) as count FROM team_members WHERE help_others = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 19986
+Mar  7 00:00:29 online1 rails[59629]: TeamMember Count (0.001410)   SELECT COUNT(*) FROM team_members WHERE active_member = 1 and goal_is_complete = 1 and person_id = 2693 
+Mar  7 00:00:29 online1 rails[59629]: Person Load (0.017762)   SELECT p.*, count(*) as num_teams_in_common FROM people p, team_members tms, team_members tmp WHERE tmp.person_id = p.id AND tms.team_id = tmp.team_id AND tms.person_id = 2693 AND tms.active_member <> 0 AND tms.goal_is_complete = 0 AND tmp.person_id <> 2693 AND tmp.active_member <> 0 AND tmp.goal_is_complete = 0 GROUP BY p.id ORDER BY num_teams_in_common DESC, p.last_name
+Mar  7 00:00:29 online1 rails[59629]: Rendering layouts/default (200 OK)
+Mar  7 00:00:29 online1 rails[59629]: Completed in 1.102098 (0 reqs/sec) | Rendering: 0.695476 (63%) | DB: 0.486258 (44%)
+Mar  7 00:00:29 online1 rails[59635]: Processing ThingsController#view (for 67.18.200.5 at Mon Mar 07 00:00:29 CST 2005)
+Mar  7 00:00:29 online1 rails[59635]: Parameters: {:id=>"9794", :action=>"view", :"things/view/9794.html/view/9794"=>nil, :controller=>"things"}
+Mar  7 00:00:29 online1 rails[59635]: Cookie set: auth=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:29 GMT
+Mar  7 00:00:29 online1 rails[59635]: Cookie set: ubid=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:29 GMT
+Mar  7 00:00:29 online1 rails[59635]: Browser Load (0.001218)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
+Mar  7 00:00:29 online1 rails[59635]: Goal Load (0.001052)   SELECT * FROM goals WHERE id = '9794' LIMIT 1
+Mar  7 00:00:29 online1 rails[59627]: Goal Load (0.002577)   SELECT g.*, gs.score as score FROM goals g, goal_similarities gs WHERE g.id = gs.similar_goal_id AND g.num_active_people > 0 AND gs.goal_similarity_type_id = 1 AND gs.goal_id = 19986 ORDER BY score DESC LIMIT 3
+Mar  7 00:00:29 online1 rails[60654]: Team Load (0.001341)   SELECT * FROM teams WHERE id = '35926' LIMIT 1
+Mar  7 00:00:29 online1 rails[59627]: Tag Load (0.002195)   SELECT tags.*, count(*) as num_goals FROM tags_teams, tags, teams WHERE tags_teams.tag_id = tags.id AND tags_teams.team_id = teams.id AND teams.num_members > 0 AND teams.goal_id = 19986 GROUP BY tags.id ORDER BY num_goals DESC LIMIT 5
+Mar  7 00:00:29 online1 rails[59627]: Rendering things/view within layouts/default
+Mar  7 00:00:29 online1 rails[59627]: Person Load (0.002223)   SELECT * FROM people WHERE id = 1694 LIMIT 1
+Mar  7 00:00:29 online1 rails[59635]: TeamMember Load (0.102165)   SELECT * FROM team_members WHERE goal_id = 9794 AND active_member = 1 AND goal_is_complete = 1 AND goal_is_complete = '1' ORDER BY num_entries DESC, updated_date DESC LIMIT 3
+Mar  7 00:00:29 online1 rails[60654]: Entry Load (0.122496)   SELECT * FROM entries WHERE team_id = 35926 AND active = 1 
+Mar  7 00:00:29 online1 rails[59635]: TeamMember Load (0.002217)   SELECT * FROM team_members WHERE goal_id = 9794 AND active_member = 1 AND goal_is_complete = 0 AND goal_is_complete = '0' ORDER BY num_entries DESC, updated_date DESC LIMIT 10
+Mar  7 00:00:29 online1 rails[60654]: TeamMember Load (0.001972)   SELECT * FROM team_members WHERE team_id = 35926 AND active_member = 1 
+Mar  7 00:00:29 online1 rails[59635]: Entry Load (0.004002)   SELECT e.* FROM entries e, teams t WHERE e.team_id = t.id AND t.goal_id = 9794 AND e.active = 1 ORDER BY e.id DESC LIMIT 10
+Mar  7 00:00:29 online1 rails[60654]: Person Load (0.004097)   SELECT * FROM people WHERE id = 3357 LIMIT 1
+Mar  7 00:00:29 online1 rails[59635]: Entry Count (0.001231)   SELECT count(*) as num_entries FROM entries e, teams t WHERE e.team_id = t.id AND t.goal_id = 9794 AND e.active = 1 AND e.active = 1 
+Mar  7 00:00:29 online1 rails[59635]: TeamMember Count (0.001088)   SELECT count(*) as count FROM team_members WHERE worth_it = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 9794
+Mar  7 00:00:29 online1 rails[60654]: Redirected to http://www.43things.com/people/progress/swirlygirl/35926
+Mar  7 00:00:29 online1 rails[59635]: TeamMember Count (0.001411)   SELECT count(*) as count FROM team_members WHERE (worth_it <> 1 or worth_it is null) AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 9794
+Mar  7 00:00:29 online1 rails[60654]: Goal Load (0.001213)   SELECT * FROM goals WHERE id = 225 LIMIT 1
+Mar  7 00:00:29 online1 rails[59635]: TeamMember Count (0.001344)   SELECT count(*) as count FROM team_members WHERE help_others = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 9794
+Mar  7 00:00:29 online1 rails[59627]: Person Load (0.090841)   SELECT * FROM people WHERE id = 4350 LIMIT 1
+Mar  7 00:00:29 online1 rails[59627]: ProfileImage Load (0.001119)   SELECT * FROM profile_images WHERE id = 2322 LIMIT 1
+Mar  7 00:00:29 online1 rails[59627]: City Load (0.000659)   SELECT * FROM cities WHERE id = 819 LIMIT 1
+Mar  7 00:00:29 online1 rails[59627]: Person Load (0.001279)   SELECT * FROM people WHERE id = 1694 LIMIT 1
+Mar  7 00:00:29 online1 rails[59627]: ProfileImage Load (0.001170)   SELECT * FROM profile_images WHERE id = 1945 LIMIT 1
+Mar  7 00:00:29 online1 rails[59627]: Rendering layouts/default (200 OK)
+Mar  7 00:00:29 online1 rails[60654]: Goal Load (0.002178)   SELECT g.*, gs.score as score FROM goals g, goal_similarities gs WHERE g.id = gs.similar_goal_id AND g.num_active_people > 0 AND gs.goal_similarity_type_id = 1 AND gs.goal_id = 225 ORDER BY score DESC LIMIT 3
+Mar  7 00:00:29 online1 rails[59627]: Completed in 0.491760 (2 reqs/sec) | Rendering: 0.197126 (40%) | DB: 0.172767 (35%)
+Mar  7 00:00:30 online1 rails[59635]: Goal Load (0.240670)   SELECT g.*, gs.score as score FROM goals g, goal_similarities gs WHERE g.id = gs.similar_goal_id AND g.num_active_people > 0 AND gs.goal_similarity_type_id = 1 AND gs.goal_id = 9794 ORDER BY score DESC LIMIT 3
+Mar  7 00:00:30 online1 rails[59635]: Tag Load (0.191137)   SELECT tags.*, count(*) as num_goals FROM tags_teams, tags, teams WHERE tags_teams.tag_id = tags.id AND tags_teams.team_id = teams.id AND teams.num_members > 0 AND teams.goal_id = 9794 GROUP BY tags.id ORDER BY num_goals DESC LIMIT 5
+Mar  7 00:00:30 online1 rails[59635]: Rendering things/view within layouts/default
+Mar  7 00:00:30 online1 rails[59635]: Person Load (0.004756)   SELECT * FROM people WHERE id = 4758 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.007942)   SELECT * FROM profile_images WHERE id = 2557 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: Team Load (0.007545)   SELECT * FROM teams WHERE id = 56225 LIMIT 1
+Mar  7 00:00:30 online1 rails[60654]: TeamMember Count (0.558625)   SELECT count(*) as count FROM team_members WHERE worth_it = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
+Mar  7 00:00:30 online1 rails[59635]: Person Load (0.003433)   SELECT * FROM people WHERE id = 1674 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: Person Load (0.007808)   SELECT * FROM people WHERE id = 1674 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.008622)   SELECT * FROM profile_images WHERE id = 751 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: City Load (0.007911)   SELECT * FROM cities WHERE id = 164 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: Person Load (0.008706)   SELECT * FROM people WHERE id = 7887 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.007501)   SELECT * FROM profile_images WHERE id = 4135 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: City Load (0.018941)   SELECT * FROM cities WHERE id = 598 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: Person Load (0.007344)   SELECT * FROM people WHERE id = 11610 LIMIT 1
+Mar  7 00:00:30 online1 rails[60654]: TeamMember Count (0.135848)   SELECT count(*) as count FROM team_members WHERE (worth_it <> 1 or worth_it is null) AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
+Mar  7 00:00:30 online1 rails[59635]: Person Load (0.075532)   SELECT * FROM people WHERE id = 8694 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.009264)   SELECT * FROM profile_images WHERE id = 4519 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: City Load (0.007255)   SELECT * FROM cities WHERE id = 808 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: Person Load (0.009140)   SELECT * FROM people WHERE id = 7413 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.007755)   SELECT * FROM profile_images WHERE id = 3908 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: City Load (0.007233)   SELECT * FROM cities WHERE id = 732 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: Person Load (0.008803)   SELECT * FROM people WHERE id = 7412 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.008236)   SELECT * FROM profile_images WHERE id = 4743 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: City Load (0.007333)   SELECT * FROM cities WHERE id = 156 LIMIT 1
+Mar  7 00:00:30 online1 rails[60654]: TeamMember Count (0.111128)   SELECT count(*) as count FROM team_members WHERE help_others = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
+Mar  7 00:00:30 online1 rails[60654]: Completed in 1.469788 (0 reqs/sec) | DB: 1.143577 (77%)
+Mar  7 00:00:30 online1 rails[59635]: Person Load (0.003778)   SELECT * FROM people WHERE id = 4765 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.010916)   SELECT * FROM profile_images WHERE id = 2571 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: Person Load (0.002019)   SELECT * FROM people WHERE id = 1452 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.001126)   SELECT * FROM profile_images WHERE id = 5516 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: City Load (0.011705)   SELECT * FROM cities WHERE id = 1061 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: Person Load (0.002113)   SELECT * FROM people WHERE id = 895 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.001151)   SELECT * FROM profile_images WHERE id = 420 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: City Load (0.000736)   SELECT * FROM cities WHERE id = 164 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: Person Load (0.001225)   SELECT * FROM people WHERE id = 1674 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.001099)   SELECT * FROM profile_images WHERE id = 751 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: Person Load (0.016354)   SELECT * FROM people WHERE id = 4758 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.078197)   SELECT * FROM profile_images WHERE id = 2557 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: Person Load (0.001518)   SELECT * FROM people WHERE id = 1674 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.001268)   SELECT * FROM profile_images WHERE id = 751 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: Person Load (0.001255)   SELECT * FROM people WHERE id = 1674 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.001137)   SELECT * FROM profile_images WHERE id = 751 LIMIT 1
+Mar  7 00:00:30 online1 rails[59635]: Rendering layouts/default (200 OK)
+Mar  7 00:00:30 online1 rails[59635]: Completed in 1.259728 (0 reqs/sec) | Rendering: 0.505973 (40%) | DB: 0.914192 (72%)
+Mar  7 00:00:30 online1 rails[59627]: Processing PeopleController#progress (for 220.255.60.111; 165.21.154.158 at Mon Mar 07 00:00:30 CST 2005)
+Mar  7 00:00:30 online1 rails[59627]: Parameters: {:id=>"swirlygirl", :on=>"35926", :action=>"progress", :"people/progress/swirlygirl/35926.html/progress/swirlygirl/35926"=>nil, :controller=>"people"}
+Mar  7 00:00:30 online1 rails[59627]: Browser Load (0.000760)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
+Mar  7 00:00:30 online1 rails[59627]: Person Load (0.001974)   SELECT * FROM people WHERE username = 'swirlygirl' LIMIT 1
+Mar  7 00:00:30 online1 rails[59627]: Team Load (0.001060)   SELECT * FROM teams WHERE id = '35926' LIMIT 1
+Mar  7 00:00:30 online1 rails[59627]: TeamMember Load (0.001061)   SELECT * FROM team_members WHERE team_id = 35926 AND active_member = 1 
+Mar  7 00:00:30 online1 rails[59627]: Entry Load (0.004121)   SELECT * FROM entries WHERE team_id = 35926 AND person_id = 3357 AND active = 1 ORDER BY created_date DESC LIMIT 0, 20
+Mar  7 00:00:30 online1 rails[59627]: Tag Load (0.001446)   SELECT t.*, j.* FROM tags_teams j, tags t WHERE t.id = j.tag_id AND j.team_id = 35926 ORDER BY t.id
+Mar  7 00:00:30 online1 rails[59627]: Goal Load (0.000904)   SELECT * FROM goals WHERE id = 225 LIMIT 1
+Mar  7 00:00:31 online1 rails[60654]: Processing PeopleController#view (for 67.18.200.5 at Mon Mar 07 00:00:31 CST 2005)
+Mar  7 00:00:31 online1 rails[60654]: Parameters: {:id=>"elizabeth", :action=>"view", :"people/view/elizabeth.html/view/elizabeth"=>nil, :controller=>"people"}
+Mar  7 00:00:31 online1 rails[60654]: Person Load (0.012043)   SELECT * FROM people WHERE id = 2077 LIMIT 1
+Mar  7 00:00:31 online1 rails[60654]: Person Load (0.005866)   SELECT * FROM people WHERE username = 'elizabeth' AND active = '1' LIMIT 1
+Mar  7 00:00:31 online1 rails[60654]: ProfileImage Load (0.007170)   SELECT * FROM profile_images WHERE id = 2153 LIMIT 1
+Mar  7 00:00:31 online1 rails[60654]: TeamMember Load (0.011367)   SELECT * FROM team_members WHERE person_id = 4042 AND active_member = '1' 
+Mar  7 00:00:31 online1 rails[60654]: Goal Load (0.007064)   SELECT g.* FROM goals g, team_members tm WHERE g.id = tm.goal_id AND tm.person_id = 4042 AND tm.active_member <> 0
+Mar  7 00:00:31 online1 rails[60654]: Team Load (0.007723)   SELECT t.* FROM teams t, team_members tm WHERE t.id = tm.team_id AND goal_is_complete = 0 AND active_member != 0 AND tm.person_id = 4042 ORDER BY sort_order, tm.joined_date
+Mar  7 00:00:31 online1 rails[60654]: Team Load (0.007425)   SELECT t.* FROM teams t, team_members tm WHERE t.id = tm.team_id AND goal_is_complete = '1' AND active_member != '0' AND tm.person_id = 4042 ORDER BY tm.updated_date DESC LIMIT 10 
+Mar  7 00:00:31 online1 rails[59627]: Tag Load (0.162564)   SELECT tags.*, count(*) as num_goals FROM tags_teams, tags, teams WHERE tags_teams.tag_id = tags.id AND tags_teams.team_id = teams.id AND teams.num_members > 0 AND teams.goal_id = 225 GROUP BY tags.id ORDER BY num_goals DESC LIMIT 5
+Mar  7 00:00:31 online1 rails[60654]: Entry Load (0.005561)   SELECT e.* FROM entries e, team_members tm, (SELECT team_id, max(id) recent_id FROM entries WHERE active <> 0 AND person_id = 4042 GROUP BY team_id) recent_entries WHERE e.id = recent_entries.recent_id AND tm.person_id = e.person_id AND tm.team_id = e.team_id AND tm.active_member <> 0
+Mar  7 00:00:31 online1 rails[60654]: Rendering people/view within layouts/default
+Mar  7 00:00:31 online1 rails[60654]: FlickrPhoto Load (0.001567)   SELECT * FROM flickr_photos WHERE person_id = 4042 
+Mar  7 00:00:31 online1 rails[60654]: ProfileImage Load (0.013853)   SELECT * FROM profile_images WHERE id = 2153 LIMIT 1
+Mar  7 00:00:31 online1 rails[60654]: City Load (0.000694)   SELECT * FROM cities WHERE id = 5 LIMIT 1
+Mar  7 00:00:31 online1 rails[59627]: Goal Load (0.002303)   SELECT g.*, gs.score as score FROM goals g, goal_similarities gs WHERE g.id = gs.similar_goal_id AND g.num_active_people > 0 AND gs.goal_similarity_type_id = 1 AND gs.goal_id = 225 ORDER BY score DESC LIMIT 3
+Mar  7 00:00:31 online1 rails[59627]: TeamMember Count (0.000653)   SELECT count(*) as count FROM team_members WHERE worth_it = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
+Mar  7 00:00:31 online1 rails[59627]: TeamMember Count (0.000800)   SELECT count(*) as count FROM team_members WHERE (worth_it <> 1 or worth_it is null) AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
+Mar  7 00:00:31 online1 rails[59627]: TeamMember Count (0.014575)   SELECT count(*) as count FROM team_members WHERE help_others = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
+Mar  7 00:00:31 online1 rails[60654]: TeamMember Count (0.006965)   SELECT COUNT(*) FROM team_members WHERE active_member = 1 and goal_is_complete = 1 and person_id = 4042 
+Mar  7 00:00:31 online1 rails[59627]: TeamMember Load (0.116729)   SELECT * FROM team_members WHERE goal_id = 225 AND active_member = 1 AND goal_is_complete = 1 AND person_id <> '3357' ORDER BY updated_date DESC LIMIT 3
+Mar  7 00:00:31 online1 rails[59627]: Rendering people/progress within layouts/default
+Mar  7 00:00:31 online1 rails[59627]: ProfileImage Load (0.021914)   SELECT * FROM profile_images WHERE id = 1770 LIMIT 1
+Mar  7 00:00:31 online1 rails[59627]: Person Load (0.001863)   SELECT * FROM people WHERE id = 6731 LIMIT 1
+Mar  7 00:00:31 online1 rails[60654]: Person Load (0.101821)   SELECT p.*, count(*) as num_teams_in_common FROM people p, team_members tms, team_members tmp WHERE tmp.person_id = p.id AND tms.team_id = tmp.team_id AND tms.person_id = 4042 AND tms.active_member <> 0 AND tms.goal_is_complete = 0 AND tmp.person_id <> 4042 AND tmp.active_member <> 0 AND tmp.goal_is_complete = 0 GROUP BY p.id ORDER BY num_teams_in_common DESC, p.last_name
+Mar  7 00:00:31 online1 rails[60654]: Rendering layouts/default (200 OK)
+Mar  7 00:00:31 online1 rails[60654]: Completed in 0.360210 (2 reqs/sec) | Rendering: 0.277921 (77%) | DB: 0.189119 (52%)
+Mar  7 00:00:31 online1 rails[59627]: ProfileImage Load (0.090583)   SELECT * FROM profile_images WHERE id = 6434 LIMIT 1
+Mar  7 00:00:31 online1 rails[59627]: Team Load (0.001336)   SELECT * FROM teams WHERE id = 79140 LIMIT 1
+Mar  7 00:00:31 online1 rails[59627]: Person Load (0.001677)   SELECT * FROM people WHERE id = 660 LIMIT 1
+Mar  7 00:00:31 online1 rails[59627]: ProfileImage Load (0.001106)   SELECT * FROM profile_images WHERE id = 370 LIMIT 1
+Mar  7 00:00:31 online1 rails[59627]: Team Load (0.001231)   SELECT * FROM teams WHERE id = 15523 LIMIT 1
+Mar  7 00:00:31 online1 rails[59627]: Person Load (0.001581)   SELECT * FROM people WHERE id = 1900 LIMIT 1
+Mar  7 00:00:31 online1 rails[59627]: ProfileImage Load (0.001111)   SELECT * FROM profile_images WHERE id = 3207 LIMIT 1
+Mar  7 00:00:31 online1 rails[59627]: Team Load (0.001221)   SELECT * FROM teams WHERE id = 37202 LIMIT 1
+Mar  7 00:00:31 online1 rails[59627]: TeamMember Load (0.379744)   SELECT * FROM team_members WHERE goal_id = 225 AND active_member = 1 AND goal_is_complete = 1 
+Mar  7 00:00:31 online1 rails[59627]: Goal Load (0.005007)   SELECT * FROM goals WHERE id = 225 LIMIT 1
+Mar  7 00:00:31 online1 rails[59627]: Cheer Load (0.004291)   SELECT * FROM cheers WHERE entity_id = 36073 AND entity_type_id = 4 
+Mar  7 00:00:31 online1 rails[59627]: Person Load (0.008794)   SELECT * FROM people WHERE id = 3357 LIMIT 1
+Mar  7 00:00:31 online1 rails[59627]: Rendering layouts/default (200 OK)
+Mar  7 00:00:31 online1 rails[59627]: Completed in 0.977398 (1 reqs/sec) | Rendering: 0.604444 (61%) | DB: 0.830409 (84%)
+Mar  7 00:00:32 online1 rails[59627]: Processing TeamsController#progress (for 220.255.60.111; 165.21.154.137 at Mon Mar 07 00:00:32 CST 2005)
+Mar  7 00:00:32 online1 rails[59627]: Parameters: {:id=>"35926", :action=>"progress", :"teams/progress/35926.html/progress/35926"=>nil, :controller=>"teams"}
+Mar  7 00:00:32 online1 rails[59627]: Browser Load (0.001007)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
+Mar  7 00:00:32 online1 rails[59627]: Team Load (0.000973)   SELECT * FROM teams WHERE id = '35926' LIMIT 1
+Mar  7 00:00:32 online1 rails[59627]: Entry Load (0.001138)   SELECT * FROM entries WHERE team_id = 35926 AND active = 1 
+Mar  7 00:00:32 online1 rails[59627]: TeamMember Load (0.001781)   SELECT * FROM team_members WHERE team_id = 35926 AND active_member = 1 
+Mar  7 00:00:32 online1 rails[59627]: Person Load (0.030966)   SELECT * FROM people WHERE id = 3357 LIMIT 1
+Mar  7 00:00:32 online1 rails[59627]: Redirected to http://www.43things.com/people/progress/swirlygirl/35926
+Mar  7 00:00:32 online1 rails[59627]: Goal Load (0.000835)   SELECT * FROM goals WHERE id = 225 LIMIT 1
+Mar  7 00:00:32 online1 rails[59627]: Goal Load (0.009688)   SELECT g.*, gs.score as score FROM goals g, goal_similarities gs WHERE g.id = gs.similar_goal_id AND g.num_active_people > 0 AND gs.goal_similarity_type_id = 1 AND gs.goal_id = 225 ORDER BY score DESC LIMIT 3
+Mar  7 00:00:32 online1 rails[59627]: TeamMember Count (0.007285)   SELECT count(*) as count FROM team_members WHERE worth_it = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
+Mar  7 00:00:32 online1 rails[59627]: TeamMember Count (0.095106)   SELECT count(*) as count FROM team_members WHERE (worth_it <> 1 or worth_it is null) AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
+Mar  7 00:00:32 online1 rails[59627]: TeamMember Count (0.000578)   SELECT count(*) as count FROM team_members WHERE help_others = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
+Mar  7 00:00:32 online1 rails[59627]: Completed in 0.211973 (4 reqs/sec) | DB: 0.149357 (70%)
+Mar  7 00:00:32 online1 rails[59635]: Processing PeopleController#progress (for 220.255.60.111; 165.21.154.158 at Mon Mar 07 00:00:32 CST 2005)
+Mar  7 00:00:32 online1 rails[59635]: Parameters: {:id=>"swirlygirl", :on=>"35926", :action=>"progress", :"people/progress/swirlygirl/35926.html/progress/swirlygirl/35926"=>nil, :controller=>"people"}
+Mar  7 00:00:32 online1 rails[59635]: Browser Load (0.000698)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
+Mar  7 00:00:32 online1 rails[59635]: Person Load (0.001319)   SELECT * FROM people WHERE username = 'swirlygirl' LIMIT 1
+
Los ficheros binarios ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4.gem y ./pkg/production_log_analyzer-1.5.4.gem son distintos
Los ficheros binarios ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4.tgz y ./pkg/production_log_analyzer-1.5.4.tgz son distintos
diff -Nur ../production_log_analyzer-1.5.0/pl_analyze-1.5.4.patch ./pl_analyze-1.5.4.patch
--- ../production_log_analyzer-1.5.0/pl_analyze-1.5.4.patch	1970-01-01 01:00:00.000000000 +0100
+++ ./pl_analyze-1.5.4.patch	2007-06-15 10:25:34.000000000 +0200
@@ -0,0 +1,3178 @@
+diff -Nur ../production_log_analyzer-1.5.0/bin/pl_analyze ./bin/pl_analyze
+--- ../production_log_analyzer-1.5.0/bin/pl_analyze	2007-06-15 10:19:53.000000000 +0200
++++ ./bin/pl_analyze	2007-06-14 18:36:50.000000000 +0200
+@@ -1,35 +1,56 @@
+ #!/usr/local/bin/ruby -w
+ 
+ require 'production_log/analyzer'
++require 'production_log/email'
+ 
+-file_name = ARGV.shift
+-
+-if file_name.nil? then
+-  puts "Usage: #{$0} file_name [-e email_recipient [-s subject]] [count]"
+-  exit 1
+-end
+-
+-email_recipient = nil
++log_files = []
++email_addresses = []
++count = 15
++progress = false
+ subject = nil
+-
+-if ARGV.first == '-e' then
+-  ARGV.shift # -e
+-  email_recipient = ARGV.shift
++ignore_missing_domain = false
++log_format = nil
++special_controllers = []
++
++while !ARGV.empty?
++  arg = ARGV.shift
++  case arg
++  when /^(--format|-f)$/
++    log_format = ARGV.shift.to_sym
++  when /^(--email|-e)$/
++    email_addresses << ARGV.shift
++  when /^(--subject|-s)$/
++    subject = ARGV.shift
++  when /^(--count|-s)$/
++    count = ARGV.shift.to_i
++  when /^(--progress|-p)$/
++    progress = true
++  when /^(--ignore-missing-domain|-i)$/
++    ignore_missing_domain = true
++  when /^(--special-controller|-c)$/
++    special_controllers << ARGV.shift
++  else
++    log_files << arg
++  end
+ end
+ 
+-if email_recipient and ARGV.first == '-s' then
+-  ARGV.shift # -s
+-  subject = ARGV.shift
+-end
+-
+-count = ARGV.shift
+-count = count.nil? ? 10 : Integer(count)
+-
+-if email_recipient.nil? then
+-  analyzer = Analyzer.new file_name
+-  analyzer.process
+-  puts analyzer.report(count)
++if log_files.empty? 
++  puts "Usage: #{$0} [--format|-f autodetect|rails|syslog] [--email|-e email_address] [--subject|-s email_subject] [--count|-c num_entries] [--progress|-p] [--ignore-missing-domain|-i] [--special-controller|-c] log_files"
+ else
+-  Analyzer.email file_name, email_recipient, subject, count
++  analyzer = Analyzer.new count
++  analyzer.ignore_missing_domain = ignore_missing_domain
++
++  log_files.each do |log_file|
++    analyzer.process log_file, progress, log_format
++  end
++
++  report = analyzer.report special_controllers
++  puts report
++
++  if !email_addresses.empty?
++    body = email report, email_addresses, subject
++    send_mail body
++  end
+ end
+ 
++
+diff -Nur ../production_log_analyzer-1.5.0/History.txt ./History.txt
+--- ../production_log_analyzer-1.5.0/History.txt	2007-06-15 10:19:53.000000000 +0200
++++ ./History.txt	2007-06-14 18:43:46.000000000 +0200
+@@ -1,3 +1,15 @@
++= 1.5.4
++
++* Support for rails logs
++* Analyzing multiple logs at the same time
++* List of most frequently encountered http codes
++* Better command line handling
++* Optional progress bar while parsing
++* Optional ignoring of http:// URLs without domain
++* Requests per second, time of first and last request logged
++* Separate statistic for additional controllers
++* Remove dependency on rails_analyzer_tools
++
+ = 1.5.0
+ 
+ * Fixed empty log bug.  Patch by Tim Lucas.
+diff -Nur ../production_log_analyzer-1.5.0/lib/production_log/analyzer.rb ./lib/production_log/analyzer.rb
+--- ../production_log_analyzer-1.5.0/lib/production_log/analyzer.rb	2007-06-15 10:19:53.000000000 +0200
++++ ./lib/production_log/analyzer.rb	2007-06-07 18:34:17.000000000 +0200
+@@ -113,11 +113,6 @@
+ class Analyzer
+ 
+   ##
+-  # The version of the production log analyzer you are using.
+-
+-  VERSION = '1.5.0'
+-
+-  ##
+   # The logfile being read by the Analyzer.
+ 
+   attr_reader :logfile_name
+@@ -138,59 +133,51 @@
+   attr_reader :render_times
+ 
+   ##
+-  # Generates and sends an email report with lots of fun stuff in it.  This
+-  # way, Mail.app will behave when given tabs.
+-
+-  def self.email(file_name, recipient, subject, count = 10)
+-    analyzer = self.new file_name
+-    analyzer.process
+-    body = analyzer.report count
+-
+-    email = self.envelope(recipient, subject)
+-    email << nil
+-    email << "<pre>#{body}</pre>"
+-    email = email.join($/) << $/
+-
+-    return email if $TESTING
+-
+-    IO.popen("/usr/sbin/sendmail -i -t", "w+") do |sm|
+-      sm.print email
+-      sm.flush
+-    end
+-  end
++  # An Array of all the http status codes for the log file
+ 
+-  def self.envelope(recipient, subject = nil) # :nodoc:
+-    envelope = {}
+-    envelope['To'] = recipient
+-    envelope['Subject'] = subject || "pl_analyze"
+-    envelope['Content-Type'] = "text/html"
++  attr_reader :http_statuses
++  
++  ##
++  # Ignore URL http:// as this can be used by monitoring apps to check if the application is working
+ 
+-    return envelope.map { |(k,v)| "#{k}: #{v}" }
+-  end
++  attr_accessor :ignore_missing_domain
+ 
+   ##
+-  # Creates a new Analyzer that will read data from +logfile_name+.
++  # Creates a new Analyzer that finds the most relevant +count+ requests.
+ 
+-  def initialize(logfile_name)
+-    @logfile_name  = logfile_name
++  def initialize(count = 20)
+     @request_times = Hash.new { |h,k| h[k] = [] }
+     @db_times      = Hash.new { |h,k| h[k] = [] }
+     @render_times  = Hash.new { |h,k| h[k] = [] }
++    @http_statuses = Hash.new { |h,k| h[k] = [] }
++    @times         = Hash.new { |h,k| h[k] = [] }
++    @urls          = Hash.new { |h,k| h[k] = [] }
++
++    @analyzed_log_files = []
++
++    @count = count
++    @ignore_missing_domain = false
+   end
+ 
+   ##
+-  # Processes the log file collecting statistics from each found LogEntry.
+-
+-  def process
+-    File.open @logfile_name do |fp|
+-      LogParser.parse fp do |entry|
+-        entry_page = entry.page
+-        next if entry_page.nil?
+-        @request_times[entry_page] << entry.request_time
+-        @db_times[entry_page] << entry.db_time
+-        @render_times[entry_page] << entry.render_time
+-      end
++  # Processes the log files collecting statistics from each found LogEntry.
++
++  def process( log_file, progress = false, log_format = :autodetect )
++    Log::Parser.parse_file(log_file, progress, log_format) do |entry|
++      entry_page = entry.page
++      next if entry_page.nil?
++      next if @ignore_missing_domain and entry.url == "http://"
++      @request_times[entry_page] << entry.request_time
++      @db_times[entry_page] << entry.db_time
++      @render_times[entry_page] << entry.render_time
++      @http_statuses[entry_page] << entry.http_status
++      @times[entry_page] << entry.time
++      @urls[entry_page] << entry.url
+     end
++
++    @analyzed_log_files << log_file
++
++    return self
+   end
+ 
+   ##
+@@ -210,8 +197,8 @@
+   ##
+   # The +limit+ slowest total request times.
+ 
+-  def slowest_request_times(limit = 10)
+-    return slowest_times(@request_times, limit)
++  def slowest_request_times
++    return slowest_times(@request_times)
+   end
+ 
+   ##
+@@ -231,8 +218,8 @@
+   ##
+   # The +limit+ slowest total database times.
+ 
+-  def slowest_db_times(limit = 10)
+-    return slowest_times(@db_times, limit)
++  def slowest_db_times
++    return slowest_times(@db_times)
+   end
+ 
+   ##
+@@ -252,8 +239,8 @@
+   ##
+   # The +limit+ slowest total render times for all requests.
+ 
+-  def slowest_render_times(limit = 10)
+-    return slowest_times(@render_times, limit)
++  def slowest_render_times
++    return slowest_times(@render_times)
+   end
+ 
+   ##
+@@ -278,17 +265,84 @@
+   end
+ 
+   ##
+-  # Builds a report containing +count+ slow items.
++  # A list of count/min/max/avg/std dev for request times 
++  # for a certain controller.
++
++  def controller_times_summary controller
++    controller_times = @request_times.dup.delete_if{ |k, v| !(k =~ Regexp.new("^#{controller}#")) }
++
++    return "Controller '#{controller}' had no requests" if controller_times.empty?
++    return summarize("#{controller}", controller_times)
++  end
++
++  ##
++  # List the http status codes and their names of the 
++  # requests analyzed, sorted by number encountered.
++
++  def request_statuses_summary
++    text = []
++    text << "HTTP Status Codes Summary"
++    columns = ['Code', "HTTP\t", 'Number', 'Fra']
++    text << columns.join("\t")
++
++    number = @http_statuses.values.flatten.size
++
++    counter = Hash.new
++
++    @http_statuses.each do |entry_page, statuses|
++      statuses.compact!
++      statuses.each do |status|
++        counter[status] = (counter[status] || 0) + 1
++      end
++    end
++
++    require 'net/http'
++    counter.sort{|a,b| a[1]<=>b[1]}.reverse.each do |count|
++      # get HTTP status code name from ruby lib for better readability
++      http_code = Net::HTTPResponse::CODE_TO_OBJ[count[0].to_s].to_s.slice(9,20) || ""
++      line =  [count[0], (http_code.length > 7) ? http_code : "#{http_code}\t", count[1], '%0.2f%' % (count[1].to_f * 100 / number)]
++      text << line.join("\t")
++    end
++
++    return text.join("\n")
++  end
++
++    
++  ##
++  # Builds a report.
+ 
+-  def report(count)
++  def report special_controllers = []
+     return "No requests to analyze" if request_times.empty?
+ 
+     text = []
+ 
++    text << "Log files analyzed:"
++    @analyzed_log_files.each{ |f| text << f}
++    text << nil
++
++    text << "URL http:// ignored!" << nil if @ignore_missing_domain
++
++    first = @times.values.flatten.min
++    last = @times.values.flatten.max
++    text << "First request:         #{first}"
++    text << "Last request:          #{last}"
++    req_per_sec = @times.values.flatten.size.to_f / (last - first)
++    text << "Requests per second/minute/hour:   #{'%0.2f' % req_per_sec}/#{'%0.2f' % (req_per_sec * 60)}/#{'%0.2f' % (req_per_sec * 3600)}"
++
++    text << nil << "-" * 72 << nil
++
++    text << request_statuses_summary
++
++    text << nil << "-" * 72 << nil
++
++    special_controllers.each do |controller|
++      text << controller_times_summary(controller) << nil << '-' * 72 << nil
++    end
++
+     text << request_times_summary
+     text << nil
+     text << "Slowest Request Times:"
+-    slowest_request_times(count).each do |time, name|
++    slowest_request_times.each do |time, name|
+       text << "\t#{name} took #{'%0.3f' % time}s"
+     end
+     text << nil
+@@ -298,7 +352,7 @@
+     text << db_times_summary
+     text << nil
+     text << "Slowest Total DB Times:"
+-    slowest_db_times(count).each do |time, name|
++    slowest_db_times.each do |time, name|
+       text << "\t#{name} took #{'%0.3f' % time}s"
+     end
+     text << nil
+@@ -308,7 +362,7 @@
+     text << render_times_summary
+     text << nil
+     text << "Slowest Total Render Times:"
+-    slowest_render_times(count).each do |time, name|
++    slowest_render_times.each do |time, name|
+       text << "\t#{name} took #{'%0.3f' % time}s"
+     end
+     text << nil
+@@ -323,32 +377,37 @@
+     list = []
+ 
+     # header
+-    record = [pad_request_name("#{title} Summary"), 'Count', 'Avg', 'Std Dev',
++    record = [pad_request_name("#{title} Summary"), 'Count', 'Fra', 'Avg', 'Std Dev',
+               'Min', 'Max']
+     list << record.join("\t")
+ 
+     # all requests
+     times = records.values.flatten
++    num_records = times.size
+     record = [times.average, times.standard_deviation, times.min, times.max]
+     record.map! { |v| "%0.3f" % v }
+-    record.unshift [pad_request_name('ALL REQUESTS'), times.size]
++    record.unshift [pad_request_name('ALL REQUESTS'), times.size, "100%"]
+     list << record.join("\t")
+ 
+     # spacer
+     list << nil
+ 
+-    records.sort_by { |k,v| v.size}.reverse_each do |req, times|
++    sorted_records = records.sort_by{ |k,v| v.size}.reverse[0..@count-1]
++
++    sorted_records.each do |req, times|
+       record = [times.average, times.standard_deviation, times.min, times.max]
+       record.map! { |v| "%0.3f" % v }
+-      record.unshift ["#{pad_request_name req}", times.size]
++      record.unshift ["#{pad_request_name req}", times.size, "%0.1f%" % (times.size.to_f * 100 / num_records)]
+       list << record.join("\t")
+     end
+ 
++    list << "... [#{records.size - sorted_records.size} not shown]" if sorted_records.size < records.size
++
+     return list.join("\n")
+   end
+ 
+-  def slowest_times(records, limit) # :nodoc:
+-    slowest_times = SlowestTimes.new limit
++  def slowest_times(records) # :nodoc:
++    slowest_times = SlowestTimes.new @count
+ 
+     records.each do |name, times|
+       times.each do |time|
+diff -Nur ../production_log_analyzer-1.5.0/lib/production_log/email.rb ./lib/production_log/email.rb
+--- ../production_log_analyzer-1.5.0/lib/production_log/email.rb	1970-01-01 01:00:00.000000000 +0100
++++ ./lib/production_log/email.rb	2007-06-06 12:15:27.000000000 +0200
+@@ -0,0 +1,21 @@
++def email report, addresses, subject = nil
++  email = envelope(addresses, subject).map { |(k,v)| "#{k}: #{v}" }
++  email << nil
++  email << "<pre>#{report}</pre>"
++  email = email.join($/) << $/
++end
++
++def envelope addresses, subject = nil
++  envelope = {}
++  envelope['To'] = (addresses.class == Array) ? addresses.join(', ') : addresses
++  envelope['Subject'] = subject || $0
++  envelope['Content-Type'] = "text/html"
++  envelope
++end
++
++def send_mail email
++  IO.popen("/usr/sbin/sendmail -i -t", "w+") do |sm|
++    sm.print email
++    sm.flush
++  end
++end
+diff -Nur ../production_log_analyzer-1.5.0/lib/production_log/parser.rb ./lib/production_log/parser.rb
+--- ../production_log_analyzer-1.5.0/lib/production_log/parser.rb	2007-06-15 10:19:53.000000000 +0200
++++ ./lib/production_log/parser.rb	2007-06-08 12:58:22.000000000 +0200
+@@ -1,13 +1,14 @@
+ ##
+-# LogParser parses a Syslog log file looking for lines logged by the 'rails'
+-# program.  A typical log line looks like this:
++# Log::Parser parses a log file looking for lines logged by the 'rails'
++# program.  A typical syslog log line looks like this:
+ #
+ #   Mar  7 00:00:20 online1 rails[59600]: Person Load (0.001884)   SELECT * FROM people WHERE id = 10519 LIMIT 1
+ #
+-# LogParser does not work with Rails' default logger because there is no way
+-# to group all the log output of a single request.  You must use SyslogLogger.
++# Log::Parser now also works with native rails logs. However, as there is 
++# no way to group log records whose lines are mixed up, it assumes 
++# that this is not the case and that all requests are logged in sequence.
+ 
+-module LogParser
++module Log
+ 
+   ##
+   # LogEntry contains a summary of log data for a single request.
+@@ -51,6 +52,16 @@
+     attr_reader :db_time
+ 
+     ##
++    # Http return status code
++    
++    attr_reader :http_status
++
++    ##
++    # URL served by this request
++
++    attr_reader :url
++
++    ##
+     # Creates a new LogEntry from the log data in +entry+.
+ 
+     def initialize(entry)
+@@ -62,6 +73,8 @@
+       @render_time = 0
+       @db_time = 0
+       @in_component = 0
++      @http_status = nil
++      @url = nil
+ 
+       parse entry
+     end
+@@ -80,19 +93,23 @@
+           next if @in_component > 0
+           @page = $1
+           @ip   = $2
+-          @time = $3
+-        when /^Completed in ([\S]+) .+ Rendering: ([\S]+) .+ DB: ([\S]+)/ then
++          @time = Time.parse($3)
++        when /^Completed in ([\S]+) .+ Rendering: ([\S]+) .+ DB: ([\S]+)( .+ ([\d]{3,3}) \D*\[(\S*).*\])?/ then
+           next if @in_component > 0
+           @request_time = $1.to_f
+           @render_time = $2.to_f
+           @db_time = $3.to_f
+-        when /^Completed in ([\S]+) .+ DB: ([\S]+)/ then # Redirect
++          @http_status = $5.to_i if $5
++          @url = $6 if $6
++        when /^Completed in ([\S]+) .+ DB: ([\S]+)( .+ ([\d]{3,3}) \D*\[(\S*).*\])?/ then # Redirect
+           next if @in_component > 0
+           @request_time = $1.to_f
+           @render_time = 0
+           @db_time = $2.to_f
+-        when /(.+?) \(([^)]+)\)   / then
+-          @queries << [$1, $2.to_f]
++          @http_status = $4.to_i if $4
++          @url = $5 if $5
++#        when /(.+?) \(([^)]+)\)   / then # commmented out as parsing speed can suffer a lot by this
++#          @queries << [$1, $2.to_f]
+         when /^Start rendering component / then
+           @in_component += 1
+         when /^End of component rendering$/ then
+@@ -117,42 +134,151 @@
+ 
+   end
+ 
+-  ##
+-  # Parses IO stream +stream+, creating a LogEntry for each recognizable log
+-  # entry.
+-  #
+-  # Log entries are recognised as starting with Processing, continuing with
+-  # the same process id through Completed.
+-
+-  def self.parse(stream) # :yields: log_entry
+-    buckets = Hash.new { |h,k| h[k] = [] }
+-    comp_count = Hash.new 0
++  class Parser
++
++    def initialize
++      @buckets = Hash.new { |h,k| h[k] = [] }
++      @comp_count = Hash.new 0
++
++      @entry = []
++    end
++
++    ##
++    # Parse log lines, using format +log_format+. Expects +log+ to respond to each_line
++
++    def self.parse(log, log_format)
++      parser = Parser.new
++
++      case log_format
++      when :rails
++        log.each_line do |line|
++          parser.parse_rails(line) do |entry|
++            yield entry
++          end
++        end
++      when :syslog
++        log.each_line do |line|
++          parser.parse_syslog(line) do |entry|
++            yield entry
++          end
++        end
++      end
++    end
++
++    ##
++    # Parse one log file. If +progress+ is set, display progress information (this 
++    # requires the 'progressbar' gem to be installed). Log format may be :rails or 
++    # :syslog; if no format is specified, autodetection is used.
++
++    def self.parse_file(log_file, progress = false, log_format = :autodetect)
++      begin
++        log_format = detect_log_format log_file if log_format == :autodetect or 
++                                                   log_format.nil?
++      rescue RuntimeError
++        return        
++      end
++
++      parser = Parser.new
++      
++      if progress
++        num_lines = File.size(log_file) / 84
++        require 'progressbar'
++        pbar = ProgressBar.new("parsing", num_lines)
++      end
++      
++      case log_format
++      when :rails
++        File.open(log_file).each_line do |line|
++          parser.parse_rails(line) do |entry|
++            yield entry
++          end
++          pbar.inc if progress
++        end
++      when :syslog
++        File.open(log_file).each_line do |line|
++          parser.parse_syslog(line) do |entry|
++            yield entry
++          end
++          pbar.inc if progress
++        end
++        parser.yield_incomplete_buckets do |entry|
++          yield entry
++        end
++      end
++      pbar.finish if progress
++    end
++
++    ##
++    # Parse one syslog log line. Log entries are recognised as 
++    # starting with Processing, continuing with the same process id 
++    # through Completed.
+ 
+-    stream.each_line do |line|
++    def parse_syslog(line) # :yields: log_entry
++     
+       line =~ / ([^ ]+) ([^ ]+)\[(\d+)\]: (.*)/
+-      next if $2.nil? or $2 == 'newsyslog'
++      return if $2.nil? or $2 == 'newsyslog'
++
+       bucket = [$1, $2, $3].join '-'
+       data = $4
+ 
+-      buckets[bucket] << data
++      @buckets[bucket] << data
+ 
+       case data
+       when /^Start rendering component / then
+-        comp_count[bucket] += 1
++        @comp_count[bucket] += 1
+       when /^End of component rendering$/ then
+-        comp_count[bucket] -= 1
++        @comp_count[bucket] -= 1
+       when /^Completed/ then
+-        next unless comp_count[bucket] == 0
+-        entry = buckets.delete bucket
+-        next unless entry.any? { |l| l =~ /^Processing/ }
++        return unless @comp_count[bucket] == 0
++        entry =  @buckets.delete bucket
++        return unless entry.any? { |l| l =~ /^Processing/ }
+         yield LogEntry.new(entry)
++      when /^ActionController::RoutingError/
++        @buckets.delete bucket
+       end
+     end
+ 
+-    buckets.each do |bucket, data|
+-      yield LogEntry.new(data)
++    def yield_incomplete_buckets #:nodoc:
++      @buckets.each do |bucket, data|
++        yield LogEntry.new(data)
++      end
+     end
+-  end
+ 
++    ##
++    # Parse one rails log line.
++
++    def parse_rails(line) # :yields: log_entry
++      return unless line =~ /[^ ]/ # no empty lines
++
++      @entry << line
++
++      case line
++      when /^Completed/
++        yield LogEntry.new(@entry)
++        @entry = []
++      when /^ActionController::RoutingError/
++        @entry = []
++      end
++    end
++
++    ##
++    # Detect the log format of the given +log_file+. Returns 
++    # :rails or :syslog if detected correcty.
++
++    def self.detect_log_format log_file
++      File.open(log_file) do |file|
++        15.times do
++          case file.gets
++          when /^Processing /
++            return :rails
++          when / ([^ ]+) ([^ ]+)\[(\d+)\]: (.*)/
++            return :syslog
++          end
++        end
++      end
++      raise "Unable to determine log format for #{log_file}"
++    end
++
++  end
+ end
+ 
+diff -Nur ../production_log_analyzer-1.5.0/Manifest.txt ./Manifest.txt
+--- ../production_log_analyzer-1.5.0/Manifest.txt	2007-06-15 10:19:53.000000000 +0200
++++ ./Manifest.txt	2007-06-14 18:32:10.000000000 +0200
+@@ -9,10 +9,15 @@
+ lib/production_log/action_grep.rb
+ lib/production_log/analyzer.rb
+ lib/production_log/parser.rb
++lib/production_log/email.rb
+ test/test.syslog.0.14.x.log
+ test/test.syslog.1.2.shortname.log
+-test/test.syslog.empty.log
++test/test.empty.log
+ test/test.syslog.log
+ test/test_action_grep.rb
+ test/test_analyzer.rb
++test/analyzer_report.txt
+ test/test_parser.rb
++test/test.railslog.log
++test/test.railslog.route_error.log
++test/test_email.rb
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/bin/action_errors ./pkg/production_log_analyzer-1.5.4/bin/action_errors
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/bin/action_errors	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/bin/action_errors	2007-05-16 23:37:35.000000000 +0200
+@@ -0,0 +1,46 @@
++#!/usr/local/bin/ruby -ws
++
++$h ||= false
++$r ||= false
++$o ||= false
++
++$r = $r ? ($r.to_i rescue false) : false
++
++if $h then
++  $stderr.puts "Usage: #{$0} [-r=N] LOGFILE"
++  $stderr.puts "\t-r=N\tShow routing errors with N or more occurances"
++  $stderr.puts "\t-o\tShow errors with one occurance"
++  exit
++end
++
++errors = {}
++counts = Hash.new 0
++
++ARGF.each_line do |line|
++  line =~ /\]: (.*?)     (.*)/
++  next if $1.nil?
++  msg = $1
++  trace = $2
++  key = msg.gsub(/\d/, '#')
++  counts[key] += 1
++  next if counts[key] > 1
++  trace = trace.split('     ')[0..-2].map { |l| l.strip }.join("\n\t")
++  error = "#{msg}\n\t#{trace}"
++  errors[key] = error
++end
++
++counts.sort_by { |_,c| -c }.each do |key, count|
++  next if count == 1 and not $o
++  error = errors[key]
++
++  if error =~ /^ActionController::RoutingError/ then
++    next unless $r
++    next if $r and count < $r
++  end
++
++  puts "count: #{count}"
++  puts "{{{"
++  puts error
++  puts "}}}"
++end
++
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/bin/action_grep ./pkg/production_log_analyzer-1.5.4/bin/action_grep
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/bin/action_grep	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/bin/action_grep	2007-05-16 23:37:35.000000000 +0200
+@@ -0,0 +1,19 @@
++#!/usr/local/bin/ruby -w
++
++require 'production_log/action_grep'
++
++action_name = ARGV.shift
++file_name = ARGV.shift
++
++if action_name.nil? or file_name.nil? then
++  puts "Usage: #{$0} action_name file_name"
++  exit 1
++end
++
++begin
++  ActionGrep.grep action_name, file_name
++rescue ArgumentError => e
++  puts e
++  exit 1
++end
++
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/bin/pl_analyze ./pkg/production_log_analyzer-1.5.4/bin/pl_analyze
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/bin/pl_analyze	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/bin/pl_analyze	2007-06-14 18:36:50.000000000 +0200
+@@ -0,0 +1,56 @@
++#!/usr/local/bin/ruby -w
++
++require 'production_log/analyzer'
++require 'production_log/email'
++
++log_files = []
++email_addresses = []
++count = 15
++progress = false
++subject = nil
++ignore_missing_domain = false
++log_format = nil
++special_controllers = []
++
++while !ARGV.empty?
++  arg = ARGV.shift
++  case arg
++  when /^(--format|-f)$/
++    log_format = ARGV.shift.to_sym
++  when /^(--email|-e)$/
++    email_addresses << ARGV.shift
++  when /^(--subject|-s)$/
++    subject = ARGV.shift
++  when /^(--count|-s)$/
++    count = ARGV.shift.to_i
++  when /^(--progress|-p)$/
++    progress = true
++  when /^(--ignore-missing-domain|-i)$/
++    ignore_missing_domain = true
++  when /^(--special-controller|-c)$/
++    special_controllers << ARGV.shift
++  else
++    log_files << arg
++  end
++end
++
++if log_files.empty? 
++  puts "Usage: #{$0} [--format|-f autodetect|rails|syslog] [--email|-e email_address] [--subject|-s email_subject] [--count|-c num_entries] [--progress|-p] [--ignore-missing-domain|-i] [--special-controller|-c] log_files"
++else
++  analyzer = Analyzer.new count
++  analyzer.ignore_missing_domain = ignore_missing_domain
++
++  log_files.each do |log_file|
++    analyzer.process log_file, progress, log_format
++  end
++
++  report = analyzer.report special_controllers
++  puts report
++
++  if !email_addresses.empty?
++    body = email report, email_addresses, subject
++    send_mail body
++  end
++end
++
++
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/History.txt ./pkg/production_log_analyzer-1.5.4/History.txt
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/History.txt	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/History.txt	2007-06-14 18:43:46.000000000 +0200
+@@ -0,0 +1,46 @@
++= 1.5.4
++
++* Support for rails logs
++* Analyzing multiple logs at the same time
++* List of most frequently encountered http codes
++* Better command line handling
++* Optional progress bar while parsing
++* Optional ignoring of http:// URLs without domain
++* Requests per second, time of first and last request logged
++* Separate statistic for additional controllers
++* Remove dependency on rails_analyzer_tools
++
++= 1.5.0
++
++* Fixed empty log bug.  Patch by Tim Lucas.
++* Fixed bug where sometimes lines would be logged before the
++  Processing line.  Patch by Geoff Grosenbach.
++
++= 1.4.0
++
++* Switched to Hoe
++* Allowed action_errors to suppress routing errors with > 3 occurances
++* action_grep now works correctly with components
++* pl_analyze now works correctly with components
++* Added action_errors to extract error counts from logs
++* Retabbed to match the rest of the world
++
++= 1.3.0
++
++* Added action_grep
++* Added support for newer log format
++
++= 1.2.0
++
++* pl_analyze calculates per-action statistics
++* pl_analyze can send an email with its output
++
++= 1.1.0
++
++* RDoc
++* Other various fixes lost to time.
++
++= 1.0.0
++
++* Birthday!
++
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/lib/production_log/action_grep.rb ./pkg/production_log_analyzer-1.5.4/lib/production_log/action_grep.rb
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/lib/production_log/action_grep.rb	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/lib/production_log/action_grep.rb	2007-05-16 23:37:35.000000000 +0200
+@@ -0,0 +1,42 @@
++module ActionGrep; end
++
++class << ActionGrep
++
++  def grep(action_name, file_name)
++    unless action_name =~ /\A([A-Z][A-Za-z\d]*)(?:#([A-Za-z]\w*))?\Z/ then
++      raise ArgumentError, "Invalid action name #{action_name} expected something like SomeController#action"
++    end
++
++    unless File.file? file_name and File.readable? file_name then
++      raise ArgumentError, "Unable to read #{file_name}"
++    end
++
++    buckets = Hash.new { |h,k| h[k] = [] }
++    comp_count = Hash.new 0
++
++    File.open file_name do |fp|
++      fp.each_line do |line|
++        line =~ / ([^ ]+) ([^ ]+)\[(\d+)\]: (.*)/
++        next if $2.nil? or $2 == 'newsyslog'
++        bucket = [$1, $2, $3].join '-'
++        data = $4
++
++        buckets[bucket] << line
++
++        case data
++        when /^Start rendering component / then
++          comp_count[bucket] += 1
++        when /^End of component rendering$/ then
++          comp_count[bucket] -= 1
++        when /^Completed/ then
++          next unless comp_count[bucket] == 0
++          action = buckets.delete bucket
++          next unless action.any? { |l| l =~ /: Processing #{action_name}/ }
++          puts action.join
++        end
++      end
++    end
++  end
++
++end
++
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/lib/production_log/analyzer.rb ./pkg/production_log_analyzer-1.5.4/lib/production_log/analyzer.rb
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/lib/production_log/analyzer.rb	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/lib/production_log/analyzer.rb	2007-06-07 18:34:17.000000000 +0200
+@@ -0,0 +1,455 @@
++$TESTING = false unless defined? $TESTING
++
++require 'production_log/parser'
++
++module Enumerable
++
++  ##
++  # Sum of all the elements of the Enumerable
++
++  def sum
++    return self.inject(0) { |acc, i| acc + i }
++  end
++
++  ##
++  # Average of all the elements of the Enumerable
++  #
++  # The Enumerable must respond to #length
++
++  def average
++    return self.sum / self.length.to_f
++  end
++
++  ##
++  # Sample variance of all the elements of the Enumerable
++  #
++  # The Enumerable must respond to #length
++
++  def sample_variance
++    avg = self.average
++    sum = self.inject(0) { |acc, i| acc + (i - avg) ** 2 }
++    return (1 / self.length.to_f * sum)
++  end
++
++  ##
++  # Standard deviation of all the elements of the Enumerable
++  #
++  # The Enumerable must respond to #length
++
++  def standard_deviation
++    return Math.sqrt(self.sample_variance)
++  end
++
++end
++
++##
++# A list that only stores +limit+ items.
++
++class SizedList < Array
++
++  ##
++  # Creates a new SizedList that can hold up to +limit+ items.  Whenever
++  # adding a new item to the SizedList would make the list larger than
++  # +limit+, +delete_block+ is called.
++  #
++  # +delete_block+ is passed the list and the item being added.
++  # +delete_block+ must take action to remove an item and return true or
++  # return false if the item should not be added to the list.
++
++  def initialize(limit, &delete_block)
++    @limit = limit
++    @delete_block = delete_block
++  end
++
++  ##
++  # Attempts to add +obj+ to the list.
++
++  def <<(obj)
++    return super if self.length < @limit
++    return super if @delete_block.call self, obj
++  end
++
++end
++
++##
++# Stores +limit+ time/object pairs, keeping only the largest +limit+ items.
++#
++# Sample usage:
++#
++#   l = SlowestTimes.new 5
++#   
++#   l << [Time.now, 'one']
++#   l << [Time.now, 'two']
++#   l << [Time.now, 'three']
++#   l << [Time.now, 'four']
++#   l << [Time.now, 'five']
++#   l << [Time.now, 'six']
++#
++#   p l.map { |i| i.last }
++
++class SlowestTimes < SizedList
++
++  ##
++  # Creates a new SlowestTimes SizedList that holds only +limit+ time/object
++  # pairs.
++
++  def initialize(limit)
++    super limit do |arr, new_item|
++      fastest_time = arr.sort_by { |time, name| time }.first
++      if fastest_time.first < new_item.first then
++        arr.delete_at index(fastest_time)
++        true
++      else
++        false
++      end
++    end
++  end
++
++end
++
++##
++# Calculates statistics for production logs.
++
++class Analyzer
++
++  ##
++  # The logfile being read by the Analyzer.
++
++  attr_reader :logfile_name
++
++  ##
++  # An Array of all the request total times for the log file.
++
++  attr_reader :request_times
++
++  ##
++  # An Array of all the request database times for the log file.
++
++  attr_reader :db_times
++
++  ##
++  # An Array of all the request render times for the log file.
++
++  attr_reader :render_times
++
++  ##
++  # An Array of all the http status codes for the log file
++
++  attr_reader :http_statuses
++  
++  ##
++  # Ignore URL http:// as this can be used by monitoring apps to check if the application is working
++
++  attr_accessor :ignore_missing_domain
++
++  ##
++  # Creates a new Analyzer that finds the most relevant +count+ requests.
++
++  def initialize(count = 20)
++    @request_times = Hash.new { |h,k| h[k] = [] }
++    @db_times      = Hash.new { |h,k| h[k] = [] }
++    @render_times  = Hash.new { |h,k| h[k] = [] }
++    @http_statuses = Hash.new { |h,k| h[k] = [] }
++    @times         = Hash.new { |h,k| h[k] = [] }
++    @urls          = Hash.new { |h,k| h[k] = [] }
++
++    @analyzed_log_files = []
++
++    @count = count
++    @ignore_missing_domain = false
++  end
++
++  ##
++  # Processes the log files collecting statistics from each found LogEntry.
++
++  def process( log_file, progress = false, log_format = :autodetect )
++    Log::Parser.parse_file(log_file, progress, log_format) do |entry|
++      entry_page = entry.page
++      next if entry_page.nil?
++      next if @ignore_missing_domain and entry.url == "http://"
++      @request_times[entry_page] << entry.request_time
++      @db_times[entry_page] << entry.db_time
++      @render_times[entry_page] << entry.render_time
++      @http_statuses[entry_page] << entry.http_status
++      @times[entry_page] << entry.time
++      @urls[entry_page] << entry.url
++    end
++
++    @analyzed_log_files << log_file
++
++    return self
++  end
++
++  ##
++  # The average total request time for all requests.
++
++  def average_request_time
++    return time_average(@request_times)
++  end
++
++  ##
++  # The standard deviation of the total request time for all requests.
++
++  def request_time_std_dev
++    return time_std_dev(@request_times)
++  end
++
++  ##
++  # The +limit+ slowest total request times.
++
++  def slowest_request_times
++    return slowest_times(@request_times)
++  end
++
++  ##
++  # The average total database time for all requests.
++
++  def average_db_time
++    return time_average(@db_times)
++  end
++
++  ##
++  # The standard deviation of the total database time for all requests.
++
++  def db_time_std_dev
++    return time_std_dev(@db_times)
++  end
++
++  ##
++  # The +limit+ slowest total database times.
++
++  def slowest_db_times
++    return slowest_times(@db_times)
++  end
++
++  ##
++  # The average total render time for all requests.
++
++  def average_render_time
++    return time_average(@render_times)
++  end
++
++  ##
++  # The standard deviation of the total render time for all requests.
++
++  def render_time_std_dev
++    return time_std_dev(@render_times)
++  end
++
++  ##
++  # The +limit+ slowest total render times for all requests.
++
++  def slowest_render_times
++    return slowest_times(@render_times)
++  end
++
++  ##
++  # A list of count/min/max/avg/std dev for request times.
++
++  def request_times_summary
++    return summarize("Request Times", @request_times)
++  end
++
++  ##
++  # A list of count/min/max/avg/std dev for database times.
++
++  def db_times_summary
++    return summarize("DB Times", @db_times)
++  end
++
++  ##
++  # A list of count/min/max/avg/std dev for request times.
++
++  def render_times_summary
++    return summarize("Render Times", @render_times)
++  end
++
++  ##
++  # A list of count/min/max/avg/std dev for request times 
++  # for a certain controller.
++
++  def controller_times_summary controller
++    controller_times = @request_times.dup.delete_if{ |k, v| !(k =~ Regexp.new("^#{controller}#")) }
++
++    return "Controller '#{controller}' had no requests" if controller_times.empty?
++    return summarize("#{controller}", controller_times)
++  end
++
++  ##
++  # List the http status codes and their names of the 
++  # requests analyzed, sorted by number encountered.
++
++  def request_statuses_summary
++    text = []
++    text << "HTTP Status Codes Summary"
++    columns = ['Code', "HTTP\t", 'Number', 'Fra']
++    text << columns.join("\t")
++
++    number = @http_statuses.values.flatten.size
++
++    counter = Hash.new
++
++    @http_statuses.each do |entry_page, statuses|
++      statuses.compact!
++      statuses.each do |status|
++        counter[status] = (counter[status] || 0) + 1
++      end
++    end
++
++    require 'net/http'
++    counter.sort{|a,b| a[1]<=>b[1]}.reverse.each do |count|
++      # get HTTP status code name from ruby lib for better readability
++      http_code = Net::HTTPResponse::CODE_TO_OBJ[count[0].to_s].to_s.slice(9,20) || ""
++      line =  [count[0], (http_code.length > 7) ? http_code : "#{http_code}\t", count[1], '%0.2f%' % (count[1].to_f * 100 / number)]
++      text << line.join("\t")
++    end
++
++    return text.join("\n")
++  end
++
++    
++  ##
++  # Builds a report.
++
++  def report special_controllers = []
++    return "No requests to analyze" if request_times.empty?
++
++    text = []
++
++    text << "Log files analyzed:"
++    @analyzed_log_files.each{ |f| text << f}
++    text << nil
++
++    text << "URL http:// ignored!" << nil if @ignore_missing_domain
++
++    first = @times.values.flatten.min
++    last = @times.values.flatten.max
++    text << "First request:         #{first}"
++    text << "Last request:          #{last}"
++    req_per_sec = @times.values.flatten.size.to_f / (last - first)
++    text << "Requests per second/minute/hour:   #{'%0.2f' % req_per_sec}/#{'%0.2f' % (req_per_sec * 60)}/#{'%0.2f' % (req_per_sec * 3600)}"
++
++    text << nil << "-" * 72 << nil
++
++    text << request_statuses_summary
++
++    text << nil << "-" * 72 << nil
++
++    special_controllers.each do |controller|
++      text << controller_times_summary(controller) << nil << '-' * 72 << nil
++    end
++
++    text << request_times_summary
++    text << nil
++    text << "Slowest Request Times:"
++    slowest_request_times.each do |time, name|
++      text << "\t#{name} took #{'%0.3f' % time}s"
++    end
++    text << nil
++    text << "-" * 72
++    text << nil
++
++    text << db_times_summary
++    text << nil
++    text << "Slowest Total DB Times:"
++    slowest_db_times.each do |time, name|
++      text << "\t#{name} took #{'%0.3f' % time}s"
++    end
++    text << nil
++    text << "-" * 72
++    text << nil
++
++    text << render_times_summary
++    text << nil
++    text << "Slowest Total Render Times:"
++    slowest_render_times.each do |time, name|
++      text << "\t#{name} took #{'%0.3f' % time}s"
++    end
++    text << nil
++
++    return text.join($/)
++  end
++
++  private unless $TESTING
++
++  def summarize(title, records) # :nodoc:
++    record = nil
++    list = []
++
++    # header
++    record = [pad_request_name("#{title} Summary"), 'Count', 'Fra', 'Avg', 'Std Dev',
++              'Min', 'Max']
++    list << record.join("\t")
++
++    # all requests
++    times = records.values.flatten
++    num_records = times.size
++    record = [times.average, times.standard_deviation, times.min, times.max]
++    record.map! { |v| "%0.3f" % v }
++    record.unshift [pad_request_name('ALL REQUESTS'), times.size, "100%"]
++    list << record.join("\t")
++
++    # spacer
++    list << nil
++
++    sorted_records = records.sort_by{ |k,v| v.size}.reverse[0..@count-1]
++
++    sorted_records.each do |req, times|
++      record = [times.average, times.standard_deviation, times.min, times.max]
++      record.map! { |v| "%0.3f" % v }
++      record.unshift ["#{pad_request_name req}", times.size, "%0.1f%" % (times.size.to_f * 100 / num_records)]
++      list << record.join("\t")
++    end
++
++    list << "... [#{records.size - sorted_records.size} not shown]" if sorted_records.size < records.size
++
++    return list.join("\n")
++  end
++
++  def slowest_times(records) # :nodoc:
++    slowest_times = SlowestTimes.new @count
++
++    records.each do |name, times|
++      times.each do |time|
++        slowest_times << [time, name]
++      end
++    end
++
++    return slowest_times.sort_by { |time, name| time }.reverse
++  end
++
++  def time_average(records) # :nodoc:
++    times = records.values.flatten
++    times.delete 0
++    return times.average
++  end
++
++  def time_std_dev(records) # :nodoc:
++    times = records.values.flatten
++    times.delete 0
++    return times.standard_deviation
++  end
++
++  def longest_request_name # :nodoc:
++    return @longest_req if defined? @longest_req
++
++    names = @request_times.keys.map do |name|
++      (name||'Unknown').length + 1 # + : - HACK where does nil come from?
++    end
++
++    @longest_req = names.max
++
++    @longest_req = 'Unknown'.length + 1 if @longest_req.nil?
++
++    return @longest_req
++  end
++
++  def pad_request_name(name) # :nodoc:
++    name = (name||'Unknown') + ':' # HACK where does nil come from?
++    padding_width = longest_request_name - name.length
++    padding_width = 0 if padding_width < 0
++    name += (' ' * padding_width)
++  end
++
++end
++
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/lib/production_log/email.rb ./pkg/production_log_analyzer-1.5.4/lib/production_log/email.rb
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/lib/production_log/email.rb	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/lib/production_log/email.rb	2007-06-06 12:15:27.000000000 +0200
+@@ -0,0 +1,21 @@
++def email report, addresses, subject = nil
++  email = envelope(addresses, subject).map { |(k,v)| "#{k}: #{v}" }
++  email << nil
++  email << "<pre>#{report}</pre>"
++  email = email.join($/) << $/
++end
++
++def envelope addresses, subject = nil
++  envelope = {}
++  envelope['To'] = (addresses.class == Array) ? addresses.join(', ') : addresses
++  envelope['Subject'] = subject || $0
++  envelope['Content-Type'] = "text/html"
++  envelope
++end
++
++def send_mail email
++  IO.popen("/usr/sbin/sendmail -i -t", "w+") do |sm|
++    sm.print email
++    sm.flush
++  end
++end
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/lib/production_log/parser.rb ./pkg/production_log_analyzer-1.5.4/lib/production_log/parser.rb
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/lib/production_log/parser.rb	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/lib/production_log/parser.rb	2007-06-08 12:58:22.000000000 +0200
+@@ -0,0 +1,284 @@
++##
++# Log::Parser parses a log file looking for lines logged by the 'rails'
++# program.  A typical syslog log line looks like this:
++#
++#   Mar  7 00:00:20 online1 rails[59600]: Person Load (0.001884)   SELECT * FROM people WHERE id = 10519 LIMIT 1
++#
++# Log::Parser now also works with native rails logs. However, as there is 
++# no way to group log records whose lines are mixed up, it assumes 
++# that this is not the case and that all requests are logged in sequence.
++
++module Log
++
++  ##
++  # LogEntry contains a summary of log data for a single request.
++
++  class LogEntry
++
++    ##
++    # Controller and action for this request
++
++    attr_reader :page
++
++    ##
++    # Requesting IP
++
++    attr_reader :ip
++
++    ##
++    # Time the request was made
++
++    attr_reader :time
++
++    ##
++    # Array of SQL queries containing query type and time taken.  The
++    # complete text of the SQL query is not saved to reduct memory usage.
++
++    attr_reader :queries
++
++    ##
++    # Total request time, including database, render and other.
++
++    attr_reader :request_time
++
++    ##
++    # Total render time.
++
++    attr_reader :render_time
++
++    ##
++    # Total database time
++
++    attr_reader :db_time
++
++    ##
++    # Http return status code
++    
++    attr_reader :http_status
++
++    ##
++    # URL served by this request
++
++    attr_reader :url
++
++    ##
++    # Creates a new LogEntry from the log data in +entry+.
++
++    def initialize(entry)
++      @page = nil
++      @ip = nil
++      @time = nil
++      @queries = []
++      @request_time = 0
++      @render_time = 0
++      @db_time = 0
++      @in_component = 0
++      @http_status = nil
++      @url = nil
++
++      parse entry
++    end
++
++    ##
++    # Extracts log data from +entry+, which is an Array of lines from the
++    # same request.
++
++    def parse(entry)
++      entry.each do |line|
++        case line
++        when /^Parameters/, /^Cookie set/, /^Rendering/,
++          /^Redirected/ then
++          # nothing
++        when /^Processing ([\S]+) \(for (.+) at (.*)\)/ then
++          next if @in_component > 0
++          @page = $1
++          @ip   = $2
++          @time = Time.parse($3)
++        when /^Completed in ([\S]+) .+ Rendering: ([\S]+) .+ DB: ([\S]+)( .+ ([\d]{3,3}) \D*\[(\S*).*\])?/ then
++          next if @in_component > 0
++          @request_time = $1.to_f
++          @render_time = $2.to_f
++          @db_time = $3.to_f
++          @http_status = $5.to_i if $5
++          @url = $6 if $6
++        when /^Completed in ([\S]+) .+ DB: ([\S]+)( .+ ([\d]{3,3}) \D*\[(\S*).*\])?/ then # Redirect
++          next if @in_component > 0
++          @request_time = $1.to_f
++          @render_time = 0
++          @db_time = $2.to_f
++          @http_status = $4.to_i if $4
++          @url = $5 if $5
++#        when /(.+?) \(([^)]+)\)   / then # commmented out as parsing speed can suffer a lot by this
++#          @queries << [$1, $2.to_f]
++        when /^Start rendering component / then
++          @in_component += 1
++        when /^End of component rendering$/ then
++          @in_component -= 1
++        when /^Fragment hit: / then
++        else # noop
++#          raise "Can't handle #{line.inspect}" if $TESTING
++        end
++      end
++    end
++
++    def ==(other) # :nodoc:
++      other.class == self.class and
++      other.page == self.page and
++      other.ip == self.ip and
++      other.time == self.time and
++      other.queries == self.queries and
++      other.request_time == self.request_time and
++      other.render_time == self.render_time and
++      other.db_time == self.db_time
++    end
++
++  end
++
++  class Parser
++
++    def initialize
++      @buckets = Hash.new { |h,k| h[k] = [] }
++      @comp_count = Hash.new 0
++
++      @entry = []
++    end
++
++    ##
++    # Parse log lines, using format +log_format+. Expects +log+ to respond to each_line
++
++    def self.parse(log, log_format)
++      parser = Parser.new
++
++      case log_format
++      when :rails
++        log.each_line do |line|
++          parser.parse_rails(line) do |entry|
++            yield entry
++          end
++        end
++      when :syslog
++        log.each_line do |line|
++          parser.parse_syslog(line) do |entry|
++            yield entry
++          end
++        end
++      end
++    end
++
++    ##
++    # Parse one log file. If +progress+ is set, display progress information (this 
++    # requires the 'progressbar' gem to be installed). Log format may be :rails or 
++    # :syslog; if no format is specified, autodetection is used.
++
++    def self.parse_file(log_file, progress = false, log_format = :autodetect)
++      begin
++        log_format = detect_log_format log_file if log_format == :autodetect or 
++                                                   log_format.nil?
++      rescue RuntimeError
++        return        
++      end
++
++      parser = Parser.new
++      
++      if progress
++        num_lines = File.size(log_file) / 84
++        require 'progressbar'
++        pbar = ProgressBar.new("parsing", num_lines)
++      end
++      
++      case log_format
++      when :rails
++        File.open(log_file).each_line do |line|
++          parser.parse_rails(line) do |entry|
++            yield entry
++          end
++          pbar.inc if progress
++        end
++      when :syslog
++        File.open(log_file).each_line do |line|
++          parser.parse_syslog(line) do |entry|
++            yield entry
++          end
++          pbar.inc if progress
++        end
++        parser.yield_incomplete_buckets do |entry|
++          yield entry
++        end
++      end
++      pbar.finish if progress
++    end
++
++    ##
++    # Parse one syslog log line. Log entries are recognised as 
++    # starting with Processing, continuing with the same process id 
++    # through Completed.
++
++    def parse_syslog(line) # :yields: log_entry
++     
++      line =~ / ([^ ]+) ([^ ]+)\[(\d+)\]: (.*)/
++      return if $2.nil? or $2 == 'newsyslog'
++
++      bucket = [$1, $2, $3].join '-'
++      data = $4
++
++      @buckets[bucket] << data
++
++      case data
++      when /^Start rendering component / then
++        @comp_count[bucket] += 1
++      when /^End of component rendering$/ then
++        @comp_count[bucket] -= 1
++      when /^Completed/ then
++        return unless @comp_count[bucket] == 0
++        entry =  @buckets.delete bucket
++        return unless entry.any? { |l| l =~ /^Processing/ }
++        yield LogEntry.new(entry)
++      when /^ActionController::RoutingError/
++        @buckets.delete bucket
++      end
++    end
++
++    def yield_incomplete_buckets #:nodoc:
++      @buckets.each do |bucket, data|
++        yield LogEntry.new(data)
++      end
++    end
++
++    ##
++    # Parse one rails log line.
++
++    def parse_rails(line) # :yields: log_entry
++      return unless line =~ /[^ ]/ # no empty lines
++
++      @entry << line
++
++      case line
++      when /^Completed/
++        yield LogEntry.new(@entry)
++        @entry = []
++      when /^ActionController::RoutingError/
++        @entry = []
++      end
++    end
++
++    ##
++    # Detect the log format of the given +log_file+. Returns 
++    # :rails or :syslog if detected correcty.
++
++    def self.detect_log_format log_file
++      File.open(log_file) do |file|
++        15.times do
++          case file.gets
++          when /^Processing /
++            return :rails
++          when / ([^ ]+) ([^ ]+)\[(\d+)\]: (.*)/
++            return :syslog
++          end
++        end
++      end
++      raise "Unable to determine log format for #{log_file}"
++    end
++
++  end
++end
++
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/LICENSE.txt ./pkg/production_log_analyzer-1.5.4/LICENSE.txt
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/LICENSE.txt	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/LICENSE.txt	2007-05-16 23:37:35.000000000 +0200
+@@ -0,0 +1,27 @@
++Copyright 2005, 2007 Eric Hodel, The Robot Co-op.  All rights reserved.
++
++Redistribution and use in source and binary forms, with or without
++modification, are permitted provided that the following conditions
++are met:
++
++1. Redistributions of source code must retain the above copyright
++   notice, this list of conditions and the following disclaimer.
++2. Redistributions in binary form must reproduce the above copyright
++   notice, this list of conditions and the following disclaimer in the
++   documentation and/or other materials provided with the distribution.
++3. Neither the names of the authors nor the names of their contributors
++   may be used to endorse or promote products derived from this software
++   without specific prior written permission.
++
++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
++OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
++WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
++LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
++OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
++OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
++BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
++WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
++OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
++EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/Manifest.txt ./pkg/production_log_analyzer-1.5.4/Manifest.txt
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/Manifest.txt	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/Manifest.txt	2007-06-14 18:32:10.000000000 +0200
+@@ -0,0 +1,23 @@
++History.txt
++LICENSE.txt
++Manifest.txt
++README.txt
++Rakefile
++bin/action_errors
++bin/action_grep
++bin/pl_analyze
++lib/production_log/action_grep.rb
++lib/production_log/analyzer.rb
++lib/production_log/parser.rb
++lib/production_log/email.rb
++test/test.syslog.0.14.x.log
++test/test.syslog.1.2.shortname.log
++test/test.empty.log
++test/test.syslog.log
++test/test_action_grep.rb
++test/test_analyzer.rb
++test/analyzer_report.txt
++test/test_parser.rb
++test/test.railslog.log
++test/test.railslog.route_error.log
++test/test_email.rb
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/Rakefile ./pkg/production_log_analyzer-1.5.4/Rakefile
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/Rakefile	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/Rakefile	2007-06-14 18:41:12.000000000 +0200
+@@ -0,0 +1,15 @@
++require 'hoe'
++
++$:.unshift './lib'
++require 'production_log/analyzer'
++
++Hoe.new 'production_log_analyzer', '1.5.4' do |p|
++  p.summary = p.paragraphs_of('README.txt', 1).join ' '
++  p.description = p.paragraphs_of('README.txt', 7).join ' '
++  p.author = 'Eric Hodel'
++  p.email = 'drbrain@segment7.net'
++  p.url = p.paragraphs_of('README.txt', 2).join ' '
++
++  p.rubyforge_name = 'seattlerb'
++end
++
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/README.txt ./pkg/production_log_analyzer-1.5.4/README.txt
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/README.txt	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/README.txt	2007-06-05 17:43:36.000000000 +0200
+@@ -0,0 +1,156 @@
++= production_log_analyzer
++
++production_log_analyzer lets you find out which actions on a Rails
++site are used most and which ones are slowing your server down.
++
++http://seattlerb.rubyforge.org/production_log_analyzer
++
++http://rubyforge.org/projects/seattlerb
++
++Bug reports:
++
++http://rubyforge.org/tracker/?func=add&group_id=1513&atid=5921
++
++== About
++
++production_log_analyzer provides three tools to analyze log files
++created by rails applications.  pl_analyze can be used for getting daily reports,
++action_grep for pulling log lines for a single action and
++action_errors to summarize errors with counts.
++
++The PL Analyzer also includes action_grep which lets you grab lines from a log
++that only match a single action.
++
++  action_grep RssController#uber /var/log/production.log
++
++The log formats syslog and rails native are supported. PL Analyzer will by 
++default try to detect the log file format automatically.
++
++Warning: as of now, action_grep and action_errors don't support the 
++rails log format
++
++== Installing
++
++  sudo gem install production_log_analyzer
++
++=== Setup
++
++You might want to use syslog for logging your production rails app as that 
++offers you features such as remote logging. Instructions to set up SyslogLogger 
++with rails are provided here:
++
++http://seattlerb.rubyforge.org/SyslogLogger/
++
++Then:
++
++Set up a cronjob (or something like that) to run log files through pl_analyze.
++
++== Using pl_analyze
++
++pl_analyze offers a number of command line options to change its behaviour. To 
++see a list of options, simply execute pl_analyze.
++
++Basic use:
++
++Run pl_analyze with the name of one or more log files to analyze.
++
++  pl_analyze /var/log/production.log
++
++To see a progress bar while parsing, use the option --progress (this requires the 
++gem 'progressbar' to be installed).
++
++  pl_analyze --progress /var/log/production.log
++
++If you want, you can run it from a cron something like this:
++
++  /usr/bin/gzip -dc /var/log/production.log.0.gz | /usr/local/bin/pl_analyze /dev/stdin
++
++Or, have pl_analyze email you (which is preferred, because tabs get preserved):
++
++  /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"`"
++
++In the future, pl_analyze will be able to read from STDIN.
++
++== Sample output
++
++  Request Times Summary:          Count   Avg     Std Dev Min     Max
++  ALL REQUESTS:                   11      0.576   0.508   0.000   1.470
++  
++  ThingsController#view:          3       0.716   0.387   0.396   1.260
++  TeamsController#progress:       2       0.841   0.629   0.212   1.470
++  RssController#uber:             2       0.035   0.000   0.035   0.035
++  PeopleController#progress:      2       0.489   0.489   0.000   0.977
++  PeopleController#view:          2       0.731   0.371   0.360   1.102
++  
++  Average Request Time: 0.634
++  Request Time Std Dev: 0.498
++  
++  Slowest Request Times:
++          TeamsController#progress took 1.470s
++          ThingsController#view took 1.260s
++          PeopleController#view took 1.102s
++          PeopleController#progress took 0.977s
++          ThingsController#view took 0.492s
++          ThingsController#view took 0.396s
++          PeopleController#view took 0.360s
++          TeamsController#progress took 0.212s
++          RssController#uber took 0.035s
++          RssController#uber took 0.035s
++  
++  ------------------------------------------------------------------------
++  
++  DB Times Summary:               Count   Avg     Std Dev Min     Max
++  ALL REQUESTS:                   11      0.366   0.393   0.000   1.144
++  
++  ThingsController#view:          3       0.403   0.362   0.122   0.914
++  TeamsController#progress:       2       0.646   0.497   0.149   1.144
++  RssController#uber:             2       0.008   0.000   0.008   0.008
++  PeopleController#progress:      2       0.415   0.415   0.000   0.830
++  PeopleController#view:          2       0.338   0.149   0.189   0.486
++  
++  Average DB Time: 0.402
++  DB Time Std Dev: 0.394
++  
++  Slowest Total DB Times:
++          TeamsController#progress took 1.144s
++          ThingsController#view took 0.914s
++          PeopleController#progress took 0.830s
++          PeopleController#view took 0.486s
++          PeopleController#view took 0.189s
++          ThingsController#view took 0.173s
++          TeamsController#progress took 0.149s
++          ThingsController#view took 0.122s
++          RssController#uber took 0.008s
++          RssController#uber took 0.008s
++  
++  ------------------------------------------------------------------------
++  
++  Render Times Summary:           Count   Avg     Std Dev Min     Max
++  ALL REQUESTS:                   11      0.219   0.253   0.000   0.695
++  
++  ThingsController#view:          3       0.270   0.171   0.108   0.506
++  TeamsController#progress:       2       0.000   0.000   0.000   0.000
++  RssController#uber:             2       0.012   0.000   0.012   0.012
++  PeopleController#progress:      2       0.302   0.302   0.000   0.604
++  PeopleController#view:          2       0.487   0.209   0.278   0.695
++  
++  Average Render Time: 0.302
++  Render Time Std Dev: 0.251
++  
++  Slowest Total Render Times:
++          PeopleController#view took 0.695s
++          PeopleController#progress took 0.604s
++          ThingsController#view took 0.506s
++          PeopleController#view took 0.278s
++          ThingsController#view took 0.197s
++          ThingsController#view took 0.108s
++          RssController#uber took 0.012s
++          RssController#uber took 0.012s
++          TeamsController#progress took 0.000s
++          TeamsController#progress took 0.000s
++
++== What's missing
++
++* More reports
++* Read from STDIN
++
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/analyzer_report.txt ./pkg/production_log_analyzer-1.5.4/test/analyzer_report.txt
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/analyzer_report.txt	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/test/analyzer_report.txt	2007-06-06 10:14:10.000000000 +0200
+@@ -0,0 +1,56 @@
++Log files analyzed:
++test/test.syslog.log
++
++First request:         Mon Mar 07 07:00:25 +0100 2005
++Last request:          Mon Mar 07 07:00:32 +0100 2005
++Requests per second/minute/hour:   1.57/94.29/5657.14
++
++------------------------------------------------------------------------
++
++HTTP Status Codes Summary
++Code	HTTP		Number	Fra
++
++------------------------------------------------------------------------
++
++Request Times Summary:    	Count	Fra	Avg	Std Dev	Min	Max
++ALL REQUESTS:             	11	100%	0.576	0.508	0.000	1.470
++
++ThingsController#view:    	3	27.3%	0.716	0.387	0.396	1.260
++TeamsController#progress: 	2	18.2%	0.841	0.629	0.212	1.470
++RssController#uber:       	2	18.2%	0.035	0.000	0.035	0.035
++... [2 not shown]
++
++Slowest Request Times:
++	TeamsController#progress took 1.470s
++	ThingsController#view took 1.260s
++	PeopleController#view took 1.102s
++
++------------------------------------------------------------------------
++
++DB Times Summary:         	Count	Fra	Avg	Std Dev	Min	Max
++ALL REQUESTS:             	11	100%	0.366	0.393	0.000	1.144
++
++ThingsController#view:    	3	27.3%	0.403	0.362	0.122	0.914
++TeamsController#progress: 	2	18.2%	0.646	0.497	0.149	1.144
++RssController#uber:       	2	18.2%	0.008	0.000	0.008	0.008
++... [2 not shown]
++
++Slowest Total DB Times:
++	TeamsController#progress took 1.144s
++	ThingsController#view took 0.914s
++	PeopleController#progress took 0.830s
++
++------------------------------------------------------------------------
++
++Render Times Summary:     	Count	Fra	Avg	Std Dev	Min	Max
++ALL REQUESTS:             	11	100%	0.219	0.253	0.000	0.695
++
++ThingsController#view:    	3	27.3%	0.270	0.171	0.108	0.506
++TeamsController#progress: 	2	18.2%	0.000	0.000	0.000	0.000
++RssController#uber:       	2	18.2%	0.012	0.000	0.012	0.012
++... [2 not shown]
++
++Slowest Total Render Times:
++	PeopleController#view took 0.695s
++	PeopleController#progress took 0.604s
++	ThingsController#view took 0.506s
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test_action_grep.rb ./pkg/production_log_analyzer-1.5.4/test/test_action_grep.rb
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test_action_grep.rb	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/test/test_action_grep.rb	2007-05-16 23:37:36.000000000 +0200
+@@ -0,0 +1,68 @@
++#!/usr/local/bin/ruby -w
++
++require 'stringio'
++require 'tempfile'
++require 'test/unit'
++
++require 'production_log/action_grep'
++
++class TestActionGrep < Test::Unit::TestCase
++
++  def setup
++    @syslog_file_name = File.expand_path(File.join(File.dirname(__FILE__),
++                                                   'test.syslog.log'))
++  end
++
++  def test_module_grep
++    old_stdout = $stdout.dup
++    stdout = StringIO.new
++    $stdout = stdout
++
++    ActionGrep.grep 'RssController', @syslog_file_name
++
++    stdout.rewind
++
++    lines = stdout.readlines
++
++    assert_equal 19, lines.length
++
++  ensure
++    $stdout = old_stdout
++  end
++
++  def test_module_grep_arguments
++    file = Tempfile.new File.basename(__FILE__)
++
++    assert_raises ArgumentError do
++      ActionGrep.grep 'Foo_Controller', '/tmp/no_such_file/no_really/'
++    end
++
++    assert_raises ArgumentError do
++      ActionGrep.grep 'FooController#5', '/tmp/no_such_file/no_really/'
++    end
++
++    assert_raises ArgumentError do
++      ActionGrep.grep '5', '/tmp/no_such_file/no_really/'
++    end
++
++    assert_raises ArgumentError do
++      ActionGrep.grep 'FooController', '/tmp/no_such_file/no_really'
++    end
++
++    assert_nothing_raised do
++      ActionGrep.grep 'FooController', file.path
++      ActionGrep.grep 'FooController5', file.path
++      ActionGrep.grep 'FooController#action', file.path
++      ActionGrep.grep 'FooController#action_thingy', file.path
++      ActionGrep.grep 'FooController#action_thingy5', file.path
++      ActionGrep.grep 'FooController5#action', file.path
++      ActionGrep.grep 'FooController5#action_thingy', file.path
++      ActionGrep.grep 'FooController5#action_thingy5', file.path
++    end
++
++  ensure
++    file.close
++  end
++
++end
++
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test_analyzer.rb ./pkg/production_log_analyzer-1.5.4/test/test_analyzer.rb
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test_analyzer.rb	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/test/test_analyzer.rb	2007-06-14 18:23:50.000000000 +0200
+@@ -0,0 +1,305 @@
++#!/usr/local/bin/ruby -w
++
++$TESTING = true
++
++require 'test/unit'
++
++require 'production_log/analyzer'
++
++# hack String so the tests don't fail because there are tabs/spaces in the way
++class String
++  def ==(string)
++    return false if !string.respond_to?(:to_s)
++    return (self.gsub(/\s+/, ' ') <=> string.gsub(/\s+/, ' ')) == 0
++  end
++end
++
++class TestEnumerable < Test::Unit::TestCase
++  def test_sum
++    assert_equal 45, (1..9).sum
++  end
++
++  def test_average
++    # Ranges don't have a length
++    assert_in_delta 5.0, (1..9).to_a.average, 0.01
++  end
++
++  def test_sample_variance
++    assert_in_delta 6.6666, (1..9).to_a.sample_variance, 0.0001
++  end
++
++  def test_standard_deviation
++    assert_in_delta 2.5819, (1..9).to_a.standard_deviation, 0.0001
++  end
++end
++
++class TestSizedList < Test::Unit::TestCase
++
++  def setup
++    @list = SizedList.new 10 do |arr,|
++      arr.delete_at 0
++    true
++    end
++  end
++
++  def test_append
++    assert_equal [], @list.entries
++
++    (1..10).each { |i| @list << i }
++    assert_equal 10, @list.length
++    assert_equal((1..10).to_a, @list.entries)
++
++    @list << 11
++    assert_equal 10, @list.length
++    assert_equal((2..11).to_a, @list.entries)
++  end
++
++end
++
++class TestSlowestTimes < Test::Unit::TestCase
++
++  def setup
++    @list = SlowestTimes.new 10
++  end
++
++  def test_that_it_works
++    expected = []
++
++    10.downto(1) do |i|
++      @list << [i, nil]
++      expected << [i, nil]
++    end
++
++    assert_equal expected, @list.entries
++
++    @list << [11, nil]
++    expected.pop
++    expected.push [11, nil]
++
++    assert_equal 10, @list.length
++    assert_equal expected, @list.entries
++
++    @list << [0, nil]
++
++    assert_equal expected, @list.entries
++  end
++
++end
++
++class TestAnalyzer < Test::Unit::TestCase
++
++  def setup
++    @analyzer = Analyzer.new 3
++    @analyzer.process 'test/test.syslog.log'
++  end
++
++  def test_average_db_time
++    assert_in_delta 0.4023761, @analyzer.average_db_time, 0.0000001
++  end
++
++  def test_average_render_time
++    assert_in_delta 0.3015584, @analyzer.average_render_time, 0.0000001
++  end
++
++  def test_average_request_time
++    assert_in_delta 0.6338176, @analyzer.average_request_time, 0.0000001
++  end
++
++  def test_db_time_std_dev
++    assert_in_delta 0.3941380, @analyzer.db_time_std_dev, 0.0000001
++  end
++
++  def test_db_times_summary
++    expected = <<EOF.strip
++DB Times Summary:               Count   Fra     Avg     Std Dev Min     Max
++ALL REQUESTS:                   11      100%    0.366   0.393   0.000   1.144
++
++ThingsController#view:          3       27.3%   0.403   0.362   0.122   0.914
++TeamsController#progress:       2       18.2%   0.646   0.497   0.149   1.144
++RssController#uber:             2       18.2%   0.008   0.000   0.008   0.008
++... [2 not shown]
++EOF
++
++    assert_equal expected, @analyzer.db_times_summary
++  end
++
++  def test_empty_syslog
++    analyzer = Analyzer.new
++    assert_nothing_raised do
++      analyzer.process 'test/test.empty.log'
++      analyzer.report
++    end
++    assert_equal "No requests to analyze", analyzer.report
++  end
++
++  def test_longest_request_name
++    assert_equal false, @analyzer.instance_variables.include?('@longest_req')
++
++    request_times = {
++      "ThingsController#view"     => [0],
++      "TeamsController#progress"  => [1],
++      "RssController#uber"        => [0],
++      "PeopleController#progress" => [0],
++      nil                         => [0],
++    }
++
++    @analyzer.instance_variable_set('@request_times', request_times)
++
++    assert_equal 26, @analyzer.longest_request_name
++  end
++
++  def test_pad_request_name
++    assert_equal 26, @analyzer.longest_request_name
++    assert_equal("PeopleController#view:    ",
++                 @analyzer.pad_request_name("PeopleController#view"))
++  end
++
++  def test_pad_request_name_nil
++    assert_equal 26, @analyzer.longest_request_name
++    assert_equal("Unknown:                  ",
++                 @analyzer.pad_request_name(nil))
++  end
++
++  def test_pad_request_name_short
++    analyzer = Analyzer.new
++    analyzer.process 'test/test.syslog.1.2.shortname.log'
++    longer_request_name_value = " " * (analyzer.longest_request_name + 1)
++    assert_nothing_raised do
++      analyzer.pad_request_name(longer_request_name_value)
++    end
++    assert_equal longer_request_name_value + ":", analyzer.pad_request_name(longer_request_name_value)
++  end
++
++  def test_process
++    expected_request_times = {
++      "PeopleController#view"     => [1.102098, 0.36021],
++      "ThingsController#view"     => [0.396183, 0.49176, 1.259728],
++      "TeamsController#progress"  => [1.469788, 0.211973],
++      "RssController#uber"        => [0.034519, 0.034519],
++      "PeopleController#progress" => [0.977398, 0]
++    }
++    assert_equal expected_request_times, @analyzer.request_times
++
++    expected_db_times = {
++      "PeopleController#view"     => [0.486258, 0.189119],
++      "ThingsController#view"     => [0.122158, 0.172767, 0.914192],
++      "TeamsController#progress"  => [1.143577, 0.149357],
++      "RssController#uber"        => [0.007962, 0.007962],
++      "PeopleController#progress" => [0.830409, 0]
++    }
++    assert_equal expected_db_times, @analyzer.db_times
++
++    expected_render_times = {
++      "PeopleController#view"     => [0.695476, 0.277921],
++      "ThingsController#view"     => [0.107987, 0.197126, 0.505973],
++      "TeamsController#progress"  => [0, 0],
++      "RssController#uber"        => [0.01177, 0.01177],
++      "PeopleController#progress" => [0.604444, 0]
++    }
++    assert_equal expected_render_times, @analyzer.render_times
++  end
++
++  def test_render_time_std_dev
++    assert_in_delta 0.2513925, @analyzer.render_time_std_dev, 0.0000001
++  end
++
++  def test_render_times_summary
++    expected = <<EOF.strip
++Render Times Summary:           Count   Fra     Avg     Std Dev Min     Max
++ALL REQUESTS:                   11      100%    0.219   0.253   0.000   0.695
++
++ThingsController#view:          3       27.3%   0.270   0.171   0.108   0.506
++TeamsController#progress:       2       18.2%   0.000   0.000   0.000   0.000
++RssController#uber:             2       18.2%   0.012   0.000   0.012   0.012
++... [2 not shown]
++EOF
++
++    assert_equal expected, @analyzer.render_times_summary
++  end
++
++  def test_report
++    assert_equal File.open("test/analyzer_report.txt").read, @analyzer.report
++  end
++
++  def test_request_time_std_dev
++    assert_in_delta 0.4975667, @analyzer.request_time_std_dev, 0.0000001
++  end
++
++  def test_request_times_summary
++    expected = <<EOF.strip
++Request Times Summary:          Count   Fra     Avg     Std Dev Min     Max
++ALL REQUESTS:                   11      100%    0.576   0.508   0.000   1.470
++
++ThingsController#view:          3       27.3%   0.716   0.387   0.396   1.260
++TeamsController#progress:       2       18.2%   0.841   0.629   0.212   1.470
++RssController#uber:             2       18.2%   0.035   0.000   0.035   0.035
++... [2 not shown]
++EOF
++
++    assert_equal expected, @analyzer.request_times_summary
++  end
++
++  def test_slowest_db_times
++    times = @analyzer.slowest_db_times
++    assert_equal 3, times.length
++    expected = [
++      [1.143577, "TeamsController#progress"],
++      [0.914192, "ThingsController#view"],
++      [0.830409, "PeopleController#progress"]
++    ]
++    assert_equal expected, times
++  end
++
++  def test_slowest_request_times
++    times = @analyzer.slowest_request_times
++    assert_equal 3, times.length
++    expected = [
++      [1.469788, "TeamsController#progress"],
++      [1.259728, "ThingsController#view"],
++      [1.102098, "PeopleController#view"]
++    ]
++    assert_equal expected, times
++  end
++
++  def test_slowest_render_times
++    times = @analyzer.slowest_render_times
++    assert_equal 3, times.length
++    expected = [
++      [0.695476, "PeopleController#view"],
++      [0.604444, "PeopleController#progress"],
++      [0.505973, "ThingsController#view"]
++    ]
++    assert_equal expected, times
++  end
++
++  def test_http_statuses
++    statuses = Analyzer.new.process('test/test.railslog.log').http_statuses
++    assert_equal 3, statuses.values.flatten.size
++    expected = {"MessageController#index"=>[302], "AccountController#login"=>[302, 302]}
++    assert_equal expected, statuses
++  end
++
++  def test_request_statuses_summary
++    statuses_summary = Analyzer.new.process('test/test.railslog.log').request_statuses_summary
++    expected = <<EOF.strip
++HTTP Status Codes Summary
++Code    HTTP            Number  Fra
++302     Found           3       100.00%
++EOF
++    
++    assert_equal expected, statuses_summary    
++  end
++
++  def test_controller_times_summary
++    summary = Analyzer.new.process('test/test.syslog.log').controller_times_summary('TeamsController')
++    expected = <<EOF.strip
++TeamsController Summary:        Count   Fra     Avg     Std Dev Min     Max
++ALL REQUESTS:                   2       100%    0.841   0.629   0.212   1.470
++
++TeamsController#progress:       2       100.0%  0.841   0.629   0.212   1.470
++EOF
++
++    assert_equal expected, summary
++  end
++end
++
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test_email.rb ./pkg/production_log_analyzer-1.5.4/test/test_email.rb
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test_email.rb	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/test/test_email.rb	2007-06-06 12:18:50.000000000 +0200
+@@ -0,0 +1,71 @@
++require 'production_log/email'
++
++class TestEmail < Test::Unit::TestCase
++  def test_email
++    report = Analyzer.new(1).process('test/test.syslog.log').report
++    email = email report, 'test@test.com', 'pl_analyze'
++    expected = <<EOF
++Subject: pl_analyze
++To: test@test.com
++Content-Type: text/html
++
++<pre>Log files analyzed:
++test/test.syslog.log
++
++First request:         Mon Mar 07 07:00:25 +0100 2005
++Last request:          Mon Mar 07 07:00:32 +0100 2005
++Requests per second/minute/hour:   1.57/94.29/5657.14
++
++------------------------------------------------------------------------
++
++HTTP Status Codes Summary
++Code  HTTP    Number  Fra
++
++------------------------------------------------------------------------
++
++Request Times Summary:      Count Fra Avg Std Dev Min Max
++ALL REQUESTS:               11  100%  0.576 0.508 0.000 1.470
++
++ThingsController#view:      3 27.3% 0.716 0.387 0.396 1.260
++... [4 not shown]
++
++Slowest Request Times:
++  TeamsController#progress took 1.470s
++
++  ------------------------------------------------------------------------
++
++  DB Times Summary:           Count Fra Avg Std Dev Min Max
++  ALL REQUESTS:               11  100%  0.366 0.393 0.000 1.144
++
++  ThingsController#view:      3 27.3% 0.403 0.362 0.122 0.914
++  ... [4 not shown]
++
++  Slowest Total DB Times:
++    TeamsController#progress took 1.144s
++
++    ------------------------------------------------------------------------
++
++    Render Times Summary:       Count Fra Avg Std Dev Min Max
++    ALL REQUESTS:               11  100%  0.219 0.253 0.000 0.695
++
++    ThingsController#view:      3 27.3% 0.270 0.171 0.108 0.506
++    ... [4 not shown]
++
++    Slowest Total Render Times:
++      PeopleController#view took 0.695s
++      </pre>
++EOF
++
++    assert_equal expected, email
++  end
++
++  def test_envelope
++    expected = {
++      'Subject' => 'pl_analyze',
++      'To' => 'test@test.com',
++      'Content-Type' => 'text/html' }
++    envelope = envelope 'test@test.com', 'pl_analyze'
++
++    assert_equal expected, envelope
++  end
++end
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.empty.log ./pkg/production_log_analyzer-1.5.4/test/test.empty.log
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.empty.log	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/test/test.empty.log	2007-06-15 10:24:45.000000000 +0200
+@@ -0,0 +1 @@
++<dummy line>
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test_parser.rb ./pkg/production_log_analyzer-1.5.4/test/test_parser.rb
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test_parser.rb	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/test/test_parser.rb	2007-06-14 18:24:42.000000000 +0200
+@@ -0,0 +1,327 @@
++#!/usr/local/bin/ruby -w
++
++$TESTING = true
++
++require 'tempfile'
++require 'test/unit'
++require 'stringio'
++require 'time'
++
++require 'production_log/parser'
++
++class TestLogEntry < Test::Unit::TestCase
++
++  def setup
++    request = <<EOF
++Processing TwinklerController#index (for 81.109.96.173 at Wed Dec 01 16:01:56 CST 2004)
++Parameters: {\"action\"=>\"index\", \"controller\"=>\"twinkler\"}
++Browser Load First (0.001114)   SELECT * FROM browsers WHERE ubid = 'ixsXHgUo7U9PJGgBzr7e9ocaDOc=' LIMIT 1
++Goal Count (0.001762)   SELECT COUNT(*) FROM goals WHERE browser_id = '96181' and is_active = 1 
++Rendering twinkler/index within layouts/default
++Rendering layouts/default (200 OK)
++Completed in 0.616122 (1 reqs/sec) | Rendering: 0.242475 (39%) | DB: 0.002876 (0%)
++EOF
++    @entry = Log::LogEntry.new request.split("\n")
++  end
++
++  def test_parse
++    request = <<EOF
++Processing RssController#uber (for 67.18.200.5 at Mon Mar 07 00:00:25 CST 2005)
++Parameters: {:id=>"author", :"rss/uber/author.html/uber/author"=>nil, :action=>"uber", :username=>"looch", :controller=>"rss"}
++Cookie set: auth=dc%2FGUP20BwziF%2BApGecc0pXB0PF0obi55az63ubAFtsnOOdJPkhfJH2U09yuzQD3WtdmWnydLzFcRA78kwi7Gw%3D%3D; path=/; expires=Thu, 05 Mar 2015 06:00:25 GMT
++Cookie set: ubid=kF05DqFH%2F9hRCOxTz%2Bfb8Q7UV%2FI%3D; path=/; expires=Thu, 05 Mar 2015 06:00:25 GMT
++Browser Load (0.003963)   SELECT * FROM browsers WHERE ubid = 'kF05DqFH/9hRCOxTz+fb8Q7UV/I=' LIMIT 1
++Person Load (0.002445)   SELECT * FROM people WHERE username = 'looch' AND active = '1' LIMIT 1
++ProfileImage Load (0.001554)   SELECT * FROM profile_images WHERE id = 2782 LIMIT 1
++Rendering rss/rss2.0 (200 OK)
++Completed in 0.034519 (28 reqs/sec) | Rendering: 0.011770 (34%) | DB: 0.007962 (23%)
++EOF
++    request = request.split "\n"
++
++    entry = Log::LogEntry.new request
++
++    assert_kind_of Log::LogEntry, entry
++    assert_equal "RssController#uber", entry.page
++#    assert_equal 3, entry.queries.length
++#    assert_equal ['Browser Load', 0.003963], entry.queries.first
++    assert_equal 0.034519, entry.request_time
++  end
++
++  def test_page
++    assert_equal "TwinklerController#index", @entry.page
++  end
++
++  def test_ip
++    assert_equal "81.109.96.173", @entry.ip
++  end
++
++  def test_time
++    assert_equal "Wed Dec 01 23:01:56 +0100 2004", @entry.time.to_s
++  end
++
++#  def test_queries
++#    expected = []
++#    expected << ["Browser Load First", 0.001114]
++#    expected << ["Goal Count", 0.001762]
++#    assert_equal expected, @entry.queries
++#  end
++
++  def test_request_time
++    assert_equal 0.616122, @entry.request_time
++
++    @entry = Log::LogEntry.new "Processing TwinklerController#add_thing (for 144.164.232.114 at Wed Dec 01 16:01:56 CST 2004)
++Completed in 0.261485 (3 reqs/sec) | DB: 0.009325 (3%)"
++
++    assert_equal 0.261485, @entry.request_time
++  end
++
++  def test_render_time
++    assert_equal 0.242475, @entry.render_time
++
++    @entry = Log::LogEntry.new "Processing TwinklerController#add_thing (for 144.164.232.114 at Wed Dec 01 16:01:56 CST 2004)
++Completed in 0.261485 (3 reqs/sec) | DB: 0.009325 (3%)"
++
++    assert_equal 0, @entry.render_time
++  end
++
++  def test_db_time
++    assert_equal 0.002876, @entry.db_time
++  end
++
++  def test_http_status
++    @entry_404 = Log::LogEntry.new "Completed in 0.01285 (77 reqs/sec) | Rendering: 0.00519 (40%) | DB: 0.00308 (23%) | 404 [http:// /]"
++    assert_equal 404, @entry_404.http_status
++
++    @entry_302 = Log::LogEntry.new "Completed in 0.04697 (21 reqs/sec) | DB: 0.01132 (24%) | 302 Found [http:// /]"
++    assert_equal 302, @entry_302.http_status
++
++    @entry_200 = Log::LogEntry.new "Completed in 0.04368 (22 reqs/sec) | Rendering: 0.00510 (11%) | DB: 0.00000 (0%) | 200 OK [http:// /]"
++    assert_equal 200, @entry_200.http_status
++  end
++
++  def test_url
++    entry = Log::LogEntry.new "Completed in 0.01285 (77 reqs/sec) | Rendering: 0.00519 (40%) | DB: 0.00308 (23%) | 404 [http://example.org/test/]"
++    assert_equal "http://example.org/test/", entry.url
++  end
++end
++
++class TestSyslogParser < Test::Unit::TestCase
++  include Log
++
++  def test_class_parse_syslog
++    log = StringIO.new <<-EOF
++Mar  7 00:00:25 online1 rails[59628]: Processing RssController#uber (for 67.18.200.5 at Mon Mar 07 00:00:25 CST 2005)
++Mar  7 00:00:25 online1 rails[59628]: Parameters: {:id=>"author", :"rss/uber/author.html/uber/author"=>nil, :action=>"uber", :username=>"looch", :controller=>"rss"}
++Mar  7 00:00:25 online1 rails[59628]: Cookie set: auth=dc%2FGUP20BwziF%2BApGecc0pXB0PF0obi55az63ubAFtsnOOdJPkhfJH2U09yuzQD3WtdmWnydLzFcRA78kwi7Gw%3D%3D; path=/; expires=Thu, 05 Mar 2015 06:00:25 GMT
++Mar  7 00:00:25 online1 rails[59628]: Cookie set: ubid=kF05DqFH%2F9hRCOxTz%2Bfb8Q7UV%2FI%3D; path=/; expires=Thu, 05 Mar 2015 06:00:25 GMT
++Mar  7 00:00:25 online1 rails[59628]: Browser Load (0.003963)   SELECT * FROM browsers WHERE ubid = 'kF05DqFH/9hRCOxTz+fb8Q7UV/I=' LIMIT 1
++Mar  7 00:00:25 online1 rails[59628]: Person Load (0.002445)   SELECT * FROM people WHERE username = 'looch' AND active = '1' LIMIT 1
++Mar  7 00:00:25 online1 rails[59628]: ProfileImage Load (0.001554)   SELECT * FROM profile_images WHERE id = 2782 LIMIT 1
++Mar  7 00:00:25 online1 rails[59628]: Rendering rss/rss2.0 (200 OK)
++Mar  7 00:00:25 online1 rails[59628]: Completed in 0.034519 (28 reqs/sec) | Rendering: 0.011770 (34%) | DB: 0.007962 (23%)
++        EOF
++
++    entries = []
++    
++    Parser.parse(log, :syslog) do |entry|
++      entries << entry
++    end
++
++    assert_equal 1, entries.length
++    assert_equal 'RssController#uber', entries.first.page
++  end
++
++  def test_class_parse_syslog_components
++    log = StringIO.new <<-EOF
++Jul 11 10:05:20 www rails[61243]: Processing ChatroomsController#launch (for 213.152.37.169 at Mon Jul 11 10:05:20 CDT 2005)
++Jul 11 10:05:20 www rails[61243]: Start rendering component ({:action=>"online_count", :controller=>"members"}):
++Jul 11 10:05:20 www rails[34216]: Processing ChatroomsController#launch (for 213.152.37.169 at Mon Jul 11 10:05:20 CDT 2005)
++Jul 11 10:05:20 www rails[34216]: Start rendering component ({:action=>"online_count", :controller=>"members"}):
++Jul 11 10:05:20 www rails[34216]: Processing MembersController#online_count (for 213.152.37.169 at Mon Jul 11 10:05:20 CDT 2005)
++Jul 11 10:05:20 www rails[34216]: Completed in 0.00741 (135 reqs/sec) | DB: 0.00320 (43%)
++Jul 11 10:05:20 www rails[34216]: End of component rendering
++Jul 11 10:05:28 www rails[34216]: Completed in 8.65005 (0 reqs/sec) | Rendering: 8.64820 (99%) | DB: 0.00000 (0%)
++Jul 11 10:05:20 www rails[34216]: Processing ChatroomsController#launch (for 213.152.37.169 at Mon Jul 11 10:05:20 CDT 2005)
++Jul 11 10:05:20 www rails[34216]: Start rendering component ({:action=>"online_count", :controller=>"members"}):
++Jul 11 10:05:20 www rails[34216]: Processing MembersController#online_count (for 213.152.37.169 at Mon Jul 11 10:05:20 CDT 2005)
++Jul 11 10:05:20 www rails[34216]: Completed in 0.00741 (135 reqs/sec) | DB: 0.00320 (43%)
++Jul 11 10:05:20 www rails[34216]: End of component rendering
++Jul 11 10:05:28 www rails[34216]: Completed in 8.65005 (0 reqs/sec) | Rendering: 8.64820 (99%) | DB: 0.00000 (0%)
++Jul 11 10:05:20 www rails[61243]: Processing MembersController#online_count (for 213.152.37.169 at Mon Jul 11 10:05:20 CDT 2005)
++Jul 11 10:05:20 www rails[61243]: Completed in 0.00741 (135 reqs/sec) | DB: 0.00320 (43%)
++Jul 11 10:05:20 www rails[61243]: End of component rendering
++Jul 11 10:05:28 www rails[61243]: Completed in 8.65005 (0 reqs/sec) | Rendering: 8.64820 (99%) | DB: 0.00000 (0%)
++        EOF
++
++    entries = []
++    Parser.parse(log, :syslog) { |entry| entries << entry }
++
++    assert_equal 3, entries.length
++    assert_equal 'ChatroomsController#launch', entries.first.page
++    assert_equal 8.65005, entries.first.request_time
++  end
++
++  def test_class_parse_syslog_entries_with_pre_processing_garbage
++    log = StringIO.new <<-EOF
++Jan 03 12:51:34 duo2 rails[4347]: [4;36;1mFont Load (0.000475)[0m   [0;1mSELECT * FROM fonts ORDER BY name [0m
++Jan 03 12:51:34 duo2 rails[4347]: Processing StylesheetsController#show (for 127.0.0.1 at 2007-01-03 12:51:34) [GET]
++Jan 03 12:51:34 duo2 rails[4347]: Parameters: {"action"=>"show", "id"=>"1", "controller"=>"stylesheets"}
++Jan 03 12:51:34 duo2 rails[4347]: [4;35;1mNewspaper Load (0.000970)[0m   [0mSELECT newspapers.* FROM newspapers INNER JOIN users ON newspapers.editor_in_chief = users.id WHERE (users.login = 'geoff') LIMIT 1[0m
++Jan 03 12:51:34 duo2 rails[4347]: [4;36;1mLayout Load (0.000501)[0m   [0;1mSELECT * FROM layouts WHERE (layouts.id = 1) LIMIT 1[0m
++Jan 03 12:51:34 duo2 rails[4347]: Completed in 0.00807 (123 reqs/sec) | Rendering: 0.00006 (0%) | DB: 0.00195 (24%) | 200 OK [http://geoff.localhost.com/stylesheets/show/1/styles.css]
++    EOF
++
++    entries = []
++    Parser.parse(log, :syslog) { |entry| entries << entry }
++
++    assert_equal 1, entries.length, "Number of entries was incorrect"
++    assert_equal 'StylesheetsController#show', entries.first.page
++    assert_equal 0.00807, entries.first.request_time
++  end
++
++  def test_class_parse_syslog_rails_engines_plugin
++    log = StringIO.new <<-EOF
++Jan 03 12:24:21 duo2 rails[4277]: Trying to start engine 'login_engine' from '/Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine'
++Jan 03 12:24:21 duo2 rails[4277]: adding /Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine/lib/login_engine to the load path
++Jan 03 12:24:21 duo2 rails[4277]: adding /Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine/app/views/user_notify to the load path
++Jan 03 12:24:21 duo2 rails[4277]: adding /Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine/app/views/user to the load path
++Jan 03 12:24:21 duo2 rails[4277]: adding /Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine/app/views to the load path
++Jan 03 12:24:21 duo2 rails[4277]: adding /Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine/app/models to the load path
++Jan 03 12:24:21 duo2 rails[4277]: adding /Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine/app/helpers to the load path
++Jan 03 12:24:21 duo2 rails[4277]: adding /Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine/app/controllers to the load path
++Jan 03 12:24:21 duo2 rails[4277]: Attempting to copy public engine files from '/Users/topfunky/web/rails/repos/roughunderbelly/config/../vendor/plugins/login_engine/public'
++Jan 03 12:24:21 duo2 rails[4277]: source dirs: ["/Users/topfunky/web/rails/repos/roughunderbelly/config/../vendor/plugins/login_engine/public/stylesheets"]
++Jan 03 12:24:22 duo2 rails[4277]: finally loading from application: 'exception_notifier.rb'
++Jan 03 12:24:22 duo2 rails[4277]: requiring file 'exception_notifier_helper'
++Jan 03 12:24:22 duo2 rails[4277]: checking 'login_engine' for /Users/topfunky/web/rails/repos/roughunderbelly/config/../vendor/plugins/login_engine/exception_notifier_helper.rb
++Jan 03 12:24:22 duo2 rails[4277]: finally loading from application: 'exception_notifier_helper.rb'
++Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: '/Users/topfunky/web/rails/repos/roughunderbelly/config/../app/controllers/application.rb'
++Jan 03 12:24:23 duo2 rails[4277]: requiring file 'application_helper'
++Jan 03 12:24:23 duo2 rails[4277]: checking 'login_engine' for /Users/topfunky/web/rails/repos/roughunderbelly/config/../vendor/plugins/login_engine/application_helper.rb
++Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'application_helper.rb'
++Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'exception_notifiable.rb'
++Jan 03 12:24:23 duo2 rails[4277]: requiring file 'user_helper'
++Jan 03 12:24:23 duo2 rails[4277]: checking 'login_engine' for /Users/topfunky/web/rails/repos/roughunderbelly/config/../vendor/plugins/login_engine/user_helper.rb
++Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'user_helper.rb'
++Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'user.rb'
++Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'task.rb'
++Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'client.rb'
++Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'email.rb'
++Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'worth.rb'
++Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'column_pref.rb'
++Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'timer.rb'
++Jan 03 12:24:23 duo2 rails[4277]: requiring file '/Users/topfunky/web/rails/repos/roughunderbelly/config/../app/controllers/tasks_controller.rb'
++Jan 03 12:24:23 duo2 rails[4277]: detected RAILS_ROOT, rewriting to 'app/controllers/tasks_controller.rb'
++Jan 03 12:24:23 duo2 rails[4277]: checking 'login_engine' for /Users/topfunky/web/rails/repos/roughunderbelly/config/../vendor/plugins/login_engine/app/controllers/tasks_controller.rb
++Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: '/Users/topfunky/web/rails/repos/roughunderbelly/config/../app/controllers/tasks_controller.rb'
++Jan 03 12:24:23 duo2 rails[4277]: requiring file 'tasks_helper'
++Jan 03 12:24:23 duo2 rails[4277]: checking 'login_engine' for /Users/topfunky/web/rails/repos/roughunderbelly/config/../vendor/plugins/login_engine/tasks_helper.rb
++Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'tasks_helper.rb'
++Jan 03 12:24:23 duo2 rails[4277]: requiring file 'sparklines_helper'
++Jan 03 12:24:23 duo2 rails[4277]: checking 'login_engine' for /Users/topfunky/web/rails/repos/roughunderbelly/config/../vendor/plugins/login_engine/sparklines_helper.rb
++Jan 03 12:24:23 duo2 rails[4277]: finally loading from application: 'sparklines_helper.rb'
++Jan 03 12:24:24 duo2 rails[4277]: [4;36;1mSQL (0.000072)[0m   [0;1mBEGIN[0m
++Jan 03 12:24:24 duo2 rails[4277]: [4;35;1mSQL (0.000240)[0m   [0mINSERT INTO sessions (`updated_at`, `session_id`, `data`) VALUES('2007-01-03 20:24:24', 'bdbb75323d5da69f707d5576e907706e', 'BAh7AA==\n')[0m
++Jan 03 12:24:24 duo2 rails[4277]: [4;36;1mSQL (0.000400)[0m   [0;1mCOMMIT[0m
++Jan 03 12:24:24 duo2 rails[4277]: Processing TasksController#index (for 127.0.0.1 at 2007-01-03 12:24:24) [GET]
++Jan 03 12:24:24 duo2 rails[4277]: Parameters: {"action"=>"index", "controller"=>"tasks"}
++Jan 03 12:24:24 duo2 rails[4277]: Redirected to http://localhost:3000/tasks/list
++Jan 03 12:24:24 duo2 rails[4277]: Completed in 0.00112 (896 reqs/sec) | DB: 0.00071 (63%) | 302 Found [http://localhost/]
++    EOF
++
++    entries = []
++
++    Parser.parse(log, :syslog) do |entry|
++      entries << entry
++    end
++
++    assert_equal 1, entries.length, "The number of entries was incorrect"
++    assert_equal 'TasksController#index', entries.first.page
++    assert_equal 0.00112, entries.first.request_time
++  end
++
++  def test_class_parse_syslog_multi
++    entries = []
++    parser = Parser.new
++    Parser.parse_file('test/test.syslog.log', false, :syslog) do |entry|
++      entries << entry
++    end
++
++    assert_equal 12, entries.length
++    assert_equal 'RssController#uber', entries.first.page
++
++    redirect = entries[5]
++    assert_equal 'TeamsController#progress', redirect.page
++    assert_equal 0, redirect.render_time
++
++    last = entries.last
++    assert_equal 'PeopleController#progress', last.page
++    assert_equal 0, last.request_time
++  end
++
++  def test_class_parse_syslog_0_14_x
++    entries = []
++    parser = Parser.new
++    File.open 'test/test.syslog.0.14.x.log' do |fp|
++      parser.parse_syslog fp do |entry|
++        entries << entry
++      end
++    end
++  end
++end
++
++class TestLogParser < Test::Unit::TestCase
++  include Log
++  def test_class_detect_log_format
++    assert_equal :rails, Parser.detect_log_format('test/test.railslog.log')
++    assert_equal :syslog, Parser.detect_log_format('test/test.syslog.log')
++
++    assert_raise RuntimeError do
++      Parser.detect_log_format 'test/test.empty.log'
++    end
++  end
++
++  def test_class_parse_file_detects_format_correcly
++    entries = []
++    Parser.parse_file 'test/test.railslog.log' do |entry|
++      entries << entry
++    end
++
++    assert_equal 3, entries.length
++    assert_equal 'MessageController#index', entries.first.page
++
++    entries = []
++    Parser.parse_file 'test/test.syslog.log' do |entry|
++      entries << entry
++    end
++
++    assert_equal 12, entries.length
++    assert_equal 'RssController#uber', entries.first.page
++  end
++
++  def test_parsing_routing_error_does_not_yield_entry
++    entries = []
++    Parser.parse_file 'test/test.railslog.route_error.log' do |entry|
++      entries << entry
++    end
++
++    assert_equal 2, entries.size
++  end
++end
++  
++class TestRailslogParser < Test::Unit::TestCase
++  include Log
++  def test_class_parse_multi
++    entries = []
++    parser = Parser.new
++    File.open('test/test.railslog.log').each_line do |line|
++      parser.parse_rails line do |entry|
++        entries << entry
++      end
++    end
++
++    assert_equal 3, entries.length
++    assert_equal 'MessageController#index', entries.first.page
++  end
++end
++   
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.railslog.log ./pkg/production_log_analyzer-1.5.4/test/test.railslog.log
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.railslog.log	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/test/test.railslog.log	2007-06-14 18:21:58.000000000 +0200
+@@ -0,0 +1,33 @@
++
++Processing MessageController#index (for 127.0.0.1 at 2007-01-28 18:00:30) [GET]
++   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
++   Parameters: {"action"=>"index", "controller"=>"message"}
++   ^[[4;35;1mUser Load (0.001405)^[[0m   ^[[0mSELECT * FROM users WHERE (users.`id` = 1) LIMIT 1^[[0m
++Redirected to http://localhost:3000/account/login
++Filter chain halted as [#<ActionController::Filters::ClassMethods::SymbolFilter:0x6a7938 @filter=:login_required>] returned false.
++Completed in 0.00431 (232 reqs/sec) | DB: 0.00527 (122%) | 302 Found [http://localhost/]
++ApplicationController: missing default helper module ApplicationHelper
++  ^[[4;36;1mUser Columns (0.004048)^[[0m   ^[[0;1mSHOW FIELDS FROM users^[[0m
++
++
++Processing AccountController#login (for 127.0.0.1 at 2007-01-28 18:00:30) [GET]
++   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
++   Parameters: {"action"=>"index", "controller"=>"message"}
++   ^[[4;35;1mUser Load (0.001405)^[[0m   ^[[0mSELECT * FROM users WHERE (users.`id` = 1) LIMIT 1^[[0m
++Redirected to http://localhost:3000/account/login
++Filter chain halted as [#<ActionController::Filters::ClassMethods::SymbolFilter:0x6a7938 @filter=:login_required>] returned false.
++Completed in 0.00431 (232 reqs/sec) | DB: 0.00527 (122%) | 302 Found [http://localhost/]
++ApplicationController: missing default helper module ApplicationHelper
++  ^[[4;36;1mUser Columns (0.004048)^[[0m   ^[[0;1mSHOW FIELDS FROM users^[[0m
++
++
++Processing AccountController#login (for 127.0.0.1 at 2007-01-28 18:00:31) [GET]
++   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
++   Parameters: {"action"=>"index", "controller"=>"message"}
++   ^[[4;35;1mUser Load (0.001405)^[[0m   ^[[0mSELECT * FROM users WHERE (users.`id` = 1) LIMIT 1^[[0m
++Redirected to http://localhost:3000/account/login
++Filter chain halted as [#<ActionController::Filters::ClassMethods::SymbolFilter:0x6a7938 @filter=:login_required>] returned false.
++Completed in 0.00431 (232 reqs/sec) | DB: 0.00527 (122%) | 302 Found [http://localhost/]
++ApplicationController: missing default helper module ApplicationHelper
++  ^[[4;36;1mUser Columns (0.004048)^[[0m   ^[[0;1mSHOW FIELDS FROM users^[[0m
++
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.railslog.route_error.log ./pkg/production_log_analyzer-1.5.4/test/test.railslog.route_error.log
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.railslog.route_error.log	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/test/test.railslog.route_error.log	2007-06-14 18:30:55.000000000 +0200
+@@ -0,0 +1,74 @@
++
++
++Processing MessageController#index (for 127.0.0.1 at 2007-01-28 18:00:30) [GET]
++   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
++   Parameters: {"action"=>"index", "controller"=>"message"}
++   ^[[4;35;1mUser Load (0.001405)^[[0m   ^[[0mSELECT * FROM users WHERE (users.`id` = 1) LIMIT 1^[[0m
++Redirected to http://localhost:3000/account/login
++Filter chain halted as [#<ActionController::Filters::ClassMethods::SymbolFilter:0x6a7938 @filter=:login_required>] returned false.
++Completed in 0.00431 (232 reqs/sec) | DB: 0.00527 (122%) | 302 Found [http://localhost/]
++
++Processing ApplicationController#index (for 127.0.0.1 at 2007-01-28 18:38:27) [GET]
++   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
++   Parameters: {}
++
++
++ActionController::RoutingError (no route found to match "/messages" with {:method=>:get}):
++     /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/routing.rb:1266:in `recognize_path'
++     /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/routing.rb:1256:in `recognize'
++     /opt/local/lib/ruby/gems/1.8/gems/rails-1.2.1/lib/dispatcher.rb:40:in `dispatch'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/rails.rb:78:in `process'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/rails.rb:76:in `process'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:618:in `process_client'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:617:in `process_client'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:736:in `run'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:736:in `run'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:720:in `run'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/configurator.rb:271:in `run'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/configurator.rb:270:in `run'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/bin/mongrel_rails:127:in `run'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/command.rb:211:in `run'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/bin/mongrel_rails:243
++     /opt/local/bin/mongrel_rails:18
++
++
++Rendering /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/templates/rescues/layout.rhtml (404 Page Not Found)
++ApplicationController: missing default helper module ApplicationHelper
++
++
++Processing ApplicationController#index (for 127.0.0.1 at 2007-01-28 18:38:27) [GET]
++   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
++   Parameters: {}
++
++
++ActionController::RoutingError (no route found to match "/messages" with {:method=>:get}):
++     /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/routing.rb:1266:in `recognize_path'
++     /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/routing.rb:1256:in `recognize'
++     /opt/local/lib/ruby/gems/1.8/gems/rails-1.2.1/lib/dispatcher.rb:40:in `dispatch'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/rails.rb:78:in `process'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/rails.rb:76:in `process'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:618:in `process_client'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:617:in `process_client'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:736:in `run'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:736:in `run'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:720:in `run'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/configurator.rb:271:in `run'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/configurator.rb:270:in `run'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/bin/mongrel_rails:127:in `run'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/command.rb:211:in `run'
++     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/bin/mongrel_rails:243
++     /opt/local/bin/mongrel_rails:18
++
++
++Rendering /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/templates/rescues/layout.rhtml (404 Page Not Found)
++ApplicationController: missing default helper module ApplicationHelper
++
++
++Processing MessageController#index (for 127.0.0.1 at 2007-01-28 18:00:30) [GET]
++   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
++   Parameters: {"action"=>"index", "controller"=>"message"}
++   ^[[4;35;1mUser Load (0.001405)^[[0m   ^[[0mSELECT * FROM users WHERE (users.`id` = 1) LIMIT 1^[[0m
++Redirected to http://localhost:3000/account/login
++Filter chain halted as [#<ActionController::Filters::ClassMethods::SymbolFilter:0x6a7938 @filter=:login_required>] returned false.
++Completed in 0.00431 (232 reqs/sec) | DB: 0.00527 (122%) | 302 Found [http://localhost/]
++
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.syslog.0.14.x.log ./pkg/production_log_analyzer-1.5.4/test/test.syslog.0.14.x.log
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.syslog.0.14.x.log	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/test/test.syslog.0.14.x.log	2007-05-16 23:37:36.000000000 +0200
+@@ -0,0 +1,5 @@
++//src/production_log_analyzer/dev/test/test.syslog.0.14.x.log#1 - add change 3135 (text)
++Nov  7 12:14:02 192.168.1.71 43things[24950]: Processing RssController#entries (for 10.43.199.12 at 2005-11-07 12:14:02) [GET]
++Nov  7 12:14:02 192.168.1.71 43things[24950]: Parameters: {"rss/entries/goal.html/entries/goal"=>nil, "action"=>"entries", "id"=>"goal", "controller"=>"rss", "goal_id"=>"86381"}
++Nov  7 12:14:02 192.168.1.71 43things[24950]: Rendering rss/rss2.0
++Nov  7 12:14:02 192.168.1.71 43things[24950]: Completed in 0.00860 (116 reqs/sec) | DB: 0.00328 (38%) | 200 OK [http://www.43things.com/rss/entries/goal?goal_id=86381]
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.syslog.1.2.shortname.log ./pkg/production_log_analyzer-1.5.4/test/test.syslog.1.2.shortname.log
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.syslog.1.2.shortname.log	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/test/test.syslog.1.2.shortname.log	2007-05-16 23:37:36.000000000 +0200
+@@ -0,0 +1,5 @@
++//src/production_log_analyzer/dev/test/test.syslog.1.2.shortname.log#1 - add change 3135 (text)
++Jan 17 16:19:22 tlucas rails[2265]: Processing Short#a (for 127.0.0.1 at 2007-01-17 16:19:22) [GET]
++Jan 17 16:19:22 tlucas rails[2265]: Parameters: {"action"=>"a", "controller"=>"short"}
++Jan 17 16:19:22 tlucas rails[2265]: Rendering short/a
++Jan 17 16:19:22 tlucas rails[2265]: Completed in 0.02666 (37 reqs/sec) | Rendering: 0.02261 (84%) | DB: 0.00222 (8%) | 200 OK [http://127.0.0.1/short]
+diff -Nur ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.syslog.log ./pkg/production_log_analyzer-1.5.4/test/test.syslog.log
+--- ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4/test/test.syslog.log	1970-01-01 01:00:00.000000000 +0100
++++ ./pkg/production_log_analyzer-1.5.4/test/test.syslog.log	2007-05-16 23:37:36.000000000 +0200
+@@ -0,0 +1,258 @@
++//src/production_log_analyzer/dev/test/test.syslog.log#1 - add change 3135 (text)
++Mar  7 00:00:00 online1 newsyslog[61307]: logfile turned over
++Mar  7 00:00:20 online1 rails[59600]: Goal Load (0.002112)   SELECT g.*, gs.score as score FROM goals g, goal_similarities gs WHERE g.id = gs.similar_goal_id AND g.num_active_people > 0 AND gs.goal_similarity_type_id = 1 AND gs.goal_id = 59133 ORDER BY score DESC LIMIT 3
++Mar  7 00:00:20 online1 rails[59600]: Tag Load (0.001527)   SELECT tags.*, count(*) as num_goals FROM tags_teams, tags, teams WHERE tags_teams.tag_id = tags.id AND tags_teams.team_id = teams.id AND teams.num_members > 0 AND teams.goal_id = 59133 GROUP BY tags.id ORDER BY num_goals DESC LIMIT 5
++Mar  7 00:00:20 online1 rails[59600]: Rendering things/view within layouts/default
++Mar  7 00:00:20 online1 rails[59600]: Person Load (0.001884)   SELECT * FROM people WHERE id = 10519 LIMIT 1
++Mar  7 00:00:20 online1 rails[59600]: Person Load (0.001159)   SELECT * FROM people WHERE id = 10519 LIMIT 1
++Mar  7 00:00:20 online1 rails[59600]: Rendering layouts/default (200 OK)
++Mar  7 00:00:20 online1 rails[59600]: Completed in 0.300741 (3 reqs/sec) | Rendering: 0.049924 (16%) | DB: 0.092428 (30%)
++Mar  7 00:00:25 online2 rails[59628]: -> vsize: 83628032 rssize: 79933440 runtime: 1151489566)
++Mar  7 00:00:25 online2 rails[59628]: Processing RssController#uber (for 67.18.200.5 at Mon Mar 07 00:00:25 CST 2005)
++Mar  7 00:00:25 online2 rails[59628]: Parameters: {:id=>"author", :"rss/uber/author.html/uber/author"=>nil, :action=>"uber", :username=>"looch", :controller=>"rss"}
++Mar  7 00:00:25 online2 rails[59628]: Cookie set: auth=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:25 GMT
++Mar  7 00:00:25 online2 rails[59628]: Cookie set: ubid=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:25 GMT
++Mar  7 00:00:25 online2 rails[59628]: Browser Load (0.003963)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
++Mar  7 00:00:25 online2 rails[59628]: Person Load (0.002445)   SELECT * FROM people WHERE username = 'looch' AND active = '1' LIMIT 1
++Mar  7 00:00:25 online2 rails[59628]: ProfileImage Load (0.001554)   SELECT * FROM profile_images WHERE id = 2782 LIMIT 1
++Mar  7 00:00:25 online2 rails[59628]: Rendering rss/rss2.0 (200 OK)
++Mar  7 00:00:25 online1 rails[59628]: Processing RssController#uber (for 67.18.200.5 at Mon Mar 07 00:00:25 CST 2005)
++Mar  7 00:00:25 online1 rails[59628]: Parameters: {:id=>"author", :"rss/uber/author.html/uber/author"=>nil, :action=>"uber", :username=>"looch", :controller=>"rss"}
++Mar  7 00:00:25 online1 rails[59628]: Cookie set: auth=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:25 GMT
++Mar  7 00:00:25 online1 rails[59628]: Cookie set: ubid=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:25 GMT
++Mar  7 00:00:25 online1 rails[59628]: Browser Load (0.003963)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
++Mar  7 00:00:25 online1 rails[59628]: Person Load (0.002445)   SELECT * FROM people WHERE username = 'looch' AND active = '1' LIMIT 1
++Mar  7 00:00:25 online1 rails[59628]: ProfileImage Load (0.001554)   SELECT * FROM profile_images WHERE id = 2782 LIMIT 1
++Mar  7 00:00:25 online1 rails[59628]: Rendering rss/rss2.0 (200 OK)
++Mar  7 00:00:25 online1 rails[59628]: Completed in 0.034519 (28 reqs/sec) | Rendering: 0.011770 (34%) | DB: 0.007962 (23%)
++Mar  7 00:00:25 online2 rails[59628]: Completed in 0.034519 (28 reqs/sec) | Rendering: 0.011770 (34%) | DB: 0.007962 (23%)
++Mar  7 00:00:25 online2 rails[59628]: <- vsize: 83628032 rssize: 79933440 runtime: 1151487080
++Mar  7 00:00:27 online1 rails[59645]: Processing ThingsController#view (for 67.18.200.5 at Mon Mar 07 00:00:27 CST 2005)
++Mar  7 00:00:27 online1 rails[59645]: Parameters: {:id=>"11891", :"things/view/11891.html/view/11891"=>nil, :action=>"view", :controller=>"things"}
++Mar  7 00:00:27 online1 rails[59645]: Cookie set: auth=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:27 GMT
++Mar  7 00:00:27 online1 rails[59645]: Cookie set: ubid=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:27 GMT
++Mar  7 00:00:27 online1 rails[59645]: Browser Load (0.001425)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
++Mar  7 00:00:27 online1 rails[59645]: Goal Load (0.001303)   SELECT * FROM goals WHERE id = '11891' LIMIT 1
++Mar  7 00:00:27 online1 rails[59645]: TeamMember Load (0.002593)   SELECT * FROM team_members WHERE goal_id = 11891 AND active_member = 1 AND goal_is_complete = 1 AND goal_is_complete = '1' ORDER BY num_entries DESC, updated_date DESC LIMIT 3
++Mar  7 00:00:27 online1 rails[59645]: TeamMember Load (0.070738)   SELECT * FROM team_members WHERE goal_id = 11891 AND active_member = 1 AND goal_is_complete = 0 AND goal_is_complete = '0' ORDER BY num_entries DESC, updated_date DESC LIMIT 10
++Mar  7 00:00:27 online1 rails[59645]: Entry Load (0.003074)   SELECT e.* FROM entries e, teams t WHERE e.team_id = t.id AND t.goal_id = 11891 AND e.active = 1 ORDER BY e.id DESC LIMIT 10
++Mar  7 00:00:27 online1 rails[59645]: Entry Count (0.000956)   SELECT count(*) as num_entries FROM entries e, teams t WHERE e.team_id = t.id AND t.goal_id = 11891 AND e.active = 1 AND e.active = 1 
++Mar  7 00:00:27 online1 rails[59645]: TeamMember Count (0.000993)   SELECT count(*) as count FROM team_members WHERE worth_it = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 11891
++Mar  7 00:00:27 online1 rails[59645]: TeamMember Count (0.000878)   SELECT count(*) as count FROM team_members WHERE (worth_it <> 1 or worth_it is null) AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 11891
++Mar  7 00:00:27 online1 rails[59645]: TeamMember Count (0.000878)   SELECT count(*) as count FROM team_members WHERE help_others = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 11891
++Mar  7 00:00:27 online1 rails[59645]: Goal Load (0.002212)   SELECT g.*, gs.score as score FROM goals g, goal_similarities gs WHERE g.id = gs.similar_goal_id AND g.num_active_people > 0 AND gs.goal_similarity_type_id = 1 AND gs.goal_id = 11891 ORDER BY score DESC LIMIT 3
++Mar  7 00:00:27 online1 rails[59645]: Tag Load (0.002522)   SELECT tags.*, count(*) as num_goals FROM tags_teams, tags, teams WHERE tags_teams.tag_id = tags.id AND tags_teams.team_id = teams.id AND teams.num_members > 0 AND teams.goal_id = 11891 GROUP BY tags.id ORDER BY num_goals DESC LIMIT 5
++Mar  7 00:00:27 online1 rails[59645]: Rendering things/view within layouts/default
++Mar  7 00:00:27 online1 rails[59645]: Person Load (0.018285)   SELECT * FROM people WHERE id = 2526 LIMIT 1
++Mar  7 00:00:27 online1 rails[59645]: Person Load (0.001736)   SELECT * FROM people WHERE id = 6627 LIMIT 1
++Mar  7 00:00:27 online1 rails[59645]: Person Load (0.001611)   SELECT * FROM people WHERE id = 11889 LIMIT 1
++Mar  7 00:00:27 online1 rails[59645]: Person Load (0.001879)   SELECT * FROM people WHERE id = 12472 LIMIT 1
++Mar  7 00:00:27 online1 rails[59645]: ProfileImage Load (0.001176)   SELECT * FROM profile_images WHERE id = 6024 LIMIT 1
++Mar  7 00:00:27 online1 rails[59645]: City Load (0.001226)   SELECT * FROM cities WHERE id = 43 LIMIT 1
++Mar  7 00:00:27 online1 rails[59645]: Person Load (0.001712)   SELECT * FROM people WHERE id = 2576 LIMIT 1
++Mar  7 00:00:27 online1 rails[59645]: ProfileImage Load (0.001464)   SELECT * FROM profile_images WHERE id = 1579 LIMIT 1
++Mar  7 00:00:27 online1 rails[59645]: City Load (0.001098)   SELECT * FROM cities WHERE id = 409 LIMIT 1
++Mar  7 00:00:27 online1 rails[59645]: Person Load (0.001300)   SELECT * FROM people WHERE id = 2526 LIMIT 1
++Mar  7 00:00:27 online1 rails[59645]: ProfileImage Load (0.001536)   SELECT * FROM profile_images WHERE id = 1234 LIMIT 1
++Mar  7 00:00:27 online1 rails[59645]: Person Load (0.001562)   SELECT * FROM people WHERE id = 6627 LIMIT 1
++Mar  7 00:00:27 online1 rails[59645]: Rendering layouts/default (200 OK)
++Mar  7 00:00:27 online1 rails[59645]: Completed in 0.396183 (2 reqs/sec) | Rendering: 0.107987 (27%) | DB: 0.122158 (30%)
++Mar  7 00:00:28 online1 rails[59629]: Processing PeopleController#view (for 67.18.200.5 at Mon Mar 07 00:00:28 CST 2005)
++Mar  7 00:00:28 online1 rails[59629]: Parameters: {:id=>"denstat", :action=>"view", :controller=>"people", :"people/view/denstat.html/view/denstat"=>nil}
++Mar  7 00:00:28 online1 rails[59629]: Cookie set: auth=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:28 GMT
++Mar  7 00:00:28 online1 rails[59629]: Cookie set: ubid=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:28 GMT
++Mar  7 00:00:28 online1 rails[59629]: Browser Load (0.003869)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
++Mar  7 00:00:28 online1 rails[59629]: Person Load (0.088823)   SELECT * FROM people WHERE username = 'denstat' AND active = '1' LIMIT 1
++Mar  7 00:00:28 online1 rails[59629]: ProfileImage Load (0.001174)   SELECT * FROM profile_images WHERE id = 2031 LIMIT 1
++Mar  7 00:00:28 online1 rails[59629]: TeamMember Load (0.004332)   SELECT * FROM team_members WHERE person_id = 2693 AND active_member = '1' 
++Mar  7 00:00:28 online1 rails[59629]: Goal Load (0.010591)   SELECT g.* FROM goals g, team_members tm WHERE g.id = tm.goal_id AND tm.person_id = 2693 AND tm.active_member <> 0
++Mar  7 00:00:28 online1 rails[59629]: Team Load (0.005076)   SELECT t.* FROM teams t, team_members tm WHERE t.id = tm.team_id AND goal_is_complete = 0 AND active_member != 0 AND tm.person_id = 2693 ORDER BY sort_order, tm.joined_date
++Mar  7 00:00:28 online1 rails[59629]: Team Load (0.003954)   SELECT t.* FROM teams t, team_members tm WHERE t.id = tm.team_id AND goal_is_complete = '1' AND active_member != '0' AND tm.person_id = 2693 ORDER BY tm.updated_date DESC LIMIT 10 
++Mar  7 00:00:28 online1 rails[59629]: Entry Load (0.010843)   SELECT e.* FROM entries e, team_members tm, (SELECT team_id, max(id) recent_id FROM entries WHERE active <> 0 AND person_id = 2693 GROUP BY team_id) recent_entries WHERE e.id = recent_entries.recent_id AND tm.person_id = e.person_id AND tm.team_id = e.team_id AND tm.active_member <> 0
++Mar  7 00:00:28 online1 rails[59629]: Rendering people/view within layouts/default
++Mar  7 00:00:28 online1 rails[59629]: ProfileImage Load (0.001088)   SELECT * FROM profile_images WHERE id = 2031 LIMIT 1
++Mar  7 00:00:28 online1 rails[59629]: City Load (0.000689)   SELECT * FROM cities WHERE id = 1170 LIMIT 1
++Mar  7 00:00:28 online1 rails[59629]: Team Load (0.001622)   SELECT * FROM teams WHERE id = 132991 LIMIT 1
++Mar  7 00:00:28 online1 rails[59629]: Team Load (0.001059)   SELECT * FROM teams WHERE id = 132980 LIMIT 1
++Mar  7 00:00:28 online1 rails[59629]: Team Load (0.001117)   SELECT * FROM teams WHERE id = 129534 LIMIT 1
++Mar  7 00:00:28 online1 rails[59629]: Team Load (0.001104)   SELECT * FROM teams WHERE id = 126799 LIMIT 1
++Mar  7 00:00:28 online1 rails[59629]: Team Load (0.015325)   SELECT * FROM teams WHERE id = 124130 LIMIT 1
++Mar  7 00:00:28 online1 rails[59629]: Team Load (0.001067)   SELECT * FROM teams WHERE id = 121991 LIMIT 1
++Mar  7 00:00:29 online1 rails[59629]: Team Load (0.001169)   SELECT * FROM teams WHERE id = 121978 LIMIT 1
++Mar  7 00:00:29 online1 rails[59629]: Team Load (0.001264)   SELECT * FROM teams WHERE id = 121976 LIMIT 1
++Mar  7 00:00:29 online1 rails[59629]: Team Load (0.001131)   SELECT * FROM teams WHERE id = 121971 LIMIT 1
++Mar  7 00:00:29 online1 rails[59629]: Team Load (0.000970)   SELECT * FROM teams WHERE id = 121967 LIMIT 1
++Mar  7 00:00:29 online1 rails[59629]: Team Load (0.001507)   SELECT * FROM teams WHERE id = 121868 LIMIT 1
++Mar  7 00:00:29 online1 rails[59629]: Team Load (0.075690)   SELECT * FROM teams WHERE id = 69373 LIMIT 1
++Mar  7 00:00:29 online1 rails[60654]: Processing TeamsController#progress (for 220.255.60.111; 165.21.154.137 at Mon Mar 07 00:00:29 CST 2005)
++Mar  7 00:00:29 online1 rails[60654]: Parameters: {:id=>"35926", :action=>"progress", :"teams/progress/35926.html/progress/35926"=>nil, :controller=>"teams"}
++Mar  7 00:00:29 online1 rails[59627]: Processing ThingsController#view (for 67.18.200.5 at Mon Mar 07 00:00:29 CST 2005)
++Mar  7 00:00:29 online1 rails[59627]: Parameters: {:id=>"19986", :action=>"view", :"things/view/19986.html/view/19986"=>nil, :controller=>"things"}
++Mar  7 00:00:29 online1 rails[59627]: Cookie set: auth=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:29 GMT
++Mar  7 00:00:29 online1 rails[59627]: Cookie set: ubid=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:29 GMT
++Mar  7 00:00:29 online1 rails[60654]: Browser Load (0.204679)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
++Mar  7 00:00:29 online1 rails[59627]: Browser Load (0.028144)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
++Mar  7 00:00:29 online1 rails[59629]: Team Load (0.201582)   SELECT * FROM teams WHERE id = 69343 LIMIT 1
++Mar  7 00:00:29 online1 rails[59629]: Team Load (0.001294)   SELECT * FROM teams WHERE id = 65942 LIMIT 1
++Mar  7 00:00:29 online1 rails[59627]: Goal Load (0.001371)   SELECT * FROM goals WHERE id = '19986' LIMIT 1
++Mar  7 00:00:29 online1 rails[59629]: Team Load (0.013834)   SELECT * FROM teams WHERE id = 65932 LIMIT 1
++Mar  7 00:00:29 online1 rails[59627]: TeamMember Load (0.014705)   SELECT * FROM team_members WHERE goal_id = 19986 AND active_member = 1 AND goal_is_complete = 1 AND goal_is_complete = '1' ORDER BY num_entries DESC, updated_date DESC LIMIT 3
++Mar  7 00:00:29 online1 rails[59629]: Team Load (0.001222)   SELECT * FROM teams WHERE id = 27367 LIMIT 1
++Mar  7 00:00:29 online1 rails[59627]: TeamMember Load (0.002019)   SELECT * FROM team_members WHERE goal_id = 19986 AND active_member = 1 AND goal_is_complete = 0 AND goal_is_complete = '0' ORDER BY num_entries DESC, updated_date DESC LIMIT 10
++Mar  7 00:00:29 online1 rails[59629]: Team Load (0.004668)   SELECT * FROM teams WHERE id = 42378 LIMIT 1
++Mar  7 00:00:29 online1 rails[59627]: Entry Load (0.005880)   SELECT e.* FROM entries e, teams t WHERE e.team_id = t.id AND t.goal_id = 19986 AND e.active = 1 ORDER BY e.id DESC LIMIT 10
++Mar  7 00:00:29 online1 rails[59629]: Team Load (0.001271)   SELECT * FROM teams WHERE id = 27314 LIMIT 1
++Mar  7 00:00:29 online1 rails[59627]: Entry Count (0.001063)   SELECT count(*) as num_entries FROM entries e, teams t WHERE e.team_id = t.id AND t.goal_id = 19986 AND e.active = 1 AND e.active = 1 
++Mar  7 00:00:29 online1 rails[59629]: Team Load (0.009752)   SELECT * FROM teams WHERE id = 27306 LIMIT 1
++Mar  7 00:00:29 online1 rails[59627]: TeamMember Count (0.015829)   SELECT count(*) as count FROM team_members WHERE worth_it = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 19986
++Mar  7 00:00:29 online1 rails[59627]: TeamMember Count (0.000884)   SELECT count(*) as count FROM team_members WHERE (worth_it <> 1 or worth_it is null) AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 19986
++Mar  7 00:00:29 online1 rails[59627]: TeamMember Count (0.000809)   SELECT count(*) as count FROM team_members WHERE help_others = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 19986
++Mar  7 00:00:29 online1 rails[59629]: TeamMember Count (0.001410)   SELECT COUNT(*) FROM team_members WHERE active_member = 1 and goal_is_complete = 1 and person_id = 2693 
++Mar  7 00:00:29 online1 rails[59629]: Person Load (0.017762)   SELECT p.*, count(*) as num_teams_in_common FROM people p, team_members tms, team_members tmp WHERE tmp.person_id = p.id AND tms.team_id = tmp.team_id AND tms.person_id = 2693 AND tms.active_member <> 0 AND tms.goal_is_complete = 0 AND tmp.person_id <> 2693 AND tmp.active_member <> 0 AND tmp.goal_is_complete = 0 GROUP BY p.id ORDER BY num_teams_in_common DESC, p.last_name
++Mar  7 00:00:29 online1 rails[59629]: Rendering layouts/default (200 OK)
++Mar  7 00:00:29 online1 rails[59629]: Completed in 1.102098 (0 reqs/sec) | Rendering: 0.695476 (63%) | DB: 0.486258 (44%)
++Mar  7 00:00:29 online1 rails[59635]: Processing ThingsController#view (for 67.18.200.5 at Mon Mar 07 00:00:29 CST 2005)
++Mar  7 00:00:29 online1 rails[59635]: Parameters: {:id=>"9794", :action=>"view", :"things/view/9794.html/view/9794"=>nil, :controller=>"things"}
++Mar  7 00:00:29 online1 rails[59635]: Cookie set: auth=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:29 GMT
++Mar  7 00:00:29 online1 rails[59635]: Cookie set: ubid=STUFF; path=/; expires=Thu, 05 Mar 2015 06:00:29 GMT
++Mar  7 00:00:29 online1 rails[59635]: Browser Load (0.001218)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
++Mar  7 00:00:29 online1 rails[59635]: Goal Load (0.001052)   SELECT * FROM goals WHERE id = '9794' LIMIT 1
++Mar  7 00:00:29 online1 rails[59627]: Goal Load (0.002577)   SELECT g.*, gs.score as score FROM goals g, goal_similarities gs WHERE g.id = gs.similar_goal_id AND g.num_active_people > 0 AND gs.goal_similarity_type_id = 1 AND gs.goal_id = 19986 ORDER BY score DESC LIMIT 3
++Mar  7 00:00:29 online1 rails[60654]: Team Load (0.001341)   SELECT * FROM teams WHERE id = '35926' LIMIT 1
++Mar  7 00:00:29 online1 rails[59627]: Tag Load (0.002195)   SELECT tags.*, count(*) as num_goals FROM tags_teams, tags, teams WHERE tags_teams.tag_id = tags.id AND tags_teams.team_id = teams.id AND teams.num_members > 0 AND teams.goal_id = 19986 GROUP BY tags.id ORDER BY num_goals DESC LIMIT 5
++Mar  7 00:00:29 online1 rails[59627]: Rendering things/view within layouts/default
++Mar  7 00:00:29 online1 rails[59627]: Person Load (0.002223)   SELECT * FROM people WHERE id = 1694 LIMIT 1
++Mar  7 00:00:29 online1 rails[59635]: TeamMember Load (0.102165)   SELECT * FROM team_members WHERE goal_id = 9794 AND active_member = 1 AND goal_is_complete = 1 AND goal_is_complete = '1' ORDER BY num_entries DESC, updated_date DESC LIMIT 3
++Mar  7 00:00:29 online1 rails[60654]: Entry Load (0.122496)   SELECT * FROM entries WHERE team_id = 35926 AND active = 1 
++Mar  7 00:00:29 online1 rails[59635]: TeamMember Load (0.002217)   SELECT * FROM team_members WHERE goal_id = 9794 AND active_member = 1 AND goal_is_complete = 0 AND goal_is_complete = '0' ORDER BY num_entries DESC, updated_date DESC LIMIT 10
++Mar  7 00:00:29 online1 rails[60654]: TeamMember Load (0.001972)   SELECT * FROM team_members WHERE team_id = 35926 AND active_member = 1 
++Mar  7 00:00:29 online1 rails[59635]: Entry Load (0.004002)   SELECT e.* FROM entries e, teams t WHERE e.team_id = t.id AND t.goal_id = 9794 AND e.active = 1 ORDER BY e.id DESC LIMIT 10
++Mar  7 00:00:29 online1 rails[60654]: Person Load (0.004097)   SELECT * FROM people WHERE id = 3357 LIMIT 1
++Mar  7 00:00:29 online1 rails[59635]: Entry Count (0.001231)   SELECT count(*) as num_entries FROM entries e, teams t WHERE e.team_id = t.id AND t.goal_id = 9794 AND e.active = 1 AND e.active = 1 
++Mar  7 00:00:29 online1 rails[59635]: TeamMember Count (0.001088)   SELECT count(*) as count FROM team_members WHERE worth_it = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 9794
++Mar  7 00:00:29 online1 rails[60654]: Redirected to http://www.43things.com/people/progress/swirlygirl/35926
++Mar  7 00:00:29 online1 rails[59635]: TeamMember Count (0.001411)   SELECT count(*) as count FROM team_members WHERE (worth_it <> 1 or worth_it is null) AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 9794
++Mar  7 00:00:29 online1 rails[60654]: Goal Load (0.001213)   SELECT * FROM goals WHERE id = 225 LIMIT 1
++Mar  7 00:00:29 online1 rails[59635]: TeamMember Count (0.001344)   SELECT count(*) as count FROM team_members WHERE help_others = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 9794
++Mar  7 00:00:29 online1 rails[59627]: Person Load (0.090841)   SELECT * FROM people WHERE id = 4350 LIMIT 1
++Mar  7 00:00:29 online1 rails[59627]: ProfileImage Load (0.001119)   SELECT * FROM profile_images WHERE id = 2322 LIMIT 1
++Mar  7 00:00:29 online1 rails[59627]: City Load (0.000659)   SELECT * FROM cities WHERE id = 819 LIMIT 1
++Mar  7 00:00:29 online1 rails[59627]: Person Load (0.001279)   SELECT * FROM people WHERE id = 1694 LIMIT 1
++Mar  7 00:00:29 online1 rails[59627]: ProfileImage Load (0.001170)   SELECT * FROM profile_images WHERE id = 1945 LIMIT 1
++Mar  7 00:00:29 online1 rails[59627]: Rendering layouts/default (200 OK)
++Mar  7 00:00:29 online1 rails[60654]: Goal Load (0.002178)   SELECT g.*, gs.score as score FROM goals g, goal_similarities gs WHERE g.id = gs.similar_goal_id AND g.num_active_people > 0 AND gs.goal_similarity_type_id = 1 AND gs.goal_id = 225 ORDER BY score DESC LIMIT 3
++Mar  7 00:00:29 online1 rails[59627]: Completed in 0.491760 (2 reqs/sec) | Rendering: 0.197126 (40%) | DB: 0.172767 (35%)
++Mar  7 00:00:30 online1 rails[59635]: Goal Load (0.240670)   SELECT g.*, gs.score as score FROM goals g, goal_similarities gs WHERE g.id = gs.similar_goal_id AND g.num_active_people > 0 AND gs.goal_similarity_type_id = 1 AND gs.goal_id = 9794 ORDER BY score DESC LIMIT 3
++Mar  7 00:00:30 online1 rails[59635]: Tag Load (0.191137)   SELECT tags.*, count(*) as num_goals FROM tags_teams, tags, teams WHERE tags_teams.tag_id = tags.id AND tags_teams.team_id = teams.id AND teams.num_members > 0 AND teams.goal_id = 9794 GROUP BY tags.id ORDER BY num_goals DESC LIMIT 5
++Mar  7 00:00:30 online1 rails[59635]: Rendering things/view within layouts/default
++Mar  7 00:00:30 online1 rails[59635]: Person Load (0.004756)   SELECT * FROM people WHERE id = 4758 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.007942)   SELECT * FROM profile_images WHERE id = 2557 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: Team Load (0.007545)   SELECT * FROM teams WHERE id = 56225 LIMIT 1
++Mar  7 00:00:30 online1 rails[60654]: TeamMember Count (0.558625)   SELECT count(*) as count FROM team_members WHERE worth_it = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
++Mar  7 00:00:30 online1 rails[59635]: Person Load (0.003433)   SELECT * FROM people WHERE id = 1674 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: Person Load (0.007808)   SELECT * FROM people WHERE id = 1674 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.008622)   SELECT * FROM profile_images WHERE id = 751 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: City Load (0.007911)   SELECT * FROM cities WHERE id = 164 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: Person Load (0.008706)   SELECT * FROM people WHERE id = 7887 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.007501)   SELECT * FROM profile_images WHERE id = 4135 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: City Load (0.018941)   SELECT * FROM cities WHERE id = 598 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: Person Load (0.007344)   SELECT * FROM people WHERE id = 11610 LIMIT 1
++Mar  7 00:00:30 online1 rails[60654]: TeamMember Count (0.135848)   SELECT count(*) as count FROM team_members WHERE (worth_it <> 1 or worth_it is null) AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
++Mar  7 00:00:30 online1 rails[59635]: Person Load (0.075532)   SELECT * FROM people WHERE id = 8694 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.009264)   SELECT * FROM profile_images WHERE id = 4519 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: City Load (0.007255)   SELECT * FROM cities WHERE id = 808 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: Person Load (0.009140)   SELECT * FROM people WHERE id = 7413 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.007755)   SELECT * FROM profile_images WHERE id = 3908 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: City Load (0.007233)   SELECT * FROM cities WHERE id = 732 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: Person Load (0.008803)   SELECT * FROM people WHERE id = 7412 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.008236)   SELECT * FROM profile_images WHERE id = 4743 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: City Load (0.007333)   SELECT * FROM cities WHERE id = 156 LIMIT 1
++Mar  7 00:00:30 online1 rails[60654]: TeamMember Count (0.111128)   SELECT count(*) as count FROM team_members WHERE help_others = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
++Mar  7 00:00:30 online1 rails[60654]: Completed in 1.469788 (0 reqs/sec) | DB: 1.143577 (77%)
++Mar  7 00:00:30 online1 rails[59635]: Person Load (0.003778)   SELECT * FROM people WHERE id = 4765 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.010916)   SELECT * FROM profile_images WHERE id = 2571 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: Person Load (0.002019)   SELECT * FROM people WHERE id = 1452 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.001126)   SELECT * FROM profile_images WHERE id = 5516 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: City Load (0.011705)   SELECT * FROM cities WHERE id = 1061 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: Person Load (0.002113)   SELECT * FROM people WHERE id = 895 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.001151)   SELECT * FROM profile_images WHERE id = 420 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: City Load (0.000736)   SELECT * FROM cities WHERE id = 164 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: Person Load (0.001225)   SELECT * FROM people WHERE id = 1674 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.001099)   SELECT * FROM profile_images WHERE id = 751 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: Person Load (0.016354)   SELECT * FROM people WHERE id = 4758 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.078197)   SELECT * FROM profile_images WHERE id = 2557 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: Person Load (0.001518)   SELECT * FROM people WHERE id = 1674 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.001268)   SELECT * FROM profile_images WHERE id = 751 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: Person Load (0.001255)   SELECT * FROM people WHERE id = 1674 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: ProfileImage Load (0.001137)   SELECT * FROM profile_images WHERE id = 751 LIMIT 1
++Mar  7 00:00:30 online1 rails[59635]: Rendering layouts/default (200 OK)
++Mar  7 00:00:30 online1 rails[59635]: Completed in 1.259728 (0 reqs/sec) | Rendering: 0.505973 (40%) | DB: 0.914192 (72%)
++Mar  7 00:00:30 online1 rails[59627]: Processing PeopleController#progress (for 220.255.60.111; 165.21.154.158 at Mon Mar 07 00:00:30 CST 2005)
++Mar  7 00:00:30 online1 rails[59627]: Parameters: {:id=>"swirlygirl", :on=>"35926", :action=>"progress", :"people/progress/swirlygirl/35926.html/progress/swirlygirl/35926"=>nil, :controller=>"people"}
++Mar  7 00:00:30 online1 rails[59627]: Browser Load (0.000760)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
++Mar  7 00:00:30 online1 rails[59627]: Person Load (0.001974)   SELECT * FROM people WHERE username = 'swirlygirl' LIMIT 1
++Mar  7 00:00:30 online1 rails[59627]: Team Load (0.001060)   SELECT * FROM teams WHERE id = '35926' LIMIT 1
++Mar  7 00:00:30 online1 rails[59627]: TeamMember Load (0.001061)   SELECT * FROM team_members WHERE team_id = 35926 AND active_member = 1 
++Mar  7 00:00:30 online1 rails[59627]: Entry Load (0.004121)   SELECT * FROM entries WHERE team_id = 35926 AND person_id = 3357 AND active = 1 ORDER BY created_date DESC LIMIT 0, 20
++Mar  7 00:00:30 online1 rails[59627]: Tag Load (0.001446)   SELECT t.*, j.* FROM tags_teams j, tags t WHERE t.id = j.tag_id AND j.team_id = 35926 ORDER BY t.id
++Mar  7 00:00:30 online1 rails[59627]: Goal Load (0.000904)   SELECT * FROM goals WHERE id = 225 LIMIT 1
++Mar  7 00:00:31 online1 rails[60654]: Processing PeopleController#view (for 67.18.200.5 at Mon Mar 07 00:00:31 CST 2005)
++Mar  7 00:00:31 online1 rails[60654]: Parameters: {:id=>"elizabeth", :action=>"view", :"people/view/elizabeth.html/view/elizabeth"=>nil, :controller=>"people"}
++Mar  7 00:00:31 online1 rails[60654]: Person Load (0.012043)   SELECT * FROM people WHERE id = 2077 LIMIT 1
++Mar  7 00:00:31 online1 rails[60654]: Person Load (0.005866)   SELECT * FROM people WHERE username = 'elizabeth' AND active = '1' LIMIT 1
++Mar  7 00:00:31 online1 rails[60654]: ProfileImage Load (0.007170)   SELECT * FROM profile_images WHERE id = 2153 LIMIT 1
++Mar  7 00:00:31 online1 rails[60654]: TeamMember Load (0.011367)   SELECT * FROM team_members WHERE person_id = 4042 AND active_member = '1' 
++Mar  7 00:00:31 online1 rails[60654]: Goal Load (0.007064)   SELECT g.* FROM goals g, team_members tm WHERE g.id = tm.goal_id AND tm.person_id = 4042 AND tm.active_member <> 0
++Mar  7 00:00:31 online1 rails[60654]: Team Load (0.007723)   SELECT t.* FROM teams t, team_members tm WHERE t.id = tm.team_id AND goal_is_complete = 0 AND active_member != 0 AND tm.person_id = 4042 ORDER BY sort_order, tm.joined_date
++Mar  7 00:00:31 online1 rails[60654]: Team Load (0.007425)   SELECT t.* FROM teams t, team_members tm WHERE t.id = tm.team_id AND goal_is_complete = '1' AND active_member != '0' AND tm.person_id = 4042 ORDER BY tm.updated_date DESC LIMIT 10 
++Mar  7 00:00:31 online1 rails[59627]: Tag Load (0.162564)   SELECT tags.*, count(*) as num_goals FROM tags_teams, tags, teams WHERE tags_teams.tag_id = tags.id AND tags_teams.team_id = teams.id AND teams.num_members > 0 AND teams.goal_id = 225 GROUP BY tags.id ORDER BY num_goals DESC LIMIT 5
++Mar  7 00:00:31 online1 rails[60654]: Entry Load (0.005561)   SELECT e.* FROM entries e, team_members tm, (SELECT team_id, max(id) recent_id FROM entries WHERE active <> 0 AND person_id = 4042 GROUP BY team_id) recent_entries WHERE e.id = recent_entries.recent_id AND tm.person_id = e.person_id AND tm.team_id = e.team_id AND tm.active_member <> 0
++Mar  7 00:00:31 online1 rails[60654]: Rendering people/view within layouts/default
++Mar  7 00:00:31 online1 rails[60654]: FlickrPhoto Load (0.001567)   SELECT * FROM flickr_photos WHERE person_id = 4042 
++Mar  7 00:00:31 online1 rails[60654]: ProfileImage Load (0.013853)   SELECT * FROM profile_images WHERE id = 2153 LIMIT 1
++Mar  7 00:00:31 online1 rails[60654]: City Load (0.000694)   SELECT * FROM cities WHERE id = 5 LIMIT 1
++Mar  7 00:00:31 online1 rails[59627]: Goal Load (0.002303)   SELECT g.*, gs.score as score FROM goals g, goal_similarities gs WHERE g.id = gs.similar_goal_id AND g.num_active_people > 0 AND gs.goal_similarity_type_id = 1 AND gs.goal_id = 225 ORDER BY score DESC LIMIT 3
++Mar  7 00:00:31 online1 rails[59627]: TeamMember Count (0.000653)   SELECT count(*) as count FROM team_members WHERE worth_it = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
++Mar  7 00:00:31 online1 rails[59627]: TeamMember Count (0.000800)   SELECT count(*) as count FROM team_members WHERE (worth_it <> 1 or worth_it is null) AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
++Mar  7 00:00:31 online1 rails[59627]: TeamMember Count (0.014575)   SELECT count(*) as count FROM team_members WHERE help_others = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
++Mar  7 00:00:31 online1 rails[60654]: TeamMember Count (0.006965)   SELECT COUNT(*) FROM team_members WHERE active_member = 1 and goal_is_complete = 1 and person_id = 4042 
++Mar  7 00:00:31 online1 rails[59627]: TeamMember Load (0.116729)   SELECT * FROM team_members WHERE goal_id = 225 AND active_member = 1 AND goal_is_complete = 1 AND person_id <> '3357' ORDER BY updated_date DESC LIMIT 3
++Mar  7 00:00:31 online1 rails[59627]: Rendering people/progress within layouts/default
++Mar  7 00:00:31 online1 rails[59627]: ProfileImage Load (0.021914)   SELECT * FROM profile_images WHERE id = 1770 LIMIT 1
++Mar  7 00:00:31 online1 rails[59627]: Person Load (0.001863)   SELECT * FROM people WHERE id = 6731 LIMIT 1
++Mar  7 00:00:31 online1 rails[60654]: Person Load (0.101821)   SELECT p.*, count(*) as num_teams_in_common FROM people p, team_members tms, team_members tmp WHERE tmp.person_id = p.id AND tms.team_id = tmp.team_id AND tms.person_id = 4042 AND tms.active_member <> 0 AND tms.goal_is_complete = 0 AND tmp.person_id <> 4042 AND tmp.active_member <> 0 AND tmp.goal_is_complete = 0 GROUP BY p.id ORDER BY num_teams_in_common DESC, p.last_name
++Mar  7 00:00:31 online1 rails[60654]: Rendering layouts/default (200 OK)
++Mar  7 00:00:31 online1 rails[60654]: Completed in 0.360210 (2 reqs/sec) | Rendering: 0.277921 (77%) | DB: 0.189119 (52%)
++Mar  7 00:00:31 online1 rails[59627]: ProfileImage Load (0.090583)   SELECT * FROM profile_images WHERE id = 6434 LIMIT 1
++Mar  7 00:00:31 online1 rails[59627]: Team Load (0.001336)   SELECT * FROM teams WHERE id = 79140 LIMIT 1
++Mar  7 00:00:31 online1 rails[59627]: Person Load (0.001677)   SELECT * FROM people WHERE id = 660 LIMIT 1
++Mar  7 00:00:31 online1 rails[59627]: ProfileImage Load (0.001106)   SELECT * FROM profile_images WHERE id = 370 LIMIT 1
++Mar  7 00:00:31 online1 rails[59627]: Team Load (0.001231)   SELECT * FROM teams WHERE id = 15523 LIMIT 1
++Mar  7 00:00:31 online1 rails[59627]: Person Load (0.001581)   SELECT * FROM people WHERE id = 1900 LIMIT 1
++Mar  7 00:00:31 online1 rails[59627]: ProfileImage Load (0.001111)   SELECT * FROM profile_images WHERE id = 3207 LIMIT 1
++Mar  7 00:00:31 online1 rails[59627]: Team Load (0.001221)   SELECT * FROM teams WHERE id = 37202 LIMIT 1
++Mar  7 00:00:31 online1 rails[59627]: TeamMember Load (0.379744)   SELECT * FROM team_members WHERE goal_id = 225 AND active_member = 1 AND goal_is_complete = 1 
++Mar  7 00:00:31 online1 rails[59627]: Goal Load (0.005007)   SELECT * FROM goals WHERE id = 225 LIMIT 1
++Mar  7 00:00:31 online1 rails[59627]: Cheer Load (0.004291)   SELECT * FROM cheers WHERE entity_id = 36073 AND entity_type_id = 4 
++Mar  7 00:00:31 online1 rails[59627]: Person Load (0.008794)   SELECT * FROM people WHERE id = 3357 LIMIT 1
++Mar  7 00:00:31 online1 rails[59627]: Rendering layouts/default (200 OK)
++Mar  7 00:00:31 online1 rails[59627]: Completed in 0.977398 (1 reqs/sec) | Rendering: 0.604444 (61%) | DB: 0.830409 (84%)
++Mar  7 00:00:32 online1 rails[59627]: Processing TeamsController#progress (for 220.255.60.111; 165.21.154.137 at Mon Mar 07 00:00:32 CST 2005)
++Mar  7 00:00:32 online1 rails[59627]: Parameters: {:id=>"35926", :action=>"progress", :"teams/progress/35926.html/progress/35926"=>nil, :controller=>"teams"}
++Mar  7 00:00:32 online1 rails[59627]: Browser Load (0.001007)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
++Mar  7 00:00:32 online1 rails[59627]: Team Load (0.000973)   SELECT * FROM teams WHERE id = '35926' LIMIT 1
++Mar  7 00:00:32 online1 rails[59627]: Entry Load (0.001138)   SELECT * FROM entries WHERE team_id = 35926 AND active = 1 
++Mar  7 00:00:32 online1 rails[59627]: TeamMember Load (0.001781)   SELECT * FROM team_members WHERE team_id = 35926 AND active_member = 1 
++Mar  7 00:00:32 online1 rails[59627]: Person Load (0.030966)   SELECT * FROM people WHERE id = 3357 LIMIT 1
++Mar  7 00:00:32 online1 rails[59627]: Redirected to http://www.43things.com/people/progress/swirlygirl/35926
++Mar  7 00:00:32 online1 rails[59627]: Goal Load (0.000835)   SELECT * FROM goals WHERE id = 225 LIMIT 1
++Mar  7 00:00:32 online1 rails[59627]: Goal Load (0.009688)   SELECT g.*, gs.score as score FROM goals g, goal_similarities gs WHERE g.id = gs.similar_goal_id AND g.num_active_people > 0 AND gs.goal_similarity_type_id = 1 AND gs.goal_id = 225 ORDER BY score DESC LIMIT 3
++Mar  7 00:00:32 online1 rails[59627]: TeamMember Count (0.007285)   SELECT count(*) as count FROM team_members WHERE worth_it = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
++Mar  7 00:00:32 online1 rails[59627]: TeamMember Count (0.095106)   SELECT count(*) as count FROM team_members WHERE (worth_it <> 1 or worth_it is null) AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
++Mar  7 00:00:32 online1 rails[59627]: TeamMember Count (0.000578)   SELECT count(*) as count FROM team_members WHERE help_others = 1 AND goal_is_complete = 1 AND active_member = 1 AND goal_id = 225
++Mar  7 00:00:32 online1 rails[59627]: Completed in 0.211973 (4 reqs/sec) | DB: 0.149357 (70%)
++Mar  7 00:00:32 online1 rails[59635]: Processing PeopleController#progress (for 220.255.60.111; 165.21.154.158 at Mon Mar 07 00:00:32 CST 2005)
++Mar  7 00:00:32 online1 rails[59635]: Parameters: {:id=>"swirlygirl", :on=>"35926", :action=>"progress", :"people/progress/swirlygirl/35926.html/progress/swirlygirl/35926"=>nil, :controller=>"people"}
++Mar  7 00:00:32 online1 rails[59635]: Browser Load (0.000698)   SELECT * FROM browsers WHERE ubid = 'STUFF' LIMIT 1
++Mar  7 00:00:32 online1 rails[59635]: Person Load (0.001319)   SELECT * FROM people WHERE username = 'swirlygirl' LIMIT 1
++
+Los ficheros binarios ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4.gem y ./pkg/production_log_analyzer-1.5.4.gem son distintos
+Los ficheros binarios ../production_log_analyzer-1.5.0/pkg/production_log_analyzer-1.5.4.tgz y ./pkg/production_log_analyzer-1.5.4.tgz son distintos
diff -Nur ../production_log_analyzer-1.5.0/Rakefile ./Rakefile
--- ../production_log_analyzer-1.5.0/Rakefile	2007-06-15 10:19:53.000000000 +0200
+++ ./Rakefile	2007-06-14 18:41:12.000000000 +0200
@@ -3,7 +3,7 @@
 $:.unshift './lib'
 require 'production_log/analyzer'
 
-Hoe.new 'production_log_analyzer', '1.5.0' do |p|
+Hoe.new 'production_log_analyzer', '1.5.4' do |p|
   p.summary = p.paragraphs_of('README.txt', 1).join ' '
   p.description = p.paragraphs_of('README.txt', 7).join ' '
   p.author = 'Eric Hodel'
@@ -11,7 +11,5 @@
   p.url = p.paragraphs_of('README.txt', 2).join ' '
 
   p.rubyforge_name = 'seattlerb'
-
-  p.extra_deps << ['rails_analyzer_tools', '>= 1.4.0']
 end
 
diff -Nur ../production_log_analyzer-1.5.0/README.txt ./README.txt
--- ../production_log_analyzer-1.5.0/README.txt	2007-06-15 10:19:53.000000000 +0200
+++ ./README.txt	2007-06-05 17:43:36.000000000 +0200
@@ -1,7 +1,7 @@
 = production_log_analyzer
 
 production_log_analyzer lets you find out which actions on a Rails
-site are slowing you down.
+site are used most and which ones are slowing your server down.
 
 http://seattlerb.rubyforge.org/production_log_analyzer
 
@@ -14,28 +14,30 @@
 == About
 
 production_log_analyzer provides three tools to analyze log files
-created by SyslogLogger.  pl_analyze for getting daily reports,
+created by rails applications.  pl_analyze can be used for getting daily reports,
 action_grep for pulling log lines for a single action and
 action_errors to summarize errors with counts.
 
-The analyzer currently requires the use of SyslogLogger because the
-default Logger doesn't give any way to associate lines logged to a
-request.
-
 The PL Analyzer also includes action_grep which lets you grab lines from a log
 that only match a single action.
 
   action_grep RssController#uber /var/log/production.log
 
+The log formats syslog and rails native are supported. PL Analyzer will by 
+default try to detect the log file format automatically.
+
+Warning: as of now, action_grep and action_errors don't support the 
+rails log format
+
 == Installing
 
   sudo gem install production_log_analyzer
 
 === Setup
 
-First:
-
-Set up SyslogLogger according to the instructions here:
+You might want to use syslog for logging your production rails app as that 
+offers you features such as remote logging. Instructions to set up SyslogLogger 
+with rails are provided here:
 
 http://seattlerb.rubyforge.org/SyslogLogger/
 
@@ -45,10 +47,20 @@
 
 == Using pl_analyze
 
-To run pl_analyze simply give it the name of a log file to analyze.
+pl_analyze offers a number of command line options to change its behaviour. To 
+see a list of options, simply execute pl_analyze.
+
+Basic use:
+
+Run pl_analyze with the name of one or more log files to analyze.
 
   pl_analyze /var/log/production.log
 
+To see a progress bar while parsing, use the option --progress (this requires the 
+gem 'progressbar' to be installed).
+
+  pl_analyze --progress /var/log/production.log
+
 If you want, you can run it from a cron something like this:
 
   /usr/bin/gzip -dc /var/log/production.log.0.gz | /usr/local/bin/pl_analyze /dev/stdin
@@ -140,8 +152,5 @@
 == What's missing
 
 * More reports
-* Command line arguments including:
-  * Help
-  * What type of log file you've got (if somebody sends patches with tests)
 * Read from STDIN
 
diff -Nur ../production_log_analyzer-1.5.0/test/analyzer_report.txt ./test/analyzer_report.txt
--- ../production_log_analyzer-1.5.0/test/analyzer_report.txt	1970-01-01 01:00:00.000000000 +0100
+++ ./test/analyzer_report.txt	2007-06-06 10:14:10.000000000 +0200
@@ -0,0 +1,56 @@
+Log files analyzed:
+test/test.syslog.log
+
+First request:         Mon Mar 07 07:00:25 +0100 2005
+Last request:          Mon Mar 07 07:00:32 +0100 2005
+Requests per second/minute/hour:   1.57/94.29/5657.14
+
+------------------------------------------------------------------------
+
+HTTP Status Codes Summary
+Code	HTTP		Number	Fra
+
+------------------------------------------------------------------------
+
+Request Times Summary:    	Count	Fra	Avg	Std Dev	Min	Max
+ALL REQUESTS:             	11	100%	0.576	0.508	0.000	1.470
+
+ThingsController#view:    	3	27.3%	0.716	0.387	0.396	1.260
+TeamsController#progress: 	2	18.2%	0.841	0.629	0.212	1.470
+RssController#uber:       	2	18.2%	0.035	0.000	0.035	0.035
+... [2 not shown]
+
+Slowest Request Times:
+	TeamsController#progress took 1.470s
+	ThingsController#view took 1.260s
+	PeopleController#view took 1.102s
+
+------------------------------------------------------------------------
+
+DB Times Summary:         	Count	Fra	Avg	Std Dev	Min	Max
+ALL REQUESTS:             	11	100%	0.366	0.393	0.000	1.144
+
+ThingsController#view:    	3	27.3%	0.403	0.362	0.122	0.914
+TeamsController#progress: 	2	18.2%	0.646	0.497	0.149	1.144
+RssController#uber:       	2	18.2%	0.008	0.000	0.008	0.008
+... [2 not shown]
+
+Slowest Total DB Times:
+	TeamsController#progress took 1.144s
+	ThingsController#view took 0.914s
+	PeopleController#progress took 0.830s
+
+------------------------------------------------------------------------
+
+Render Times Summary:     	Count	Fra	Avg	Std Dev	Min	Max
+ALL REQUESTS:             	11	100%	0.219	0.253	0.000	0.695
+
+ThingsController#view:    	3	27.3%	0.270	0.171	0.108	0.506
+TeamsController#progress: 	2	18.2%	0.000	0.000	0.000	0.000
+RssController#uber:       	2	18.2%	0.012	0.000	0.012	0.012
+... [2 not shown]
+
+Slowest Total Render Times:
+	PeopleController#view took 0.695s
+	PeopleController#progress took 0.604s
+	ThingsController#view took 0.506s
diff -Nur ../production_log_analyzer-1.5.0/test/test_analyzer.rb ./test/test_analyzer.rb
--- ../production_log_analyzer-1.5.0/test/test_analyzer.rb	2007-06-15 10:19:53.000000000 +0200
+++ ./test/test_analyzer.rb	2007-06-14 18:23:50.000000000 +0200
@@ -6,8 +6,15 @@
 
 require 'production_log/analyzer'
 
-class TestEnumerable < Test::Unit::TestCase
+# hack String so the tests don't fail because there are tabs/spaces in the way
+class String
+  def ==(string)
+    return false if !string.respond_to?(:to_s)
+    return (self.gsub(/\s+/, ' ') <=> string.gsub(/\s+/, ' ')) == 0
+  end
+end
 
+class TestEnumerable < Test::Unit::TestCase
   def test_sum
     assert_equal 45, (1..9).sum
   end
@@ -24,7 +31,6 @@
   def test_standard_deviation
     assert_in_delta 2.5819, (1..9).to_a.standard_deviation, 0.0001
   end
-
 end
 
 class TestSizedList < Test::Unit::TestCase
@@ -83,82 +89,8 @@
 class TestAnalyzer < Test::Unit::TestCase
 
   def setup
-    @analyzer = Analyzer.new 'test/test.syslog.log'
-    @analyzer.process
-  end
-
-  def test_self_email
-    email = Analyzer.email('test/test.syslog.log', 'devnull@robotcoop.com',
-                           nil, 1)
-    expected = <<-EOF
-Subject: pl_analyze
-To: devnull@robotcoop.com
-Content-Type: text/html
-
-<pre>Request Times Summary:    	Count	Avg	Std Dev	Min	Max
-ALL REQUESTS:             	11	0.576	0.508	0.000	1.470
-
-ThingsController#view:    	3	0.716	0.387	0.396	1.260
-TeamsController#progress: 	2	0.841	0.629	0.212	1.470
-RssController#uber:       	2	0.035	0.000	0.035	0.035
-PeopleController#progress:	2	0.489	0.489	0.000	0.977
-PeopleController#view:    	2	0.731	0.371	0.360	1.102
-
-Slowest Request Times:
-\tTeamsController#progress took 1.470s
-
-------------------------------------------------------------------------
-
-DB Times Summary:         	Count	Avg	Std Dev	Min	Max
-ALL REQUESTS:             	11	0.366	0.393	0.000	1.144
-
-ThingsController#view:    	3	0.403	0.362	0.122	0.914
-TeamsController#progress: 	2	0.646	0.497	0.149	1.144
-RssController#uber:       	2	0.008	0.000	0.008	0.008
-PeopleController#progress:	2	0.415	0.415	0.000	0.830
-PeopleController#view:    	2	0.338	0.149	0.189	0.486
-
-Slowest Total DB Times:
-\tTeamsController#progress took 1.144s
-
-------------------------------------------------------------------------
-
-Render Times Summary:     	Count	Avg	Std Dev	Min	Max
-ALL REQUESTS:             	11	0.219	0.253	0.000	0.695
-
-ThingsController#view:    	3	0.270	0.171	0.108	0.506
-TeamsController#progress: 	2	0.000	0.000	0.000	0.000
-RssController#uber:       	2	0.012	0.000	0.012	0.012
-PeopleController#progress:	2	0.302	0.302	0.000	0.604
-PeopleController#view:    	2	0.487	0.209	0.278	0.695
-
-Slowest Total Render Times:
-\tPeopleController#view took 0.695s
-</pre>
-      EOF
-
-    assert_equal expected, email
-  end
-
-  def test_self_envelope
-    expected = [
-      "Subject: pl_analyze",
-      "To: devnull@example.com",
-      "Content-Type: text/html"
-    ]
-
-    assert_equal expected, Analyzer.envelope('devnull@example.com')
-  end
-
-  def test_self_envelope_subject
-    expected = [
-      "Subject: happy fancy boom",
-      "To: devnull@example.com",
-      "Content-Type: text/html"
-    ]
-
-    assert_equal(expected,
-                 Analyzer.envelope('devnull@example.com', 'happy fancy boom'))
+    @analyzer = Analyzer.new 3
+    @analyzer.process 'test/test.syslog.log'
   end
 
   def test_average_db_time
@@ -179,30 +111,25 @@
 
   def test_db_times_summary
     expected = <<EOF.strip
-DB Times Summary:         	Count	Avg	Std Dev	Min	Max
-ALL REQUESTS:             	11	0.366	0.393	0.000	1.144
+DB Times Summary:               Count   Fra     Avg     Std Dev Min     Max
+ALL REQUESTS:                   11      100%    0.366   0.393   0.000   1.144
 
-ThingsController#view:    	3	0.403	0.362	0.122	0.914
-TeamsController#progress: 	2	0.646	0.497	0.149	1.144
-RssController#uber:       	2	0.008	0.000	0.008	0.008
-PeopleController#progress:	2	0.415	0.415	0.000	0.830
-PeopleController#view:    	2	0.338	0.149	0.189	0.486
+ThingsController#view:          3       27.3%   0.403   0.362   0.122   0.914
+TeamsController#progress:       2       18.2%   0.646   0.497   0.149   1.144
+RssController#uber:             2       18.2%   0.008   0.000   0.008   0.008
+... [2 not shown]
 EOF
 
     assert_equal expected, @analyzer.db_times_summary
   end
 
   def test_empty_syslog
-    analyzer = Analyzer.new 'test/test.syslog.empty.log'
+    analyzer = Analyzer.new
     assert_nothing_raised do
-      analyzer.process
-      analyzer.report(1)
+      analyzer.process 'test/test.empty.log'
+      analyzer.report
     end
-    assert_equal "No requests to analyze", analyzer.report(1)
-  end
-
-  def test_logfile_name
-    assert_equal 'test/test.syslog.log', @analyzer.logfile_name
+    assert_equal "No requests to analyze", analyzer.report
   end
 
   def test_longest_request_name
@@ -234,8 +161,8 @@
   end
 
   def test_pad_request_name_short
-    analyzer = Analyzer.new 'test/test.syslog.1.2.shortname.log'
-    analyzer.process
+    analyzer = Analyzer.new
+    analyzer.process 'test/test.syslog.1.2.shortname.log'
     longer_request_name_value = " " * (analyzer.longest_request_name + 1)
     assert_nothing_raised do
       analyzer.pad_request_name(longer_request_name_value)
@@ -278,90 +205,20 @@
 
   def test_render_times_summary
     expected = <<EOF.strip
-Render Times Summary:     	Count	Avg	Std Dev	Min	Max
-ALL REQUESTS:             	11	0.219	0.253	0.000	0.695
+Render Times Summary:           Count   Fra     Avg     Std Dev Min     Max
+ALL REQUESTS:                   11      100%    0.219   0.253   0.000   0.695
 
-ThingsController#view:    	3	0.270	0.171	0.108	0.506
-TeamsController#progress: 	2	0.000	0.000	0.000	0.000
-RssController#uber:       	2	0.012	0.000	0.012	0.012
-PeopleController#progress:	2	0.302	0.302	0.000	0.604
-PeopleController#view:    	2	0.487	0.209	0.278	0.695
+ThingsController#view:          3       27.3%   0.270   0.171   0.108   0.506
+TeamsController#progress:       2       18.2%   0.000   0.000   0.000   0.000
+RssController#uber:             2       18.2%   0.012   0.000   0.012   0.012
+... [2 not shown]
 EOF
 
     assert_equal expected, @analyzer.render_times_summary
   end
 
   def test_report
-    expected = <<-EOF
-Request Times Summary:    	Count	Avg	Std Dev	Min	Max
-ALL REQUESTS:             	11	0.576	0.508	0.000	1.470
-
-ThingsController#view:    	3	0.716	0.387	0.396	1.260
-TeamsController#progress: 	2	0.841	0.629	0.212	1.470
-RssController#uber:       	2	0.035	0.000	0.035	0.035
-PeopleController#progress:	2	0.489	0.489	0.000	0.977
-PeopleController#view:    	2	0.731	0.371	0.360	1.102
-
-Slowest Request Times:
-\tTeamsController#progress took 1.470s
-\tThingsController#view took 1.260s
-\tPeopleController#view took 1.102s
-\tPeopleController#progress took 0.977s
-\tThingsController#view took 0.492s
-\tThingsController#view took 0.396s
-\tPeopleController#view took 0.360s
-\tTeamsController#progress took 0.212s
-\tRssController#uber took 0.035s
-\tRssController#uber took 0.035s
-
-------------------------------------------------------------------------
-
-DB Times Summary:         	Count	Avg	Std Dev	Min	Max
-ALL REQUESTS:             	11	0.366	0.393	0.000	1.144
-
-ThingsController#view:    	3	0.403	0.362	0.122	0.914
-TeamsController#progress: 	2	0.646	0.497	0.149	1.144
-RssController#uber:       	2	0.008	0.000	0.008	0.008
-PeopleController#progress:	2	0.415	0.415	0.000	0.830
-PeopleController#view:    	2	0.338	0.149	0.189	0.486
-
-Slowest Total DB Times:
-\tTeamsController#progress took 1.144s
-\tThingsController#view took 0.914s
-\tPeopleController#progress took 0.830s
-\tPeopleController#view took 0.486s
-\tPeopleController#view took 0.189s
-\tThingsController#view took 0.173s
-\tTeamsController#progress took 0.149s
-\tThingsController#view took 0.122s
-\tRssController#uber took 0.008s
-\tRssController#uber took 0.008s
-
-------------------------------------------------------------------------
-
-Render Times Summary:     	Count	Avg	Std Dev	Min	Max
-ALL REQUESTS:             	11	0.219	0.253	0.000	0.695
-
-ThingsController#view:    	3	0.270	0.171	0.108	0.506
-TeamsController#progress: 	2	0.000	0.000	0.000	0.000
-RssController#uber:       	2	0.012	0.000	0.012	0.012
-PeopleController#progress:	2	0.302	0.302	0.000	0.604
-PeopleController#view:    	2	0.487	0.209	0.278	0.695
-
-Slowest Total Render Times:
-\tPeopleController#view took 0.695s
-\tPeopleController#progress took 0.604s
-\tThingsController#view took 0.506s
-\tPeopleController#view took 0.278s
-\tThingsController#view took 0.197s
-\tThingsController#view took 0.108s
-\tRssController#uber took 0.012s
-\tRssController#uber took 0.012s
-\tTeamsController#progress took 0.000s
-\tTeamsController#progress took 0.000s
-      EOF
-
-    assert_equal expected, @analyzer.report(10)
+    assert_equal File.open("test/analyzer_report.txt").read, @analyzer.report
   end
 
   def test_request_time_std_dev
@@ -370,21 +227,20 @@
 
   def test_request_times_summary
     expected = <<EOF.strip
-Request Times Summary:    	Count	Avg	Std Dev	Min	Max
-ALL REQUESTS:             	11	0.576	0.508	0.000	1.470
+Request Times Summary:          Count   Fra     Avg     Std Dev Min     Max
+ALL REQUESTS:                   11      100%    0.576   0.508   0.000   1.470
 
-ThingsController#view:    	3	0.716	0.387	0.396	1.260
-TeamsController#progress: 	2	0.841	0.629	0.212	1.470
-RssController#uber:       	2	0.035	0.000	0.035	0.035
-PeopleController#progress:	2	0.489	0.489	0.000	0.977
-PeopleController#view:    	2	0.731	0.371	0.360	1.102
+ThingsController#view:          3       27.3%   0.716   0.387   0.396   1.260
+TeamsController#progress:       2       18.2%   0.841   0.629   0.212   1.470
+RssController#uber:             2       18.2%   0.035   0.000   0.035   0.035
+... [2 not shown]
 EOF
 
     assert_equal expected, @analyzer.request_times_summary
   end
 
   def test_slowest_db_times
-    times = @analyzer.slowest_db_times 3
+    times = @analyzer.slowest_db_times
     assert_equal 3, times.length
     expected = [
       [1.143577, "TeamsController#progress"],
@@ -395,7 +251,7 @@
   end
 
   def test_slowest_request_times
-    times = @analyzer.slowest_request_times 3
+    times = @analyzer.slowest_request_times
     assert_equal 3, times.length
     expected = [
       [1.469788, "TeamsController#progress"],
@@ -406,7 +262,7 @@
   end
 
   def test_slowest_render_times
-    times = @analyzer.slowest_render_times 3
+    times = @analyzer.slowest_render_times
     assert_equal 3, times.length
     expected = [
       [0.695476, "PeopleController#view"],
@@ -416,5 +272,34 @@
     assert_equal expected, times
   end
 
+  def test_http_statuses
+    statuses = Analyzer.new.process('test/test.railslog.log').http_statuses
+    assert_equal 3, statuses.values.flatten.size
+    expected = {"MessageController#index"=>[302], "AccountController#login"=>[302, 302]}
+    assert_equal expected, statuses
+  end
+
+  def test_request_statuses_summary
+    statuses_summary = Analyzer.new.process('test/test.railslog.log').request_statuses_summary
+    expected = <<EOF.strip
+HTTP Status Codes Summary
+Code    HTTP            Number  Fra
+302     Found           3       100.00%
+EOF
+    
+    assert_equal expected, statuses_summary    
+  end
+
+  def test_controller_times_summary
+    summary = Analyzer.new.process('test/test.syslog.log').controller_times_summary('TeamsController')
+    expected = <<EOF.strip
+TeamsController Summary:        Count   Fra     Avg     Std Dev Min     Max
+ALL REQUESTS:                   2       100%    0.841   0.629   0.212   1.470
+
+TeamsController#progress:       2       100.0%  0.841   0.629   0.212   1.470
+EOF
+
+    assert_equal expected, summary
+  end
 end
 
diff -Nur ../production_log_analyzer-1.5.0/test/test_email.rb ./test/test_email.rb
--- ../production_log_analyzer-1.5.0/test/test_email.rb	1970-01-01 01:00:00.000000000 +0100
+++ ./test/test_email.rb	2007-06-06 12:18:50.000000000 +0200
@@ -0,0 +1,71 @@
+require 'production_log/email'
+
+class TestEmail < Test::Unit::TestCase
+  def test_email
+    report = Analyzer.new(1).process('test/test.syslog.log').report
+    email = email report, 'test@test.com', 'pl_analyze'
+    expected = <<EOF
+Subject: pl_analyze
+To: test@test.com
+Content-Type: text/html
+
+<pre>Log files analyzed:
+test/test.syslog.log
+
+First request:         Mon Mar 07 07:00:25 +0100 2005
+Last request:          Mon Mar 07 07:00:32 +0100 2005
+Requests per second/minute/hour:   1.57/94.29/5657.14
+
+------------------------------------------------------------------------
+
+HTTP Status Codes Summary
+Code  HTTP    Number  Fra
+
+------------------------------------------------------------------------
+
+Request Times Summary:      Count Fra Avg Std Dev Min Max
+ALL REQUESTS:               11  100%  0.576 0.508 0.000 1.470
+
+ThingsController#view:      3 27.3% 0.716 0.387 0.396 1.260
+... [4 not shown]
+
+Slowest Request Times:
+  TeamsController#progress took 1.470s
+
+  ------------------------------------------------------------------------
+
+  DB Times Summary:           Count Fra Avg Std Dev Min Max
+  ALL REQUESTS:               11  100%  0.366 0.393 0.000 1.144
+
+  ThingsController#view:      3 27.3% 0.403 0.362 0.122 0.914
+  ... [4 not shown]
+
+  Slowest Total DB Times:
+    TeamsController#progress took 1.144s
+
+    ------------------------------------------------------------------------
+
+    Render Times Summary:       Count Fra Avg Std Dev Min Max
+    ALL REQUESTS:               11  100%  0.219 0.253 0.000 0.695
+
+    ThingsController#view:      3 27.3% 0.270 0.171 0.108 0.506
+    ... [4 not shown]
+
+    Slowest Total Render Times:
+      PeopleController#view took 0.695s
+      </pre>
+EOF
+
+    assert_equal expected, email
+  end
+
+  def test_envelope
+    expected = {
+      'Subject' => 'pl_analyze',
+      'To' => 'test@test.com',
+      'Content-Type' => 'text/html' }
+    envelope = envelope 'test@test.com', 'pl_analyze'
+
+    assert_equal expected, envelope
+  end
+end
diff -Nur ../production_log_analyzer-1.5.0/test/test.empty.log ./test/test.empty.log
--- ../production_log_analyzer-1.5.0/test/test.empty.log	1970-01-01 01:00:00.000000000 +0100
+++ ./test/test.empty.log	2007-06-15 10:24:45.000000000 +0200
@@ -0,0 +1 @@
+<dummy line>
diff -Nur ../production_log_analyzer-1.5.0/test/test_parser.rb ./test/test_parser.rb
--- ../production_log_analyzer-1.5.0/test/test_parser.rb	2007-06-15 10:19:53.000000000 +0200
+++ ./test/test_parser.rb	2007-06-14 18:24:42.000000000 +0200
@@ -5,13 +5,14 @@
 require 'tempfile'
 require 'test/unit'
 require 'stringio'
+require 'time'
 
 require 'production_log/parser'
 
 class TestLogEntry < Test::Unit::TestCase
 
   def setup
-    @entry = LogParser::LogEntry.new <<EOF
+    request = <<EOF
 Processing TwinklerController#index (for 81.109.96.173 at Wed Dec 01 16:01:56 CST 2004)
 Parameters: {\"action\"=>\"index\", \"controller\"=>\"twinkler\"}
 Browser Load First (0.001114)   SELECT * FROM browsers WHERE ubid = 'ixsXHgUo7U9PJGgBzr7e9ocaDOc=' LIMIT 1
@@ -20,6 +21,7 @@
 Rendering layouts/default (200 OK)
 Completed in 0.616122 (1 reqs/sec) | Rendering: 0.242475 (39%) | DB: 0.002876 (0%)
 EOF
+    @entry = Log::LogEntry.new request.split("\n")
   end
 
   def test_parse
@@ -36,13 +38,12 @@
 EOF
     request = request.split "\n"
 
-    entry = LogParser::LogEntry.new []
+    entry = Log::LogEntry.new request
 
-    entry.parse request
-    assert_kind_of LogParser::LogEntry, entry
+    assert_kind_of Log::LogEntry, entry
     assert_equal "RssController#uber", entry.page
-    assert_equal 3, entry.queries.length
-    assert_equal ['Browser Load', 0.003963], entry.queries.first
+#    assert_equal 3, entry.queries.length
+#    assert_equal ['Browser Load', 0.003963], entry.queries.first
     assert_equal 0.034519, entry.request_time
   end
 
@@ -55,20 +56,20 @@
   end
 
   def test_time
-    assert_equal "Wed Dec 01 16:01:56 CST 2004", @entry.time
+    assert_equal "Wed Dec 01 23:01:56 +0100 2004", @entry.time.to_s
   end
 
-  def test_queries
-    expected = []
-    expected << ["Browser Load First", 0.001114]
-    expected << ["Goal Count", 0.001762]
-    assert_equal expected, @entry.queries
-  end
+#  def test_queries
+#    expected = []
+#    expected << ["Browser Load First", 0.001114]
+#    expected << ["Goal Count", 0.001762]
+#    assert_equal expected, @entry.queries
+#  end
 
   def test_request_time
     assert_equal 0.616122, @entry.request_time
 
-    @entry = LogParser::LogEntry.new "Processing TwinklerController#add_thing (for 144.164.232.114 at Wed Dec 01 16:01:56 CST 2004)
+    @entry = Log::LogEntry.new "Processing TwinklerController#add_thing (for 144.164.232.114 at Wed Dec 01 16:01:56 CST 2004)
 Completed in 0.261485 (3 reqs/sec) | DB: 0.009325 (3%)"
 
     assert_equal 0.261485, @entry.request_time
@@ -77,7 +78,7 @@
   def test_render_time
     assert_equal 0.242475, @entry.render_time
 
-    @entry = LogParser::LogEntry.new "Processing TwinklerController#add_thing (for 144.164.232.114 at Wed Dec 01 16:01:56 CST 2004)
+    @entry = Log::LogEntry.new "Processing TwinklerController#add_thing (for 144.164.232.114 at Wed Dec 01 16:01:56 CST 2004)
 Completed in 0.261485 (3 reqs/sec) | DB: 0.009325 (3%)"
 
     assert_equal 0, @entry.render_time
@@ -87,11 +88,27 @@
     assert_equal 0.002876, @entry.db_time
   end
 
+  def test_http_status
+    @entry_404 = Log::LogEntry.new "Completed in 0.01285 (77 reqs/sec) | Rendering: 0.00519 (40%) | DB: 0.00308 (23%) | 404 [http:// /]"
+    assert_equal 404, @entry_404.http_status
+
+    @entry_302 = Log::LogEntry.new "Completed in 0.04697 (21 reqs/sec) | DB: 0.01132 (24%) | 302 Found [http:// /]"
+    assert_equal 302, @entry_302.http_status
+
+    @entry_200 = Log::LogEntry.new "Completed in 0.04368 (22 reqs/sec) | Rendering: 0.00510 (11%) | DB: 0.00000 (0%) | 200 OK [http:// /]"
+    assert_equal 200, @entry_200.http_status
+  end
+
+  def test_url
+    entry = Log::LogEntry.new "Completed in 0.01285 (77 reqs/sec) | Rendering: 0.00519 (40%) | DB: 0.00308 (23%) | 404 [http://example.org/test/]"
+    assert_equal "http://example.org/test/", entry.url
+  end
 end
 
-class TestLogParser < Test::Unit::TestCase
+class TestSyslogParser < Test::Unit::TestCase
+  include Log
 
-  def test_class_parse
+  def test_class_parse_syslog
     log = StringIO.new <<-EOF
 Mar  7 00:00:25 online1 rails[59628]: Processing RssController#uber (for 67.18.200.5 at Mon Mar 07 00:00:25 CST 2005)
 Mar  7 00:00:25 online1 rails[59628]: Parameters: {:id=>"author", :"rss/uber/author.html/uber/author"=>nil, :action=>"uber", :username=>"looch", :controller=>"rss"}
@@ -105,8 +122,8 @@
         EOF
 
     entries = []
-
-    LogParser.parse log do |entry|
+    
+    Parser.parse(log, :syslog) do |entry|
       entries << entry
     end
 
@@ -114,7 +131,7 @@
     assert_equal 'RssController#uber', entries.first.page
   end
 
-  def test_class_parse_components
+  def test_class_parse_syslog_components
     log = StringIO.new <<-EOF
 Jul 11 10:05:20 www rails[61243]: Processing ChatroomsController#launch (for 213.152.37.169 at Mon Jul 11 10:05:20 CDT 2005)
 Jul 11 10:05:20 www rails[61243]: Start rendering component ({:action=>"online_count", :controller=>"members"}):
@@ -137,14 +154,14 @@
         EOF
 
     entries = []
-    LogParser.parse(log) { |entry| entries << entry }
+    Parser.parse(log, :syslog) { |entry| entries << entry }
 
     assert_equal 3, entries.length
     assert_equal 'ChatroomsController#launch', entries.first.page
     assert_equal 8.65005, entries.first.request_time
   end
 
-  def test_class_parse_entries_with_pre_processing_garbage
+  def test_class_parse_syslog_entries_with_pre_processing_garbage
     log = StringIO.new <<-EOF
 Jan 03 12:51:34 duo2 rails[4347]: [4;36;1mFont Load (0.000475)[0m   [0;1mSELECT * FROM fonts ORDER BY name [0m
 Jan 03 12:51:34 duo2 rails[4347]: Processing StylesheetsController#show (for 127.0.0.1 at 2007-01-03 12:51:34) [GET]
@@ -155,14 +172,14 @@
     EOF
 
     entries = []
-    LogParser.parse(log) { |entry| entries << entry }
+    Parser.parse(log, :syslog) { |entry| entries << entry }
 
     assert_equal 1, entries.length, "Number of entries was incorrect"
     assert_equal 'StylesheetsController#show', entries.first.page
     assert_equal 0.00807, entries.first.request_time
   end
 
-  def test_class_parse_rails_engines_plugin
+  def test_class_parse_syslog_rails_engines_plugin
     log = StringIO.new <<-EOF
 Jan 03 12:24:21 duo2 rails[4277]: Trying to start engine 'login_engine' from '/Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine'
 Jan 03 12:24:21 duo2 rails[4277]: adding /Users/topfunky/web/rails/repos/roughunderbelly/vendor/plugins/login_engine/lib/login_engine to the load path
@@ -213,19 +230,21 @@
     EOF
 
     entries = []
-    LogParser.parse(log) { |entry| entries << entry }
+
+    Parser.parse(log, :syslog) do |entry|
+      entries << entry
+    end
 
     assert_equal 1, entries.length, "The number of entries was incorrect"
     assert_equal 'TasksController#index', entries.first.page
     assert_equal 0.00112, entries.first.request_time
   end
 
-  def test_class_parse_multi
+  def test_class_parse_syslog_multi
     entries = []
-    File.open 'test/test.syslog.log' do |fp|
-      LogParser.parse fp do |entry|
-        entries << entry
-      end
+    parser = Parser.new
+    Parser.parse_file('test/test.syslog.log', false, :syslog) do |entry|
+      entries << entry
     end
 
     assert_equal 12, entries.length
@@ -240,14 +259,69 @@
     assert_equal 0, last.request_time
   end
 
-  def test_class_parse_0_14_x
+  def test_class_parse_syslog_0_14_x
     entries = []
+    parser = Parser.new
     File.open 'test/test.syslog.0.14.x.log' do |fp|
-      LogParser.parse fp do |entry|
+      parser.parse_syslog fp do |entry|
         entries << entry
       end
     end
   end
+end
+
+class TestLogParser < Test::Unit::TestCase
+  include Log
+  def test_class_detect_log_format
+    assert_equal :rails, Parser.detect_log_format('test/test.railslog.log')
+    assert_equal :syslog, Parser.detect_log_format('test/test.syslog.log')
+
+    assert_raise RuntimeError do
+      Parser.detect_log_format 'test/test.empty.log'
+    end
+  end
 
+  def test_class_parse_file_detects_format_correcly
+    entries = []
+    Parser.parse_file 'test/test.railslog.log' do |entry|
+      entries << entry
+    end
+
+    assert_equal 3, entries.length
+    assert_equal 'MessageController#index', entries.first.page
+
+    entries = []
+    Parser.parse_file 'test/test.syslog.log' do |entry|
+      entries << entry
+    end
+
+    assert_equal 12, entries.length
+    assert_equal 'RssController#uber', entries.first.page
+  end
+
+  def test_parsing_routing_error_does_not_yield_entry
+    entries = []
+    Parser.parse_file 'test/test.railslog.route_error.log' do |entry|
+      entries << entry
+    end
+
+    assert_equal 2, entries.size
+  end
 end
+  
+class TestRailslogParser < Test::Unit::TestCase
+  include Log
+  def test_class_parse_multi
+    entries = []
+    parser = Parser.new
+    File.open('test/test.railslog.log').each_line do |line|
+      parser.parse_rails line do |entry|
+        entries << entry
+      end
+    end
 
+    assert_equal 3, entries.length
+    assert_equal 'MessageController#index', entries.first.page
+  end
+end
+   
diff -Nur ../production_log_analyzer-1.5.0/test/test.railslog.log ./test/test.railslog.log
--- ../production_log_analyzer-1.5.0/test/test.railslog.log	1970-01-01 01:00:00.000000000 +0100
+++ ./test/test.railslog.log	2007-06-14 18:21:58.000000000 +0200
@@ -0,0 +1,33 @@
+
+Processing MessageController#index (for 127.0.0.1 at 2007-01-28 18:00:30) [GET]
+   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
+   Parameters: {"action"=>"index", "controller"=>"message"}
+   ^[[4;35;1mUser Load (0.001405)^[[0m   ^[[0mSELECT * FROM users WHERE (users.`id` = 1) LIMIT 1^[[0m
+Redirected to http://localhost:3000/account/login
+Filter chain halted as [#<ActionController::Filters::ClassMethods::SymbolFilter:0x6a7938 @filter=:login_required>] returned false.
+Completed in 0.00431 (232 reqs/sec) | DB: 0.00527 (122%) | 302 Found [http://localhost/]
+ApplicationController: missing default helper module ApplicationHelper
+  ^[[4;36;1mUser Columns (0.004048)^[[0m   ^[[0;1mSHOW FIELDS FROM users^[[0m
+
+
+Processing AccountController#login (for 127.0.0.1 at 2007-01-28 18:00:30) [GET]
+   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
+   Parameters: {"action"=>"index", "controller"=>"message"}
+   ^[[4;35;1mUser Load (0.001405)^[[0m   ^[[0mSELECT * FROM users WHERE (users.`id` = 1) LIMIT 1^[[0m
+Redirected to http://localhost:3000/account/login
+Filter chain halted as [#<ActionController::Filters::ClassMethods::SymbolFilter:0x6a7938 @filter=:login_required>] returned false.
+Completed in 0.00431 (232 reqs/sec) | DB: 0.00527 (122%) | 302 Found [http://localhost/]
+ApplicationController: missing default helper module ApplicationHelper
+  ^[[4;36;1mUser Columns (0.004048)^[[0m   ^[[0;1mSHOW FIELDS FROM users^[[0m
+
+
+Processing AccountController#login (for 127.0.0.1 at 2007-01-28 18:00:31) [GET]
+   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
+   Parameters: {"action"=>"index", "controller"=>"message"}
+   ^[[4;35;1mUser Load (0.001405)^[[0m   ^[[0mSELECT * FROM users WHERE (users.`id` = 1) LIMIT 1^[[0m
+Redirected to http://localhost:3000/account/login
+Filter chain halted as [#<ActionController::Filters::ClassMethods::SymbolFilter:0x6a7938 @filter=:login_required>] returned false.
+Completed in 0.00431 (232 reqs/sec) | DB: 0.00527 (122%) | 302 Found [http://localhost/]
+ApplicationController: missing default helper module ApplicationHelper
+  ^[[4;36;1mUser Columns (0.004048)^[[0m   ^[[0;1mSHOW FIELDS FROM users^[[0m
+
diff -Nur ../production_log_analyzer-1.5.0/test/test.railslog.route_error.log ./test/test.railslog.route_error.log
--- ../production_log_analyzer-1.5.0/test/test.railslog.route_error.log	1970-01-01 01:00:00.000000000 +0100
+++ ./test/test.railslog.route_error.log	2007-06-14 18:30:55.000000000 +0200
@@ -0,0 +1,74 @@
+
+
+Processing MessageController#index (for 127.0.0.1 at 2007-01-28 18:00:30) [GET]
+   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
+   Parameters: {"action"=>"index", "controller"=>"message"}
+   ^[[4;35;1mUser Load (0.001405)^[[0m   ^[[0mSELECT * FROM users WHERE (users.`id` = 1) LIMIT 1^[[0m
+Redirected to http://localhost:3000/account/login
+Filter chain halted as [#<ActionController::Filters::ClassMethods::SymbolFilter:0x6a7938 @filter=:login_required>] returned false.
+Completed in 0.00431 (232 reqs/sec) | DB: 0.00527 (122%) | 302 Found [http://localhost/]
+
+Processing ApplicationController#index (for 127.0.0.1 at 2007-01-28 18:38:27) [GET]
+   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
+   Parameters: {}
+
+
+ActionController::RoutingError (no route found to match "/messages" with {:method=>:get}):
+     /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/routing.rb:1266:in `recognize_path'
+     /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/routing.rb:1256:in `recognize'
+     /opt/local/lib/ruby/gems/1.8/gems/rails-1.2.1/lib/dispatcher.rb:40:in `dispatch'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/rails.rb:78:in `process'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/rails.rb:76:in `process'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:618:in `process_client'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:617:in `process_client'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:736:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:736:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:720:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/configurator.rb:271:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/configurator.rb:270:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/bin/mongrel_rails:127:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/command.rb:211:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/bin/mongrel_rails:243
+     /opt/local/bin/mongrel_rails:18
+
+
+Rendering /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/templates/rescues/layout.rhtml (404 Page Not Found)
+ApplicationController: missing default helper module ApplicationHelper
+
+
+Processing ApplicationController#index (for 127.0.0.1 at 2007-01-28 18:38:27) [GET]
+   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
+   Parameters: {}
+
+
+ActionController::RoutingError (no route found to match "/messages" with {:method=>:get}):
+     /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/routing.rb:1266:in `recognize_path'
+     /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/routing.rb:1256:in `recognize'
+     /opt/local/lib/ruby/gems/1.8/gems/rails-1.2.1/lib/dispatcher.rb:40:in `dispatch'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/rails.rb:78:in `process'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/rails.rb:76:in `process'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:618:in `process_client'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:617:in `process_client'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:736:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:736:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel.rb:720:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/configurator.rb:271:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/configurator.rb:270:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/bin/mongrel_rails:127:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/command.rb:211:in `run'
+     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.0.1/bin/mongrel_rails:243
+     /opt/local/bin/mongrel_rails:18
+
+
+Rendering /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/templates/rescues/layout.rhtml (404 Page Not Found)
+ApplicationController: missing default helper module ApplicationHelper
+
+
+Processing MessageController#index (for 127.0.0.1 at 2007-01-28 18:00:30) [GET]
+   Session ID: 4fa3d546f8332c4aa25d8a9ec027e015
+   Parameters: {"action"=>"index", "controller"=>"message"}
+   ^[[4;35;1mUser Load (0.001405)^[[0m   ^[[0mSELECT * FROM users WHERE (users.`id` = 1) LIMIT 1^[[0m
+Redirected to http://localhost:3000/account/login
+Filter chain halted as [#<ActionController::Filters::ClassMethods::SymbolFilter:0x6a7938 @filter=:login_required>] returned false.
+Completed in 0.00431 (232 reqs/sec) | DB: 0.00527 (122%) | 302 Found [http://localhost/]
+
diff -Nur ../production_log_analyzer-1.5.0/test/test.syslog.empty.log ./test/test.syslog.empty.log
--- ../production_log_analyzer-1.5.0/test/test.syslog.empty.log	2007-06-15 10:19:53.000000000 +0200
+++ ./test/test.syslog.empty.log	1970-01-01 01:00:00.000000000 +0100
@@ -1,2 +0,0 @@
-//src/production_log_analyzer/dev/test/test.syslog.empty.log#1 - add change 3135 (text)
-
