slightly more clear docs
[p5sagit/Log-Contextual.git] / lib / Log / Contextual.pm
CommitLineData
0daa11f3 1package Log::Contextual;
2
a2777569 3use strict;
4use warnings;
2033c911 5
f07824b7 6our $VERSION = '0.004100';
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
38sub before_import {
39 my ($class, $importer, $spec) = @_;
f11f9542 40
f11f9542 41 die 'Log::Contextual does not have a default import list'
675503c7 42 if $spec->config->{default};
43
5fd26f45 44 my @levels = @{$class->arg_levels($spec->config->{levels})};
675503c7 45 for my $level (@levels) {
46 if ($spec->config->{log}) {
47 $spec->add_export("&log_$level", sub (&@) {
48 _do_log( $level => _get_logger( caller ), shift @_, @_)
49 });
50 $spec->add_export("&logS_$level", sub (&@) {
51 _do_logS( $level => _get_logger( caller ), $_[0], $_[1])
52 });
53 }
54 if ($spec->config->{dlog}) {
55 $spec->add_export("&Dlog_$level", sub (&@) {
56 my ($code, @args) = @_;
57 return _do_log( $level => _get_logger( caller ), sub {
58 local $_ = (@args?Data::Dumper::Concise::Dumper @args:'()');
59 $code->(@_)
60 }, @args );
61 });
62 $spec->add_export("&DlogS_$level", sub (&$) {
63 my ($code, $ref) = @_;
64 _do_logS( $level => _get_logger( caller ), sub {
65 local $_ = Data::Dumper::Concise::Dumper $ref;
66 $code->($ref)
67 }, $ref )
68 });
a2777569 69 }
70 }
675503c7 71}
72
5fd26f45 73sub arg_logger { $_[1] }
74sub arg_levels { $_[1] || [qw(debug trace warn info error fatal)] }
75sub arg_package_logger { $_[1] }
76sub arg_default_logger { $_[1] }
77
675503c7 78sub after_import {
79 my ($class, $importer, $specs) = @_;
80
5fd26f45 81 if (my $l = $class->arg_logger($specs->config->{logger})) {
82 set_logger($l)
83 }
84
85 if (my $l = $class->arg_package_logger($specs->config->{package_logger})) {
86 _set_package_logger_for($importer, $l)
87 }
675503c7 88
5fd26f45 89 if (my $l = $class->arg_default_logger($specs->config->{default_logger})) {
90 _set_default_logger_for($importer, $l)
91 }
f11f9542 92}
2033c911 93
7cec609c 94our $Get_Logger;
5d8f2b84 95our %Default_Logger;
e2b4b29c 96our %Package_Logger;
5d8f2b84 97
3ccc9c47 98sub _set_default_logger_for {
99 my $logger = $_[1];
100 if(ref $logger ne 'CODE') {
101 die 'logger was not a CodeRef or a logger object. Please try again.'
102 unless blessed($logger);
103 $logger = do { my $l = $logger; sub { $l } }
104 }
105 $Default_Logger{$_[0]} = $logger
106}
06e908c3 107
e2b4b29c 108sub _set_package_logger_for {
109 my $logger = $_[1];
110 if(ref $logger ne 'CODE') {
111 die 'logger was not a CodeRef or a logger object. Please try again.'
112 unless blessed($logger);
113 $logger = do { my $l = $logger; sub { $l } }
114 }
115 $Package_Logger{$_[0]} = $logger
116}
117
5d8f2b84 118sub _get_logger($) {
119 my $package = shift;
120 (
e2b4b29c 121 $Package_Logger{$package} ||
5d8f2b84 122 $Get_Logger ||
123 $Default_Logger{$package} ||
124 die q( no logger set! you can't try to log something without a logger! )
06e908c3 125 )->($package);
5d8f2b84 126}
7cec609c 127
8dc5a747 128sub set_logger {
129 my $logger = $_[0];
5b094c87 130 if(ref $logger ne 'CODE') {
131 die 'logger was not a CodeRef or a logger object. Please try again.'
132 unless blessed($logger);
133 $logger = do { my $l = $logger; sub { $l } }
134 }
5d8f2b84 135
136 warn 'set_logger (or -logger) called more than once! This is a bad idea!'
137 if $Get_Logger;
8dc5a747 138 $Get_Logger = $logger;
7cec609c 139}
140
98833ffb 141sub with_logger {
142 my $logger = $_[0];
5b094c87 143 if(ref $logger ne 'CODE') {
144 die 'logger was not a CodeRef or a logger object. Please try again.'
145 unless blessed($logger);
146 $logger = do { my $l = $logger; sub { $l } }
147 }
98833ffb 148 local $Get_Logger = $logger;
80c3e48b 149 $_[1]->();
2daff231 150}
151
d11de6ae 152sub _do_log {
153 my $level = shift;
154 my $logger = shift;
155 my $code = shift;
156 my @values = @_;
157
d11de6ae 158 $logger->$level($code->(@_))
159 if $logger->${\"is_$level"};
160 @values
161}
162
163sub _do_logS {
164 my $level = shift;
165 my $logger = shift;
166 my $code = shift;
167 my $value = shift;
168
d11de6ae 169 $logger->$level($code->($value))
170 if $logger->${\"is_$level"};
709d11fe 171 $value
172}
173
0daa11f3 1741;
0a3750e2 175
176__END__
177
2daff231 178=head1 NAME
179
8bc568d2 180Log::Contextual - Simple logging interface with a contextual log
2daff231 181
182=head1 SYNOPSIS
183
9b8e24d5 184 use Log::Contextual qw( :log :dlog set_logger with_logger );
5b094c87 185 use Log::Contextual::SimpleLogger;
186 use Log::Log4perl ':easy';
187 Log::Log4perl->easy_init($DEBUG);
2daff231 188
2daff231 189
5b094c87 190 my $logger = Log::Log4perl->get_logger;
191
192 set_logger $logger;
2daff231 193
9b8e24d5 194 log_debug { 'program started' };
2daff231 195
196 sub foo {
f9bf084b 197
198 my $minilogger = Log::Contextual::SimpleLogger->new({
199 levels => [qw( trace debug )]
200 });
201
202 with_logger $minilogger => sub {
21431192 203 log_trace { 'foo entered' };
9b8e24d5 204 my ($foo, $bar) = Dlog_trace { "params for foo: $_" } @_;
2daff231 205 # ...
21431192 206 log_trace { 'foo left' };
f9bf084b 207 };
2daff231 208 }
209
5b094c87 210 foo();
211
9fe4eeb3 212Beginning with version 1.008 L<Log::Dispatchouli> also works out of the box
213with C<Log::Contextual>:
214
215 use Log::Contextual qw( :log :dlog set_logger );
216 use Log::Dispatchouli;
217 my $ld = Log::Dispatchouli->new({
218 ident => 'slrtbrfst',
219 to_stderr => 1,
220 debug => 1,
221 });
222
223 set_logger $ld;
224
225 log_debug { 'program started' };
226
2daff231 227=head1 DESCRIPTION
228
3dc9bd3c 229This module is a simple interface to extensible logging. It is bundled with a
230really basic logger, L<Log::Contextual::SimpleLogger>, but in general you
231should use a real logger instead of that. For something more serious but not
9fe4eeb3 232overly complicated, try L<Log::Dispatchouli> (see L</SYNOPSIS> for example.)
3dc9bd3c 233
a2af6976 234The reason for this module is to abstract your logging interface so that
235logging is as painless as possible, while still allowing you to switch from one
236logger to another.
237
e36f2183 238=head1 A WORK IN PROGRESS
239
240This module is certainly not complete, but we will not break the interface
241lightly, so I would say it's safe to use in production code. The main result
242from that at this point is that doing:
243
244 use Log::Contextual;
245
246will die as we do not yet know what the defaults should be. If it turns out
247that nearly everyone uses the C<:log> tag and C<:dlog> is really rare, we'll
248probably make C<:log> the default. But only time and usage will tell.
249
250=head1 IMPORT OPTIONS
251
252See L</SETTING DEFAULT IMPORT OPTIONS> for information on setting these project
253wide.
3dc9bd3c 254
c154d18a 255=head2 -logger
256
3dc9bd3c 257When you import this module you may use C<-logger> as a shortcut for
258L<set_logger>, for example:
259
260 use Log::Contextual::SimpleLogger;
9b8e24d5 261 use Log::Contextual qw( :dlog ),
262 -logger => Log::Contextual::SimpleLogger->new({ levels => [qw( debug )] });
3dc9bd3c 263
264sometimes you might want to have the logger handy for other stuff, in which
265case you might try something like the following:
266
267 my $var_log;
268 BEGIN { $var_log = VarLogger->new }
9b8e24d5 269 use Log::Contextual qw( :dlog ), -logger => $var_log;
3dc9bd3c 270
5fd26f45 271=head2 -levels
272
273The C<-levels> import option allows you to define exactly which levels your
274logger supports. So the default,
275C<< [qw(debug trace warn info error fatal)] >>, works great for
276L<Log::Log4perl>, but it doesn't support the levels for L<Log::Dispatch>. But
277supporting those levels is as easy as doing
278
279 use Log::Contextual
280 -levels => [qw( debug info notice warning error critical alert emergency )];
281
e2b4b29c 282=head2 -package_logger
283
284The C<-package_logger> import option is similar to the C<-logger> import option
285except C<-package_logger> sets the the logger for the current package.
286
287Unlike L</-default_logger>, C<-package_logger> cannot be overridden with
288L</set_logger>.
289
290 package My::Package;
291 use Log::Contextual::SimpleLogger;
292 use Log::Contextual qw( :log ),
293 -package_logger => Log::Contextual::WarnLogger->new({
294 env_prefix => 'MY_PACKAGE'
295 });
296
297If you are interested in using this package for a module you are putting on
298CPAN we recommend L<Log::Contextual::WarnLogger> for your package logger.
299
c154d18a 300=head2 -default_logger
301
302The C<-default_logger> import option is similar to the C<-logger> import option
e2b4b29c 303except C<-default_logger> sets the the B<default> logger for the current package.
c154d18a 304
305Basically it sets the logger to be used if C<set_logger> is never called; so
306
307 package My::Package;
308 use Log::Contextual::SimpleLogger;
309 use Log::Contextual qw( :log ),
310 -default_logger => Log::Contextual::WarnLogger->new({
ae59bbe3 311 env_prefix => 'MY_PACKAGE'
c154d18a 312 });
313
e36f2183 314=head1 SETTING DEFAULT IMPORT OPTIONS
3dc9bd3c 315
e36f2183 316Eventually you will get tired of writing the following in every single one of
317your packages:
3dc9bd3c 318
e36f2183 319 use Log::Log4perl;
320 use Log::Log4perl ':easy';
321 BEGIN { Log::Log4perl->easy_init($DEBUG) }
3dc9bd3c 322
e36f2183 323 use Log::Contextual -logger => Log::Log4perl->get_logger;
324
325You can set any of the import options for your whole project if you define your
326own C<Log::Contextual> subclass as follows:
327
328 package MyApp::Log::Contextual;
329
330 use base 'Log::Contextual';
331
332 use Log::Log4perl ':easy';
333 Log::Log4perl->easy_init($DEBUG)
334
335 sub arg_logger { $_[1] || Log::Log4perl->get_logger }
336 sub arg_levels { [qw(debug trace warn info error fatal custom_level)] }
337
338 # and *maybe* even these:
339 sub arg_package_logger { $_[1] }
340 sub arg_default_logger { $_[1] }
341
342Note the C<< $_[1] || >> in C<arg_logger>. All of these methods are passed the
343values passed in from the arguments to the subclass, so you can either throw
344them away, honor them, die on usage, or whatever. To be clear, if you define
345your subclass, and someone uses it as follows:
346
347 use MyApp::Log::Contextual -logger => $foo, -levels => [qw(bar baz biff)];
348
349Your C<arg_logger> method will get C<$foo> and your C<arg_levels>
350will get C<[qw(bar baz biff)]>;
2daff231 351
352=head1 FUNCTIONS
353
354=head2 set_logger
355
356 my $logger = WarnLogger->new;
21431192 357 set_logger $logger;
358
0e13e261 359Arguments: C<Ref|CodeRef $returning_logger>
2daff231 360
21431192 361C<set_logger> will just set the current logger to whatever you pass it. It
362expects a C<CodeRef>, but if you pass it something else it will wrap it in a
06e908c3 363C<CodeRef> for you. C<set_logger> is really meant only to be called from a
364top-level script. To avoid foot-shooting the function will warn if you call it
365more than once.
2daff231 366
367=head2 with_logger
368
369 my $logger = WarnLogger->new;
21431192 370 with_logger $logger => sub {
2daff231 371 if (1 == 0) {
372 log_fatal { 'Non Logical Universe Detected' };
373 } else {
374 log_info { 'All is good' };
375 }
80c3e48b 376 };
2daff231 377
0e13e261 378Arguments: C<Ref|CodeRef $returning_logger, CodeRef $to_execute>
2daff231 379
21431192 380C<with_logger> sets the logger for the scope of the C<CodeRef> C<$to_execute>.
0e13e261 381As with L</set_logger>, C<with_logger> will wrap C<$returning_logger> with a
21431192 382C<CodeRef> if needed.
2daff231 383
21431192 384=head2 log_$level
2daff231 385
0e13e261 386Import Tag: C<:log>
3dc9bd3c 387
0e13e261 388Arguments: C<CodeRef $returning_message, @args>
2daff231 389
21431192 390All of the following six functions work the same except that a different method
391is called on the underlying C<$logger> object. The basic pattern is:
2daff231 392
0e13e261 393 sub log_$level (&@) {
21431192 394 if ($logger->is_$level) {
0e13e261 395 $logger->$level(shift->(@_));
21431192 396 }
0e13e261 397 @_
21431192 398 }
2daff231 399
0e13e261 400Note that the function returns it's arguments. This can be used in a number of
401ways, but often it's convenient just for partial inspection of passthrough data
402
403 my @friends = log_trace {
404 'friends list being generated, data from first friend: ' .
405 Dumper($_[0]->TO_JSON)
406 } generate_friend_list();
407
408If you want complete inspection of passthrough data, take a look at the
409L</Dlog_$level> functions.
410
21431192 411=head3 log_trace
2daff231 412
21431192 413 log_trace { 'entered method foo with args ' join q{,}, @args };
2daff231 414
21431192 415=head3 log_debug
2daff231 416
21431192 417 log_debug { 'entered method foo' };
2daff231 418
21431192 419=head3 log_info
2daff231 420
21431192 421 log_info { 'started process foo' };
2daff231 422
21431192 423=head3 log_warn
2daff231 424
21431192 425 log_warn { 'possible misconfiguration at line 10' };
2daff231 426
21431192 427=head3 log_error
2daff231 428
21431192 429 log_error { 'non-numeric user input!' };
2daff231 430
21431192 431=head3 log_fatal
2daff231 432
433 log_fatal { '1 is never equal to 0!' };
434
0e13e261 435=head2 logS_$level
436
437Import Tag: C<:log>
438
439Arguments: C<CodeRef $returning_message, Item $arg>
440
441This is really just a special case of the L</log_$level> functions. It forces
442scalar context when that is what you need. Other than that it works exactly
443same:
444
445 my $friend = logS_trace {
446 'I only have one friend: ' . Dumper($_[0]->TO_JSON)
447 } friend();
448
449See also: L</DlogS_$level>.
450
21431192 451=head2 Dlog_$level
452
0e13e261 453Import Tag: C<:dlog>
3dc9bd3c 454
0e13e261 455Arguments: C<CodeRef $returning_message, @args>
2daff231 456
0e13e261 457All of the following six functions work the same as their L</log_$level>
9b8e24d5 458brethren, except they return what is passed into them and put the stringified
21431192 459(with L<Data::Dumper::Concise>) version of their args into C<$_>. This means
460you can do cool things like the following:
461
462 my @nicks = Dlog_debug { "names: $_" } map $_->value, $frew->names->all;
463
464and the output might look something like:
465
466 names: "fREW"
467 "fRIOUX"
468 "fROOH"
469 "fRUE"
470 "fiSMBoC"
471
472=head3 Dlog_trace
473
9b8e24d5 474 my ($foo, $bar) = Dlog_trace { "entered method foo with args: $_" } @_;
21431192 475
476=head3 Dlog_debug
477
478 Dlog_debug { "random data structure: $_" } { foo => $bar };
479
480=head3 Dlog_info
481
482 return Dlog_info { "html from method returned: $_" } "<html>...</html>";
483
484=head3 Dlog_warn
485
486 Dlog_warn { "probably invalid value: $_" } $foo;
487
488=head3 Dlog_error
489
490 Dlog_error { "non-numeric user input! ($_)" } $port;
2daff231 491
21431192 492=head3 Dlog_fatal
2daff231 493
21431192 494 Dlog_fatal { '1 is never equal to 0!' } 'ZOMG ZOMG' if 1 == 0;
2daff231 495
83b33eb5 496=head2 DlogS_$level
497
0e13e261 498Import Tag: C<:dlog>
3dc9bd3c 499
0e13e261 500Arguments: C<CodeRef $returning_message, Item $arg>
83b33eb5 501
0e13e261 502Like L</logS_$level>, these functions are a special case of L</Dlog_$level>.
503They only take a single scalar after the C<$returning_message> instead of
504slurping up (and also setting C<wantarray>) all the C<@args>
83b33eb5 505
506 my $pals_rs = DlogS_debug { "pals resultset: $_" }
507 $schema->resultset('Pals')->search({ perlers => 1 });
508
3dc9bd3c 509=head1 LOGGER INTERFACE
510
511Because this module is ultimately pretty looking glue (glittery?) with the
512awesome benefit of the Contextual part, users will often want to make their
513favorite logger work with it. The following are the methods that should be
514implemented in the logger:
515
516 is_trace
517 is_debug
518 is_info
519 is_warn
520 is_error
521 is_fatal
522 trace
523 debug
524 info
525 warn
526 error
527 fatal
528
529The first six merely need to return true if that level is enabled. The latter
530six take the results of whatever the user returned from their coderef and log
531them. For a basic example see L<Log::Contextual::SimpleLogger>.
532
2daff231 533=head1 AUTHOR
534
535frew - Arthur Axel "fREW" Schmidt <frioux@gmail.com>
536
537=head1 DESIGNER
538
539mst - Matt S. Trout <mst@shadowcat.co.uk>
540
541=head1 COPYRIGHT
542
543Copyright (c) 2010 the Log::Contextual L</AUTHOR> and L</DESIGNER> as listed
544above.
545
546=head1 LICENSE
547
548This library is free software and may be distributed under the same terms as
549Perl 5 itself.
550
551=cut
552