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