1Log::Contextual(3)    User Contributed Perl Documentation   Log::Contextual(3)
2
3
4

NAME

6       Log::Contextual - Simple logging interface with a contextual log
7

VERSION

9       version 0.008001
10

SYNOPSIS

12        use Log::Contextual qw( :log :dlog set_logger with_logger );
13        use Log::Contextual::SimpleLogger;
14        use Log::Log4perl ':easy';
15        Log::Log4perl->easy_init($DEBUG);
16
17        my $logger  = Log::Log4perl->get_logger;
18
19        set_logger $logger;
20
21        log_debug { 'program started' };
22
23        sub foo {
24
25          my $minilogger = Log::Contextual::SimpleLogger->new({
26            levels => [qw( trace debug )]
27          });
28
29          my @args = @_;
30
31          with_logger $minilogger => sub {
32            log_trace { 'foo entered' };
33            my ($foo, $bar) = Dlog_trace { "params for foo: $_" } @args;
34            # ...
35            slog_trace 'foo left';
36          };
37        }
38
39        foo();
40
41       Beginning with version 1.008 Log::Dispatchouli also works out of the
42       box with "Log::Contextual":
43
44        use Log::Contextual qw( :log :dlog set_logger );
45        use Log::Dispatchouli;
46        my $ld = Log::Dispatchouli->new({
47           ident     => 'slrtbrfst',
48           to_stderr => 1,
49           debug     => 1,
50        });
51
52        set_logger $ld;
53
54        log_debug { 'program started' };
55

DESCRIPTION

57       Major benefits:
58
59       • Efficient
60
61         The default logging functions take blocks, so if a log level is
62         disabled, the block will not run:
63
64          # the following won't run if debug is off
65          log_debug { "the new count in the database is " . $rs->count };
66
67         Similarly, the "D" prefixed methods only "Dumper" the input if the
68         level is enabled.
69
70       • Handy
71
72         The logging functions return their arguments, so you can stick them
73         in the middle of expressions:
74
75          for (log_debug { "downloading:\n" . join qq(\n), @_ } @urls) { ... }
76
77       • Generic
78
79         "Log::Contextual" is an interface for all major loggers.  If you log
80         through "Log::Contextual" you will be able to swap underlying loggers
81         later.
82
83       • Powerful
84
85         "Log::Contextual" chooses which logger to use based on user defined
86         "CodeRef"s.  Normally you don't need to know this, but you can take
87         advantage of it when you need to later.
88
89       • Scalable
90
91         If you just want to add logging to your basic application, start with
92         Log::Contextual::SimpleLogger and then as your needs grow you can
93         switch to Log::Dispatchouli or Log::Dispatch or Log::Log4perl or
94         whatever else.
95
96       This module is a simple interface to extensible logging.  It exists to
97       abstract your logging interface so that logging is as painless as
98       possible, while still allowing you to switch from one logger to
99       another.
100
101       It is bundled with a really basic logger,
102       Log::Contextual::SimpleLogger, but in general you should use a real
103       logger instead.  For something more serious but not overly complicated,
104       try Log::Dispatchouli (see "SYNOPSIS" for example.)
105

A WORK IN PROGRESS

107       This module is certainly not complete, but we will not break the
108       interface lightly, so I would say it's safe to use in production code.
109       The main result from that at this point is that doing:
110
111        use Log::Contextual;
112
113       will die as we do not yet know what the defaults should be.  If it
114       turns out that nearly everyone uses the ":log" tag and ":dlog" is
115       really rare, we'll probably make ":log" the default.  But only time and
116       usage will tell.
117

IMPORT OPTIONS

