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