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