refactor log router API to use named args and clearer names for those args
[p5sagit/Log-Contextual.git] / lib / Log / Contextual.pm
1 package Log::Contextual;
2
3 use strict;
4 use warnings;
5
6 our $VERSION = '0.004202';
7
8 my @levels = qw(debug trace warn info error fatal);
9
10 use Exporter::Declare;
11 use Exporter::Declare::Export::Generator;
12 use Data::Dumper::Concise;
13 use Scalar::Util 'blessed';
14
15 my @dlog = ((map "Dlog_$_", @levels), (map "DlogS_$_", @levels));
16
17 my @log = ((map "log_$_", @levels), (map "logS_$_", @levels));
18
19 eval {
20    require Log::Log4perl;
21    die if $Log::Log4perl::VERSION < 1.29;
22    Log::Log4perl->wrapper_register(__PACKAGE__)
23 };
24
25 # ____ is because tags must have at least one export and we don't want to
26 # export anything but the levels selected
27 sub ____ {}
28
29 exports ('____',
30    @dlog, @log,
31    qw( set_logger with_logger )
32 );
33
34 export_tag dlog => ('____');
35 export_tag log  => ('____');
36 import_arguments qw(logger package_logger default_logger);
37
38 sub router {
39    our $Router_Instance ||= do {
40       require Log::Contextual::Router;
41       Log::Contextual::Router->new
42    }
43 }
44
45 sub arg_logger { $_[1] }
46 sub arg_levels { $_[1] || [qw(debug trace warn info error fatal)] }
47 sub arg_package_logger { $_[1] }
48 sub arg_default_logger { $_[1] }
49
50 sub before_import {
51    my ($class, $importer, $spec) = @_;
52    my $router = $class->router;
53    my $exports = $spec->exports;
54    my %router_args = (exporter => $class, target => $importer, arguments => $spec->argument_info);
55
56    die 'Log::Contextual does not have a default import list'
57       if $spec->config->{default};
58
59    $router->before_import(%router_args);
60
61    $spec->add_export('&set_logger', sub {
62       my $router = $class->router;
63
64       die ref($router) . " does not support set_logger()"
65          unless $router->does('Log::Contextual::Role::Router::SetLogger');
66
67       return $router->set_logger(@_);
68    }) if $exports->{'&set_logger'};
69
70    $spec->add_export('&with_logger', sub {
71       my $router = $class->router;
72
73       die ref($router) . " does not support with_logger()"
74          unless $router->does('Log::Contextual::Role::Router::WithLogger');
75
76       return $router->with_logger(@_);
77    }) if $exports->{'&with_logger'};
78
79    my @levels = @{$class->arg_levels($spec->config->{levels})};
80    for my $level (@levels) {
81       if ($spec->config->{log}) {
82          $spec->add_export("&log_$level", sub (&@) {
83             my ($code, @args) = @_;
84             $router->handle_log_request(
85                exporter => $class, caller_package => scalar(caller), caller_level => 1,
86                message_level => $level, message_sub => $code, message_args => \@args,
87             );
88             return @args;
89          });
90          $spec->add_export("&logS_$level", sub (&@) {
91             my ($code, @args) = @_;
92             $router->handle_log_request(
93                exporter => $class, caller_package => scalar(caller), caller_level => 1,
94                message_level => $level, message_sub => $code, message_args => \@args,
95             );
96             return $args[0];
97          });
98       }
99       if ($spec->config->{dlog}) {
100          $spec->add_export("&Dlog_$level", sub (&@) {
101             my ($code, @args) = @_;
102             my $wrapped = sub {
103                local $_ = (@_?Data::Dumper::Concise::Dumper @_:'()');
104                &$code;
105             };
106             $router->handle_log_request(
107                exporter => $class, caller_package => scalar(caller), caller_level => 1,
108                message_level => $level, message_sub => $wrapped, message_args => \@args,
109             );
110             return @args;
111          });
112          $spec->add_export("&DlogS_$level", sub (&$) {
113             my ($code, $ref) = @_;
114             my $wrapped = sub {
115                local $_ = Data::Dumper::Concise::Dumper($_[0]);
116                &$code;
117             };
118             $router->handle_log_request(
119                exporter => $class, caller_package => scalar(caller), caller_level => 1,
120                message_level => $level, message_sub => $wrapped, message_args => [ $ref ],
121             );
122             return $ref;
123          });
124       }
125    }
126 }
127
128 sub after_import {
129    my ($class, $importer, $spec) = @_;
130    my %router_args = (exporter => $class, target => $importer, arguments => $spec->argument_info);
131    $class->router->after_import(%router_args);
132 }
133
134 1;
135
136 __END__
137
138 =head1 NAME
139
140 Log::Contextual - Simple logging interface with a contextual log
141
142 =head1 SYNOPSIS
143
144  use Log::Contextual qw( :log :dlog set_logger with_logger );
145  use Log::Contextual::SimpleLogger;
146  use Log::Log4perl ':easy';
147  Log::Log4perl->easy_init($DEBUG);
148
149  my $logger  = Log::Log4perl->get_logger;
150
151  set_logger $logger;
152
153  log_debug { 'program started' };
154
155  sub foo {
156
157    my $minilogger = Log::Contextual::SimpleLogger->new({
158      levels => [qw( trace debug )]
159    });
160
161    with_logger $minilogger => sub {
162      log_trace { 'foo entered' };
163      my ($foo, $bar) = Dlog_trace { "params for foo: $_" } @_;
164      # ...
165      log_trace { 'foo left' };
166    };
167  }
168
169  foo();
170
171 Beginning with version 1.008 L<Log::Dispatchouli> also works out of the box
172 with C<Log::Contextual>:
173
174  use Log::Contextual qw( :log :dlog set_logger );
175  use Log::Dispatchouli;
176  my $ld = Log::Dispatchouli->new({
177     ident     => 'slrtbrfst',
178     to_stderr => 1,
179     debug     => 1,
180  });
181
182  set_logger $ld;
183
184  log_debug { 'program started' };
185
186 =head1 DESCRIPTION
187
188 Major benefits:
189
190 =over 2
191
192 =item * Efficient
193
194 The logging functions take blocks, so if a log level is disabled, the
195 block will not run:
196
197  # the following won't run if debug is off
198  log_debug { "the new count in the database is " . $rs->count };
199
200 Similarly, the C<D> prefixed methods only C<Dumper> the input if the level is
201 enabled.
202
203 =item * Handy
204
205 The logging functions return their arguments, so you can stick them in
206 the middle of expressions:
207
208  for (log_debug { "downloading:\n" . join qq(\n), @_ } @urls) { ... }
209
210 =item * Generic
211
212 C<Log::Contextual> is an interface for all major loggers.  If you log through
213 C<Log::Contextual> you will be able to swap underlying loggers later.
214
215 =item * Powerful
216
217 C<Log::Contextual> chooses which logger to use based on L<< user defined C<CodeRef>s|/LOGGER CODEREF >>.
218 Normally you don't need to know this, but you can take advantage of it when you
219 need to later
220
221 =item * Scalable
222
223 If you just want to add logging to your extremely basic application, start with
224 L<Log::Contextual::SimpleLogger> and then as your needs grow you can switch to
225 L<Log::Dispatchouli> or L<Log::Dispatch> or L<Log::Log4perl> or whatever else.
226
227 =back
228
229 This module is a simple interface to extensible logging.  It exists to
230 abstract your logging interface so that logging is as painless as possible,
231 while still allowing you to switch from one logger to another.
232
233 It is bundled with a really basic logger, L<Log::Contextual::SimpleLogger>,
234 but in general you should use a real logger instead of that.  For something
235 more serious but not overly complicated, try L<Log::Dispatchouli> (see
236 L</SYNOPSIS> for example.)
237
238 =head1 A WORK IN PROGRESS
239
240 This module is certainly not complete, but we will not break the interface
241 lightly, so I would say it's safe to use in production code.  The main result
242 from that at this point is that doing:
243
244  use Log::Contextual;
245
246 will die as we do not yet know what the defaults should be.  If it turns out
247 that nearly everyone uses the C<:log> tag and C<:dlog> is really rare, we'll
248 probably make C<:log> the default.  But only time and usage will tell.
249
250 =head1 IMPORT OPTIONS
251
252 See L</SETTING DEFAULT IMPORT OPTIONS> for information on setting these project
253 wide.
254
255 =head2 -logger
256
257 When you import this module you may use C<-logger> as a shortcut for
258 L<set_logger>, for example:
259
260  use Log::Contextual::SimpleLogger;
261  use Log::Contextual qw( :dlog ),
262    -logger => Log::Contextual::SimpleLogger->new({ levels => [qw( debug )] });
263
264 sometimes you might want to have the logger handy for other stuff, in which
265 case you might try something like the following:
266
267  my $var_log;
268  BEGIN { $var_log = VarLogger->new }
269  use Log::Contextual qw( :dlog ), -logger => $var_log;
270
271 =head2 -levels
272
273 The C<-levels> import option allows you to define exactly which levels your
274 logger supports.  So the default,
275 C<< [qw(debug trace warn info error fatal)] >>, works great for
276 L<Log::Log4perl>, but it doesn't support the levels for L<Log::Dispatch>.  But
277 supporting those levels is as easy as doing
278
279  use Log::Contextual
280    -levels => [qw( debug info notice warning error critical alert emergency )];
281
282 =head2 -package_logger
283
284 The C<-package_logger> import option is similar to the C<-logger> import option
285 except C<-package_logger> sets the the logger for the current package.
286
287 Unlike L</-default_logger>, C<-package_logger> cannot be overridden with
288 L</set_logger>.
289
290  package My::Package;
291  use Log::Contextual::SimpleLogger;
292  use Log::Contextual qw( :log ),
293    -package_logger => Log::Contextual::WarnLogger->new({
294       env_prefix => 'MY_PACKAGE'
295    });
296
297 If you are interested in using this package for a module you are putting on
298 CPAN we recommend L<Log::Contextual::WarnLogger> for your package logger.
299
300 =head2 -default_logger
301
302 The C<-default_logger> import option is similar to the C<-logger> import option
303 except C<-default_logger> sets the the B<default> logger for the current package.
304
305 Basically it sets the logger to be used if C<set_logger> is never called; so
306
307  package My::Package;
308  use Log::Contextual::SimpleLogger;
309  use Log::Contextual qw( :log ),
310    -default_logger => Log::Contextual::WarnLogger->new({
311       env_prefix => 'MY_PACKAGE'
312    });
313
314 =head1 SETTING DEFAULT IMPORT OPTIONS
315
316 Eventually you will get tired of writing the following in every single one of
317 your packages:
318
319  use Log::Log4perl;
320  use Log::Log4perl ':easy';
321  BEGIN { Log::Log4perl->easy_init($DEBUG) }
322
323  use Log::Contextual -logger => Log::Log4perl->get_logger;
324
325 You can set any of the import options for your whole project if you define your
326 own C<Log::Contextual> subclass as follows:
327
328  package MyApp::Log::Contextual;
329
330  use base 'Log::Contextual';
331
332  use Log::Log4perl ':easy';
333  Log::Log4perl->easy_init($DEBUG)
334
335  sub arg_default_logger { $_[1] || Log::Log4perl->get_logger }
336  sub arg_levels { [qw(debug trace warn info error fatal custom_level)] }
337
338  # or maybe instead of default_logger
339  sub arg_package_logger { $_[1] }
340
341  # and almost definitely not this, which is only here for completeness
342  sub arg_logger { $_[1] }
343
344 Note the C<< $_[1] || >> in C<arg_default_logger>.  All of these methods are
345 passed the values passed in from the arguments to the subclass, so you can
346 either throw them away, honor them, die on usage, or whatever.  To be clear,
347 if you define your subclass, and someone uses it as follows:
348
349  use MyApp::Log::Contextual -default_logger => $foo,
350                             -levels => [qw(bar baz biff)];
351
352 Your C<arg_default_logger> method will get C<$foo> and your C<arg_levels>
353 will get C<[qw(bar baz biff)]>;
354
355 =head1 FUNCTIONS
356
357 =head2 set_logger
358
359  my $logger = WarnLogger->new;
360  set_logger $logger;
361
362 Arguments: L</LOGGER CODEREF>
363
364 C<set_logger> will just set the current logger to whatever you pass it.  It
365 expects a C<CodeRef>, but if you pass it something else it will wrap it in a
366 C<CodeRef> for you.  C<set_logger> is really meant only to be called from a
367 top-level script.  To avoid foot-shooting the function will warn if you call it
368 more than once.
369
370 =head2 with_logger
371
372  my $logger = WarnLogger->new;
373  with_logger $logger => sub {
374     if (1 == 0) {
375        log_fatal { 'Non Logical Universe Detected' };
376     } else {
377        log_info  { 'All is good' };
378     }
379  };
380
381 Arguments: L</LOGGER CODEREF>, C<CodeRef $to_execute>
382
383 C<with_logger> sets the logger for the scope of the C<CodeRef> C<$to_execute>.
384 As with L</set_logger>, C<with_logger> will wrap C<$returning_logger> with a
385 C<CodeRef> if needed.
386
387 =head2 log_$level
388
389 Import Tag: C<:log>
390
391 Arguments: C<CodeRef $returning_message, @args>
392
393 C<log_$level> functions all work the same except that a different method
394 is called on the underlying C<$logger> object.  The basic pattern is:
395
396  sub log_$level (&@) {
397    if ($logger->is_$level) {
398      $logger->$level(shift->(@_));
399    }
400    @_
401  }
402
403 Note that the function returns it's arguments.  This can be used in a number of
404 ways, but often it's convenient just for partial inspection of passthrough data
405
406  my @friends = log_trace {
407    'friends list being generated, data from first friend: ' .
408      Dumper($_[0]->TO_JSON)
409  } generate_friend_list();
410
411 If you want complete inspection of passthrough data, take a look at the
412 L</Dlog_$level> functions.
413
414 Which functions are exported depends on what was passed to L</-levels>.  The
415 default (no C<-levels> option passed) would export:
416
417 =over 2
418
419 =item log_trace
420
421 =item log_debug
422
423 =item log_info
424
425 =item log_warn
426
427 =item log_error
428
429 =item log_fatal
430
431 =back
432
433 =head2 logS_$level
434
435 Import Tag: C<:log>
436
437 Arguments: C<CodeRef $returning_message, Item $arg>
438
439 This is really just a special case of the L</log_$level> functions.  It forces
440 scalar context when that is what you need.  Other than that it works exactly
441 same:
442
443  my $friend = logS_trace {
444    'I only have one friend: ' .  Dumper($_[0]->TO_JSON)
445  } friend();
446
447 See also: L</DlogS_$level>.
448
449 =head2 Dlog_$level
450
451 Import Tag: C<:dlog>
452
453 Arguments: C<CodeRef $returning_message, @args>
454
455 All of the following six functions work the same as their L</log_$level>
456 brethren, except they return what is passed into them and put the stringified
457 (with L<Data::Dumper::Concise>) version of their args into C<$_>.  This means
458 you can do cool things like the following:
459
460  my @nicks = Dlog_debug { "names: $_" } map $_->value, $frew->names->all;
461
462 and the output might look something like:
463
464  names: "fREW"
465  "fRIOUX"
466  "fROOH"
467  "fRUE"
468  "fiSMBoC"
469
470 Which functions are exported depends on what was passed to L</-levels>.  The
471 default (no C<-levels> option passed) would export:
472
473 =over 2
474
475 =item Dlog_trace
476
477 =item Dlog_debug
478
479 =item Dlog_info
480
481 =item Dlog_warn
482
483 =item Dlog_error
484
485 =item Dlog_fatal
486
487 =back
488
489 =head2 DlogS_$level
490
491 Import Tag: C<:dlog>
492
493 Arguments: C<CodeRef $returning_message, Item $arg>
494
495 Like L</logS_$level>, these functions are a special case of L</Dlog_$level>.
496 They only take a single scalar after the C<$returning_message> instead of
497 slurping up (and also setting C<wantarray>) all the C<@args>
498
499  my $pals_rs = DlogS_debug { "pals resultset: $_" }
500    $schema->resultset('Pals')->search({ perlers => 1 });
501
502 =head1 LOGGER CODEREF
503
504 Anywhere a logger object can be passed, a coderef is accepted.  This is so
505 that the user can use different logger objects based on runtime information.
506 The logger coderef is passed the package of the caller the caller level the
507 coderef needs to use if it wants more caller information.  The latter is in
508 a hashref to allow for more options in the future.
509
510 Here is a basic example of a logger that exploits C<caller> to reproduce the
511 output of C<warn> with a logger:
512
513  my @caller_info;
514  my $var_log = Log::Contextual::SimpleLogger->new({
515     levels  => [qw(trace debug info warn error fatal)],
516     coderef => sub { chomp($_[0]); warn "$_[0] at $caller_info[1] line $caller_info[2].\n" }
517  });
518  my $warn_faker = sub {
519     my ($package, $args) = @_;
520     @caller_info = caller($args->{caller_level});
521     $var_log
522  };
523  set_logger($warn_faker);
524  log_debug { 'test' };
525
526 The following is an example that uses the information passed to the logger
527 coderef.  It sets the global logger to C<$l3>, the logger for the C<A1>
528 package to C<$l1>, except the C<lol> method in C<A1> which uses the C<$l2>
529 logger and lastly the logger for the C<A2> package to C<$l2>.
530
531 Note that it increases the caller level as it dispatches based on where
532 the caller of the log function, not the log function itself.
533
534  my $complex_dispatcher = do {
535
536     my $l1 = ...;
537     my $l2 = ...;
538     my $l3 = ...;
539
540     my %registry = (
541        -logger => $l3,
542        A1 => {
543           -logger => $l1,
544           lol     => $l2,
545        },
546        A2 => { -logger => $l2 },
547     );
548
549     sub {
550        my ( $package, $info ) = @_;
551
552        my $logger = $registry{'-logger'};
553        if (my $r = $registry{$package}) {
554           $logger = $r->{'-logger'} if $r->{'-logger'};
555           my (undef, undef, undef, $sub) = caller($info->{caller_level} + 1);
556           $sub =~ s/^\Q$package\E:://g;
557           $logger = $r->{$sub} if $r->{$sub};
558        }
559        return $logger;
560     }
561  };
562
563  set_logger $complex_dispatcher;
564
565 =head1 LOGGER INTERFACE
566
567 Because this module is ultimately pretty looking glue (glittery?) with the
568 awesome benefit of the Contextual part, users will often want to make their
569 favorite logger work with it.  The following are the methods that should be
570 implemented in the logger:
571
572  is_trace
573  is_debug
574  is_info
575  is_warn
576  is_error
577  is_fatal
578  trace
579  debug
580  info
581  warn
582  error
583  fatal
584
585 The first six merely need to return true if that level is enabled.  The latter
586 six take the results of whatever the user returned from their coderef and log
587 them.  For a basic example see L<Log::Contextual::SimpleLogger>.
588
589 =head1 LOG ROUTING
590
591 In between the loggers and the log functions is a log router that is responsible for
592 finding a logger to handle the log event and passing the log information to the
593 logger. This relationship is described in the documentation for C<Log::Contextual::Role::Router>.
594
595 C<Log::Contextual> and packages that extend it will by default share a router singleton that
596 implements the with_logger() and set_logger() functions and also respects the -logger,
597 -package_logger, and -default_logger import options with their associated default value
598 functions. The router singleton is available as the return value of the router() function. Users
599 of Log::Contextual may overload router() to return instances of custom log routers that
600 could for example work with loggers that use a different interface.
601
602 =head1 AUTHOR
603
604 frew - Arthur Axel "fREW" Schmidt <frioux@gmail.com>
605
606 =head1 CONTRIBUTORS
607
608 triddle - Tyler Riddle <t.riddle@shadowcat.co.uk>
609
610 =head1 DESIGNER
611
612 mst - Matt S. Trout <mst@shadowcat.co.uk>
613
614 =head1 COPYRIGHT
615
616 Copyright (c) 2012 the Log::Contextual L</AUTHOR> and L</DESIGNER> as listed
617 above.
618
619 =head1 LICENSE
620
621 This library is free software and may be distributed under the same terms as
622 Perl 5 itself.
623
624 =cut
625