119       See "SETTING DEFAULT IMPORT OPTIONS" for information on setting these
120       project wide.
121
122   -logger
123       When you import this module you may use "-logger" as a shortcut for
124       "set_logger", for example:
125
126        use Log::Contextual::SimpleLogger;
127        use Log::Contextual qw( :dlog ),
128          -logger => Log::Contextual::SimpleLogger->new({ levels => [qw( debug )] });
129
130       sometimes you might want to have the logger handy for other stuff, in
131       which case you might try something like the following:
132
133        my $var_log;
134        BEGIN { $var_log = VarLogger->new }
135        use Log::Contextual qw( :dlog ), -logger => $var_log;
136
137   -levels
138       The "-levels" import option allows you to define exactly which levels
139       your logger supports.  So the default, "[qw(debug trace warn info error
140       fatal)]", works great for Log::Log4perl, but it doesn't support the
141       levels for Log::Dispatch.  But supporting those levels is as easy as
142       doing
143
144        use Log::Contextual
145          -levels => [qw( debug info notice warning error critical alert emergency )];
146
147   -package_logger
148       The "-package_logger" import option is similar to the "-logger" import
149       option except "-package_logger" sets the logger for the current
150       package.
151
152       Unlike "-default_logger", "-package_logger" cannot be overridden with
153       "set_logger" or "with_logger".
154
155        package My::Package;
156        use Log::Contextual::SimpleLogger;
157        use Log::Contextual qw( :log ),
158          -package_logger => Log::Contextual::WarnLogger->new({
159             env_prefix => 'MY_PACKAGE'
160          });
161
162       If you are interested in using this package for a module you are
163       putting on CPAN we recommend Log::Contextual::WarnLogger for your
164       package logger.
165
166   -default_logger
167       The "-default_logger" import option is similar to the "-logger" import
168       option except "-default_logger" sets the default logger for the current
169       package.
170
171       Basically it sets the logger to be used if "set_logger" is never
172       called; so
173
174        package My::Package;
175        use Log::Contextual::SimpleLogger;
176        use Log::Contextual qw( :log ),
177          -default_logger => Log::Contextual::WarnLogger->new({
178             env_prefix => 'MY_PACKAGE'
179          });
180

SETTING DEFAULT IMPORT OPTIONS

182       Eventually you will get tired of writing the following in every single
183       one of your packages:
184
185        use Log::Log4perl;
186        use Log::Log4perl ':easy';
187        BEGIN { Log::Log4perl->easy_init($DEBUG) }
188
189        use Log::Contextual -logger => Log::Log4perl->get_logger;
190
191       You can set any of the import options for your whole project if you
192       define your own "Log::Contextual" subclass as follows:
193
194        package MyApp::Log::Contextual;
195
196        use base 'Log::Contextual';
197
198        use Log::Log4perl ':easy';
199        Log::Log4perl->easy_init($DEBUG)
200
201        sub arg_default_logger { $_[1] || Log::Log4perl->get_logger }
202        sub arg_levels { [qw(debug trace warn info error fatal custom_level)] }
203        sub default_import { ':log' }
204
205        # or maybe instead of default_logger
206        sub arg_package_logger { $_[1] }
207
208        # and almost definitely not this, which is only here for completeness
209        sub arg_logger { $_[1] }
210
211       Note the "$_[1] ||" in "arg_default_logger".  All of these methods are
212       passed the values passed in from the arguments to the subclass, so you
213       can either throw them away, honor them, die on usage, etc.  To be
214       clear, if you define your subclass, and someone uses it as follows:
215
216        use MyApp::Log::Contextual -default_logger => $foo,
217                                   -levels => [qw(bar baz biff)];
218
219       Your "arg_default_logger" method will get $foo and your "arg_levels"
220       will get "[qw(bar baz biff)]";
221
222       Additionally, the "default_import" method is what happens if a user
223       tries to use your subclass with no arguments.  The default just dies,
224       but if you'd like to change the default to import a tag merely return
225       the tags you'd like to import.  So the following will all work:
226
227        sub default_import { ':log' }
228
229        sub default_import { ':dlog' }
230
231        sub default_import { qw(:dlog :log ) }
232
233       See Log::Contextual::Easy::Default for an example of a subclass of
234       "Log::Contextual" that makes use of default import options.
235

