OK, I hacked together a simple replacement logger, and this is what I get
with logging *enabled* (merb -e production)

  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
  3.69     0.34      0.34      100     3.40    76.60  MerbHandler#process
  3.25     0.64      0.30      800     0.38     0.73  Merb::Logger#info
  3.15     0.93      0.29     3724     0.08     0.20  Hash#[]
  3.04     1.21      0.28      400     0.70     1.47  Hash#inspect
  2.93     1.48      0.27      300     0.90     2.10  Hash#each_pair
  2.60     1.72      0.24       52     4.62    43.27  Kernel.sleep
  2.60     1.96      0.24      200     1.20     1.75  
Mongrel::HttpResponse#send_status
  2.49     2.19      0.23      400     0.58     1.02  
Merb::AbstractController#after_filters
  2.17     2.39      0.20      100     2.00     2.60  
Mongrel::HttpRequest#initialize
  2.17     2.59      0.20      100     2.00    25.80  IO#close
  2.06     2.78      0.19     1600     0.12     0.73  Class#new
  2.06     2.97      0.19      100     1.90     4.60  
Merb::Request#query_params  1.95     3.15      0.18      200     0.90     1.60  
Mash#include?
  1.95     3.33      0.18      400     0.45     0.45  String#split
  1.84     3.50      0.17      100     1.70     8.50  
Merb::AbstractController#dispatch
  1.63     3.65      0.15      700     0.21     0.29  Mash#convert_key
  1.52     3.79      0.14      100     1.40    19.90  Merb::Controller#dispatch
  1.52     3.93      0.14      100     1.40    70.10  
Mongrel::HttpServer#process_client
  1.41     4.06      0.13      100     1.30     2.80  Merb::Router#match
  1.41     4.19      0.13      800     0.16     0.59  
Merb::Request#route_params  1.41     4.32      0.13      300     0.43     0.67  
Mongrel::HeaderOut#[]=
  1.30     4.44      0.12      300     0.40     0.40  Hash#key?
  1.30     4.56      0.12      400     0.30     0.30  NilClass#inspect
  1.19     4.67      0.11      400     0.28     0.28  Mash#convert_value
  1.19     4.78      0.11     1300     0.08     0.08  Kernel.class
  1.19     4.89      0.11      900     0.12     0.12  String#[]
  1.19     5.00      0.11      100     1.10    56.20  Merb::Dispatcher#handle
  1.08     5.10      0.10      400     0.25     0.70  
Merb::AbstractController#before_filters
  1.08     5.20      0.10      200     0.50     2.10  Mash#default
... snip less than 1%
  0.00     9.22      0.00        1     0.00  9220.00  #toplevel

Performance test using ab -n 1000 http://localhost:4000/hello/world

[Before patch]

