rack-devel archive mirror (unofficial) https://groups.google.com/group/rack-devel
 help / color / mirror / Atom feed
From: Eric Wong <normalperson@yhbt.net>
To: rack-devel@googlegroups.com
Subject: Re: Downsizing CommonLogger
Date: Tue, 11 Aug 2009 19:38:13 -0700	[thread overview]
Message-ID: <20090812023813.GA5916@dcvr.yhbt.net> (raw)
In-Reply-To: <20090812011427.GA4737@dcvr.yhbt.net>


Eric Wong <normalperson@yhbt.net> wrote:
> Christian Neukirchen <chneukirchen@gmail.com> wrote:
> > Joshua Peek <josh@joshpeek.com> writes:
> > 
> > > I'll pull it in soon.
> > 
> > Does it log the right size for chunked outputs?  In logfiles, I want those...
> 
> I'm very late to the party, but no, it doesn't.
> 
> This also miscalculates request time if the body is being generated
> dynamically during #each.

I'd rather revert the original patch here for simplicity.

But if the original _dup was too much overhead maybe we can reach a
middle ground and only fall back to wrapping #each iff there's no
Content-Length header to be found.

I don't can't find any real applications to benchmark with; but the
following patch may be considered (try Content-Length first, fallback to
wrapping body#each):

diff --git a/lib/rack/commonlogger.rb b/lib/rack/commonlogger.rb
index 880f0fb..0316014 100644
--- a/lib/rack/commonlogger.rb
+++ b/lib/rack/commonlogger.rb
@@ -8,25 +8,8 @@ module Rack
     #             %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
     FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
 
-    def initialize(app, logger=nil)
-      @app = app
-      @logger = logger
-    end
-
-    def call(env)
-      began_at = Time.now
-      status, header, body = @app.call(env)
-      log(env, status, header, began_at)
-      [status, header, body]
-    end
-
-    private
-
-    def log(env, status, header, began_at)
+    def self.log(logger, env, status, began_at, length)
       now = Time.now
-      length = extract_content_length(header)
-
-      logger = @logger || env['rack.errors']
       logger.write FORMAT % [
         env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
         env["REMOTE_USER"] || "-",
@@ -40,13 +23,55 @@ module Rack
         now - began_at ]
     end
 
+    def initialize(app, logger=nil)
+      @app = app
+      @logger = logger
+    end
+
+    def call(env)
+      began_at = Time.now
+      status, header, body = @app.call(env)
+      logger = @logger || env['rack.errors']
+
+      if length = extract_content_length(header)
+        self.class.log(logger, env, status, began_at, length)
+      else
+        body = BodyWrapper.new(body, [ logger, env, status, began_at ])
+      end
+
+      [ status, header, body ]
+    end
+
+  private
+
     def extract_content_length(headers)
       headers.each do |key, value|
         if key.downcase == 'content-length'
           return value.to_s == '0' ? '-' : value
         end
       end
-      '-'
+      nil
     end
+
+    class BodyWrapper
+
+      def initialize(body, log_params)
+        @body = body
+        @log_params = log_params
+      end
+
+      def each
+        length = 0
+        @body.each { |part|
+          length += Rack::Utils.bytesize(part)
+          yield part
+        }
+        ensure
+          @log_params << length
+          CommonLogger.log(*@log_params)
+      end
+
+    end
+
   end
 end
diff --git a/test/spec_rack_commonlogger.rb b/test/spec_rack_commonlogger.rb
index 46a72e8..2b6bcf1 100644
--- a/test/spec_rack_commonlogger.rb
+++ b/test/spec_rack_commonlogger.rb
@@ -6,6 +6,13 @@ require 'rack/lobster'
 require 'rack/mock'
 
 context "Rack::CommonLogger" do
+  app_without_length_body = Object.new
+  def app_without_length_body.each
+    [ "5\r\nhello\r\n", "5\r\nworld\r\n", "0\r\n" ].each { |part|
+      yield part
+    }
+  end
+
   app = lambda { |env|
     [200,
      {"Content-Type" => "text/html", "Content-Length" => length.to_s},
@@ -13,7 +20,7 @@ context "Rack::CommonLogger" do
   app_without_length = lambda { |env|
     [200,
      {"Content-Type" => "text/html"},
-     []]}
+     app_without_length_body]}
   app_with_zero_length = lambda { |env|
     [200,
      {"Content-Type" => "text/html", "Content-Length" => "0"},
@@ -33,11 +40,11 @@ context "Rack::CommonLogger" do
     log.string.should =~ /"GET \/ " 200 #{length} /
   end
 
-  specify "should log - content length if header is missing" do
+  specify "should log correct content length if header is missing" do
     res = Rack::MockRequest.new(Rack::CommonLogger.new(app_without_length)).get("/")
 
     res.errors.should.not.be.empty
-    res.errors.should =~ /"GET \/ " 200 - /
+    res.errors.should =~ /"GET \/ " 200 23 /
   end
 
   specify "should log - content length if header is zero" do
-- 
Eric Wong

  reply	other threads:[~2009-08-12  2:38 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-04-29 15:03 Downsizing CommonLogger Jeremy Kemper
2009-04-29 15:21 ` Michael Fellinger
2009-05-14  1:52   ` Jeremy Kemper
2009-05-14  5:14     ` Matt Todd
2009-05-14 14:28       ` Joshua Peek
2009-05-16  8:58         ` Christian Neukirchen
2009-08-12  1:14           ` Eric Wong
2009-08-12  2:38             ` Eric Wong [this message]
2009-08-12 21:56               ` Jeremy Kemper
2009-08-13  5:44                 ` Eric Wong
2009-08-13 10:14                 ` Christian Neukirchen
2009-08-13 16:18                   ` Jeremy Kemper
2009-08-13 16:26                     ` Jeremy Kemper
2009-08-13 16:30                     ` Yehuda Katz
2009-08-13 16:33                       ` Brian Lopez
2009-08-13 16:36                         ` Jeremy Kemper
2009-08-13 18:43                         ` Scytrin dai Kinthra
2009-08-14  3:33                       ` Eric Wong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://groups.google.com/group/rack-devel

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090812023813.GA5916@dcvr.yhbt.net \
    --to=rack-devel@googlegroups.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).