1 package Log::Contextual;
3 # ABSTRACT: Simple logging interface with a contextual log
8 my @levels = qw(debug trace warn info error fatal);
10 use Exporter::Declare;
11 use Exporter::Declare::Export::Generator;
12 use Data::Dumper::Concise;
13 use Scalar::Util 'blessed';
15 my @dlog = ((map "Dlog_$_", @levels), (map "DlogS_$_", @levels));
17 my @log = ((map "log_$_", @levels), (map "logS_$_", @levels));
20 require Log::Log4perl;
21 die if $Log::Log4perl::VERSION < 1.29;
22 Log::Log4perl->wrapper_register(__PACKAGE__)
25 # ____ is because tags must have at least one export and we don't want to
26 # export anything but the levels selected
29 exports('____', @dlog, @log, qw( set_logger with_logger ));
31 export_tag dlog => ('____');
32 export_tag log => ('____');
33 import_arguments qw(logger package_logger default_logger);
36 our $Router_Instance ||= do {
37 require Log::Contextual::Router;
38 Log::Contextual::Router->new
45 die 'Log::Contextual does not have a default import list';
50 sub arg_logger { $_[1] }
51 sub arg_levels { $_[1] || [qw(debug trace warn info error fatal)] }
52 sub arg_package_logger { $_[1] }
53 sub arg_default_logger { $_[1] }
56 my ($class, $importer, $spec) = @_;
57 my $router = $class->router;
58 my $exports = $spec->exports;
62 arguments => $spec->argument_info
65 my @tags = $class->default_import($spec)
66 if $spec->config->{default};
69 die "only tags are supported for defaults at this time"
70 unless $_ =~ /^:(.*)$/;
72 $spec->config->{$1} = 1;
75 $router->before_import(%router_args);
77 if ($exports->{'&set_logger'}) {
78 die ref($router) . " does not support set_logger()"
79 unless $router->does('Log::Contextual::Role::Router::SetLogger');
81 $spec->add_export('&set_logger', sub { $router->set_logger(@_) })
84 if ($exports->{'&with_logger'}) {
85 die ref($router) . " does not support with_logger()"
86 unless $router->does('Log::Contextual::Role::Router::WithLogger');
88 $spec->add_export('&with_logger', sub { $router->with_logger(@_) })
91 my @levels = @{$class->arg_levels($spec->config->{levels})};
92 for my $level (@levels) {
93 if ($spec->config->{log} || $exports->{"&log_$level"}) {
97 my ($code, @args) = @_;
98 $router->handle_log_request(
100 caller_package => scalar(caller),
102 message_level => $level,
103 message_sub => $code,
104 message_args => \@args,
109 if ($spec->config->{log} || $exports->{"&logS_$level"}) {
113 my ($code, @args) = @_;
114 $router->handle_log_request(
116 caller_package => scalar(caller),
118 message_level => $level,
119 message_sub => $code,
120 message_args => \@args,
125 if ($spec->config->{dlog} || $exports->{"&Dlog_$level"}) {
129 my ($code, @args) = @_;
131 local $_ = (@_ ? Data::Dumper::Concise::Dumper @_ : '()');
134 $router->handle_log_request(
136 caller_package => scalar(caller),
138 message_level => $level,
139 message_sub => $wrapped,
140 message_args => \@args,
145 if ($spec->config->{dlog} || $exports->{"&DlogS_$level"}) {
149 my ($code, $ref) = @_;
151 local $_ = Data::Dumper::Concise::Dumper($_[0]);
154 $router->handle_log_request(
156 caller_package => scalar(caller),
158 message_level => $level,
159 message_sub => $wrapped,
160 message_args => [$ref],
169 my ($class, $importer, $spec) = @_;
173 arguments => $spec->argument_info
175 $class->router->after_import(%router_args);
180 my $sub = "${_}_logger";
181 *{"Log::Contextual::$sub"} = sub {
182 die "$sub is no longer a direct sub in Log::Contextual. "
183 . 'Note that this feature was never tested nor documented. '
184 . "Please fix your code to import $sub instead of trying to use it directly"
194 use Log::Contextual qw( :log :dlog set_logger with_logger );
195 use Log::Contextual::SimpleLogger;
196 use Log::Log4perl ':easy';
197 Log::Log4perl->easy_init($DEBUG);
199 my $logger = Log::Log4perl->get_logger;
203 log_debug { 'program started' };
207 my $minilogger = Log::Contextual::SimpleLogger->new({
208 levels => [qw( trace debug )]
213 with_logger $minilogger => sub {
214 log_trace { 'foo entered' };
215 my ($foo, $bar) = Dlog_trace { "params for foo: $_" } @args;
217 log_trace { 'foo left' };
223 Beginning with version 1.008 L<Log::Dispatchouli> also works out of the box
224 with C<Log::Contextual>:
226 use Log::Contextual qw( :log :dlog set_logger );
227 use Log::Dispatchouli;
228 my $ld = Log::Dispatchouli->new({
229 ident => 'slrtbrfst',
236 log_debug { 'program started' };
246 The logging functions take blocks, so if a log level is disabled, the
249 # the following won't run if debug is off
250 log_debug { "the new count in the database is " . $rs->count };
252 Similarly, the C<D> prefixed methods only C<Dumper> the input if the level is
257 The logging functions return their arguments, so you can stick them in
258 the middle of expressions:
260 for (log_debug { "downloading:\n" . join qq(\n), @_ } @urls) { ... }
264 C<Log::Contextual> is an interface for all major loggers. If you log through
265 C<Log::Contextual> you will be able to swap underlying loggers later.
269 C<Log::Contextual> chooses which logger to use based on L<< user defined C<CodeRef>s|/LOGGER CODEREF >>.
270 Normally you don't need to know this, but you can take advantage of it when you
275 If you just want to add logging to your extremely basic application, start with
276 L<Log::Contextual::SimpleLogger> and then as your needs grow you can switch to
277 L<Log::Dispatchouli> or L<Log::Dispatch> or L<Log::Log4perl> or whatever else.
281 This module is a simple interface to extensible logging. It exists to
282 abstract your logging interface so that logging is as painless as possible,
283 while still allowing you to switch from one logger to another.
285 It is bundled with a really basic logger, L<Log::Contextual::SimpleLogger>,
286 but in general you should use a real logger instead of that. For something
287 more serious but not overly complicated, try L<Log::Dispatchouli> (see
288 L</SYNOPSIS> for example.)
290 =head1 A WORK IN PROGRESS
292 This module is certainly not complete, but we will not break the interface
293 lightly, so I would say it's safe to use in production code. The main result
294 from that at this point is that doing:
298 will die as we do not yet know what the defaults should be. If it turns out
299 that nearly everyone uses the C<:log> tag and C<:dlog> is really rare, we'll
300 probably make C<:log> the default. But only time and usage will tell.
302 =head1 IMPORT OPTIONS
304 See L</SETTING DEFAULT IMPORT OPTIONS> for information on setting these project
309 When you import this module you may use C<-logger> as a shortcut for
310 L</set_logger>, for example:
312 use Log::Contextual::SimpleLogger;
313 use Log::Contextual qw( :dlog ),
314 -logger => Log::Contextual::SimpleLogger->new({ levels => [qw( debug )] });
316 sometimes you might want to have the logger handy for other stuff, in which
317 case you might try something like the following:
320 BEGIN { $var_log = VarLogger->new }
321 use Log::Contextual qw( :dlog ), -logger => $var_log;
325 The C<-levels> import option allows you to define exactly which levels your
326 logger supports. So the default,
327 C<< [qw(debug trace warn info error fatal)] >>, works great for
328 L<Log::Log4perl>, but it doesn't support the levels for L<Log::Dispatch>. But
329 supporting those levels is as easy as doing
332 -levels => [qw( debug info notice warning error critical alert emergency )];
334 =head2 -package_logger
336 The C<-package_logger> import option is similar to the C<-logger> import option
337 except C<-package_logger> sets the logger for the current package.
339 Unlike L</-default_logger>, C<-package_logger> cannot be overridden with
343 use Log::Contextual::SimpleLogger;
344 use Log::Contextual qw( :log ),
345 -package_logger => Log::Contextual::WarnLogger->new({
346 env_prefix => 'MY_PACKAGE'
349 If you are interested in using this package for a module you are putting on
350 CPAN we recommend L<Log::Contextual::WarnLogger> for your package logger.
352 =head2 -default_logger
354 The C<-default_logger> import option is similar to the C<-logger> import option
355 except C<-default_logger> sets the B<default> logger for the current package.
357 Basically it sets the logger to be used if C<set_logger> is never called; so
360 use Log::Contextual::SimpleLogger;
361 use Log::Contextual qw( :log ),
362 -default_logger => Log::Contextual::WarnLogger->new({
363 env_prefix => 'MY_PACKAGE'
366 =head1 SETTING DEFAULT IMPORT OPTIONS
368 Eventually you will get tired of writing the following in every single one of
372 use Log::Log4perl ':easy';
373 BEGIN { Log::Log4perl->easy_init($DEBUG) }
375 use Log::Contextual -logger => Log::Log4perl->get_logger;
377 You can set any of the import options for your whole project if you define your
378 own C<Log::Contextual> subclass as follows:
380 package MyApp::Log::Contextual;
382 use base 'Log::Contextual';
384 use Log::Log4perl ':easy';
385 Log::Log4perl->easy_init($DEBUG)
387 sub arg_default_logger { $_[1] || Log::Log4perl->get_logger }
388 sub arg_levels { [qw(debug trace warn info error fatal custom_level)] }
389 sub default_import { ':log' }
391 # or maybe instead of default_logger
392 sub arg_package_logger { $_[1] }
394 # and almost definitely not this, which is only here for completeness
395 sub arg_logger { $_[1] }
397 Note the C<< $_[1] || >> in C<arg_default_logger>. All of these methods are
398 passed the values passed in from the arguments to the subclass, so you can
399 either throw them away, honor them, die on usage, or whatever. To be clear,
400 if you define your subclass, and someone uses it as follows:
402 use MyApp::Log::Contextual -default_logger => $foo,
403 -levels => [qw(bar baz biff)];
405 Your C<arg_default_logger> method will get C<$foo> and your C<arg_levels>
406 will get C<[qw(bar baz biff)]>;
408 Additionally, the C<default_import> method is what happens if a user tries to
409 use your subclass with no arguments. The default just dies, but if you'd like
410 to change the default to import a tag merely return the tags you'd like to
411 import. So the following will all work:
413 sub default_import { ':log' }
415 sub default_import { ':dlog' }
417 sub default_import { qw(:dlog :log ) }
419 See L<Log::Contextual::Easy::Default> for an example of a subclass of
420 C<Log::Contextual> that makes use of default import options.
426 my $logger = WarnLogger->new;
429 Arguments: L</LOGGER CODEREF>
431 C<set_logger> will just set the current logger to whatever you pass it. It
432 expects a C<CodeRef>, but if you pass it something else it will wrap it in a
433 C<CodeRef> for you. C<set_logger> is really meant only to be called from a
434 top-level script. To avoid foot-shooting the function will warn if you call it
439 my $logger = WarnLogger->new;
440 with_logger $logger => sub {
442 log_fatal { 'Non Logical Universe Detected' };
444 log_info { 'All is good' };
448 Arguments: L</LOGGER CODEREF>, C<CodeRef $to_execute>
450 C<with_logger> sets the logger for the scope of the C<CodeRef> C<$to_execute>.
451 As with L</set_logger>, C<with_logger> will wrap C<$returning_logger> with a
452 C<CodeRef> if needed.
458 Arguments: C<CodeRef $returning_message, @args>
460 C<log_$level> functions all work the same except that a different method
461 is called on the underlying C<$logger> object. The basic pattern is:
463 sub log_$level (&@) {
464 if ($logger->is_$level) {
465 $logger->$level(shift->(@_));
470 Note that the function returns it's arguments. This can be used in a number of
471 ways, but often it's convenient just for partial inspection of passthrough data
473 my @friends = log_trace {
474 'friends list being generated, data from first friend: ' .
475 Dumper($_[0]->TO_JSON)
476 } generate_friend_list();
478 If you want complete inspection of passthrough data, take a look at the
479 L</Dlog_$level> functions.
481 Which functions are exported depends on what was passed to L</-levels>. The
482 default (no C<-levels> option passed) would export:
504 Arguments: C<CodeRef $returning_message, Item $arg>
506 This is really just a special case of the L</log_$level> functions. It forces
507 scalar context when that is what you need. Other than that it works exactly
510 my $friend = logS_trace {
511 'I only have one friend: ' . Dumper($_[0]->TO_JSON)
514 See also: L</DlogS_$level>.
520 Arguments: C<CodeRef $returning_message, @args>
522 All of the following six functions work the same as their L</log_$level>
523 brethren, except they return what is passed into them and put the stringified
524 (with L<Data::Dumper::Concise>) version of their args into C<$_>. This means
525 you can do cool things like the following:
527 my @nicks = Dlog_debug { "names: $_" } map $_->value, $frew->names->all;
529 and the output might look something like:
537 Which functions are exported depends on what was passed to L</-levels>. The
538 default (no C<-levels> option passed) would export:
560 Arguments: C<CodeRef $returning_message, Item $arg>
562 Like L</logS_$level>, these functions are a special case of L</Dlog_$level>.
563 They only take a single scalar after the C<$returning_message> instead of
564 slurping up (and also setting C<wantarray>) all the C<@args>
566 my $pals_rs = DlogS_debug { "pals resultset: $_" }
567 $schema->resultset('Pals')->search({ perlers => 1 });
569 =head1 LOGGER CODEREF
571 Anywhere a logger object can be passed, a coderef is accepted. This is so
572 that the user can use different logger objects based on runtime information.
573 The logger coderef is passed the package of the caller the caller level the
574 coderef needs to use if it wants more caller information. The latter is in
575 a hashref to allow for more options in the future.
577 Here is a basic example of a logger that exploits C<caller> to reproduce the
578 output of C<warn> with a logger:
581 my $var_log = Log::Contextual::SimpleLogger->new({
582 levels => [qw(trace debug info warn error fatal)],
583 coderef => sub { chomp($_[0]); warn "$_[0] at $caller_info[1] line $caller_info[2].\n" }
585 my $warn_faker = sub {
586 my ($package, $args) = @_;
587 @caller_info = caller($args->{caller_level});
590 set_logger($warn_faker);
591 log_debug { 'test' };
593 The following is an example that uses the information passed to the logger
594 coderef. It sets the global logger to C<$l3>, the logger for the C<A1>
595 package to C<$l1>, except the C<lol> method in C<A1> which uses the C<$l2>
596 logger and lastly the logger for the C<A2> package to C<$l2>.
598 Note that it increases the caller level as it dispatches based on where
599 the caller of the log function, not the log function itself.
601 my $complex_dispatcher = do {
613 A2 => { -logger => $l2 },
617 my ( $package, $info ) = @_;
619 my $logger = $registry{'-logger'};
620 if (my $r = $registry{$package}) {
621 $logger = $r->{'-logger'} if $r->{'-logger'};
622 my (undef, undef, undef, $sub) = caller($info->{caller_level} + 1);
623 $sub =~ s/^\Q$package\E:://g;
624 $logger = $r->{$sub} if $r->{$sub};
630 set_logger $complex_dispatcher;
632 =head1 LOGGER INTERFACE
634 Because this module is ultimately pretty looking glue (glittery?) with the
635 awesome benefit of the Contextual part, users will often want to make their
636 favorite logger work with it. The following are the methods that should be
637 implemented in the logger:
652 The first six merely need to return true if that level is enabled. The latter
653 six take the results of whatever the user returned from their coderef and log
654 them. For a basic example see L<Log::Contextual::SimpleLogger>.
658 In between the loggers and the log functions is a log router that is responsible for
659 finding a logger to handle the log event and passing the log information to the
660 logger. This relationship is described in the documentation for C<Log::Contextual::Role::Router>.
662 C<Log::Contextual> and packages that extend it will by default share a router singleton that
663 implements the with_logger() and set_logger() functions and also respects the -logger,
664 -package_logger, and -default_logger import options with their associated default value
665 functions. The router singleton is available as the return value of the router() function. Users
666 of Log::Contextual may overload router() to return instances of custom log routers that
667 could for example work with loggers that use a different interface.
673 triddle - Tyler Riddle <t.riddle@shadowcat.co.uk>
675 voj - Jakob Voß <voss@gbv.de>
679 mst - Matt S. Trout <mst@shadowcat.co.uk>