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