1#+##############################################################################
2#                                                                              #
3# File: No/Worries/Log.pm                                                      #
4#                                                                              #
5# Description: logging without worries                                         #
6#                                                                              #
7#-##############################################################################
8
9#
10# module definition
11#
12
13package No::Worries::Log;
14use strict;
15use warnings;
16our $VERSION  = "1.6";
17our $REVISION = sprintf("%d.%02d", q$Revision: 1.17 $ =~ /(\d+)\.(\d+)/);
18
19#
20# used modules
21#
22
23use IO::Handle qw();
24use No::Worries qw($HostName $ProgramName);
25use No::Worries::Date qw(date_stamp);
26use No::Worries::Export qw(export_control);
27use No::Worries::File qw(file_read);
28use No::Worries::Die qw(dief);
29
30#
31# constants
32#
33
34use constant _LEVEL_ERROR   => "error";
35use constant _LEVEL_WARNING => "warning";
36use constant _LEVEL_INFO    => "info";
37use constant _LEVEL_DEBUG   => "debug";
38use constant _LEVEL_TRACE   => "trace";
39
40#
41# global variables
42#
43
44our($Handler);
45
46our(
47    %_KnownLevel,           # hash with known levels
48    %_InterestingLevel,     # hash with interesting levels
49    %_Level2Char,           # hash mapping levels to chars for the output
50    $_MaybeInterestingInfo, # filtering sub (partial)
51    $_InterestingInfo,      # filtering sub (complete)
52    $_ConfigTag,            # dev:ino:mtime of the last configuration file used
53);
54
55#+++############################################################################
56#                                                                              #
57# configuring                                                                  #
58#                                                                              #
59#---############################################################################
60
61#
62# configure the module from the given file (if needed)
63#
64
65sub log_configure ($) {
66    my($path) = @_;
67    my(@stat, $tag);
68
69    @stat = stat($path) or dief("cannot stat(%s): %s", $path, $!);
70    $tag = join(":", $stat[0], $stat[1], $stat[9]);
71    return(0) if $_ConfigTag and $_ConfigTag eq $tag;
72    log_filter(file_read($path));
73    $_ConfigTag = $tag;
74    return(1);
75}
76
77#+++############################################################################
78#                                                                              #
79# filtering                                                                    #
80#                                                                              #
81#---############################################################################
82
83#
84# return the Perl code to use for a given filtering expression
85#
86
87sub _expr_code ($@) {
88    my($partial, $attr, $op, $value) = @_;
89
90    # for partial filtering, we do not care about the message
91    return("1") if $attr eq "message" and $partial;
92    # for the attributes we know about, it is easy
93    return("\$info->{$attr} $op $value")
94        if $attr =~ /^(level|time|program|host|file|line|sub|message)$/;
95    # for the other attributes, the test always fails if not defined
96    return("(defined(\$info->{$attr}) and \$info->{$attr} $op $value)");
97}
98
99#
100# compile the given filter
101#
102
103sub _compile_filter ($@) {
104    my($partial, @filter) = @_;
105    my(@code, $code, $sub);
106
107    @code = (
108        "package No::Worries::Log::Filter;",
109        "use strict;",
110        "use warnings;",
111        "\$sub = sub {",
112        "  my(\$info) = \@_;",
113        "  return(1) if",
114    );
115    foreach my $expr (@filter) {
116        if (ref($expr) eq "ARRAY") {
117            push(@code, "    " . _expr_code($partial, @{ $expr }));
118        } else {
119            push(@code, "    $expr");
120        }
121    }
122    $code[-1] .= ";";
123    push(@code,
124         "  return(0);",
125         "}",
126    );
127    $code = join("\n", @code);
128    eval($code); ## no critic 'BuiltinFunctions::ProhibitStringyEval'
129    dief("invalid code built: %s", $@) if $@;
130    return($sub);
131}
132
133#
134# parse a single filtering expression
135#
136
137sub _parse_expr ($$$) {
138    my($line, $level, $expr) = @_;
139    my($attr, $op, $value);
140
141    # we first parse the (attr, op, value) triplet
142    if ($_KnownLevel{$expr}) {
143        # there can be only one level per filter line and we keep track of it
144        dief("invalid filter line: %s", $line) if ${ $level };
145        ${ $level } = $expr;
146        ($attr, $op, $value) = ("level", "==", $expr);
147    } elsif ($expr =~ /^(\w+)(==|!=)$/ and $1 ne "level") {
148        # special case for comparison with empty string
149        ($attr, $op, $value) = ($1, $2, "");
150    } elsif ($expr =~ /^(\w+)(==|!=|=~|!~|>=?|<=?)(\S+)$/ and $1 ne "level") {
151        # normal case
152        ($attr, $op, $value) = ($1, $2, $3);
153    } else {
154        dief("invalid filter expression: %s", $expr);
155    }
156    # we then check the value
157    if ($op eq "=~" or $op eq "!~") {
158        # match: check that the value is a valid regular expression
159        eval { $expr =~ /$value/ };
160        dief("invalid regexp: %s", $value) if $@;
161        $value = "m\0$value\0";
162    } elsif ($op eq "==" or $op eq "!=") {
163        # equality: adjust according to type
164        unless ($value =~ /^-?\d+$/) {
165            $op = $op eq "==" ? "eq" : "ne";
166            $value = "qq\0$value\0";
167        }
168    } else {
169        # numerical: check that the value is a valid integer
170        dief("invalid integer: %s", $value) unless $value =~ /^-?\d+$/;
171    }
172    # so far, so good
173    return([ $attr, $op, $value ]);
174}
175
176#
177# parse and compile the filter to use
178#
179
180sub log_filter ($) {
181    my($filter) = @_;
182    my($and_re, $or_re, $level, @list, @filter, %il, $ii, $mii);
183
184    # strip comments and empty lines and extra spaces
185    @list = ();
186    foreach my $line (split(/\n/, $filter)) {
187        $line =~ s/^\s+//;
188        $line =~ s/\s+$//;
189        $line =~ s/\s+/ /g;
190        next if $line eq "" or $line =~ /^\#/;
191        push(@list, $line);
192    }
193    $filter = join("\n", @list);
194    # find out how to split lines and expressions
195    if ($filter =~ /\s(and|or)\s/) {
196        # with syntactical sugar
197        $and_re = qr/\s+and\s+/;
198        $or_re = qr/\s+or\s+/;
199    } else {
200        # without syntactical sugar
201        $and_re = qr/ /;
202        $or_re = qr/\n/;
203    }
204    # parse line by line
205    foreach my $line (split($or_re, $filter)) {
206        $line =~ s/^\s+//;
207        $line =~ s/\s+$//;
208        next if $line eq "" or $line =~ /^\#/;
209        $level = "";
210        @list = ();
211        foreach my $expr (split($and_re, $line)) {
212            $expr = _parse_expr($line, \$level, $expr);
213            # each expression within a line is AND'ed
214            push(@list, $expr, "and");
215        }
216        if ($level) {
217            # one level specified
218            $il{$level}++;
219        } else {
220            # no level specified => all are potentially interesting
221            foreach my $kl (keys(%_KnownLevel)) {
222                $il{$kl}++;
223            }
224        }
225        # remove the last "and"
226        pop(@list);
227        # each line within a filter is OR'ed
228        push(@filter, @list, "or");
229    }
230    if (@filter) {
231        # non-empty filter => remove the last "or"
232        pop(@filter);
233    } else {
234        # empty filter => default behavior
235        %il = (_LEVEL_INFO() => 1);
236        @filter = ("1");
237    }
238    $ii  = _compile_filter(0, @filter);
239    $mii = _compile_filter(1, @filter);
240    # so far, so good...
241    %_InterestingLevel = %il;
242    $_InterestingInfo = $ii;
243    $_MaybeInterestingInfo = $mii;
244}
245
246#+++############################################################################
247#                                                                              #
248# outputting                                                                   #
249#                                                                              #
250#---############################################################################
251
252#
253# default handler: print compact yet user friendly output to STDOUT or STDERR
254#
255
256sub log2std ($) {
257    my($info) = @_;
258    my($id, $string, $fh);
259
260    $id = $INC{"threads.pm"} ? "$info->{pid}.$info->{tid}": $info->{pid};
261    $string = sprintf("%s %s %s[%s]: %s\n",
262                      $_Level2Char{$info->{level}}, date_stamp($info->{time}),
263                      $info->{program}, $id, $info->{message});
264    $fh = $info->{level} eq _LEVEL_INFO ? *STDOUT : *STDERR;
265    $fh->print($string);
266    $fh->flush();
267    return(1);
268}
269
270#
271# dump handler: print all attributes to STDERR
272#
273
274sub log2dump ($) {
275    my($info) = @_;
276    my(@list);
277
278    foreach my $attr (sort(keys(%{ $info }))) {
279        if ($info->{$attr} =~ /^[\w\.\-\/]*$/) {
280            push(@list, "$attr=$info->{$attr}");
281        } else {
282            push(@list, "$attr=\'$info->{$attr}\'");
283        }
284    }
285    STDERR->print("% @list\n");
286    STDERR->flush();
287    return(1);
288}
289
290#+++############################################################################
291#                                                                              #
292# formatting                                                                   #
293#                                                                              #
294#---############################################################################
295
296#
297# format the message
298#
299
300sub _message ($$) {
301    my($message, $info) = @_;
302    my(@list, $format, $pos);
303
304    @list = @{ $message };
305    unless (@list) {
306        # no message given => empty string
307        return("");
308    }
309    $format = shift(@list);
310    if (ref($format) eq "CODE") {
311        # code message => result of the call
312        return($format->(@list));
313    }
314    if (ref($format)) {
315        # unexpected first argument
316        dief("unexpected argument: %s", $format);
317    }
318    unless (@list) {
319        # plain message
320        return($format);
321    }
322    # sprintf message => format it
323    $pos = 0;
324    foreach my $arg (@list) {
325        if (ref($arg) eq "SCALAR") {
326            # attribute argument
327            dief("unknown attribute: %s", ${ $arg })
328                unless defined($info->{${ $arg }});
329            $arg = $info->{${ $arg }};
330        } elsif (not ref($arg)) {
331            # plain argument
332            dief("undefined argument at position %d", $pos)
333                unless defined($arg);
334        } else {
335            dief("unexpected argument: %s", $arg);
336        }
337        $pos++;
338    }
339    return(sprintf($format, @list));
340}
341
342#+++############################################################################
343#                                                                              #
344# handling                                                                     #
345#                                                                              #
346#---############################################################################
347
348#
349# handle information
350#
351
352sub _handle ($$) {
353    my($message, $info) = @_;
354    my(@list);
355
356    # build the info to log with minimal (= cheap to get) information
357    $info->{time} = time();
358    $info->{program} = $ProgramName;
359    $info->{host} = $HostName;
360    $info->{pid} = $$;
361    $info->{tid} = threads->tid() if $INC{"threads.pm"};
362    @list = caller(1);
363    $info->{file} = $list[1];
364    $info->{line} = $list[2];
365    @list = caller(2);
366    $info->{caller} = defined($list[3]) ? $list[3] : "main";
367    # check if we may care about this info
368    return(0) unless $_MaybeInterestingInfo->($info);
369    # format the message
370    $info->{message} = _message($message, $info);
371    # we always strip trailing spaces
372    $info->{message} =~ s/\s+$//;
373    # check if we really care about this info
374    return(0) unless $_InterestingInfo->($info);
375    # now send it to the right final handler
376    return($Handler->($info));
377}
378
379#+++############################################################################
380#                                                                              #
381# public API                                                                   #
382#                                                                              #
383#---############################################################################
384
385#
386# check whether a level is "active"
387#
388
389sub log_wants_error   () { return($_InterestingLevel{_LEVEL_ERROR()})   }
390sub log_wants_warning () { return($_InterestingLevel{_LEVEL_WARNING()}) }
391sub log_wants_info    () { return($_InterestingLevel{_LEVEL_INFO()})    }
392sub log_wants_debug   () { return($_InterestingLevel{_LEVEL_DEBUG()})   }
393sub log_wants_trace   () { return($_InterestingLevel{_LEVEL_TRACE()})   }
394
395#
396# log error information
397#
398
399sub log_error (@) {
400    my(@args) = @_;
401    my($attrs);
402
403    return(0) unless $_InterestingLevel{_LEVEL_ERROR()};
404    if (@args and ref($args[-1]) eq "HASH") {
405        $attrs = pop(@args);
406    } else {
407        $attrs = {};
408    }
409    return(_handle(\@args, { %{ $attrs }, level => _LEVEL_ERROR }));
410}
411
412#
413# log warning information
414#
415
416sub log_warning (@) {
417    my(@args) = @_;
418    my($attrs);
419
420    return(0) unless $_InterestingLevel{_LEVEL_WARNING()};
421    if (@args and ref($args[-1]) eq "HASH") {
422        $attrs = pop(@args);
423    } else {
424        $attrs = {};
425    }
426    return(_handle(\@args, { %{ $attrs }, level => _LEVEL_WARNING }));
427}
428
429#
430# log informational information ;-)
431#
432
433sub log_info (@) {
434    my(@args) = @_;
435    my($attrs);
436
437    return(0) unless $_InterestingLevel{_LEVEL_INFO()};
438    if (@args and ref($args[-1]) eq "HASH") {
439        $attrs = pop(@args);
440    } else {
441        $attrs = {};
442    }
443    return(_handle(\@args, { %{ $attrs }, level => _LEVEL_INFO }));
444}
445
446#
447# log debugging information
448#
449
450sub log_debug (@) {
451    my(@args) = @_;
452    my($attrs);
453
454    return(0) unless $_InterestingLevel{_LEVEL_DEBUG()};
455    if (@args and ref($args[-1]) eq "HASH") {
456        $attrs = pop(@args);
457    } else {
458        $attrs = {};
459    }
460    return(_handle(\@args, { %{ $attrs }, level => _LEVEL_DEBUG }));
461}
462
463#
464# log tracing information (fixed message)
465#
466
467sub log_trace () {
468    return(0) unless $_InterestingLevel{_LEVEL_TRACE()};
469    return(_handle(
470        [ "in %s at %s line %s", \ "caller", \ "file", \ "line" ],
471        { level => _LEVEL_TRACE },
472    ));
473}
474
475#+++############################################################################
476#                                                                              #
477# module initialization                                                        #
478#                                                                              #
479#---############################################################################
480
481# we select the relevant handler to use
482if ($ENV{NO_WORRIES} and $ENV{NO_WORRIES} =~ /\b(log2dump)\b/) {
483    $Handler = \&log2dump;
484} else {
485    $Handler = \&log2std;
486};
487
488# here are all the known levels
489%_Level2Char = (
490    error   => "!",
491    warning => "?",
492    info    => ":",
493    debug   => "#",
494    trace   => "=",
495);
496foreach my $level (keys(%_Level2Char)) {
497    $_KnownLevel{$level}++;
498}
499
500# by default we only care about informational level or higher
501%_InterestingLevel = %_KnownLevel;
502delete($_InterestingLevel{_LEVEL_DEBUG()});
503delete($_InterestingLevel{_LEVEL_TRACE()});
504
505# by default we do not filter anything out
506$_MaybeInterestingInfo = $_InterestingInfo = sub { return(1) };
507
508#
509# export control
510#
511
512sub import : method {
513    my($pkg, %exported);
514
515    $pkg = shift(@_);
516    grep($exported{$_}++,
517         map("log_$_", qw(configure filter)),
518         map("log_$_",       qw(error warning info debug trace)),
519         map("log_wants_$_", qw(error warning info debug trace)),
520    );
521    $exported{"log2std"}  = sub { $Handler = \&log2std };
522    $exported{"log2dump"} = sub { $Handler = \&log2dump };
523    export_control(scalar(caller()), $pkg, \%exported, @_);
524}
525
5261;
527
528__DATA__
529
530=head1 NAME
531
532No::Worries::Log - logging without worries
533
534=head1 SYNOPSIS
535
536  use No::Worries::Log qw(*);
537
538  # log an information level message with sprintf()-like syntax
539  log_info("accepted connection from %s:%d", inet_ntoa($addr), $port);
540
541  # log expensive debugging information only if needed
542  if (log_wants_debug()) {
543      $string = ... whatever ...
544      log_debug($string, { component => "webui" });
545  }
546
547  # log a low-level trace: this is cheap and can be added in many places
548  sub foo () {
549      log_trace();
550      ... code...
551  }
552
553  # specify the filter to use: debug messages from web* components
554  log_filter(<<EOT);
555      debug component=~^web
556  EOT
557
558=head1 DESCRIPTION
559
560This module eases information logging by providing convenient
561functions to log and filter information. All the functions die() on
562error.
563
564It provides five main functions to submit information to be logged:
565
566=over
567
568=item * log_error(ARGUMENTS): for error information
569
570=item * log_warning(ARGUMENTS): for warning information
571
572=item * log_info(ARGUMENTS): for (normal) information
573
574=item * log_debug(ARGUMENTS): for debugging information
575
576=item * log_trace(): for a low level trace
577
578=back
579
580The supplied information is structured and can contain extra
581attributes (key/value pairs) like in the SYNOPSIS example.
582
583If the information passes through the filter, it is given to the
584handler for logging.
585
586=head1 ATTRIBUTES
587
588An information "object" always contains the following attributes:
589
590=over
591
592=item * C<level>: the information level as C<error>, C<warning>, C<info>,
593C<debug> or C<trace>
594
595=item * C<time>: Unix time indicating when the information got submitted
596
597=item * C<caller>: the name of the caller's subroutine or C<main>
598
599=item * C<file>: the file path
600
601=item * C<line>: the line number
602
603=item * C<program>: the program name, as known by the No::Worries module
604
605=item * C<host>: the host name, see $No::Worries::HostName
606
607=item * C<pid>: the process identifier
608
609=item * C<tid>: the thread identifier (in case threads are used)
610
611=item * C<message>: the formatted message string
612
613=back
614
615In addition, extra attributes can be given when calling log_error(),
616log_warning(), log_info() or log_debug().
617
618These attributes are mainly used for filtering (see next section) but
619can also be used for formatting.
620
621=head1 FILTER
622
623The filter defines which information should be logged (i.e. given to
624the handler) or not. It can be controlled via the log_filter() and
625log_configure() functions.
626
627The filter is described via a multi-line string. Each line is made of
628one or more space separated expressions that must be all true. A
629filter matches if any of its lines matches. Empty lines and comments
630are allowed for readability.
631
632A filter expression can be either C<error>, C<warning>, C<info>,
633C<debug> or C<trace> (meaning that the level must match it) or of the
634form I<{attr}{op}{value}> where I<{attr}> is the attribute name,
635I<{op}> is the operation (either C<=~>, C<!~>, C<==>, C<!=>, C<E<lt>>,
636C<E<lt>=>, C<E<gt>> or C<E<gt>=>) and I<value> is the value to use for
637the test (either an integer, a string or a regular expression).
638
639If the value is not an integer, it will be treated like the contents
640of a double quoted string or a regular expression, so escape sequences
641will be honored. For parsing reasons (expressions are space
642separated), the value cannot contain space characters. If you need
643some, they have to be escaped like in the examples below.
644
645Here are commented examples:
646
647  # comments start with a 'hash' sign
648  # all info level
649  info
650
651  # debug level with messages matching "permission denied"
652  # (expressions are space separated so the space must be escaped)
653  debug message=~permission\x20denied
654
655  # debug level from any subroutine in Foo::Bar on host "venus"
656  debug caller=~^Foo::Bar:: host==venus
657
658  # trace level at the end of the file foo.pm
659  trace file=/full/path/foo.pm line>999
660
661Note: user-supplied attributes can also be used in filters. If they
662are not defined, the match will fail. For instance:
663
664  # we want to see only debug messages with a low karma
665  log_filter("debug karma<=42");
666  # the following will be logged
667  log_debug("yes", { karma => 7 });
668  # the following will not be logged
669  log_debug("no", { karma => 1999 });
670  log_debug("no");
671
672You can also use an alternative syntax with explicit C<or> and
673C<and>. This is very convenient to fit the filter in a single line
674(for instance when given on the command line). For instance:
675
676  # multi-line style filter
677  info
678  debug caller==main
679
680is equivalent to:
681
682  info or debug and caller==main
683
684=head1 HANDLER
685
686If the information successfully passes through the filter it is given
687to the handler, i.e. the code reference stored in
688$No::Worries::Log::Handler.
689
690The default handler prints compact yet user friendly output to STDOUT
691(C<info> level) or STDERR (otherwise).
692
693The L<No::Worries::Syslog> module contains a similar handler to log
694information to syslog.
695
696Here is how to change the variable to a handler that dumps all the
697information attributes to STDERR:
698
699  $No::Worries::Log::Handler = \&No::Worries::Log::log2dump;
700
701The same can be achived at module loading time by using for instance:
702
703  use No::Worries::Log qw(* log2dump);
704
705You can put your own code in $No::Worries::Log::Handler. It will be
706called with a single argument: the structured information as a hash
707reference. This can be useful for ad-hoc filtering or to do something
708else that logging to STDOUT/STDERR or syslog.
709
710=head1 FUNCTIONS
711
712This module provides the following functions (none of them being
713exported by default):
714
715=over
716
717=item log_filter(FILTER)
718
719use the given filter (string) to configure what should gets logged or not
720
721=item log_configure(PATH)
722
723use the given path (file) to configure what should gets logged or not;
724this reads the file if needed (i.e. if it changed since last time) and
725calls log_filter()
726
727=item log_wants_error()
728
729return true if the current filter may pass error level information
730
731=item log_wants_warning()
732
733return true if the current filter may pass warning level information
734
735=item log_wants_info()
736
737return true if the current filter may pass info level information
738
739=item log_wants_debug()
740
741return true if the current filter may pass debug level information
742
743=item log_wants_trace()
744
745return true if the current filter may pass trace level information
746
747=item log_error(ARGUMENTS)
748
749give an error level information to the module to get logged if the
750current filter lets it pass; see below for its ARGUMENTS
751
752=item log_warning(ARGUMENTS)
753
754give a warning level information to the module to get logged if the
755current filter lets it pass; see below for its ARGUMENTS
756
757=item log_info(ARGUMENTS)
758
759give an info level information to the module to get logged if the
760current filter lets it pass; see below for its ARGUMENTS
761
762=item log_debug(ARGUMENTS)
763
764give a debug level information to the module to get logged if the
765current filter lets it pass; see below for its ARGUMENTS
766
767=item log_trace()
768
769give a trace level information to the module to get logged if the
770current filter lets it pass; the trace information contains the name
771of the caller subroutine, the file path and the line number
772
773=item log2std(INFO)
774
775handler for $No::Worries::Log::Handler to send information to
776STDOUT/STDERR in a compact yet user friendly way; this is not exported
777and must be called explicitly
778
779=item log2dump(INFO)
780
781handler for $No::Worries::Log::Handler that dumps all the information
782attributes to STDERR; this is not exported and must be called
783explicitly
784
785=back
786
787=head1 USAGE
788
789log_error(), log_warning(), log_info() and log_debug() can be called
790in different ways:
791
792=over
793
794=item * log_xxx(): no arguments, same as giving an empty string
795
796=item * log_xxx("string"): the message will be the given string
797
798=item * log_xxx("format", @args): the message will be the result of sprintf()
799
800=item * log_xxx(\&code): the message will be the return value of the code
801
802=item * log_xxx(\&code, @args): idem but also supplying arguments to give
803
804=back
805
806In addition, in all cases, an optional last argument containing
807user-supplied attributes can be given as a hash reference. For
808instance:
809
810  log_info("foo is %s", $foo, { component => "webui" });
811
812Note that the following:
813
814  log_debug(\&dump_hash, \%big_hash);
815
816will treat the last argument as being the attributes hash. If this is
817not what you want, you should supply an empty attributes hash so that
818\%big_hash gets interpreted as an argument to give to dump_hash():
819
820  log_debug(\&dump_hash, \%big_hash, {});
821
822With the sprintf() style usage, you can supply string references as
823arguments. They will be replaced by the corresponding attributes. For
824instance:
825
826  log_debug("unexpected data: %s [line %d]", $data, \"line");
827
828The usages with a code reference are useful for expensive operations
829that you want to perform only when you are pretty sure that the
830information will be logged. The code reference will be called only
831after an initial filtering. For instance:
832
833  # expensive code to return a message to maybe log
834  sub dump_state ($) {
835      my($data) = @_;
836      ... heavy work ...
837      return(... something ...);
838  }
839  # subroutine that may want to dump its state
840  sub foo () {
841      ... some code ...
842      log_debug(\&dump_state, $some_data);
843      ... some code ...
844  }
845  # filter that only cares about debug information from main::bar
846  log_filter("debug caller==main::bar");
847  # the following will not call dump_state()
848  foo();
849
850=head1 GLOBAL VARIABLES
851
852This module uses the following global variables (none of them being
853exported):
854
855=over
856
857=item $Handler
858
859the subroutine (code reference) to call for every information that
860successfully passes through the filter, the default is normally
861\&No::Worries::Log::log2std() (see below)
862
863=back
864
865=head1 ENVIRONMENT VARIABLES
866
867This module uses the C<NO_WORRIES> environment variable to find out
868which handler to use by default. Supported values are:
869
870=over
871
872=item C<log2std>
873
874use No::Worries::Log::log2std() (this is the default anyway)
875
876=item C<log2dump>
877
878use No::Worries::Log::log2dump()
879
880=back
881
882=head1 SEE ALSO
883
884L<No::Worries>,
885L<No::Worries::Syslog>.
886
887=head1 AUTHOR
888
889Lionel Cons L<http://cern.ch/lionel.cons>
890
891Copyright (C) CERN 2012-2019
892