line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Mojolicious::Plugin::AccessLog; |
2
|
|
|
|
|
|
|
|
3
|
9
|
|
|
9
|
|
12653
|
use Mojo::Base 'Mojolicious::Plugin'; |
|
9
|
|
|
|
|
27
|
|
|
9
|
|
|
|
|
72
|
|
4
|
9
|
|
|
9
|
|
1999
|
use Mojo::IOLoop; |
|
9
|
|
|
|
|
30
|
|
|
9
|
|
|
|
|
92
|
|
5
|
|
|
|
|
|
|
|
6
|
9
|
|
|
9
|
|
259
|
use File::Spec; |
|
9
|
|
|
|
|
22
|
|
|
9
|
|
|
|
|
196
|
|
7
|
9
|
|
|
9
|
|
51
|
use IO::File; |
|
9
|
|
|
|
|
17
|
|
|
9
|
|
|
|
|
1738
|
|
8
|
9
|
|
|
9
|
|
75
|
use POSIX qw(setlocale strftime LC_ALL); |
|
9
|
|
|
|
|
20
|
|
|
9
|
|
|
|
|
94
|
|
9
|
9
|
|
|
9
|
|
925
|
use Scalar::Util qw(blessed reftype weaken); |
|
9
|
|
|
|
|
29
|
|
|
9
|
|
|
|
|
622
|
|
10
|
9
|
|
|
9
|
|
100
|
use Socket qw(inet_aton AF_INET); |
|
9
|
|
|
|
|
40
|
|
|
9
|
|
|
|
|
561
|
|
11
|
9
|
|
|
9
|
|
66
|
use Time::HiRes qw(gettimeofday tv_interval); |
|
9
|
|
|
|
|
19
|
|
|
9
|
|
|
|
|
110
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
our $VERSION = '0.010001'; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
my $DEFAULT_FORMAT = 'common'; |
16
|
|
|
|
|
|
|
my %FORMATS = ( |
17
|
|
|
|
|
|
|
$DEFAULT_FORMAT => '%h %l %u %t "%r" %>s %b', |
18
|
|
|
|
|
|
|
combined => '%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i"', |
19
|
|
|
|
|
|
|
combinedio => '%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i" %I %O', |
20
|
|
|
|
|
|
|
); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# some systems (Windows) don't support %z correctly |
23
|
|
|
|
|
|
|
my $TZOFFSET = strftime('%z', localtime) !~ /^[+-]\d{4}$/ && do { |
24
|
|
|
|
|
|
|
require Time::Local; |
25
|
|
|
|
|
|
|
my $t = time; |
26
|
|
|
|
|
|
|
my $d = (Time::Local::timegm(localtime($t)) - $t) / 60; |
27
|
|
|
|
|
|
|
sprintf '%+03d%02u', int($d / 60), $d % 60; |
28
|
|
|
|
|
|
|
}; |
29
|
|
|
|
|
|
|
# some systems (Windows) don't support %s |
30
|
|
|
|
|
|
|
my $NOEPOCHSECS = strftime('%s', localtime) !~ /^\d+$/; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
sub register { |
33
|
9
|
|
|
9
|
1
|
475
|
my ($self, $app, $conf) = @_; |
34
|
9
|
|
66
|
|
|
61
|
my $log = $conf->{log} // $app->log->handle; |
35
|
9
|
|
|
|
|
148
|
my ($pkg, $f, $l) = caller 2; # :-/ |
36
|
|
|
|
|
|
|
|
37
|
9
|
50
|
|
|
|
51
|
unless ($log) { # somebody cleared $app->log->handle? |
38
|
|
|
|
|
|
|
# Log a warning nevertheless - there might be an event handler. |
39
|
0
|
|
|
|
|
0
|
$app->log->warn(__PACKAGE__ . ': Log handle is not defined'); |
40
|
0
|
|
|
|
|
0
|
return; |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
|
43
|
9
|
|
100
|
|
|
56
|
my $reftype = reftype $log // ''; |
44
|
9
|
|
|
|
|
24
|
my $logger; |
45
|
|
|
|
|
|
|
|
46
|
9
|
100
|
66
|
|
|
77
|
if ($reftype eq 'GLOB') { |
|
|
100
|
66
|
|
|
|
|
|
|
100
|
33
|
|
|
|
|
|
|
50
|
|
|
|
|
|
47
|
4
|
|
|
|
|
39
|
eval { $log->autoflush(1) }; |
|
4
|
|
|
|
|
43
|
|
48
|
4
|
|
|
31
|
|
342
|
$logger = sub { $log->print($_[0]) }; |
|
31
|
|
|
|
|
953
|
|
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
elsif (blessed($log) and my $l = $log->can('print') || $log->can('info')) { |
51
|
2
|
|
|
13
|
|
35
|
$logger = sub { $l->($log, $_[0]) }; |
|
13
|
|
|
|
|
59
|
|
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
elsif ($reftype eq 'CODE') { |
54
|
2
|
|
|
|
|
25
|
$logger = $log; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
elsif (defined $log and not ref $log) { |
57
|
1
|
50
|
|
|
|
23
|
File::Spec->file_name_is_absolute($log) |
58
|
|
|
|
|
|
|
or $log = $app->home->rel_file($log); |
59
|
|
|
|
|
|
|
|
60
|
1
|
50
|
|
|
|
8
|
my $fh = IO::File->new($log, '>>') |
61
|
|
|
|
|
|
|
or die <<""; |
62
|
|
|
|
|
|
|
Can't open log file "$log": $! at $f line $l. |
63
|
|
|
|
|
|
|
|
64
|
1
|
|
|
|
|
349
|
$fh->autoflush(1); |
65
|
1
|
|
|
7
|
|
63
|
$logger = sub { $fh->print($_[0]) }; |
|
7
|
|
|
|
|
40
|
|
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
else { |
68
|
0
|
|
|
|
|
0
|
$app->log->error(__PACKAGE__ . ': not a valid "log" value'); |
69
|
0
|
|
|
|
|
0
|
return; |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
9
|
50
|
|
|
|
43
|
if ($conf->{uname_helper}) { |
73
|
0
|
|
|
|
|
0
|
warn <<""; |
74
|
|
|
|
|
|
|
uname_helper is DEPRECATED in favor of \$c->req->env->{REMOTE_USER} at $f line $l. |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
|
77
|
0
|
|
|
|
|
0
|
my $helper_name = $conf->{uname_helper}; |
78
|
|
|
|
|
|
|
|
79
|
0
|
0
|
|
|
|
0
|
$helper_name = 'set_username' if $helper_name !~ /^[\_A-za-z]\w*$/; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
$app->helper( |
82
|
0
|
|
|
0
|
|
0
|
$helper_name => sub { $_[0]->req->env->{REMOTE_USER} = $_[1] } |
83
|
0
|
|
|
|
|
0
|
); |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
9
|
|
|
|
|
26
|
my @handler; |
87
|
|
|
|
|
|
|
my $strftime = sub { |
88
|
69
|
|
|
69
|
|
339
|
my ($fmt, @time) = @_; |
89
|
69
|
50
|
|
|
|
212
|
$fmt =~ s/%z/$TZOFFSET/g if $TZOFFSET; |
90
|
69
|
50
|
|
|
|
176
|
$fmt =~ s/%s/time()/ge if $NOEPOCHSECS; |
|
0
|
|
|
|
|
0
|
|
91
|
69
|
|
|
|
|
389
|
my $old_locale = setlocale(LC_ALL); |
92
|
69
|
|
|
|
|
898
|
setlocale(LC_ALL, 'C'); |
93
|
69
|
|
|
|
|
2142
|
my $out = strftime($fmt, @time); |
94
|
69
|
|
|
|
|
731
|
setlocale(LC_ALL, $old_locale); |
95
|
69
|
|
|
|
|
577
|
return $out; |
96
|
9
|
|
|
|
|
41
|
}; |
97
|
9
|
|
66
|
|
|
94
|
my $format = $FORMATS{$conf->{format} // $DEFAULT_FORMAT}; |
98
|
9
|
|
|
|
|
20
|
my $safe_re; |
99
|
|
|
|
|
|
|
|
100
|
9
|
100
|
|
|
|
27
|
if ($format) { |
101
|
|
|
|
|
|
|
# Apache default log formats don't quote username, which might |
102
|
|
|
|
|
|
|
# have spaces. |
103
|
6
|
|
|
|
|
46
|
$safe_re = qr/([^[:print:]]|\s)/; |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
else { |
106
|
|
|
|
|
|
|
# For custom log format appropriate quoting is the user's responsibility. |
107
|
3
|
|
|
|
|
7
|
$format = $conf->{format}; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# each handler is called with following parameters: |
111
|
|
|
|
|
|
|
# 0: $tx, 1: $tx->req, 2: $tx->res, 3: $tx->req->url, |
112
|
|
|
|
|
|
|
# 4: $request_start_time, 5: $process_time, 6: $bytes_in, 7: $bytes_out |
113
|
|
|
|
|
|
|
# 8: HTTP request start line |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
my $block_handler = sub { |
116
|
22
|
|
|
22
|
|
70
|
my ($block, $type) = @_; |
117
|
|
|
|
|
|
|
|
118
|
104
|
|
100
|
|
|
1317
|
return sub { _safe($_[1]->headers->header($block) // '-') } |
119
|
22
|
100
|
|
|
|
91
|
if $type eq 'i'; |
120
|
|
|
|
|
|
|
|
121
|
18
|
|
50
|
|
|
252
|
return sub { $_[2]->headers->header($block) // '-' } |
122
|
7
|
100
|
|
|
|
19
|
if $type eq 'o'; |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
return sub { |
125
|
24
|
50
|
|
|
|
157
|
return $_[4][0] |
126
|
|
|
|
|
|
|
if $block eq 'sec'; |
127
|
24
|
50
|
|
|
|
48
|
return sprintf "%u%03u", $_[4][0], int($_[4][1] / 1000) |
128
|
|
|
|
|
|
|
if $block eq 'msec'; |
129
|
24
|
50
|
|
|
|
54
|
return sprintf "%u%06u", @{$_[4]} |
|
0
|
|
|
|
|
0
|
|
130
|
|
|
|
|
|
|
if $block eq 'usec'; |
131
|
24
|
100
|
|
|
|
86
|
return sprintf('%03u', $_[4][1] / 1000) |
132
|
|
|
|
|
|
|
if $block eq 'msec_frac'; |
133
|
18
|
50
|
|
|
|
38
|
return sprintf('%06u', $_[4][1]) |
134
|
|
|
|
|
|
|
if $block eq 'usec_frac'; |
135
|
18
|
|
|
|
|
388
|
return $strftime->($block, localtime($_[4][0])); |
136
|
|
|
|
|
|
|
} |
137
|
4
|
50
|
|
|
|
17
|
if $type eq 't'; |
138
|
|
|
|
|
|
|
|
139
|
0
|
|
0
|
|
|
0
|
return sub { _safe($_[1]->cookie($block // '')) } |
140
|
0
|
0
|
|
|
|
0
|
if $type eq 'C'; |
141
|
|
|
|
|
|
|
|
142
|
0
|
|
0
|
|
|
0
|
return sub { _safe($_[1]->env->{$block // ''}) } |
143
|
0
|
0
|
|
|
|
0
|
if $type eq 'e'; |
144
|
|
|
|
|
|
|
|
145
|
0
|
|
|
|
|
0
|
$app->log->error("{$block}$type not supported"); |
146
|
|
|
|
|
|
|
|
147
|
0
|
|
|
|
|
0
|
return '-'; |
148
|
9
|
|
|
|
|
42
|
}; |
149
|
|
|
|
|
|
|
|
150
|
9
|
50
|
|
14
|
|
29
|
my $servername_cb = sub { $_[3]->base->host || '-' }; |
|
14
|
|
|
|
|
181
|
|
151
|
9
|
50
|
|
58
|
|
25
|
my $remoteaddr_cb = sub { $_[0]->remote_address || '-' }; |
|
58
|
|
|
|
|
206
|
|
152
|
|
|
|
|
|
|
my %char_handler = ( |
153
|
|
|
|
|
|
|
'%' => '%', |
154
|
|
|
|
|
|
|
a => $remoteaddr_cb, |
155
|
7
|
|
50
|
7
|
|
144
|
A => sub { $_[0]->local_address // '-' }, |
156
|
|
|
|
|
|
|
b => sub { |
157
|
60
|
100
|
100
|
60
|
|
709
|
$_[7] && ($_[7] - $_[2]->header_size - $_[2]->start_line_size) || '-' |
158
|
|
|
|
|
|
|
}, |
159
|
|
|
|
|
|
|
B => sub { |
160
|
16
|
100
|
|
16
|
|
438
|
$_[7] ? $_[7] - $_[2]->header_size - $_[2]->start_line_size : '0' |
161
|
|
|
|
|
|
|
}, |
162
|
7
|
|
|
7
|
|
152
|
D => sub { int($_[5] * 1000000) }, |
163
|
|
|
|
|
|
|
h => $remoteaddr_cb, |
164
|
7
|
|
|
7
|
|
110
|
H => sub { 'HTTP/' . $_[1]->version }, |
165
|
19
|
|
|
19
|
|
222
|
I => sub { $_[6] }, |
166
|
|
|
|
|
|
|
l => '-', |
167
|
7
|
|
|
7
|
|
64
|
m => sub { $_[1]->method }, |
168
|
19
|
|
|
19
|
|
220
|
O => sub { $_[7] }, |
169
|
7
|
|
|
7
|
|
54
|
p => sub { $_[0]->local_port }, |
170
|
7
|
|
|
7
|
|
57
|
P => sub { $$ }, |
171
|
|
|
|
|
|
|
q => sub { |
172
|
7
|
100
|
|
7
|
|
27
|
my $s = $_[3]->query->to_string or return ''; |
173
|
2
|
|
|
|
|
564
|
return '?' . $s; |
174
|
|
|
|
|
|
|
}, |
175
|
51
|
|
|
51
|
|
380
|
r => sub { $_[8] }, |
176
|
66
|
|
100
|
66
|
|
290
|
s => sub { $_[2]->code // '-' }, |
177
|
|
|
|
|
|
|
t => sub { |
178
|
51
|
|
|
51
|
|
1709
|
$strftime->('[%d/%b/%Y:%H:%M:%S %z]', localtime($_[4][0])) |
179
|
|
|
|
|
|
|
}, |
180
|
7
|
|
|
7
|
|
33
|
T => sub { int $_[5] }, |
181
|
|
|
|
|
|
|
u => sub { |
182
|
51
|
|
|
51
|
|
1053
|
my $env = $_[1]->env; |
183
|
|
|
|
|
|
|
my $user = |
184
|
|
|
|
|
|
|
exists($env->{REMOTE_USER}) ? |
185
|
|
|
|
|
|
|
length($env->{REMOTE_USER} // '') ? |
186
|
51
|
50
|
50
|
|
|
576
|
$env->{REMOTE_USER} : '-' : |
|
|
100
|
100
|
|
|
|
|
187
|
|
|
|
|
|
|
(split ':', $_[3]->base->userinfo || '-:')[0]; |
188
|
|
|
|
|
|
|
|
189
|
51
|
|
|
|
|
654
|
return _safe($user, $safe_re) |
190
|
|
|
|
|
|
|
}, |
191
|
7
|
|
|
7
|
|
29
|
U => sub { $_[3]->path }, |
192
|
9
|
|
|
|
|
270
|
v => $servername_cb, |
193
|
|
|
|
|
|
|
V => $servername_cb, |
194
|
|
|
|
|
|
|
); |
195
|
|
|
|
|
|
|
|
196
|
9
|
50
|
|
|
|
37
|
if ($conf->{hostname_lookups}) { |
197
|
|
|
|
|
|
|
$char_handler{h} = sub { |
198
|
0
|
0
|
|
0
|
|
0
|
my $ip = $_[0]->remote_address or return '-'; |
199
|
0
|
|
|
|
|
0
|
return gethostbyaddr(inet_aton($ip), AF_INET); |
200
|
0
|
|
|
|
|
0
|
}; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
my $char_handler = sub { |
204
|
70
|
|
|
70
|
|
128
|
my $char = shift; |
205
|
70
|
|
|
|
|
124
|
my $cb = $char_handler{$char}; |
206
|
|
|
|
|
|
|
|
207
|
70
|
50
|
|
|
|
201
|
return $char_handler{$char} if $char_handler{$char}; |
208
|
|
|
|
|
|
|
|
209
|
0
|
|
|
|
|
0
|
$app->log->error("\%$char not supported."); |
210
|
|
|
|
|
|
|
|
211
|
0
|
|
|
|
|
0
|
return '-'; |
212
|
9
|
|
|
|
|
34
|
}; |
213
|
|
|
|
|
|
|
|
214
|
9
|
|
|
|
|
104
|
$format =~ s~ |
215
|
|
|
|
|
|
|
(?: |
216
|
|
|
|
|
|
|
\%\{(.+?)\}([a-z]) | |
217
|
|
|
|
|
|
|
\%(?:[<>])?([a-zA-Z\%]) |
218
|
|
|
|
|
|
|
) |
219
|
|
|
|
|
|
|
~ |
220
|
92
|
100
|
|
|
|
300
|
push @handler, $1 ? $block_handler->($1, $2) : $char_handler->($3); |
221
|
92
|
|
|
|
|
364
|
'%s'; |
222
|
|
|
|
|
|
|
~egx; |
223
|
|
|
|
|
|
|
|
224
|
9
|
|
|
|
|
44
|
chomp $format; |
225
|
9
|
|
33
|
|
|
80
|
$format .= $conf->{lf} // $/ // "\n"; |
|
|
|
50
|
|
|
|
|
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
$app->hook(after_build_tx => sub { |
228
|
66
|
|
|
66
|
|
421366
|
my $tx = $_[0]; |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
$tx->on(connection => sub { |
231
|
66
|
|
|
|
|
2272
|
my ($tx, $connection) = @_; |
232
|
66
|
|
|
|
|
134
|
my $bcr = my $bcw = 0; |
233
|
66
|
|
|
|
|
115
|
my $sl; |
234
|
66
|
|
|
|
|
265
|
my $t = [gettimeofday]; |
235
|
66
|
|
|
|
|
298
|
my $s = Mojo::IOLoop->stream($connection); |
236
|
|
|
|
|
|
|
my $r = $s->on(read => sub { |
237
|
|
|
|
|
|
|
# get the unmodified HTTP request start line |
238
|
971
|
|
66
|
|
|
11437517
|
$sl //= substr($_[1], 0, index($_[1], "\r\n")); |
239
|
971
|
|
|
|
|
2182
|
$bcr += length $_[1]; |
240
|
66
|
|
|
|
|
1101
|
}); |
241
|
66
|
|
|
|
|
514
|
my $w = $s->on(write => sub { $bcw += length $_[1] }); |
|
81
|
|
|
|
|
2038252
|
|
242
|
|
|
|
|
|
|
|
243
|
66
|
|
|
|
|
504
|
weaken $s; |
244
|
66
|
|
|
|
|
204
|
weaken $r; |
245
|
66
|
|
|
|
|
165
|
weaken $w; |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
$tx->on(finish => sub { |
248
|
66
|
|
|
|
|
30109396
|
my $tx = shift; |
249
|
66
|
|
|
|
|
261
|
my $dt = tv_interval($t); |
250
|
|
|
|
|
|
|
|
251
|
66
|
|
|
|
|
1219
|
$s->unsubscribe(read => $r); |
252
|
66
|
|
|
|
|
1522
|
$s->unsubscribe(write => $w); |
253
|
66
|
|
|
|
|
1202
|
$logger->(_log($tx, $format, \@handler, $t, $dt, $bcr, $bcw, $sl)); |
254
|
66
|
|
|
|
|
341
|
}); |
255
|
66
|
|
|
|
|
626
|
}); |
256
|
9
|
|
|
|
|
114
|
}); |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
sub _log { |
260
|
66
|
|
|
66
|
|
221
|
my ($tx, $format, $handler) = (shift, shift, shift); |
261
|
66
|
|
|
|
|
219
|
my $req = $tx->req; |
262
|
66
|
|
|
|
|
410
|
my @args = ($tx, $req, $tx->res, $req->url, @_); |
263
|
|
|
|
|
|
|
|
264
|
66
|
100
|
50
|
|
|
788
|
sprintf $format, map(ref() ? ($_->(@args))[0] // '' : $_, @$handler); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub _safe { |
268
|
155
|
|
|
155
|
|
1963
|
my $string = shift; |
269
|
155
|
|
66
|
|
|
706
|
my $re = shift // qr/([^[:print:]])/; |
270
|
|
|
|
|
|
|
|
271
|
155
|
50
|
|
|
|
1015
|
$string =~ s/$re/'\x' . unpack('H*', $1)/eg |
|
16
|
|
|
|
|
131
|
|
272
|
|
|
|
|
|
|
if defined $string; |
273
|
|
|
|
|
|
|
|
274
|
155
|
|
|
|
|
1042
|
return $string; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
1; |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
__END__ |