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