1 package Log::Contextual;
6 our $VERSION = '0.00305';
8 my @levels = qw(debug trace warn info error fatal);
10 use Exporter::Declare;
11 use Exporter::Declare::Export::Generator;
12 use Data::Dumper::Concise;
13 use Scalar::Util 'blessed';
15 my @dlog = ((map "Dlog_$_", @levels), (map "DlogS_$_", @levels));
17 my @log = ((map "log_$_", @levels), (map "logS_$_", @levels));
20 require Log::Log4perl;
21 die if $Log::Log4perl::VERSION < 1.29;
22 Log::Log4perl->wrapper_register(__PACKAGE__)
27 qw( set_logger with_logger )
30 export_tag dlog => @dlog;
31 export_tag log => @log;
32 import_arguments qw(logger package_logger default_logger);
35 my ($class, $importer, $spec) = @_;
37 die 'Log::Contextual does not have a default import list'
38 if $spec->config->{default};
40 my @levels = qw(debug trace warn info error fatal);
41 if ( my $levels = $spec->config->{levels} ) {
44 for my $level (@levels) {
45 if ($spec->config->{log}) {
46 $spec->add_export("&log_$level", sub (&@) {
47 _do_log( $level => _get_logger( caller ), shift @_, @_)
49 $spec->add_export("&logS_$level", sub (&@) {
50 _do_logS( $level => _get_logger( caller ), $_[0], $_[1])
53 if ($spec->config->{dlog}) {
54 $spec->add_export("&Dlog_$level", sub (&@) {
55 my ($code, @args) = @_;
56 return _do_log( $level => _get_logger( caller ), sub {
57 local $_ = (@args?Data::Dumper::Concise::Dumper @args:'()');
61 $spec->add_export("&DlogS_$level", sub (&$) {
62 my ($code, $ref) = @_;
63 _do_logS( $level => _get_logger( caller ), sub {
64 local $_ = Data::Dumper::Concise::Dumper $ref;
73 my ($class, $importer, $specs) = @_;
75 set_logger( $specs->config->{logger} )
76 if $specs->config->{logger};
78 _set_package_logger_for( $importer, $specs->config->{package_logger} )
79 if $specs->config->{package_logger};
81 _set_default_logger_for( $importer, $specs->config->{default_logger} )
82 if $specs->config->{default_logger};
89 sub _set_default_logger_for {
91 if(ref $logger ne 'CODE') {
92 die 'logger was not a CodeRef or a logger object. Please try again.'
93 unless blessed($logger);
94 $logger = do { my $l = $logger; sub { $l } }
96 $Default_Logger{$_[0]} = $logger
99 sub _set_package_logger_for {
101 if(ref $logger ne 'CODE') {
102 die 'logger was not a CodeRef or a logger object. Please try again.'
103 unless blessed($logger);
104 $logger = do { my $l = $logger; sub { $l } }
106 $Package_Logger{$_[0]} = $logger
112 $Package_Logger{$package} ||
114 $Default_Logger{$package} ||
115 die q( no logger set! you can't try to log something without a logger! )
121 if(ref $logger ne 'CODE') {
122 die 'logger was not a CodeRef or a logger object. Please try again.'
123 unless blessed($logger);
124 $logger = do { my $l = $logger; sub { $l } }
127 warn 'set_logger (or -logger) called more than once! This is a bad idea!'
129 $Get_Logger = $logger;
134 if(ref $logger ne 'CODE') {
135 die 'logger was not a CodeRef or a logger object. Please try again.'
136 unless blessed($logger);
137 $logger = do { my $l = $logger; sub { $l } }
139 local $Get_Logger = $logger;
149 $logger->$level($code->(@_))
150 if $logger->${\"is_$level"};
160 $logger->$level($code->($value))
161 if $logger->${\"is_$level"};
171 Log::Contextual - Simple logging interface with a contextual log
175 use Log::Contextual qw( :log :dlog set_logger with_logger );
176 use Log::Contextual::SimpleLogger;
177 use Log::Log4perl ':easy';
178 Log::Log4perl->easy_init($DEBUG);
181 my $logger = Log::Log4perl->get_logger;
185 log_debug { 'program started' };
188 with_logger(Log::Contextual::SimpleLogger->new({
189 levels => [qw( trace debug )]
191 log_trace { 'foo entered' };
192 my ($foo, $bar) = Dlog_trace { "params for foo: $_" } @_;
194 log_trace { 'foo left' };
200 Beginning with version 1.008 L<Log::Dispatchouli> also works out of the box
201 with C<Log::Contextual>:
203 use Log::Contextual qw( :log :dlog set_logger );
204 use Log::Dispatchouli;
205 my $ld = Log::Dispatchouli->new({
206 ident => 'slrtbrfst',
213 log_debug { 'program started' };
217 This module is a simple interface to extensible logging. It is bundled with a
218 really basic logger, L<Log::Contextual::SimpleLogger>, but in general you
219 should use a real logger instead of that. For something more serious but not
220 overly complicated, try L<Log::Dispatchouli> (see L</SYNOPSIS> for example.)
222 The reason for this module is to abstract your logging interface so that
223 logging is as painless as possible, while still allowing you to switch from one
230 When you import this module you may use C<-logger> as a shortcut for
231 L<set_logger>, for example:
233 use Log::Contextual::SimpleLogger;
234 use Log::Contextual qw( :dlog ),
235 -logger => Log::Contextual::SimpleLogger->new({ levels => [qw( debug )] });
237 sometimes you might want to have the logger handy for other stuff, in which
238 case you might try something like the following:
241 BEGIN { $var_log = VarLogger->new }
242 use Log::Contextual qw( :dlog ), -logger => $var_log;
244 =head2 -package_logger
246 The C<-package_logger> import option is similar to the C<-logger> import option
247 except C<-package_logger> sets the the logger for the current package.
249 Unlike L</-default_logger>, C<-package_logger> cannot be overridden with
253 use Log::Contextual::SimpleLogger;
254 use Log::Contextual qw( :log ),
255 -package_logger => Log::Contextual::WarnLogger->new({
256 env_prefix => 'MY_PACKAGE'
259 If you are interested in using this package for a module you are putting on
260 CPAN we recommend L<Log::Contextual::WarnLogger> for your package logger.
262 =head2 -default_logger
264 The C<-default_logger> import option is similar to the C<-logger> import option
265 except C<-default_logger> sets the the B<default> logger for the current package.
267 Basically it sets the logger to be used if C<set_logger> is never called; so
270 use Log::Contextual::SimpleLogger;
271 use Log::Contextual qw( :log ),
272 -default_logger => Log::Contextual::WarnLogger->new({
273 env_prefix => 'MY_PACKAGE'
276 =head1 A WORK IN PROGRESS
278 This module is certainly not complete, but we will not break the interface
279 lightly, so I would say it's safe to use in production code. The main result
280 from that at this point is that doing:
284 will die as we do not yet know what the defaults should be. If it turns out
285 that nearly everyone uses the C<:log> tag and C<:dlog> is really rare, we'll
286 probably make C<:log> the default. But only time and usage will tell.
292 my $logger = WarnLogger->new;
295 Arguments: C<Ref|CodeRef $returning_logger>
297 C<set_logger> will just set the current logger to whatever you pass it. It
298 expects a C<CodeRef>, but if you pass it something else it will wrap it in a
299 C<CodeRef> for you. C<set_logger> is really meant only to be called from a
300 top-level script. To avoid foot-shooting the function will warn if you call it
305 my $logger = WarnLogger->new;
306 with_logger $logger => sub {
308 log_fatal { 'Non Logical Universe Detected' };
310 log_info { 'All is good' };
314 Arguments: C<Ref|CodeRef $returning_logger, CodeRef $to_execute>
316 C<with_logger> sets the logger for the scope of the C<CodeRef> C<$to_execute>.
317 As with L</set_logger>, C<with_logger> will wrap C<$returning_logger> with a
318 C<CodeRef> if needed.
324 Arguments: C<CodeRef $returning_message, @args>
326 All of the following six functions work the same except that a different method
327 is called on the underlying C<$logger> object. The basic pattern is:
329 sub log_$level (&@) {
330 if ($logger->is_$level) {
331 $logger->$level(shift->(@_));
336 Note that the function returns it's arguments. This can be used in a number of
337 ways, but often it's convenient just for partial inspection of passthrough data
339 my @friends = log_trace {
340 'friends list being generated, data from first friend: ' .
341 Dumper($_[0]->TO_JSON)
342 } generate_friend_list();
344 If you want complete inspection of passthrough data, take a look at the
345 L</Dlog_$level> functions.
349 log_trace { 'entered method foo with args ' join q{,}, @args };
353 log_debug { 'entered method foo' };
357 log_info { 'started process foo' };
361 log_warn { 'possible misconfiguration at line 10' };
365 log_error { 'non-numeric user input!' };
369 log_fatal { '1 is never equal to 0!' };
375 Arguments: C<CodeRef $returning_message, Item $arg>
377 This is really just a special case of the L</log_$level> functions. It forces
378 scalar context when that is what you need. Other than that it works exactly
381 my $friend = logS_trace {
382 'I only have one friend: ' . Dumper($_[0]->TO_JSON)
385 See also: L</DlogS_$level>.
391 Arguments: C<CodeRef $returning_message, @args>
393 All of the following six functions work the same as their L</log_$level>
394 brethren, except they return what is passed into them and put the stringified
395 (with L<Data::Dumper::Concise>) version of their args into C<$_>. This means
396 you can do cool things like the following:
398 my @nicks = Dlog_debug { "names: $_" } map $_->value, $frew->names->all;
400 and the output might look something like:
410 my ($foo, $bar) = Dlog_trace { "entered method foo with args: $_" } @_;
414 Dlog_debug { "random data structure: $_" } { foo => $bar };
418 return Dlog_info { "html from method returned: $_" } "<html>...</html>";
422 Dlog_warn { "probably invalid value: $_" } $foo;
426 Dlog_error { "non-numeric user input! ($_)" } $port;
430 Dlog_fatal { '1 is never equal to 0!' } 'ZOMG ZOMG' if 1 == 0;
436 Arguments: C<CodeRef $returning_message, Item $arg>
438 Like L</logS_$level>, these functions are a special case of L</Dlog_$level>.
439 They only take a single scalar after the C<$returning_message> instead of
440 slurping up (and also setting C<wantarray>) all the C<@args>
442 my $pals_rs = DlogS_debug { "pals resultset: $_" }
443 $schema->resultset('Pals')->search({ perlers => 1 });
445 =head1 LOGGER INTERFACE
447 Because this module is ultimately pretty looking glue (glittery?) with the
448 awesome benefit of the Contextual part, users will often want to make their
449 favorite logger work with it. The following are the methods that should be
450 implemented in the logger:
465 The first six merely need to return true if that level is enabled. The latter
466 six take the results of whatever the user returned from their coderef and log
467 them. For a basic example see L<Log::Contextual::SimpleLogger>.
471 frew - Arthur Axel "fREW" Schmidt <frioux@gmail.com>
475 mst - Matt S. Trout <mst@shadowcat.co.uk>
479 Copyright (c) 2010 the Log::Contextual L</AUTHOR> and L</DESIGNER> as listed
484 This library is free software and may be distributed under the same terms as