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