Requests per second:    276.78 [#/sec] (mean)
Time per request:       3.613 [ms] (mean)

[After patch]

Requests per second:    401.49 [#/sec] (mean)
Time per request:       2.491 [ms] (mean)

This is on a 1.1GHz Pentium M. It would be interesting to see what people
with higher-performance servers achieve.

Cheers,

Brian.

P.S. In this code, a single write is done at the end of each request. I've
not protected it with a mutex, because I think that IO#write is atomic, but
I may be wrong.

More performance could perhaps be achieved by not flushing after every
request, but by doing this in a background thread. However I think it might
make more sense if we're going this route to give each request its own log
buffer, so that logs don't get interspersed from overlapping requests in a
multi-threaded application.

Also, I notice that the logger class and initialisation is currently
hard-coded into merb.rb; perhaps it would be better to make this
configurable.
--- lib/merb.rb.orig    2007-09-19 09:55:24.000000000 +0100
+++ lib/merb.rb 2007-09-19 09:56:37.000000000 +0100
@@ -10,7 +10,7 @@
 end
 require 'fileutils'
 require 'merb/erubis_ext'
-require 'logger'
+require 'merb/logger'
 require 'json'
 require 'set'
 autoload :MerbUploadHandler, 'merb/upload_handler'
@@ -67,8 +67,8 @@
 MERB_VIEW_ROOT = MERB_ROOT / "app/views"
 MERB_SKELETON_DIR = File.join(MERB_FRAMEWORK_ROOT, 
'../app_generators/merb/templates')
 logpath = $TESTING ? "#{MERB_ROOT}/merb_test.log" : 
"#{MERB_ROOT}/log/merb.#{Merb::Server.config[:port]}.log"
-MERB_LOGGER = Logger.new(logpath)
-MERB_LOGGER.level = Logger.const_get(Merb::Server.config[:log_level].upcase) 
rescue Logger::INFO
+MERB_LOGGER = Merb::Logger.new(logpath)
+MERB_LOGGER.level = 
Merb::Logger.const_get(Merb::Server.config[:log_level].upcase) rescue 
Merb::Logger::INFO
 MERB_PATHS = [ 
   "/app/models/**/*.rb",
   "/app/controllers/application.rb",
@@ -82,4 +82,4 @@
 if $TESTING
   test_files = File.join(lib, 'test', '*.rb')
   Dir[test_files].each { |file| require file }
-end
\ No newline at end of file
+end
--- lib/merb/mongrel_handler.rb.orig    2007-09-16 19:32:04.000000000 +0100
+++ lib/merb/mongrel_handler.rb 2007-09-19 10:10:39.000000000 +0100
@@ -145,8 +145,8 @@
       total_request_time = Time.now - start
       benchmarks[:total_request_time] = total_request_time
         
-      MERB_LOGGER.info("Request Times: #{benchmarks.inspect}")
-      MERB_LOGGER.info("Response status: #{response.status}\nComplete Request 
took: #{total_request_time} seconds, #{1.0/total_request_time} 
Requests/Second\n\n")
+      MERB_LOGGER.info("Request Times: #{benchmarks.inspect}\nResponse status: 
#{response.status}\nComplete Request took: #{total_request_time} seconds, 
#{1.0/total_request_time} Requests/Second\n\n")
+      MERB_LOGGER.flush
     end
   rescue Object => e
     # if an exception is raised here then something is 
--- lib/merb/logger.rb.orig     2007-09-19 10:14:33.000000000 +0100
+++ lib/merb/logger.rb  2007-09-19 10:06:37.000000000 +0100
@@ -0,0 +1,70 @@
+module Merb
+  # Logging is called several times for each request, so keep it short and
+  # sweet. Use << for atomic add to buffer, and slice! for atomic removal.
+
+  class Logger
+    module Severity
+      DEBUG = 0
+      INFO = 1
+      WARN = 2
+      ERROR = 3
+      FATAL = 4
+      UNKNOWN = 5
+    end
+    include Severity
+
+    attr_accessor :level
+    attr_reader :buf
+    def initialize(log)
+      @level = level
+      @buf = ""
+      if log.respond_to?(:write)
+        @log = log
+      elsif File.exist?(log)
+        @log = open(log, (File::WRONLY | File::APPEND))
+        @log.sync = true
+      else
+        @log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
+        @log.sync = true
+        @log.write("# Logfile created on %s" % [Time.now.to_s])
+      end
+    end
+
+    def flush
+      return if @buf.size == 0
+      @log.write @buf.slice!(0..-1)
+    end
+
+    def close
+      flush
+      @log.close if @log.respond_to? :close
+      @log = nil
+    end
+
+    def debug(msg)
+      return if @level > DEBUG
+      msg << "\n" unless msg[-1] == ?\n
+      @buf << msg
+    end
+    def info(msg)
+      return if @level > INFO
+      msg << "\n" unless msg[-1] == ?\n
+      @buf << msg
+    end
+    def warn(msg)
+      return if @level > WARN
+      msg << "\n" unless msg[-1] == ?\n
+      @buf << msg
+    end
+    def error(msg)
+      return if @level > ERROR
+      msg << "\n" unless msg[-1] == ?\n
+      @buf << msg
+    end
+    def error(msg)
+      return if @level > FATAL
+      msg << "\n" unless msg[-1] == ?\n
+      @buf << msg
+    end
+  end
+end
_______________________________________________
Merb-devel mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/merb-devel

Reply via email to