FUNCTIONS

237   set_logger
238        my $logger = WarnLogger->new;
239        set_logger $logger;
240
241       Arguments: "LOGGER CODEREF"
242
243       "set_logger" will just set the current logger to whatever you pass it.
244       It expects a "CodeRef", but if you pass it something else it will wrap
245       it in a "CodeRef" for you.  "set_logger" is really meant only to be
246       called from a top-level script.  To avoid foot-shooting the function
247       will warn if you call it more than once.
248
249   with_logger
250        my $logger = WarnLogger->new;
251        with_logger $logger => sub {
252           if (1 == 0) {
253              log_fatal { 'Non Logical Universe Detected' };
254           } else {
255              log_info  { 'All is good' };
256           }
257        };
258
259       Arguments: "LOGGER CODEREF", "CodeRef $to_execute"
260
261       "with_logger" sets the logger for the scope of the "CodeRef"
262       $to_execute.  As with "set_logger", "with_logger" will wrap
263       $returning_logger with a "CodeRef" if needed.
264
265   has_logger
266        my $logger = WarnLogger->new;
267        set_logger $logger unless has_logger;
268
269       Arguments: none
270
271       "has_logger" will return true if a logger has been set.
272
273   log_$level
274       Import Tag: ":log"
275
276       Arguments: "CodeRef $returning_message, @args"
277
278       "log_$level" functions all work the same except that a different method
279       is called on the underlying $logger object.  The basic pattern is:
280
281        sub log_$level (&@) {
282          if ($logger->is_$level) {
283            $logger->$level(shift->(@_));
284          }
285          @_
286        }
287
288       Note that the function returns it's arguments.  This can be used in a
289       number of ways, but often it's convenient just for partial inspection
290       of passthrough data
291
292        my @friends = log_trace {
293          'friends list being generated, data from first friend: ' .
294            Dumper($_[0]->TO_JSON)
295        } generate_friend_list();
296
297       If you want complete inspection of passthrough data, take a look at the
298       "Dlog_$level" functions.
299
300       Which functions are exported depends on what was passed to "-levels".
301       The default (no "-levels" option passed) would export:
302
303       log_trace
304       log_debug
305       log_info
306       log_warn
307       log_error
308       log_fatal
309         Note: "log_fatal" does not call "die" for you, see "EXCEPTIONS AND
310         ERROR HANDLING"
311
312   slog_$level
313       Mostly the same as "log_$level", but expects a string as first
314       argument, not a block. Arguments are passed through just the same, but
315       since it's just a string, interpolation of arguments into it must be
316       done manually.
317
318        my @friends = slog_trace 'friends list being generated.', generate_friend_list();
319
320   logS_$level
321       Import Tag: ":log"
322
323       Arguments: "CodeRef $returning_message, Item $arg"
324
325       This is really just a special case of the "log_$level" functions.  It
326       forces scalar context when that is what you need.  Other than that it
327       works exactly same:
328
329        my $friend = logS_trace {
330          'I only have one friend: ' .  Dumper($_[0]->TO_JSON)
331        } friend();
332
333       See also: "DlogS_$level".
334
335   slogS_$level
336       Mostly the same as "logS_$level", but expects a string as first
337       argument, not a block. Arguments are passed through just the same, but
338       since it's just a string, interpolation of arguments into it must be
339       done manually.
340
341        my $friend = slogS_trace 'I only have one friend.', friend();
342
343   Dlog_$level
344       Import Tag: ":dlog"
345
346       Arguments: "CodeRef $returning_message, @args"
347
348       All of the following six functions work the same as their "log_$level"
349       brethren, except they return what is passed into them and put the
350       stringified (with Data::Dumper::Concise) version of their args into $_.
351       This means you can do cool things like the following:
352
353        my @nicks = Dlog_debug { "names: $_" } map $_->value, $frew->names->all;
354
355       and the output might look something like:
356
357        names: "fREW"
358        "fRIOUX"
359        "fROOH"
360        "fRUE"
361        "fiSMBoC"
362
363       Which functions are exported depends on what was passed to "-levels".
364       The default (no "-levels" option passed) would export:
365
366       Dlog_trace
367       Dlog_debug
368       Dlog_info
369       Dlog_warn
370       Dlog_error
371       Dlog_fatal
372         Note: "Dlog_fatal" does not call "die" for you, see "EXCEPTIONS AND
373         ERROR HANDLING"
374
375   Dslog_$level
376       Mostly the same as "Dlog_$level", but expects a string as first
377       argument, not a block. Arguments are passed through just the same, but
378       since it's just a string, no interpolation point can be used, instead
379       the Dumper output is appended.
380
381        my @nicks = Dslog_debug "names: ", map $_->value, $frew->names->all;
382
383   DlogS_$level
384       Import Tag: ":dlog"
385
386       Arguments: "CodeRef $returning_message, Item $arg"
387
388       Like "logS_$level", these functions are a special case of
389       "Dlog_$level".  They only take a single scalar after the
390       $returning_message instead of slurping up (and also setting
391       "wantarray") all the @args
392
393        my $pals_rs = DlogS_debug { "pals resultset: $_" }
394          $schema->resultset('Pals')->search({ perlers => 1 });
395
396   DslogS_$level
397       Mostly the same as "DlogS_$level", but expects a string as first
398       argument, not a block. Arguments are passed through just the same, but
399       since it's just a string, no interpolation point can be used, instead
400       the Dumper output is appended.
401
402        my $pals_rs = DslogS_debug "pals resultset: ",
403          $schema->resultset('Pals')->search({ perlers => 1 });
404

