1 package Log::Contextual;
6 our $VERSION = '0.00304';
8 my @levels = qw(debug trace warn info error fatal);
11 use Data::Dumper::Concise;
12 use Scalar::Util 'blessed';
14 BEGIN { our @ISA = qw(Exporter) }
16 my @dlog = ((map "Dlog_$_", @levels), (map "DlogS_$_", @levels));
18 my @log = ((map "log_$_", @levels), (map "logS_$_", @levels));
21 require Log::Log4perl;
22 die if $Log::Log4perl::VERSION < 1.29;
23 Log::Log4perl->wrapper_register(__PACKAGE__)
28 qw( set_logger with_logger )
39 die 'Log::Contextual does not have a default import list'
42 for my $idx ( 0 .. $#_ ) {
44 if ( defined $val && $val eq '-logger' ) {
45 set_logger($_[$idx + 1]);
47 } elsif ( defined $val && $val eq '-package_logger' ) {
48 _set_package_logger_for(scalar caller, $_[$idx + 1]);
50 } elsif ( defined $val && $val eq '-default_logger' ) {
51 _set_default_logger_for(scalar caller, $_[$idx + 1]);
55 $package->export_to_level(1, $package, @_);
62 sub _set_default_logger_for {
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 } }
69 $Default_Logger{$_[0]} = $logger
72 sub _set_package_logger_for {
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 } }
79 $Package_Logger{$_[0]} = $logger
85 $Package_Logger{$package} ||
87 $Default_Logger{$package} ||
88 die q( no logger set! you can't try to log something without a logger! )
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 } }
100 warn 'set_logger (or -logger) called more than once! This is a bad idea!'
102 $Get_Logger = $logger;
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 } }
112 local $Get_Logger = $logger;
122 $logger->$level($code->(@_))
123 if $logger->${\"is_$level"};
133 $logger->$level($code->($value))
134 if $logger->${\"is_$level"};
138 for my $level (@levels) {
141 *{"log_$level"} = sub (&@) {
142 _do_log( $level => _get_logger( caller ), shift @_, @_)
145 *{"logS_$level"} = sub (&$) {
146 _do_logS( $level => _get_logger( caller ), $_[0], $_[1])
149 *{"Dlog_$level"} = sub (&@) {
150 my ($code, @args) = @_;
151 return _do_log( $level => _get_logger( caller ), sub {
152 local $_ = (@args?Data::Dumper::Concise::Dumper @args:'()');
157 *{"DlogS_$level"} = sub (&$) {
158 my ($code, $ref) = @_;
159 _do_logS( $level => _get_logger( caller ), sub {
160 local $_ = Data::Dumper::Concise::Dumper $ref;
172 Log::Contextual - Simple logging interface with a contextual log
176 use Log::Contextual qw( :log :dlog set_logger with_logger );
177 use Log::Contextual::SimpleLogger;
178 use Log::Log4perl ':easy';
179 Log::Log4perl->easy_init($DEBUG);
182 my $logger = Log::Log4perl->get_logger;
186 log_debug { 'program started' };
189 with_logger(Log::Contextual::SimpleLogger->new({
190 levels => [qw( trace debug )]
192 log_trace { 'foo entered' };
193 my ($foo, $bar) = Dlog_trace { "params for foo: $_" } @_;
195 log_trace { 'foo left' };
201 Beginning with version 1.008 L<Log::Dispatchouli> also works out of the box
202 with C<Log::Contextual>:
204 use Log::Contextual qw( :log :dlog set_logger );
205 use Log::Dispatchouli;
206 my $ld = Log::Dispatchouli->new({
207 ident => 'slrtbrfst',
214 log_debug { 'program started' };
218 This module is a simple interface to extensible logging. It is bundled with a
219 really basic logger, L<Log::Contextual::SimpleLogger>, but in general you
220 should use a real logger instead of that. For something more serious but not
221 overly complicated, try L<Log::Dispatchouli> (see L</SYNOPSIS> for example.)
223 The reason for this module is to abstract your logging interface so that
224 logging is as painless as possible, while still allowing you to switch from one
231 When you import this module you may use C<-logger> as a shortcut for
232 L<set_logger>, for example:
234 use Log::Contextual::SimpleLogger;
235 use Log::Contextual qw( :dlog ),
236 -logger => Log::Contextual::SimpleLogger->new({ levels => [qw( debug )] });
238 sometimes you might want to have the logger handy for other stuff, in which
239 case you might try something like the following:
242 BEGIN { $var_log = VarLogger->new }
243 use Log::Contextual qw( :dlog ), -logger => $var_log;
245 =head2 -package_logger
247 The C<-package_logger> import option is similar to the C<-logger> import option
248 except C<-package_logger> sets the the logger for the current package.
250 Unlike L</-default_logger>, C<-package_logger> cannot be overridden with
254 use Log::Contextual::SimpleLogger;
255 use Log::Contextual qw( :log ),
256 -package_logger => Log::Contextual::WarnLogger->new({
257 env_prefix => 'MY_PACKAGE'
260 If you are interested in using this package for a module you are putting on
261 CPAN we recommend L<Log::Contextual::WarnLogger> for your package logger.
263 =head2 -default_logger
265 The C<-default_logger> import option is similar to the C<-logger> import option
266 except C<-default_logger> sets the the B<default> logger for the current package.
268 Basically it sets the logger to be used if C<set_logger> is never called; so
271 use Log::Contextual::SimpleLogger;
272 use Log::Contextual qw( :log ),
273 -default_logger => Log::Contextual::WarnLogger->new({
274 env_prefix => 'MY_PACKAGE'
277 =head1 A WORK IN PROGRESS
279 This module is certainly not complete, but we will not break the interface
280 lightly, so I would say it's safe to use in production code. The main result
281 from that at this point is that doing:
285 will die as we do not yet know what the defaults should be. If it turns out
286 that nearly everyone uses the C<:log> tag and C<:dlog> is really rare, we'll
287 probably make C<:log> the default. But only time and usage will tell.
293 my $logger = WarnLogger->new;
296 Arguments: C<Ref|CodeRef $returning_logger>
298 C<set_logger> will just set the current logger to whatever you pass it. It
299 expects a C<CodeRef>, but if you pass it something else it will wrap it in a
300 C<CodeRef> for you. C<set_logger> is really meant only to be called from a
301 top-level script. To avoid foot-shooting the function will warn if you call it
306 my $logger = WarnLogger->new;
307 with_logger $logger => sub {
309 log_fatal { 'Non Logical Universe Detected' };
311 log_info { 'All is good' };
315 Arguments: C<Ref|CodeRef $returning_logger, CodeRef $to_execute>
317 C<with_logger> sets the logger for the scope of the C<CodeRef> C<$to_execute>.
318 As with L</set_logger>, C<with_logger> will wrap C<$returning_logger> with a
319 C<CodeRef> if needed.
325 Arguments: C<CodeRef $returning_message, @args>
327 All of the following six functions work the same except that a different method
328 is called on the underlying C<$logger> object. The basic pattern is:
330 sub log_$level (&@) {
331 if ($logger->is_$level) {
332 $logger->$level(shift->(@_));
337 Note that the function returns it's arguments. This can be used in a number of
338 ways, but often it's convenient just for partial inspection of passthrough data
340 my @friends = log_trace {
341 'friends list being generated, data from first friend: ' .
342 Dumper($_[0]->TO_JSON)
343 } generate_friend_list();
345 If you want complete inspection of passthrough data, take a look at the
346 L</Dlog_$level> functions.
350 log_trace { 'entered method foo with args ' join q{,}, @args };
354 log_debug { 'entered method foo' };
358 log_info { 'started process foo' };
362 log_warn { 'possible misconfiguration at line 10' };
366 log_error { 'non-numeric user input!' };
370 log_fatal { '1 is never equal to 0!' };
376 Arguments: C<CodeRef $returning_message, Item $arg>
378 This is really just a special case of the L</log_$level> functions. It forces
379 scalar context when that is what you need. Other than that it works exactly
382 my $friend = logS_trace {
383 'I only have one friend: ' . Dumper($_[0]->TO_JSON)
386 See also: L</DlogS_$level>.
392 Arguments: C<CodeRef $returning_message, @args>
394 All of the following six functions work the same as their L</log_$level>
395 brethren, except they return what is passed into them and put the stringified
396 (with L<Data::Dumper::Concise>) version of their args into C<$_>. This means
397 you can do cool things like the following:
399 my @nicks = Dlog_debug { "names: $_" } map $_->value, $frew->names->all;
401 and the output might look something like:
411 my ($foo, $bar) = Dlog_trace { "entered method foo with args: $_" } @_;
415 Dlog_debug { "random data structure: $_" } { foo => $bar };
419 return Dlog_info { "html from method returned: $_" } "<html>...</html>";
423 Dlog_warn { "probably invalid value: $_" } $foo;
427 Dlog_error { "non-numeric user input! ($_)" } $port;
431 Dlog_fatal { '1 is never equal to 0!' } 'ZOMG ZOMG' if 1 == 0;
437 Arguments: C<CodeRef $returning_message, Item $arg>
439 Like L</logS_$level>, these functions are a special case of L</Dlog_$level>.
440 They only take a single scalar after the C<$returning_message> instead of
441 slurping up (and also setting C<wantarray>) all the C<@args>
443 my $pals_rs = DlogS_debug { "pals resultset: $_" }
444 $schema->resultset('Pals')->search({ perlers => 1 });
446 =head1 LOGGER INTERFACE
448 Because this module is ultimately pretty looking glue (glittery?) with the
449 awesome benefit of the Contextual part, users will often want to make their
450 favorite logger work with it. The following are the methods that should be
451 implemented in the logger:
466 The first six merely need to return true if that level is enabled. The latter
467 six take the results of whatever the user returned from their coderef and log
468 them. For a basic example see L<Log::Contextual::SimpleLogger>.
472 frew - Arthur Axel "fREW" Schmidt <frioux@gmail.com>
476 mst - Matt S. Trout <mst@shadowcat.co.uk>
480 Copyright (c) 2010 the Log::Contextual L</AUTHOR> and L</DESIGNER> as listed
485 This library is free software and may be distributed under the same terms as