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