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