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