Release 0.00305
[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
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 (&@) {
0502df63 150 my ($code, @args) = @_;
151 return _do_log( $level => _get_logger( caller ), sub {
152 local $_ = (@args?Data::Dumper::Concise::Dumper @args:'()');
153 $code->(@_)
154 }, @args );
ae9785e2 155 };
a4273dbf 156
ae9785e2 157 *{"DlogS_$level"} = sub (&$) {
0502df63 158 my ($code, $ref) = @_;
159 _do_logS( $level => _get_logger( caller ), sub {
160 local $_ = Data::Dumper::Concise::Dumper $ref;
161 $code->($ref)
162 }, $ref )
ae9785e2 163 };
f11f9542 164}
165
0daa11f3 1661;
0a3750e2 167
168__END__
169
2daff231 170=head1 NAME
171
8bc568d2 172Log::Contextual - Simple logging interface with a contextual log
2daff231 173
174=head1 SYNOPSIS
175
9b8e24d5 176 use Log::Contextual qw( :log :dlog set_logger with_logger );
5b094c87 177 use Log::Contextual::SimpleLogger;
178 use Log::Log4perl ':easy';
179 Log::Log4perl->easy_init($DEBUG);
2daff231 180
2daff231 181
5b094c87 182 my $logger = Log::Log4perl->get_logger;
183
184 set_logger $logger;
2daff231 185
9b8e24d5 186 log_debug { 'program started' };
2daff231 187
188 sub foo {
9b8e24d5 189 with_logger(Log::Contextual::SimpleLogger->new({
190 levels => [qw( trace debug )]
21431192 191 }) => sub {
192 log_trace { 'foo entered' };
9b8e24d5 193 my ($foo, $bar) = Dlog_trace { "params for foo: $_" } @_;
2daff231 194 # ...
21431192 195 log_trace { 'foo left' };
9b8e24d5 196 });
2daff231 197 }
198
5b094c87 199 foo();
200
9fe4eeb3 201Beginning with version 1.008 L<Log::Dispatchouli> also works out of the box
202with C<Log::Contextual>:
203
204 use Log::Contextual qw( :log :dlog set_logger );
205 use Log::Dispatchouli;
206 my $ld = Log::Dispatchouli->new({
207 ident => 'slrtbrfst',
208 to_stderr => 1,
209 debug => 1,
210 });
211
212 set_logger $ld;
213
214 log_debug { 'program started' };
215
2daff231 216=head1 DESCRIPTION
217
3dc9bd3c 218This module is a simple interface to extensible logging. It is bundled with a
219really basic logger, L<Log::Contextual::SimpleLogger>, but in general you
220should use a real logger instead of that. For something more serious but not
9fe4eeb3 221overly complicated, try L<Log::Dispatchouli> (see L</SYNOPSIS> for example.)
3dc9bd3c 222
a2af6976 223The reason for this module is to abstract your logging interface so that
224logging is as painless as possible, while still allowing you to switch from one
225logger to another.
226
3dc9bd3c 227=head1 OPTIONS
228
c154d18a 229=head2 -logger
230
3dc9bd3c 231When you import this module you may use C<-logger> as a shortcut for
232L<set_logger>, for example:
233
234 use Log::Contextual::SimpleLogger;
9b8e24d5 235 use Log::Contextual qw( :dlog ),
236 -logger => Log::Contextual::SimpleLogger->new({ levels => [qw( debug )] });
3dc9bd3c 237
238sometimes you might want to have the logger handy for other stuff, in which
239case you might try something like the following:
240
241 my $var_log;
242 BEGIN { $var_log = VarLogger->new }
9b8e24d5 243 use Log::Contextual qw( :dlog ), -logger => $var_log;
3dc9bd3c 244
e2b4b29c 245=head2 -package_logger
246
247The C<-package_logger> import option is similar to the C<-logger> import option
248except C<-package_logger> sets the the logger for the current package.
249
250Unlike L</-default_logger>, C<-package_logger> cannot be overridden with
251L</set_logger>.
252
253 package My::Package;
254 use Log::Contextual::SimpleLogger;
255 use Log::Contextual qw( :log ),
256 -package_logger => Log::Contextual::WarnLogger->new({
257 env_prefix => 'MY_PACKAGE'
258 });
259
260If you are interested in using this package for a module you are putting on
261CPAN we recommend L<Log::Contextual::WarnLogger> for your package logger.
262
c154d18a 263=head2 -default_logger
264
265The C<-default_logger> import option is similar to the C<-logger> import option
e2b4b29c 266except C<-default_logger> sets the the B<default> logger for the current package.
c154d18a 267
268Basically it sets the logger to be used if C<set_logger> is never called; so
269
270 package My::Package;
271 use Log::Contextual::SimpleLogger;
272 use Log::Contextual qw( :log ),
273 -default_logger => Log::Contextual::WarnLogger->new({
ae59bbe3 274 env_prefix => 'MY_PACKAGE'
c154d18a 275 });
276
3dc9bd3c 277=head1 A WORK IN PROGRESS
278
279This module is certainly not complete, but we will not break the interface
280lightly, so I would say it's safe to use in production code. The main result
281from that at this point is that doing:
282
283 use Log::Contextual;
284
285will die as we do not yet know what the defaults should be. If it turns out
286that nearly everyone uses the C<:log> tag and C<:dlog> is really rare, we'll
9b8e24d5 287probably make C<:log> the default. But only time and usage will tell.
2daff231 288
289=head1 FUNCTIONS
290
291=head2 set_logger
292
293 my $logger = WarnLogger->new;
21431192 294 set_logger $logger;
295
0e13e261 296Arguments: C<Ref|CodeRef $returning_logger>
2daff231 297
21431192 298C<set_logger> will just set the current logger to whatever you pass it. It
299expects a C<CodeRef>, but if you pass it something else it will wrap it in a
06e908c3 300C<CodeRef> for you. C<set_logger> is really meant only to be called from a
301top-level script. To avoid foot-shooting the function will warn if you call it
302more than once.
2daff231 303
304=head2 with_logger
305
306 my $logger = WarnLogger->new;
21431192 307 with_logger $logger => sub {
2daff231 308 if (1 == 0) {
309 log_fatal { 'Non Logical Universe Detected' };
310 } else {
311 log_info { 'All is good' };
312 }
80c3e48b 313 };
2daff231 314
0e13e261 315Arguments: C<Ref|CodeRef $returning_logger, CodeRef $to_execute>
2daff231 316
21431192 317C<with_logger> sets the logger for the scope of the C<CodeRef> C<$to_execute>.
0e13e261 318As with L</set_logger>, C<with_logger> will wrap C<$returning_logger> with a
21431192 319C<CodeRef> if needed.
2daff231 320
21431192 321=head2 log_$level
2daff231 322
0e13e261 323Import Tag: C<:log>
3dc9bd3c 324
0e13e261 325Arguments: C<CodeRef $returning_message, @args>
2daff231 326
21431192 327All of the following six functions work the same except that a different method
328is called on the underlying C<$logger> object. The basic pattern is:
2daff231 329
0e13e261 330 sub log_$level (&@) {
21431192 331 if ($logger->is_$level) {
0e13e261 332 $logger->$level(shift->(@_));
21431192 333 }
0e13e261 334 @_
21431192 335 }
2daff231 336
0e13e261 337Note that the function returns it's arguments. This can be used in a number of
338ways, but often it's convenient just for partial inspection of passthrough data
339
340 my @friends = log_trace {
341 'friends list being generated, data from first friend: ' .
342 Dumper($_[0]->TO_JSON)
343 } generate_friend_list();
344
345If you want complete inspection of passthrough data, take a look at the
346L</Dlog_$level> functions.
347
21431192 348=head3 log_trace
2daff231 349
21431192 350 log_trace { 'entered method foo with args ' join q{,}, @args };
2daff231 351
21431192 352=head3 log_debug
2daff231 353
21431192 354 log_debug { 'entered method foo' };
2daff231 355
21431192 356=head3 log_info
2daff231 357
21431192 358 log_info { 'started process foo' };
2daff231 359
21431192 360=head3 log_warn
2daff231 361
21431192 362 log_warn { 'possible misconfiguration at line 10' };
2daff231 363
21431192 364=head3 log_error
2daff231 365
21431192 366 log_error { 'non-numeric user input!' };
2daff231 367
21431192 368=head3 log_fatal
2daff231 369
370 log_fatal { '1 is never equal to 0!' };
371
0e13e261 372=head2 logS_$level
373
374Import Tag: C<:log>
375
376Arguments: C<CodeRef $returning_message, Item $arg>
377
378This is really just a special case of the L</log_$level> functions. It forces
379scalar context when that is what you need. Other than that it works exactly
380same:
381
382 my $friend = logS_trace {
383 'I only have one friend: ' . Dumper($_[0]->TO_JSON)
384 } friend();
385
386See also: L</DlogS_$level>.
387
21431192 388=head2 Dlog_$level
389
0e13e261 390Import Tag: C<:dlog>
3dc9bd3c 391
0e13e261 392Arguments: C<CodeRef $returning_message, @args>
2daff231 393
0e13e261 394All of the following six functions work the same as their L</log_$level>
9b8e24d5 395brethren, except they return what is passed into them and put the stringified
21431192 396(with L<Data::Dumper::Concise>) version of their args into C<$_>. This means
397you can do cool things like the following:
398
399 my @nicks = Dlog_debug { "names: $_" } map $_->value, $frew->names->all;
400
401and the output might look something like:
402
403 names: "fREW"
404 "fRIOUX"
405 "fROOH"
406 "fRUE"
407 "fiSMBoC"
408
409=head3 Dlog_trace
410
9b8e24d5 411 my ($foo, $bar) = Dlog_trace { "entered method foo with args: $_" } @_;
21431192 412
413=head3 Dlog_debug
414
415 Dlog_debug { "random data structure: $_" } { foo => $bar };
416
417=head3 Dlog_info
418
419 return Dlog_info { "html from method returned: $_" } "<html>...</html>";
420
421=head3 Dlog_warn
422
423 Dlog_warn { "probably invalid value: $_" } $foo;
424
425=head3 Dlog_error
426
427 Dlog_error { "non-numeric user input! ($_)" } $port;
2daff231 428
21431192 429=head3 Dlog_fatal
2daff231 430
21431192 431 Dlog_fatal { '1 is never equal to 0!' } 'ZOMG ZOMG' if 1 == 0;
2daff231 432
83b33eb5 433=head2 DlogS_$level
434
0e13e261 435Import Tag: C<:dlog>
3dc9bd3c 436
0e13e261 437Arguments: C<CodeRef $returning_message, Item $arg>
83b33eb5 438
0e13e261 439Like L</logS_$level>, these functions are a special case of L</Dlog_$level>.
440They only take a single scalar after the C<$returning_message> instead of
441slurping up (and also setting C<wantarray>) all the C<@args>
83b33eb5 442
443 my $pals_rs = DlogS_debug { "pals resultset: $_" }
444 $schema->resultset('Pals')->search({ perlers => 1 });
445
3dc9bd3c 446=head1 LOGGER INTERFACE
447
448Because this module is ultimately pretty looking glue (glittery?) with the
449awesome benefit of the Contextual part, users will often want to make their
450favorite logger work with it. The following are the methods that should be
451implemented in the logger:
452
453 is_trace
454 is_debug
455 is_info
456 is_warn
457 is_error
458 is_fatal
459 trace
460 debug
461 info
462 warn
463 error
464 fatal
465
466The first six merely need to return true if that level is enabled. The latter
467six take the results of whatever the user returned from their coderef and log
468them. For a basic example see L<Log::Contextual::SimpleLogger>.
469
2daff231 470=head1 AUTHOR
471
472frew - Arthur Axel "fREW" Schmidt <frioux@gmail.com>
473
474=head1 DESIGNER
475
476mst - Matt S. Trout <mst@shadowcat.co.uk>
477
478=head1 COPYRIGHT
479
480Copyright (c) 2010 the Log::Contextual L</AUTHOR> and L</DESIGNER> as listed
481above.
482
483=head1 LICENSE
484
485This library is free software and may be distributed under the same terms as
486Perl 5 itself.
487
488=cut
489