Add pod for the external method 'psgienv'
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Log.pm
1 package Catalyst::Log;
2
3 use Moose;
4 with 'MooseX::Emulate::Class::Accessor::Fast';
5
6 use Data::Dump;
7 use Class::MOP ();
8 use Carp qw/ cluck /;
9
10 our %LEVELS = (); # Levels stored as bit field, ergo debug = 1, warn = 2 etc
11 our %LEVEL_MATCH = (); # Stored as additive, thus debug = 31, warn = 30 etc
12
13 has level => (is => 'rw');
14 has _body => (is => 'rw');
15 has abort => (is => 'rw');
16 has _psgi_logger => (is => 'rw', predicate => '_has_psgi_logger', clearer => '_clear_psgi_logger');
17 has _psgi_errors => (is => 'rw', predicate => '_has_psgi_errors', clearer => '_clear_psgi_errors');
18
19 sub clear_psgi {
20     my $self = shift;
21     $self->_clear_psgi_logger;
22     $self->_clear_psgi_errors;
23 }
24
25 sub psgienv {
26     my ($self, $env) = @_;
27
28     $self->_psgi_logger($env->{'psgix.logger'}) if $env->{'psgix.logger'};
29     $self->_psgi_errors($env->{'psgi.errors'}) if $env->{'psgi.errors'};
30 }
31
32
33 {
34     my @levels = qw[ debug info warn error fatal ];
35
36     my $meta = Class::MOP::get_metaclass_by_name(__PACKAGE__);
37     my $summed_level = 0;
38     for ( my $i = $#levels ; $i >= 0 ; $i-- ) {
39
40         my $name  = $levels[$i];
41
42         my $level = 1 << $i;
43         $summed_level |= $level;
44
45         $LEVELS{$name} = $level;
46         $LEVEL_MATCH{$name} = $summed_level;
47
48        $meta->add_method($name, sub {
49             my $self = shift;
50
51             if ( $self->level & $level ) {
52                 $self->_log( $name, @_ );
53             }
54         });
55
56         $meta->add_method("is_$name", sub {
57             my $self = shift;
58             return $self->level & $level;
59         });;
60     }
61 }
62
63 around new => sub {
64     my $orig = shift;
65     my $class = shift;
66     my $self = $class->$orig;
67
68     $self->levels( scalar(@_) ? @_ : keys %LEVELS );
69
70     return $self;
71 };
72
73 sub levels {
74     my ( $self, @levels ) = @_;
75     $self->level(0);
76     $self->enable(@levels);
77 }
78
79 sub enable {
80     my ( $self, @levels ) = @_;
81     my $level = $self->level;
82     for(map { $LEVEL_MATCH{$_} } @levels){
83       $level |= $_;
84     }
85     $self->level($level);
86 }
87
88 sub disable {
89     my ( $self, @levels ) = @_;
90     my $level = $self->level;
91     for(map { $LEVELS{$_} } @levels){
92       $level &= ~$_;
93     }
94     $self->level($level);
95 }
96
97 our $HAS_DUMPED;
98 sub _dump {
99     my $self = shift;
100     unless ($HAS_DUMPED++) {
101         cluck("Catalyst::Log::_dump is deprecated and will be removed. Please change to using your own Dumper.\n");
102     }
103     $self->info( Data::Dump::dump(@_) );
104 }
105
106 sub _log {
107     my $self    = shift;
108     my $level   = shift;
109     my $message = join( "\n", @_ );
110     if ($self->can('_has_psgi_logger') and $self->_has_psgi_logger) {
111         $self->_psgi_logger->({
112                 level => $level,
113                 message => $message,
114             });
115     } else {
116         $message .= "\n" unless $message =~ /\n$/;
117         my $body = $self->_body;
118         $body .= sprintf( "[%s] %s", $level, $message );
119         $self->_body($body);
120     }
121 }
122
123 sub _flush {
124     my $self = shift;
125     if ( $self->abort || !$self->_body ) {
126         $self->abort(undef);
127     }
128     else {
129         $self->_send_to_log( $self->_body );
130     }
131     $self->_body(undef);
132 }
133
134 sub _send_to_log {
135     my $self = shift;
136     if ($self->can('_has_psgi_errors') and $self->_has_psgi_errors) {
137         $self->_psgi_errors->print(@_);
138     } else {
139         print STDERR @_;
140     }
141 }
142
143 # 5.7 compat code.
144 # Alias _body to body, add a before modifier to warn..
145 my $meta = __PACKAGE__->meta; # Calling meta method here fine as we happen at compile time.
146 $meta->add_method('body', $meta->get_method('_body'));
147 my %package_hash; # Only warn once per method, per package.
148                   # I haven't provided a way to disable them, patches welcome.
149 $meta->add_before_method_modifier('body', sub {
150     my $class = blessed(shift);
151     $package_hash{$class}++ || do {
152         warn("Class $class is calling the deprecated method Catalyst::Log->body method,\n"
153             . "this will be removed in Catalyst 5.81");
154     };
155 });
156 # End 5.70 backwards compatibility hacks.
157
158 no Moose;
159 __PACKAGE__->meta->make_immutable(inline_constructor => 0);
160
161 1;
162
163 __END__
164
165 =head1 NAME
166
167 Catalyst::Log - Catalyst Log Class
168
169 =head1 SYNOPSIS
170
171     $log = $c->log;
172     $log->debug($message);
173     $log->info($message);
174     $log->warn($message);
175     $log->error($message);
176     $log->fatal($message);
177
178     if ( $log->is_debug ) {
179          # expensive debugging
180     }
181
182
183 See L<Catalyst>.
184
185 =head1 DESCRIPTION
186
187 This module provides the default, simple logging functionality for Catalyst.
188 If you want something different set C<< $c->log >> in your application module,
189 e.g.:
190
191     $c->log( MyLogger->new );
192
193 Your logging object is expected to provide the interface described here.
194 Good alternatives to consider are Log::Log4Perl and Log::Dispatch.
195
196 If you want to be able to log arbitrary warnings, you can do something along
197 the lines of
198
199     $SIG{__WARN__} = sub { MyApp->log->warn(@_); };
200
201 however this is (a) global, (b) hairy and (c) may have unexpected side effects.
202 Don't say we didn't warn you.
203
204 =head1 LOG LEVELS
205
206 =head2 debug
207
208     $log->is_debug;
209     $log->debug($message);
210
211 =head2 info
212
213     $log->is_info;
214     $log->info($message);
215
216 =head2 warn
217
218     $log->is_warn;
219     $log->warn($message);
220
221 =head2 error
222
223     $log->is_error;
224     $log->error($message);
225
226 =head2 fatal
227
228     $log->is_fatal;
229     $log->fatal($message);
230
231 =head1 METHODS
232
233 =head2 new
234
235 Constructor. Defaults to enable all levels unless levels are provided in
236 arguments.
237
238     $log = Catalyst::Log->new;
239     $log = Catalyst::Log->new( 'warn', 'error' );
240
241 =head2 level
242
243 Contains a bitmask of the currently set log levels.
244
245 =head2 levels
246
247 Set log levels
248
249     $log->levels( 'warn', 'error', 'fatal' );
250
251 =head2 enable
252
253 Enable log levels
254
255     $log->enable( 'warn', 'error' );
256
257 =head2 disable
258
259 Disable log levels
260
261     $log->disable( 'warn', 'error' );
262
263 =head2 is_debug
264
265 =head2 is_error
266
267 =head2 is_fatal
268
269 =head2 is_info
270
271 =head2 is_warn
272
273 Is the log level active?
274
275 =head2 abort
276
277 Should Catalyst emit logs for this request? Will be reset at the end of
278 each request.
279
280 *NOTE* This method is not compatible with other log apis, so if you plan
281 to use Log4Perl or another logger, you should call it like this:
282
283     $c->log->abort(1) if $c->log->can('abort');
284
285 =head2 _send_to_log
286
287  $log->_send_to_log( @messages );
288
289 This protected method is what actually sends the log information to STDERR.
290 You may subclass this module and override this method to get finer control
291 over the log output.
292
293 =head2 psgienv $env
294
295     $log->psgienv($env);
296
297 NOTE: This is not meant for public consumption.
298
299 Set the PSGI environment for this request. This ensures logs will be sent to
300 the right place. If the environment has a C<psgix.logger>, it will be used. If
301 not, we will send logs to C<psgi.errors> if that exists. As a last fallback, we
302 will send to STDERR as before.
303
304 =head2 meta
305
306 =head1 SEE ALSO
307
308 L<Catalyst>.
309
310 =head1 AUTHORS
311
312 Catalyst Contributors, see Catalyst.pm
313
314 =head1 COPYRIGHT
315
316 This library is free software. You can redistribute it and/or modify
317 it under the same terms as Perl itself.
318
319 =cut
320
321 __PACKAGE__->meta->make_immutable;
322
323 1;