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