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