1Log::Contextual(3) User Contributed Perl Documentation Log::Contextual(3)
2
3
4
6 Log::Contextual - Simple logging interface with a contextual log
7
9 version 0.008001
10
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
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
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
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
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
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
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
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
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
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
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
572 mst - Matt S. Trout <mst@shadowcat.co.uk>
573
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.36.0 2023-01-20 Log::Contextual(3)