release 0.00303
[p5sagit/Log-Contextual.git] / lib / Log / Contextual.pm
CommitLineData
0daa11f3 1package Log::Contextual;
2
a2777569 3use strict;
4use warnings;
2033c911 5
e4ca04fd 6our $VERSION = '0.00303';
2033c911 7
8require Exporter;
f11f9542 9use Data::Dumper::Concise;
5b094c87 10use Scalar::Util 'blessed';
2033c911 11
a2777569 12BEGIN { our @ISA = qw(Exporter) }
2033c911 13
9b8e24d5 14my @dlog = (qw(
f11f9542 15 Dlog_debug DlogS_debug
16 Dlog_trace DlogS_trace
17 Dlog_warn DlogS_warn
18 Dlog_info DlogS_info
19 Dlog_error DlogS_error
20 Dlog_fatal DlogS_fatal
9b8e24d5 21 ));
f11f9542 22
9b8e24d5 23my @log = (qw(
9061df76 24 log_debug logS_debug
25 log_trace logS_trace
26 log_warn logS_warn
27 log_info logS_info
28 log_error logS_error
29 log_fatal logS_fatal
9b8e24d5 30 ));
f11f9542 31
b144ba01 32eval {
33 require Log::Log4perl;
34 die if $Log::Log4perl::VERSION < 1.29;
35 Log::Log4perl->wrapper_register(__PACKAGE__)
36};
37
a2777569 38our @EXPORT_OK = (
f11f9542 39 @dlog, @log,
9b8e24d5 40 qw( set_logger with_logger )
f11f9542 41);
42
a2777569 43our %EXPORT_TAGS = (
f11f9542 44 dlog => \@dlog,
45 log => \@log,
3dc9bd3c 46 all => [@dlog, @log],
f11f9542 47);
48
49sub import {
a2777569 50 my $package = shift;
f11f9542 51 die 'Log::Contextual does not have a default import list'
a2777569 52 unless @_;
53
54 for my $idx ( 0 .. $#_ ) {
3ccc9c47 55 my $val = $_[$idx];
56 if ( defined $val && $val eq '-logger' ) {
a2777569 57 set_logger($_[$idx + 1]);
58 splice @_, $idx, 2;
e2b4b29c 59 } elsif ( defined $val && $val eq '-package_logger' ) {
60 _set_package_logger_for(scalar caller, $_[$idx + 1]);
61 splice @_, $idx, 2;
3ccc9c47 62 } elsif ( defined $val && $val eq '-default_logger' ) {
63 _set_default_logger_for(scalar caller, $_[$idx + 1]);
06e908c3 64 splice @_, $idx, 2;
a2777569 65 }
66 }
67 $package->export_to_level(1, $package, @_);
f11f9542 68}
2033c911 69
7cec609c 70our $Get_Logger;
5d8f2b84 71our %Default_Logger;
e2b4b29c 72our %Package_Logger;
5d8f2b84 73
3ccc9c47 74sub _set_default_logger_for {
75 my $logger = $_[1];
76 if(ref $logger ne 'CODE') {
77 die 'logger was not a CodeRef or a logger object. Please try again.'
78 unless blessed($logger);
79 $logger = do { my $l = $logger; sub { $l } }
80 }
81 $Default_Logger{$_[0]} = $logger
82}
06e908c3 83
e2b4b29c 84sub _set_package_logger_for {
85 my $logger = $_[1];
86 if(ref $logger ne 'CODE') {
87 die 'logger was not a CodeRef or a logger object. Please try again.'
88 unless blessed($logger);
89 $logger = do { my $l = $logger; sub { $l } }
90 }
91 $Package_Logger{$_[0]} = $logger
92}
93
5d8f2b84 94sub _get_logger($) {
95 my $package = shift;
96 (
e2b4b29c 97 $Package_Logger{$package} ||
5d8f2b84 98 $Get_Logger ||
99 $Default_Logger{$package} ||
100 die q( no logger set! you can't try to log something without a logger! )
06e908c3 101 )->($package);
5d8f2b84 102}
7cec609c 103
8dc5a747 104sub set_logger {
105 my $logger = $_[0];
5b094c87 106 if(ref $logger ne 'CODE') {
107 die 'logger was not a CodeRef or a logger object. Please try again.'
108 unless blessed($logger);
109 $logger = do { my $l = $logger; sub { $l } }
110 }
5d8f2b84 111
112 warn 'set_logger (or -logger) called more than once! This is a bad idea!'
113 if $Get_Logger;
8dc5a747 114 $Get_Logger = $logger;
7cec609c 115}
116
98833ffb 117sub with_logger {
118 my $logger = $_[0];
5b094c87 119 if(ref $logger ne 'CODE') {
120 die 'logger was not a CodeRef or a logger object. Please try again.'
121 unless blessed($logger);
122 $logger = do { my $l = $logger; sub { $l } }
123 }
98833ffb 124 local $Get_Logger = $logger;
80c3e48b 125 $_[1]->();
2daff231 126}
127
d11de6ae 128sub _do_log {
129 my $level = shift;
130 my $logger = shift;
131 my $code = shift;
132 my @values = @_;
133
d11de6ae 134 $logger->$level($code->(@_))
135 if $logger->${\"is_$level"};
136 @values
137}
138
139sub _do_logS {
140 my $level = shift;
141 my $logger = shift;
142 my $code = shift;
143 my $value = shift;
144
d11de6ae 145 $logger->$level($code->($value))
146 if $logger->${\"is_$level"};
709d11fe 147 $value
148}
149
d11de6ae 150sub log_trace (&@) { _do_log( trace => _get_logger( caller ), shift @_, @_) }
151sub log_debug (&@) { _do_log( debug => _get_logger( caller ), shift @_, @_) }
152sub log_info (&@) { _do_log( info => _get_logger( caller ), shift @_, @_) }
153sub log_warn (&@) { _do_log( warn => _get_logger( caller ), shift @_, @_) }
154sub log_error (&@) { _do_log( error => _get_logger( caller ), shift @_, @_) }
155sub log_fatal (&@) { _do_log( fatal => _get_logger( caller ), shift @_, @_) }
709d11fe 156
d11de6ae 157sub logS_trace (&$) { _do_logS( trace => _get_logger( caller ), $_[0], $_[1]) }
158sub logS_debug (&$) { _do_logS( debug => _get_logger( caller ), $_[0], $_[1]) }
159sub logS_info (&$) { _do_logS( info => _get_logger( caller ), $_[0], $_[1]) }
160sub logS_warn (&$) { _do_logS( warn => _get_logger( caller ), $_[0], $_[1]) }
161sub logS_error (&$) { _do_logS( error => _get_logger( caller ), $_[0], $_[1]) }
162sub logS_fatal (&$) { _do_logS( fatal => _get_logger( caller ), $_[0], $_[1]) }
709d11fe 163
f11f9542 164
165sub Dlog_trace (&@) {
166 my $code = shift;
d11de6ae 167 local $_ = (@_?Data::Dumper::Concise::Dumper @_:'()');
168 return _do_log( trace => _get_logger( caller ), $code, @_ );
f11f9542 169}
170
a4273dbf 171sub Dlog_debug (&@) {
172 my $code = shift;
d11de6ae 173 local $_ = (@_?Data::Dumper::Concise::Dumper @_:'()');
174 return _do_log( debug => _get_logger( caller ), $code, @_ );
a4273dbf 175}
176
177sub Dlog_info (&@) {
178 my $code = shift;
d11de6ae 179 local $_ = (@_?Data::Dumper::Concise::Dumper @_:'()');
180 return _do_log( info => _get_logger( caller ), $code, @_ );
a4273dbf 181}
182
183sub Dlog_warn (&@) {
184 my $code = shift;
d11de6ae 185 local $_ = (@_?Data::Dumper::Concise::Dumper @_:'()');
186 return _do_log( warn => _get_logger( caller ), $code, @_ );
a4273dbf 187}
188
189sub Dlog_error (&@) {
190 my $code = shift;
d11de6ae 191 local $_ = (@_?Data::Dumper::Concise::Dumper @_:'()');
192 return _do_log( error => _get_logger( caller ), $code, @_ );
a4273dbf 193}
194
195sub Dlog_fatal (&@) {
196 my $code = shift;
d11de6ae 197 local $_ = (@_?Data::Dumper::Concise::Dumper @_:'()');
198 return _do_log( fatal => _get_logger( caller ), $code, @_ );
a4273dbf 199}
200
201
f11f9542 202sub DlogS_trace (&$) {
d11de6ae 203 local $_ = Data::Dumper::Concise::Dumper $_[1];
204 _do_logS( trace => _get_logger( caller ), $_[0], $_[1] )
f11f9542 205}
206
f11f9542 207sub DlogS_debug (&$) {
d11de6ae 208 local $_ = Data::Dumper::Concise::Dumper $_[1];
209 _do_logS( debug => _get_logger( caller ), $_[0], $_[1] )
f11f9542 210}
211
f11f9542 212sub DlogS_info (&$) {
d11de6ae 213 local $_ = Data::Dumper::Concise::Dumper $_[1];
214 _do_logS( info => _get_logger( caller ), $_[0], $_[1] )
f11f9542 215}
216
f11f9542 217sub DlogS_warn (&$) {
d11de6ae 218 local $_ = Data::Dumper::Concise::Dumper $_[1];
219 _do_logS( warn => _get_logger( caller ), $_[0], $_[1] )
f11f9542 220}
221
f11f9542 222sub DlogS_error (&$) {
d11de6ae 223 local $_ = Data::Dumper::Concise::Dumper $_[1];
224 _do_logS( error => _get_logger( caller ), $_[0], $_[1] )
f11f9542 225}
226
f11f9542 227sub DlogS_fatal (&$) {
d11de6ae 228 local $_ = Data::Dumper::Concise::Dumper $_[1];
229 _do_logS( fatal => _get_logger( caller ), $_[0], $_[1] )
f11f9542 230}
231
0daa11f3 2321;
0a3750e2 233
234__END__
235
2daff231 236=head1 NAME
237
8bc568d2 238Log::Contextual - Simple logging interface with a contextual log
2daff231 239
240=head1 SYNOPSIS
241
9b8e24d5 242 use Log::Contextual qw( :log :dlog set_logger with_logger );
5b094c87 243 use Log::Contextual::SimpleLogger;
244 use Log::Log4perl ':easy';
245 Log::Log4perl->easy_init($DEBUG);
2daff231 246
2daff231 247
5b094c87 248 my $logger = Log::Log4perl->get_logger;
249
250 set_logger $logger;
2daff231 251
9b8e24d5 252 log_debug { 'program started' };
2daff231 253
254 sub foo {
9b8e24d5 255 with_logger(Log::Contextual::SimpleLogger->new({
256 levels => [qw( trace debug )]
21431192 257 }) => sub {
258 log_trace { 'foo entered' };
9b8e24d5 259 my ($foo, $bar) = Dlog_trace { "params for foo: $_" } @_;
2daff231 260 # ...
21431192 261 log_trace { 'foo left' };
9b8e24d5 262 });
2daff231 263 }
264
5b094c87 265 foo();
266
9fe4eeb3 267Beginning with version 1.008 L<Log::Dispatchouli> also works out of the box
268with C<Log::Contextual>:
269
270 use Log::Contextual qw( :log :dlog set_logger );
271 use Log::Dispatchouli;
272 my $ld = Log::Dispatchouli->new({
273 ident => 'slrtbrfst',
274 to_stderr => 1,
275 debug => 1,
276 });
277
278 set_logger $ld;
279
280 log_debug { 'program started' };
281
2daff231 282=head1 DESCRIPTION
283
3dc9bd3c 284This module is a simple interface to extensible logging. It is bundled with a
285really basic logger, L<Log::Contextual::SimpleLogger>, but in general you
286should use a real logger instead of that. For something more serious but not
9fe4eeb3 287overly complicated, try L<Log::Dispatchouli> (see L</SYNOPSIS> for example.)
3dc9bd3c 288
289=head1 OPTIONS
290
c154d18a 291=head2 -logger
292
3dc9bd3c 293When you import this module you may use C<-logger> as a shortcut for
294L<set_logger>, for example:
295
296 use Log::Contextual::SimpleLogger;
9b8e24d5 297 use Log::Contextual qw( :dlog ),
298 -logger => Log::Contextual::SimpleLogger->new({ levels => [qw( debug )] });
3dc9bd3c 299
300sometimes you might want to have the logger handy for other stuff, in which
301case you might try something like the following:
302
303 my $var_log;
304 BEGIN { $var_log = VarLogger->new }
9b8e24d5 305 use Log::Contextual qw( :dlog ), -logger => $var_log;
3dc9bd3c 306
e2b4b29c 307=head2 -package_logger
308
309The C<-package_logger> import option is similar to the C<-logger> import option
310except C<-package_logger> sets the the logger for the current package.
311
312Unlike L</-default_logger>, C<-package_logger> cannot be overridden with
313L</set_logger>.
314
315 package My::Package;
316 use Log::Contextual::SimpleLogger;
317 use Log::Contextual qw( :log ),
318 -package_logger => Log::Contextual::WarnLogger->new({
319 env_prefix => 'MY_PACKAGE'
320 });
321
322If you are interested in using this package for a module you are putting on
323CPAN we recommend L<Log::Contextual::WarnLogger> for your package logger.
324
c154d18a 325=head2 -default_logger
326
327The C<-default_logger> import option is similar to the C<-logger> import option
e2b4b29c 328except C<-default_logger> sets the the B<default> logger for the current package.
c154d18a 329
330Basically it sets the logger to be used if C<set_logger> is never called; so
331
332 package My::Package;
333 use Log::Contextual::SimpleLogger;
334 use Log::Contextual qw( :log ),
335 -default_logger => Log::Contextual::WarnLogger->new({
ae59bbe3 336 env_prefix => 'MY_PACKAGE'
c154d18a 337 });
338
3dc9bd3c 339=head1 A WORK IN PROGRESS
340
341This module is certainly not complete, but we will not break the interface
342lightly, so I would say it's safe to use in production code. The main result
343from that at this point is that doing:
344
345 use Log::Contextual;
346
347will die as we do not yet know what the defaults should be. If it turns out
348that nearly everyone uses the C<:log> tag and C<:dlog> is really rare, we'll
9b8e24d5 349probably make C<:log> the default. But only time and usage will tell.
2daff231 350
351=head1 FUNCTIONS
352
353=head2 set_logger
354
355 my $logger = WarnLogger->new;
21431192 356 set_logger $logger;
357
0e13e261 358Arguments: C<Ref|CodeRef $returning_logger>
2daff231 359
21431192 360C<set_logger> will just set the current logger to whatever you pass it. It
361expects a C<CodeRef>, but if you pass it something else it will wrap it in a
06e908c3 362C<CodeRef> for you. C<set_logger> is really meant only to be called from a
363top-level script. To avoid foot-shooting the function will warn if you call it
364more than once.
2daff231 365
366=head2 with_logger
367
368 my $logger = WarnLogger->new;
21431192 369 with_logger $logger => sub {
2daff231 370 if (1 == 0) {
371 log_fatal { 'Non Logical Universe Detected' };
372 } else {
373 log_info { 'All is good' };
374 }
80c3e48b 375 };
2daff231 376
0e13e261 377Arguments: C<Ref|CodeRef $returning_logger, CodeRef $to_execute>
2daff231 378
21431192 379C<with_logger> sets the logger for the scope of the C<CodeRef> C<$to_execute>.
0e13e261 380As with L</set_logger>, C<with_logger> will wrap C<$returning_logger> with a
21431192 381C<CodeRef> if needed.
2daff231 382
21431192 383=head2 log_$level
2daff231 384
0e13e261 385Import Tag: C<:log>
3dc9bd3c 386
0e13e261 387Arguments: C<CodeRef $returning_message, @args>
2daff231 388
21431192 389All of the following six functions work the same except that a different method
390is called on the underlying C<$logger> object. The basic pattern is:
2daff231 391
0e13e261 392 sub log_$level (&@) {
21431192 393 if ($logger->is_$level) {
0e13e261 394 $logger->$level(shift->(@_));
21431192 395 }
0e13e261 396 @_
21431192 397 }
2daff231 398
0e13e261 399Note that the function returns it's arguments. This can be used in a number of
400ways, but often it's convenient just for partial inspection of passthrough data
401
402 my @friends = log_trace {
403 'friends list being generated, data from first friend: ' .
404 Dumper($_[0]->TO_JSON)
405 } generate_friend_list();
406
407If you want complete inspection of passthrough data, take a look at the
408L</Dlog_$level> functions.
409
21431192 410=head3 log_trace
2daff231 411
21431192 412 log_trace { 'entered method foo with args ' join q{,}, @args };
2daff231 413
21431192 414=head3 log_debug
2daff231 415
21431192 416 log_debug { 'entered method foo' };
2daff231 417
21431192 418=head3 log_info
2daff231 419
21431192 420 log_info { 'started process foo' };
2daff231 421
21431192 422=head3 log_warn
2daff231 423
21431192 424 log_warn { 'possible misconfiguration at line 10' };
2daff231 425
21431192 426=head3 log_error
2daff231 427
21431192 428 log_error { 'non-numeric user input!' };
2daff231 429
21431192 430=head3 log_fatal
2daff231 431
432 log_fatal { '1 is never equal to 0!' };
433
0e13e261 434=head2 logS_$level
435
436Import Tag: C<:log>
437
438Arguments: C<CodeRef $returning_message, Item $arg>
439
440This is really just a special case of the L</log_$level> functions. It forces
441scalar context when that is what you need. Other than that it works exactly
442same:
443
444 my $friend = logS_trace {
445 'I only have one friend: ' . Dumper($_[0]->TO_JSON)
446 } friend();
447
448See also: L</DlogS_$level>.
449
21431192 450=head2 Dlog_$level
451
0e13e261 452Import Tag: C<:dlog>
3dc9bd3c 453
0e13e261 454Arguments: C<CodeRef $returning_message, @args>
2daff231 455
0e13e261 456All of the following six functions work the same as their L</log_$level>
9b8e24d5 457brethren, except they return what is passed into them and put the stringified
21431192 458(with L<Data::Dumper::Concise>) version of their args into C<$_>. This means
459you can do cool things like the following:
460
461 my @nicks = Dlog_debug { "names: $_" } map $_->value, $frew->names->all;
462
463and the output might look something like:
464
465 names: "fREW"
466 "fRIOUX"
467 "fROOH"
468 "fRUE"
469 "fiSMBoC"
470
471=head3 Dlog_trace
472
9b8e24d5 473 my ($foo, $bar) = Dlog_trace { "entered method foo with args: $_" } @_;
21431192 474
475=head3 Dlog_debug
476
477 Dlog_debug { "random data structure: $_" } { foo => $bar };
478
479=head3 Dlog_info
480
481 return Dlog_info { "html from method returned: $_" } "<html>...</html>";
482
483=head3 Dlog_warn
484
485 Dlog_warn { "probably invalid value: $_" } $foo;
486
487=head3 Dlog_error
488
489 Dlog_error { "non-numeric user input! ($_)" } $port;
2daff231 490
21431192 491=head3 Dlog_fatal
2daff231 492
21431192 493 Dlog_fatal { '1 is never equal to 0!' } 'ZOMG ZOMG' if 1 == 0;
2daff231 494
83b33eb5 495=head2 DlogS_$level
496
0e13e261 497Import Tag: C<:dlog>
3dc9bd3c 498
0e13e261 499Arguments: C<CodeRef $returning_message, Item $arg>
83b33eb5 500
0e13e261 501Like L</logS_$level>, these functions are a special case of L</Dlog_$level>.
502They only take a single scalar after the C<$returning_message> instead of
503slurping up (and also setting C<wantarray>) all the C<@args>
83b33eb5 504
505 my $pals_rs = DlogS_debug { "pals resultset: $_" }
506 $schema->resultset('Pals')->search({ perlers => 1 });
507
3dc9bd3c 508=head1 LOGGER INTERFACE
509
510Because this module is ultimately pretty looking glue (glittery?) with the
511awesome benefit of the Contextual part, users will often want to make their
512favorite logger work with it. The following are the methods that should be
513implemented in the logger:
514
515 is_trace
516 is_debug
517 is_info
518 is_warn
519 is_error
520 is_fatal
521 trace
522 debug
523 info
524 warn
525 error
526 fatal
527
528The first six merely need to return true if that level is enabled. The latter
529six take the results of whatever the user returned from their coderef and log
530them. For a basic example see L<Log::Contextual::SimpleLogger>.
531
2daff231 532=head1 AUTHOR
533
534frew - Arthur Axel "fREW" Schmidt <frioux@gmail.com>
535
536=head1 DESIGNER
537
538mst - Matt S. Trout <mst@shadowcat.co.uk>
539
540=head1 COPYRIGHT
541
542Copyright (c) 2010 the Log::Contextual L</AUTHOR> and L</DESIGNER> as listed
543above.
544
545=head1 LICENSE
546
547This library is free software and may be distributed under the same terms as
548Perl 5 itself.
549
550=cut
551