line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Catalyst::Plugin::AccessLog; |
2
|
|
|
|
|
|
|
# ABSTRACT: Request logging from within Catalyst |
3
|
|
|
|
|
|
|
our $VERSION = '1.10'; # VERSION |
4
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:ARODLAND'; # AUTHORITY |
5
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
900
|
use namespace::autoclean; |
|
1
|
|
|
|
|
13096
|
|
|
1
|
|
|
|
|
5
|
|
7
|
1
|
|
|
1
|
|
502
|
use Moose::Role; |
|
1
|
|
|
|
|
310819
|
|
|
1
|
|
|
|
|
7
|
|
8
|
1
|
|
|
1
|
|
4158
|
use Scalar::Util qw(reftype blessed); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
92
|
|
9
|
1
|
|
|
1
|
|
818
|
use Catalyst::Utils; |
|
1
|
|
|
|
|
67680
|
|
|
1
|
|
|
|
|
585
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
after 'setup_finalize' => sub { # Init ourselves |
12
|
|
|
|
|
|
|
my $c = shift; |
13
|
|
|
|
|
|
|
my $default_config = { |
14
|
|
|
|
|
|
|
formatter => { |
15
|
|
|
|
|
|
|
class => 'Catalyst::Plugin::AccessLog::Formatter', |
16
|
|
|
|
|
|
|
}, |
17
|
|
|
|
|
|
|
hostname_lookups => 0, |
18
|
|
|
|
|
|
|
enable_stats => 1, |
19
|
|
|
|
|
|
|
target => \*STDERR, |
20
|
|
|
|
|
|
|
}; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
my $config = $c->config->{'Plugin::AccessLog'} = Catalyst::Utils::merge_hashes( |
23
|
|
|
|
|
|
|
$default_config, |
24
|
|
|
|
|
|
|
$c->config->{'Plugin::AccessLog'} |
25
|
|
|
|
|
|
|
); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
if (!ref $config->{target}) { |
28
|
|
|
|
|
|
|
open my $output, '>>', $config->{target} or die qq[Error opening "$config->{target}" for log output]; |
29
|
|
|
|
|
|
|
select((select($output), $|=1)[0]); |
30
|
|
|
|
|
|
|
$config->{target} = $output; |
31
|
|
|
|
|
|
|
} |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
Catalyst::Utils::ensure_class_loaded( $config->{formatter}{class} ); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
}; |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
override 'use_stats' => sub { |
38
|
|
|
|
|
|
|
my ($c) = @_; |
39
|
|
|
|
|
|
|
if ($c->config->{'Plugin::AccessLog'}{enable_stats}) { |
40
|
|
|
|
|
|
|
return 1; |
41
|
|
|
|
|
|
|
} else { |
42
|
|
|
|
|
|
|
return super; |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
}; |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub access_log_write { |
47
|
0
|
|
|
0
|
0
|
|
my $c = shift; |
48
|
0
|
|
|
|
|
|
my $output = join "", @_; |
49
|
0
|
0
|
|
|
|
|
$output .= "\n" unless $output =~ /\n\Z/; |
50
|
|
|
|
|
|
|
|
51
|
0
|
|
|
|
|
|
my $target = $c->config->{'Plugin::AccessLog'}{target}; |
52
|
0
|
0
|
0
|
|
|
|
if (reftype($target) eq 'GLOB' or blessed($target) && $target->isa('IO::Handle')) { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
53
|
0
|
|
|
|
|
|
print $target $output; |
54
|
|
|
|
|
|
|
} elsif (reftype($target) eq 'CODE') { |
55
|
0
|
|
|
|
|
|
$target->($output, $c); |
56
|
|
|
|
|
|
|
} elsif ($target->can('info')) { # Logger object |
57
|
0
|
|
|
|
|
|
$target->info($output); |
58
|
|
|
|
|
|
|
} else { |
59
|
0
|
|
|
|
|
|
warn "Don't know how to log to config->{'Plugin::AccessLog'}{target}"; |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
after 'finalize' => sub { |
64
|
|
|
|
|
|
|
my $c = shift; |
65
|
|
|
|
|
|
|
my $config = $c->config->{'Plugin::AccessLog'}; |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
my %formatter_opts = %{ $config->{formatter} }; |
68
|
|
|
|
|
|
|
my $formatter_class = delete $formatter_opts{class}; |
69
|
|
|
|
|
|
|
my $formatter = $formatter_class->new( %formatter_opts ); |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
my $line = $formatter->format_line($c); |
72
|
|
|
|
|
|
|
$c->access_log_write($line); |
73
|
|
|
|
|
|
|
}; |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
1; |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=head1 DEPRECATION NOTICE |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
This module doesn't work well on Catalyst 5.9 or above, and no longer |
80
|
|
|
|
|
|
|
passes its tests. Repairing it isn't possible. Using this module for |
81
|
|
|
|
|
|
|
anything new isn't recommended; use L<Plack::Middleware::AccessLog> or log |
82
|
|
|
|
|
|
|
at the proxy layer. It remains online in support of existing users. |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
=head1 SYNOPSIS |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
Requires Catalyst 5.8 or above. |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# In lib/MyApp.pm context |
89
|
|
|
|
|
|
|
use Catalyst qw( |
90
|
|
|
|
|
|
|
ConfigLoader |
91
|
|
|
|
|
|
|
-Stats=1 |
92
|
|
|
|
|
|
|
AccessLog |
93
|
|
|
|
|
|
|
... other plugins here ... |
94
|
|
|
|
|
|
|
); |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
__PACKAGE__->config( |
97
|
|
|
|
|
|
|
'Plugin::AccessLog' => { |
98
|
|
|
|
|
|
|
formatter => { |
99
|
|
|
|
|
|
|
format => '%[time] %[remote_address] %[path] %[status] %[size]', |
100
|
|
|
|
|
|
|
time_format => '%c', |
101
|
|
|
|
|
|
|
time_zone => 'America/Chicago', |
102
|
|
|
|
|
|
|
}, |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
); |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
__PACKAGE__->setup(); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=head1 DESCRIPTION |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
This plugin isn't for "debug" logging. Instead it enables you to create |
111
|
|
|
|
|
|
|
"access logs" from within a Catalyst application instead of requiring a |
112
|
|
|
|
|
|
|
webserver to do it for you. It will work even with Catalyst debug logging |
113
|
|
|
|
|
|
|
turned off (but see C<enable_stats> below). |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
=head1 CONFIGURATION |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
All configuration is optional; by default the plugin will log to STDERR in a |
118
|
|
|
|
|
|
|
format compatible with the "Common Log Format" |
119
|
|
|
|
|
|
|
(L<http://en.wikipedia.org/wiki/Common_Log_Format>). |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=over 4 |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=item target |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
B<Default:> C<\*STDERR> |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
Where to log to. If C<target> is a filehandle or something that |
128
|
|
|
|
|
|
|
C<< isa("IO::Handle") >>, lines of logging information will be C<print>ed to |
129
|
|
|
|
|
|
|
it. If C<target> is an object with an C<info> method it's assumed to be a |
130
|
|
|
|
|
|
|
logging object (e.g. L<Log::Dispatch> or L<Log::Log4perl>) and lines will be |
131
|
|
|
|
|
|
|
passed to the C<info> method. If it's a C<CODE> ref then it will be called |
132
|
|
|
|
|
|
|
with each line of logging output. If it's an unblessed scalar it will be |
133
|
|
|
|
|
|
|
interpreted as a filename and the plugin will try to open it for append |
134
|
|
|
|
|
|
|
and write lines to it. |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=item formatter |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
B<Default:> C<< { class => "Catalyst::Plugin::AccessLog::Formatter" } >> |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
The formatter to use. Defaults to the Formatter class included in this |
141
|
|
|
|
|
|
|
distribution. This option must be a hashref. The C<class> option is taken as |
142
|
|
|
|
|
|
|
the name of the class to use as the formatter; all other keys are passed to |
143
|
|
|
|
|
|
|
that class's constructor. See L<Catalyst::Plugin::AccessLog::Formatter> for |
144
|
|
|
|
|
|
|
the keys supported by that module. |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
=item enable_stats |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
B<Default:> B<true> |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
C<Catalyst::Plugin::AccessLog> works without regard to Catalyst's debug |
151
|
|
|
|
|
|
|
logging option. However, the time-related escapes are only available if the |
152
|
|
|
|
|
|
|
C<Catalyst::Stats> statistics collection is enabled, and by default stats are |
153
|
|
|
|
|
|
|
tied to the value of the debug flag. If this option is set, stats will be |
154
|
|
|
|
|
|
|
enabled for the application regardless of the C<-Stats> or C<-Debug> flags, or |
155
|
|
|
|
|
|
|
the C<MYAPP_STATS> or C<MYAPP_DEBUG> environment variables. |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=back |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=head1 NOTES |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=head2 Logging to C<< $c->log >> |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
It is generally not recommended to write the access log to C<< $c->log >>, |
164
|
|
|
|
|
|
|
especially if static file handling is enabled. However, there might be a |
165
|
|
|
|
|
|
|
good reason to do it somewhere. If the logging target is a coderef, it will |
166
|
|
|
|
|
|
|
receive C<$c> as its second argument. You can log to C<< $c->log >> with: |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
target => sub { pop->log->info(shift) } |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
Don't store C<$c> anywhere that persists after the lifetime of the coderef |
171
|
|
|
|
|
|
|
or bad things will happen to you and everyone you know. |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=head1 SOURCE, BUGS, ETC. |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
L<http://github.com/arodland/Catalyst-Plugin-AccessLog> |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=cut |