LOGGER CODEREF

406       Anywhere a logger object can be passed, a coderef is accepted.  This is
407       so that the user can use different logger objects based on runtime
408       information.  The logger coderef is passed the package of the caller,
409       and the caller level the coderef needs to use if it wants more caller
410       information.  The latter is in a hashref to allow for more options in
411       the future.
412
413       Here is a basic example of a logger that exploits "caller" to reproduce
414       the output of "warn" with a logger:
415
416        my @caller_info;
417        my $var_log = Log::Contextual::SimpleLogger->new({
418           levels  => [qw(trace debug info warn error fatal)],
419           coderef => sub { chomp($_[0]); warn "$_[0] at $caller_info[1] line $caller_info[2].\n" }
420        });
421        my $warn_faker = sub {
422           my ($package, $args) = @_;
423           @caller_info = caller($args->{caller_level});
424           $var_log
425        };
426        set_logger($warn_faker);
427        log_debug { 'test' };
428
429       The following is an example that uses the information passed to the
430       logger coderef.  It sets the global logger to $l3, the logger for the
431       "A1" package to $l1, except the "lol" method in "A1" which uses the $l2
432       logger and lastly the logger for the "A2" package to $l2.
433
434       Note that it increases the caller level as it dispatches based on where
435       the caller of the log function, not the log function itself.
436
437        my $complex_dispatcher = do {
438
439           my $l1 = ...;
440           my $l2 = ...;
441           my $l3 = ...;
442
443           my %registry = (
444              -logger => $l3,
445              A1 => {
446                 -logger => $l1,
447                 lol     => $l2,
448              },
449              A2 => { -logger => $l2 },
450           );
451
452           sub {
453              my ( $package, $info ) = @_;
454
455              my $logger = $registry{'-logger'};
456              if (my $r = $registry{$package}) {
457                 $logger = $r->{'-logger'} if $r->{'-logger'};
458                 my (undef, undef, undef, $sub) = caller($info->{caller_level} + 1);
459                 $sub =~ s/^\Q$package\E:://g;
460                 $logger = $r->{$sub} if $r->{$sub};
461              }
462              return $logger;
463           }
464        };
465
466        set_logger $complex_dispatcher;
467

