Fix regression that caused D* subs to dumper even if the log level was off
[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, @args) = @_;
151      return _do_log( $level => _get_logger( caller ), sub {
152         local $_ = (@args?Data::Dumper::Concise::Dumper @args:'()');
153         $code->(@_)
154      }, @args );
155    };
156
157    *{"DlogS_$level"} = sub (&$) {
158      my ($code, $ref) = @_;
159      _do_logS( $level => _get_logger( caller ), sub {
160         local $_ = Data::Dumper::Concise::Dumper $ref;
161         $code->($ref)
162      }, $ref )
163    };
164 }
165
166 1;
167
168 __END__
169
170 =head1 NAME
171
172 Log::Contextual - Simple logging interface with a contextual log
173
174 =head1 SYNOPSIS
175
176  use Log::Contextual qw( :log :dlog set_logger with_logger );
177  use Log::Contextual::SimpleLogger;
178  use Log::Log4perl ':easy';
179  Log::Log4perl->easy_init($DEBUG);
180
181
182  my $logger  = Log::Log4perl->get_logger;
183
184  set_logger $logger;
185
186  log_debug { 'program started' };
187
188  sub foo {
189    with_logger(Log::Contextual::SimpleLogger->new({
190        levels => [qw( trace debug )]
191      }) => sub {
192      log_trace { 'foo entered' };
193      my ($foo, $bar) = Dlog_trace { "params for foo: $_" } @_;
194      # ...
195      log_trace { 'foo left' };
196    });
197  }
198
199  foo();
200
201 Beginning with version 1.008 L<Log::Dispatchouli> also works out of the box
202 with C<Log::Contextual>:
203
204  use Log::Contextual qw( :log :dlog set_logger );
205  use Log::Dispatchouli;
206  my $ld = Log::Dispatchouli->new({
207     ident     => 'slrtbrfst',
208     to_stderr => 1,
209     debug     => 1,
210  });
211
212  set_logger $ld;
213
214  log_debug { 'program started' };
215
216 =head1 DESCRIPTION
217
218 This module is a simple interface to extensible logging.  It is bundled with a
219 really basic logger, L<Log::Contextual::SimpleLogger>, but in general you
220 should use a real logger instead of that.  For something more serious but not
221 overly complicated, try L<Log::Dispatchouli> (see L</SYNOPSIS> for example.)
222
223 The reason for this module is to abstract your logging interface so that
224 logging is as painless as possible, while still allowing you to switch from one
225 logger to another.
226
227 =head1 OPTIONS
228
229 =head2 -logger
230
231 When you import this module you may use C<-logger> as a shortcut for
232 L<set_logger>, for example:
233
234  use Log::Contextual::SimpleLogger;
235  use Log::Contextual qw( :dlog ),
236    -logger => Log::Contextual::SimpleLogger->new({ levels => [qw( debug )] });
237
238 sometimes you might want to have the logger handy for other stuff, in which
239 case you might try something like the following:
240
241  my $var_log;
242  BEGIN { $var_log = VarLogger->new }
243  use Log::Contextual qw( :dlog ), -logger => $var_log;
244
245 =head2 -package_logger
246
247 The C<-package_logger> import option is similar to the C<-logger> import option
248 except C<-package_logger> sets the the logger for the current package.
249
250 Unlike L</-default_logger>, C<-package_logger> cannot be overridden with
251 L</set_logger>.
252
253  package My::Package;
254  use Log::Contextual::SimpleLogger;
255  use Log::Contextual qw( :log ),
256    -package_logger => Log::Contextual::WarnLogger->new({
257       env_prefix => 'MY_PACKAGE'
258    });
259
260 If you are interested in using this package for a module you are putting on
261 CPAN we recommend L<Log::Contextual::WarnLogger> for your package logger.
262
263 =head2 -default_logger
264
265 The C<-default_logger> import option is similar to the C<-logger> import option
266 except C<-default_logger> sets the the B<default> logger for the current package.
267
268 Basically it sets the logger to be used if C<set_logger> is never called; so
269
270  package My::Package;
271  use Log::Contextual::SimpleLogger;
272  use Log::Contextual qw( :log ),
273    -default_logger => Log::Contextual::WarnLogger->new({
274       env_prefix => 'MY_PACKAGE'
275    });
276
277 =head1 A WORK IN PROGRESS
278
279 This module is certainly not complete, but we will not break the interface
280 lightly, so I would say it's safe to use in production code.  The main result
281 from that at this point is that doing:
282
283  use Log::Contextual;
284
285 will die as we do not yet know what the defaults should be.  If it turns out
286 that nearly everyone uses the C<:log> tag and C<:dlog> is really rare, we'll
287 probably make C<:log> the default.  But only time and usage will tell.
288
289 =head1 FUNCTIONS
290
291 =head2 set_logger
292
293  my $logger = WarnLogger->new;
294  set_logger $logger;
295
296 Arguments: C<Ref|CodeRef $returning_logger>
297
298 C<set_logger> will just set the current logger to whatever you pass it.  It
299 expects a C<CodeRef>, but if you pass it something else it will wrap it in a
300 C<CodeRef> for you.  C<set_logger> is really meant only to be called from a
301 top-level script.  To avoid foot-shooting the function will warn if you call it
302 more than once.
303
304 =head2 with_logger
305
306  my $logger = WarnLogger->new;
307  with_logger $logger => sub {
308     if (1 == 0) {
309        log_fatal { 'Non Logical Universe Detected' };
310     } else {
311        log_info  { 'All is good' };
312     }
313  };
314
315 Arguments: C<Ref|CodeRef $returning_logger, CodeRef $to_execute>
316
317 C<with_logger> sets the logger for the scope of the C<CodeRef> C<$to_execute>.
318 As with L</set_logger>, C<with_logger> will wrap C<$returning_logger> with a
319 C<CodeRef> if needed.
320
321 =head2 log_$level
322
323 Import Tag: C<:log>
324
325 Arguments: C<CodeRef $returning_message, @args>
326
327 All of the following six functions work the same except that a different method
328 is called on the underlying C<$logger> object.  The basic pattern is:
329
330  sub log_$level (&@) {
331    if ($logger->is_$level) {
332      $logger->$level(shift->(@_));
333    }
334    @_
335  }
336
337 Note that the function returns it's arguments.  This can be used in a number of
338 ways, but often it's convenient just for partial inspection of passthrough data
339
340  my @friends = log_trace {
341    'friends list being generated, data from first friend: ' .
342      Dumper($_[0]->TO_JSON)
343  } generate_friend_list();
344
345 If you want complete inspection of passthrough data, take a look at the
346 L</Dlog_$level> functions.
347
348 =head3 log_trace
349
350  log_trace { 'entered method foo with args ' join q{,}, @args };
351
352 =head3 log_debug
353
354  log_debug { 'entered method foo' };
355
356 =head3 log_info
357
358  log_info { 'started process foo' };
359
360 =head3 log_warn
361
362  log_warn { 'possible misconfiguration at line 10' };
363
364 =head3 log_error
365
366  log_error { 'non-numeric user input!' };
367
368 =head3 log_fatal
369
370  log_fatal { '1 is never equal to 0!' };
371
372 =head2 logS_$level
373
374 Import Tag: C<:log>
375
376 Arguments: C<CodeRef $returning_message, Item $arg>
377
378 This is really just a special case of the L</log_$level> functions.  It forces
379 scalar context when that is what you need.  Other than that it works exactly
380 same:
381
382  my $friend = logS_trace {
383    'I only have one friend: ' .  Dumper($_[0]->TO_JSON)
384  } friend();
385
386 See also: L</DlogS_$level>.
387
388 =head2 Dlog_$level
389
390 Import Tag: C<:dlog>
391
392 Arguments: C<CodeRef $returning_message, @args>
393
394 All of the following six functions work the same as their L</log_$level>
395 brethren, except they return what is passed into them and put the stringified
396 (with L<Data::Dumper::Concise>) version of their args into C<$_>.  This means
397 you can do cool things like the following:
398
399  my @nicks = Dlog_debug { "names: $_" } map $_->value, $frew->names->all;
400
401 and the output might look something like:
402
403  names: "fREW"
404  "fRIOUX"
405  "fROOH"
406  "fRUE"
407  "fiSMBoC"
408
409 =head3 Dlog_trace
410
411  my ($foo, $bar) = Dlog_trace { "entered method foo with args: $_" } @_;
412
413 =head3 Dlog_debug
414
415  Dlog_debug { "random data structure: $_" } { foo => $bar };
416
417 =head3 Dlog_info
418
419  return Dlog_info { "html from method returned: $_" } "<html>...</html>";
420
421 =head3 Dlog_warn
422
423  Dlog_warn { "probably invalid value: $_" } $foo;
424
425 =head3 Dlog_error
426
427  Dlog_error { "non-numeric user input! ($_)" } $port;
428
429 =head3 Dlog_fatal
430
431  Dlog_fatal { '1 is never equal to 0!' } 'ZOMG ZOMG' if 1 == 0;
432
433 =head2 DlogS_$level
434
435 Import Tag: C<:dlog>
436
437 Arguments: C<CodeRef $returning_message, Item $arg>
438
439 Like L</logS_$level>, these functions are a special case of L</Dlog_$level>.
440 They only take a single scalar after the C<$returning_message> instead of
441 slurping up (and also setting C<wantarray>) all the C<@args>
442
443  my $pals_rs = DlogS_debug { "pals resultset: $_" }
444    $schema->resultset('Pals')->search({ perlers => 1 });
445
446 =head1 LOGGER INTERFACE
447
448 Because this module is ultimately pretty looking glue (glittery?) with the
449 awesome benefit of the Contextual part, users will often want to make their
450 favorite logger work with it.  The following are the methods that should be
451 implemented in the logger:
452
453  is_trace
454  is_debug
455  is_info
456  is_warn
457  is_error
458  is_fatal
459  trace
460  debug
461  info
462  warn
463  error
464  fatal
465
466 The first six merely need to return true if that level is enabled.  The latter
467 six take the results of whatever the user returned from their coderef and log
468 them.  For a basic example see L<Log::Contextual::SimpleLogger>.
469
470 =head1 AUTHOR
471
472 frew - Arthur Axel "fREW" Schmidt <frioux@gmail.com>
473
474 =head1 DESIGNER
475
476 mst - Matt S. Trout <mst@shadowcat.co.uk>
477
478 =head1 COPYRIGHT
479
480 Copyright (c) 2010 the Log::Contextual L</AUTHOR> and L</DESIGNER> as listed
481 above.
482
483 =head1 LICENSE
484
485 This library is free software and may be distributed under the same terms as
486 Perl 5 itself.
487
488 =cut
489