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