From a70468036d9b780bc7ec921f7feb6e1275778169 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 28 Aug 2009 20:47:43 -0700 Subject: initial import --- lib/clogger/format.rb | 25 ++++++++++ lib/clogger/pure.rb | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 lib/clogger/format.rb create mode 100644 lib/clogger/pure.rb (limited to 'lib/clogger') diff --git a/lib/clogger/format.rb b/lib/clogger/format.rb new file mode 100644 index 0000000..9e4f59f --- /dev/null +++ b/lib/clogger/format.rb @@ -0,0 +1,25 @@ +# -*- encoding: binary -*- + +class Clogger + + # predefined log formats in wide use + module Format + # common log format used by Apache: + # http://httpd.apache.org/docs/2.2/logs.html + Common = "$remote_addr - $remote_user [$time_local] " \ + '"$request" $status $response_length'.freeze + + # combined log format used by Apache: + # http://httpd.apache.org/docs/2.2/logs.html + Combined = %Q|#{Common} "$http_referer" "$http_user_agent"|.freeze + + # combined log format used by nginx: + # http://wiki.nginx.org/NginxHttpLogModule + NginxCombined = Combined.gsub(/response_length/, 'body_bytes_sent').freeze + + # log format used by Rack 1.0 + Rack_1_0 = "$ip - $remote_user [$time_local{%d/%b/%Y %H:%M:%S}] " \ + '"$request" $status $response_length $request_time{4}'.freeze + end + +end diff --git a/lib/clogger/pure.rb b/lib/clogger/pure.rb new file mode 100644 index 0000000..11c03f4 --- /dev/null +++ b/lib/clogger/pure.rb @@ -0,0 +1,126 @@ +# -*- encoding: binary -*- +# :stopdoc: +# +# Not at all optimized for performance, this was written based on +# the original C extension code so it's not very Ruby-ish... +class Clogger + + def initialize(app, opts = {}) + @app = app + @logger = opts[:logger] + @fmt_ops = compile_format(opts[:format] || Format::Common) + @wrap_body = need_wrap_body?(@fmt_ops) + @reentrant = nil + @body_bytes_sent = 0 + end + + def call(env) + @start = Time.now + status, headers, body = @app.call(env) + if wrap_body? + @reentrant = env['rack.multithread'] + @env, @status, @headers, @body = env, status, headers, body + return [ status, headers, reentrant? ? self.dup : self ] + end + log(env, status, headers) + [ status, headers, body ] + end + + def each + @body_bytes_sent = 0 + @body.each do |part| + @body_bytes_sent += part.size + yield part + end + ensure + log(@env, @status, @headers) + end + + def close + @body.close + end + + def reentrant? + @reentrant + end + + def wrap_body? + @wrap_body + end + + def fileno + @logger.fileno rescue nil + end + +private + + def byte_xs(s) + s = s.dup + s.force_encoding(Encoding::BINARY) if defined?(Encoding::BINARY) + s.gsub!(/(['"\x00-\x1f])/) { |x| "\\x#{$1.unpack('H2').first}" } + s + end + + SPECIAL_RMAP = SPECIAL_VARS.inject([]) { |ary, (k,v)| ary[v] = k; ary } + + def special_var(special_nr, env, status, headers) + case SPECIAL_RMAP[special_nr] + when :body_bytes_sent + @body_bytes_sent.to_s + when :status + status = status.to_i + status >= 100 && status <= 999 ? ('%03d' % status) : '-' + when :request + qs = env['QUERY_STRING'] + qs.empty? or qs = "?#{byte_xs(qs)}" + "#{env['REQUEST_METHOD']} " \ + "#{byte_xs(env['PATH_INFO'])}#{qs} " \ + "#{byte_xs(env['HTTP_VERSION'])}" + when :request_length + env['rack.input'].size.to_s + when :response_length + @body_bytes_sent == 0 ? '-' : @body_bytes_sent.to_s + when :ip + xff = env['HTTP_X_FORWARDED_FOR'] and return byte_xs(xff) + env['REMOTE_ADDR'] || '-' + when :pid + $$.to_s + else + raise "EDOOFUS #{special_nr}" + end + end + + def time_format(sec, usec, format, div) + format % [ sec, usec / div ] + end + + def log(env, status, headers) + (@logger || env['rack.errors']) << @fmt_ops.map { |op| + case op[0] + when OP_LITERAL; op[1] + when OP_REQUEST; byte_xs(env[op[1]] || "-") + when OP_RESPONSE; byte_xs(get_sent_header(headers, op[1])) + when OP_SPECIAL; special_var(op[1], env, status, headers) + when OP_EVAL; eval(op[1]).to_s rescue "-" + when OP_TIME_LOCAL; Time.now.strftime(op[1]) + when OP_TIME_UTC; Time.now.utc.strftime(op[1]) + when OP_REQUEST_TIME + t = Time.now - @start + time_format(t.to_i, (t - t.to_i) * 1000000, op[1], op[2]) + when OP_TIME + t = Time.now + time_format(t.sec, t.usec, op[1], op[2]) + when OP_COOKIE + (env['rack.request.cookie_hash'][op[1]] rescue "-") || "-" + else + raise "EDOOFUS #{op.inspect}" + end + }.join('') + end + + def get_sent_header(headers, match) + headers.each { |key, value| match == key.downcase and return value } + "-" + end + +end -- cgit v1.2.3-24-ge0c7