LOGGER INTERFACE

469       Because this module is ultimately pretty looking glue (glittery?) with
470       the awesome benefit of the Contextual part, users will often want to
471       make their favorite logger work with it.  The following are the methods
472       that should be implemented in the logger:
473
474        is_trace
475        is_debug
476        is_info
477        is_warn
478        is_error
479        is_fatal
480        trace
481        debug
482        info
483        warn
484        error
485        fatal
486
487       The first six merely need to return true if that level is enabled.  The
488       latter six take the results of whatever the user returned from their
489       coderef and log them.  For a basic example see
490       Log::Contextual::SimpleLogger.
491

LOG ROUTING

493       In between the loggers and the log functions is a log router that is
494       responsible for finding a logger to handle the log event and passing
495       the log information to the logger. This relationship is described in
496       the documentation for "Log::Contextual::Role::Router".
497
498       "Log::Contextual" and packages that extend it will by default share a
499       router singleton that implements the with_logger() and set_logger()
500       functions and also respects the -logger, -package_logger, and
501       -default_logger import options with their associated default value
502       functions. The router singleton is available as the return value of the
503       router() function. Users of Log::Contextual may overload router() to
504       return instances of custom log routers that could for example work with
505       loggers that use a different interface.
506

EXCEPTIONS AND ERROR HANDLING

508       "Log::Contextual", by design, does not intentionally invoke "die" on
509       your behalf(*see footnote*) for "log_fatal".
510
511       Logging events are characterized as information, not flow control, and
512       conflating the two results in negative design anti-patterns.
513
514       As such, "log_fatal" would at be better used to communicate information
515       about a future failure, for example:
516
517         if ( condition ) {
518           log_fatal { "Bad Condition is true" };
519           die My::Exception->new();
520         }
521
522       This has a number of benefits:
523
524       •   You're more likely to want to use useful Exception Objects and flow
525           control instead of cheating with log messages.
526
527       •   You're less likely to run a risk of losing what the actual problem
528           was when some error occurs in your creation of the Exception Object
529
530       •   You're less likely to run the risk of losing important log context
531           due to exceptions occurring mid way through "die" unwinding and
532           "exit" global destruction.
533
534       If you're still too lazy to use exceptions, then you can do what you
535       probably want as follows:
536
537         if ( ... ) {
538           log_fatal { "Bad condition is true" };
539           die "Bad condtion is true";
540         }
541
542       Or for ":dlog" style:
543
544         use Data::Dumper::Consise qw( Dumper );
545         if ( ... ) {
546           # Dlog_fatal but not
547           my $reason = "Bad condtion is true because: " . Dumper($thing);
548           log_fatal { $reason };
549           die $reason;
550         }
551
552   footnote
553       The underlying behaviour of "log_fatal" is dependent on the backing
554       library.
555
556       All the Loggers shipping with "Log::Contextual" behave this way, as do
557       many of the supported loggers, like "Log::Log4perl". However, not all
558       loggers work this way, and one must be careful.
559
560       "Log::Dispatch" doesn't support implementing "log_fatal" at all
561
562       "Log::Dispatchouli" implements "log_fatal" using "die" ( via Carp )
563

CONTRIBUTORS

565       kentnl - Kent Fredric <kentfredric@gmail.com>
566
567       triddle - Tyler Riddle <t.riddle@shadowcat.co.uk>
568
569       voj - Jakob Voß <voss@gbv.de>
570

DESIGNER

572       mst - Matt S. Trout <mst@shadowcat.co.uk>
573

AUTHOR

575       Arthur Axel "fREW" Schmidt <frioux+cpan@gmail.com>
576
578       This software is copyright (c) 2018 by Arthur Axel "fREW" Schmidt.
579
580       This is free software; you can redistribute it and/or modify it under
581       the same terms as the Perl 5 programming language system itself.
582
583
584
585perl v5.32.1                      2021-01-27                Log::Contextual(3)
Impressum