1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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
|