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