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