1 package Log::Contextual;
6 our $VERSION = '0.004202';
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
31 qw( set_logger with_logger )
34 export_tag dlog => ('____');
35 export_tag log => ('____');
36 import_arguments qw(logger package_logger default_logger);
39 our $Router_Instance ||= do {
40 require Log::Contextual::Router;
41 Log::Contextual::Router->new
45 sub arg_logger { $_[1] }
46 sub arg_levels { $_[1] || [qw(debug trace warn info error fatal)] }
47 sub arg_package_logger { $_[1] }
48 sub arg_default_logger { $_[1] }
51 my ($class, $importer, $spec) = @_;
52 my $router = $class->router;
53 my $exports = $spec->exports;
55 die 'Log::Contextual does not have a default import list'
56 if $spec->config->{default};
58 $router->before_import(@_);
60 $spec->add_export('&set_logger', sub {
61 my $router = $class->router;
63 die ref($router) . " does not support set_logger()"
64 unless $router->does('Log::Contextual::Role::Router::SetLogger');
66 return $router->set_logger(@_);
67 }) if $exports->{'&set_logger'};
69 $spec->add_export('&with_logger', sub {
70 my $router = $class->router;
72 die ref($router) . " does not support with_logger()"
73 unless $router->does('Log::Contextual::Role::Router::WithLogger');
75 return $router->with_logger(@_);
76 }) if $exports->{'&with_logger'};
78 my @levels = @{$class->arg_levels($spec->config->{levels})};
79 for my $level (@levels) {
80 if ($spec->config->{log}) {
81 $spec->add_export("&log_$level", sub (&@) {
82 my ($code, @args) = @_;
83 $router->handle_log_request({
84 package => scalar(caller),
90 $spec->add_export("&logS_$level", sub (&@) {
91 my ($code, @args) = @_;
92 $router->handle_log_request({
93 package => scalar(caller),
100 if ($spec->config->{dlog}) {
101 $spec->add_export("&Dlog_$level", sub (&@) {
102 my ($code, @args) = @_;
104 local $_ = (@_?Data::Dumper::Concise::Dumper @_:'()');
107 $router->handle_log_request({
108 package => scalar(caller),
114 $spec->add_export("&DlogS_$level", sub (&$) {
115 my ($code, $ref) = @_;
117 local $_ = Data::Dumper::Concise::Dumper($_[0]);
120 $router->handle_log_request({
121 package => scalar(caller),
131 sub after_import { $_[0]->router->after_import(@_) }
139 Log::Contextual - Simple logging interface with a contextual log
143 use Log::Contextual qw( :log :dlog set_logger with_logger );
144 use Log::Contextual::SimpleLogger;
145 use Log::Log4perl ':easy';
146 Log::Log4perl->easy_init($DEBUG);
148 my $logger = Log::Log4perl->get_logger;
152 log_debug { 'program started' };
156 my $minilogger = Log::Contextual::SimpleLogger->new({
157 levels => [qw( trace debug )]
160 with_logger $minilogger => sub {
161 log_trace { 'foo entered' };
162 my ($foo, $bar) = Dlog_trace { "params for foo: $_" } @_;
164 log_trace { 'foo left' };
170 Beginning with version 1.008 L<Log::Dispatchouli> also works out of the box
171 with C<Log::Contextual>:
173 use Log::Contextual qw( :log :dlog set_logger );
174 use Log::Dispatchouli;
175 my $ld = Log::Dispatchouli->new({
176 ident => 'slrtbrfst',
183 log_debug { 'program started' };
193 The logging functions take blocks, so if a log level is disabled, the
196 # the following won't run if debug is off
197 log_debug { "the new count in the database is " . $rs->count };
199 Similarly, the C<D> prefixed methods only C<Dumper> the input if the level is
204 The logging functions return their arguments, so you can stick them in
205 the middle of expressions:
207 for (log_debug { "downloading:\n" . join qq(\n), @_ } @urls) { ... }
211 C<Log::Contextual> is an interface for all major loggers. If you log through
212 C<Log::Contextual> you will be able to swap underlying loggers later.
216 C<Log::Contextual> chooses which logger to use based on L<< user defined C<CodeRef>s|/LOGGER CODEREF >>.
217 Normally you don't need to know this, but you can take advantage of it when you
222 If you just want to add logging to your extremely basic application, start with
223 L<Log::Contextual::SimpleLogger> and then as your needs grow you can switch to
224 L<Log::Dispatchouli> or L<Log::Dispatch> or L<Log::Log4perl> or whatever else.
228 This module is a simple interface to extensible logging. It exists to
229 abstract your logging interface so that logging is as painless as possible,
230 while still allowing you to switch from one logger to another.
232 It is bundled with a really basic logger, L<Log::Contextual::SimpleLogger>,
233 but in general you should use a real logger instead of that. For something
234 more serious but not overly complicated, try L<Log::Dispatchouli> (see
235 L</SYNOPSIS> for example.)
237 =head1 A WORK IN PROGRESS
239 This module is certainly not complete, but we will not break the interface
240 lightly, so I would say it's safe to use in production code. The main result
241 from that at this point is that doing:
245 will die as we do not yet know what the defaults should be. If it turns out
246 that nearly everyone uses the C<:log> tag and C<:dlog> is really rare, we'll
247 probably make C<:log> the default. But only time and usage will tell.
249 =head1 IMPORT OPTIONS
251 See L</SETTING DEFAULT IMPORT OPTIONS> for information on setting these project
256 When you import this module you may use C<-logger> as a shortcut for
257 L<set_logger>, for example:
259 use Log::Contextual::SimpleLogger;
260 use Log::Contextual qw( :dlog ),
261 -logger => Log::Contextual::SimpleLogger->new({ levels => [qw( debug )] });
263 sometimes you might want to have the logger handy for other stuff, in which
264 case you might try something like the following:
267 BEGIN { $var_log = VarLogger->new }
268 use Log::Contextual qw( :dlog ), -logger => $var_log;
272 The C<-levels> import option allows you to define exactly which levels your
273 logger supports. So the default,
274 C<< [qw(debug trace warn info error fatal)] >>, works great for
275 L<Log::Log4perl>, but it doesn't support the levels for L<Log::Dispatch>. But
276 supporting those levels is as easy as doing
279 -levels => [qw( debug info notice warning error critical alert emergency )];
281 =head2 -package_logger
283 The C<-package_logger> import option is similar to the C<-logger> import option
284 except C<-package_logger> sets the the logger for the current package.
286 Unlike L</-default_logger>, C<-package_logger> cannot be overridden with
290 use Log::Contextual::SimpleLogger;
291 use Log::Contextual qw( :log ),
292 -package_logger => Log::Contextual::WarnLogger->new({
293 env_prefix => 'MY_PACKAGE'
296 If you are interested in using this package for a module you are putting on
297 CPAN we recommend L<Log::Contextual::WarnLogger> for your package logger.
299 =head2 -default_logger
301 The C<-default_logger> import option is similar to the C<-logger> import option
302 except C<-default_logger> sets the the B<default> logger for the current package.
304 Basically it sets the logger to be used if C<set_logger> is never called; so
307 use Log::Contextual::SimpleLogger;
308 use Log::Contextual qw( :log ),
309 -default_logger => Log::Contextual::WarnLogger->new({
310 env_prefix => 'MY_PACKAGE'
313 =head1 SETTING DEFAULT IMPORT OPTIONS
315 Eventually you will get tired of writing the following in every single one of
319 use Log::Log4perl ':easy';
320 BEGIN { Log::Log4perl->easy_init($DEBUG) }
322 use Log::Contextual -logger => Log::Log4perl->get_logger;
324 You can set any of the import options for your whole project if you define your
325 own C<Log::Contextual> subclass as follows:
327 package MyApp::Log::Contextual;
329 use base 'Log::Contextual';
331 use Log::Log4perl ':easy';
332 Log::Log4perl->easy_init($DEBUG)
334 sub arg_default_logger { $_[1] || Log::Log4perl->get_logger }
335 sub arg_levels { [qw(debug trace warn info error fatal custom_level)] }
337 # or maybe instead of default_logger
338 sub arg_package_logger { $_[1] }
340 # and almost definitely not this, which is only here for completeness
341 sub arg_logger { $_[1] }
343 Note the C<< $_[1] || >> in C<arg_default_logger>. All of these methods are
344 passed the values passed in from the arguments to the subclass, so you can
345 either throw them away, honor them, die on usage, or whatever. To be clear,
346 if you define your subclass, and someone uses it as follows:
348 use MyApp::Log::Contextual -default_logger => $foo,
349 -levels => [qw(bar baz biff)];
351 Your C<arg_default_logger> method will get C<$foo> and your C<arg_levels>
352 will get C<[qw(bar baz biff)]>;
358 my $logger = WarnLogger->new;
361 Arguments: L</LOGGER CODEREF>
363 C<set_logger> will just set the current logger to whatever you pass it. It
364 expects a C<CodeRef>, but if you pass it something else it will wrap it in a
365 C<CodeRef> for you. C<set_logger> is really meant only to be called from a
366 top-level script. To avoid foot-shooting the function will warn if you call it
371 my $logger = WarnLogger->new;
372 with_logger $logger => sub {
374 log_fatal { 'Non Logical Universe Detected' };
376 log_info { 'All is good' };
380 Arguments: L</LOGGER CODEREF>, C<CodeRef $to_execute>
382 C<with_logger> sets the logger for the scope of the C<CodeRef> C<$to_execute>.
383 As with L</set_logger>, C<with_logger> will wrap C<$returning_logger> with a
384 C<CodeRef> if needed.
390 Arguments: C<CodeRef $returning_message, @args>
392 C<log_$level> functions all work the same except that a different method
393 is called on the underlying C<$logger> object. The basic pattern is:
395 sub log_$level (&@) {
396 if ($logger->is_$level) {
397 $logger->$level(shift->(@_));
402 Note that the function returns it's arguments. This can be used in a number of
403 ways, but often it's convenient just for partial inspection of passthrough data
405 my @friends = log_trace {
406 'friends list being generated, data from first friend: ' .
407 Dumper($_[0]->TO_JSON)
408 } generate_friend_list();
410 If you want complete inspection of passthrough data, take a look at the
411 L</Dlog_$level> functions.
413 Which functions are exported depends on what was passed to L</-levels>. The
414 default (no C<-levels> option passed) would export:
436 Arguments: C<CodeRef $returning_message, Item $arg>
438 This is really just a special case of the L</log_$level> functions. It forces
439 scalar context when that is what you need. Other than that it works exactly
442 my $friend = logS_trace {
443 'I only have one friend: ' . Dumper($_[0]->TO_JSON)
446 See also: L</DlogS_$level>.
452 Arguments: C<CodeRef $returning_message, @args>
454 All of the following six functions work the same as their L</log_$level>
455 brethren, except they return what is passed into them and put the stringified
456 (with L<Data::Dumper::Concise>) version of their args into C<$_>. This means
457 you can do cool things like the following:
459 my @nicks = Dlog_debug { "names: $_" } map $_->value, $frew->names->all;
461 and the output might look something like:
469 Which functions are exported depends on what was passed to L</-levels>. The
470 default (no C<-levels> option passed) would export:
492 Arguments: C<CodeRef $returning_message, Item $arg>
494 Like L</logS_$level>, these functions are a special case of L</Dlog_$level>.
495 They only take a single scalar after the C<$returning_message> instead of
496 slurping up (and also setting C<wantarray>) all the C<@args>
498 my $pals_rs = DlogS_debug { "pals resultset: $_" }
499 $schema->resultset('Pals')->search({ perlers => 1 });
501 =head1 LOGGER CODEREF
503 Anywhere a logger object can be passed, a coderef is accepted. This is so
504 that the user can use different logger objects based on runtime information.
505 The logger coderef is passed the package of the caller the caller level the
506 coderef needs to use if it wants more caller information. The latter is in
507 a hashref to allow for more options in the future.
509 Here is a basic example of a logger that exploits C<caller> to reproduce the
510 output of C<warn> with a logger:
513 my $var_log = Log::Contextual::SimpleLogger->new({
514 levels => [qw(trace debug info warn error fatal)],
515 coderef => sub { chomp($_[0]); warn "$_[0] at $caller_info[1] line $caller_info[2].\n" }
517 my $warn_faker = sub {
518 my ($package, $args) = @_;
519 @caller_info = caller($args->{caller_level});
522 set_logger($warn_faker);
523 log_debug { 'test' };
525 The following is an example that uses the information passed to the logger
526 coderef. It sets the global logger to C<$l3>, the logger for the C<A1>
527 package to C<$l1>, except the C<lol> method in C<A1> which uses the C<$l2>
528 logger and lastly the logger for the C<A2> package to C<$l2>.
530 Note that it increases the caller level as it dispatches based on where
531 the caller of the log function, not the log function itself.
533 my $complex_dispatcher = do {
545 A2 => { -logger => $l2 },
549 my ( $package, $info ) = @_;
551 my $logger = $registry{'-logger'};
552 if (my $r = $registry{$package}) {
553 $logger = $r->{'-logger'} if $r->{'-logger'};
554 my (undef, undef, undef, $sub) = caller($info->{caller_level} + 1);
555 $sub =~ s/^\Q$package\E:://g;
556 $logger = $r->{$sub} if $r->{$sub};
562 set_logger $complex_dispatcher;
564 =head1 LOGGER INTERFACE
566 Because this module is ultimately pretty looking glue (glittery?) with the
567 awesome benefit of the Contextual part, users will often want to make their
568 favorite logger work with it. The following are the methods that should be
569 implemented in the logger:
584 The first six merely need to return true if that level is enabled. The latter
585 six take the results of whatever the user returned from their coderef and log
586 them. For a basic example see L<Log::Contextual::SimpleLogger>.
590 frew - Arthur Axel "fREW" Schmidt <frioux@gmail.com>
594 mst - Matt S. Trout <mst@shadowcat.co.uk>
598 Copyright (c) 2012 the Log::Contextual L</AUTHOR> and L</DESIGNER> as listed
603 This library is free software and may be distributed under the same terms as