--- AnyEvent/lib/AnyEvent/Log.pm 2011/08/21 03:25:47 1.25 +++ AnyEvent/lib/AnyEvent/Log.pm 2011/08/25 04:56:16 1.32 @@ -251,7 +251,7 @@ $format = $format->() if ref $format; $format = sprintf $format, @args if @args; $format =~ s/\n$//; - $now = AE::now; + $now = now; }; # format msg @@ -517,10 +517,7 @@ $LOG->slaves; $LOG->title ('$AnyEvent::Log::LOG'); - $LOG->log_cb (sub { - warn shift; - 0 - }); + $LOG->log_to_warn; $FILTER->slaves ($LOG); $FILTER->title ('$AnyEvent::Log::FILTER'); @@ -791,7 +788,7 @@ string, but it could just as well be an array reference that just stores the values. -If, for some reaosn, you want to use C to find out more baout the +If, for some reason, you want to use C to find out more baout the logger then you should walk up the call stack until you are no longer inside the C package. @@ -820,6 +817,11 @@ 0 }); +=item $ctx->log_to_warn + +Sets the C to simply use C to report any messages +(usually this logs to STDERR). + =item $ctx->log_to_file ($path) Sets the C to log to a file (by appending), unbuffered. @@ -830,6 +832,10 @@ is much slower, but allows you to change/move/rename/delete the file at basically any time. +Needless(?) to say, if you do not want to be bitten by some evil person +calling C, the path should be absolute. Doesn't help with +C, but hey... + =item $ctx->log_to_syslog ([$log_flags]) Logs all messages via L, mapping C to C and all @@ -854,6 +860,15 @@ $ctx->[4] = $cb; } +sub log_to_warn { + my ($ctx, $path) = @_; + + $ctx->log_cb (sub { + warn shift; + 0 + }); +} + sub log_to_file { my ($ctx, $path) = @_; @@ -866,7 +881,7 @@ }); } -sub log_to_file { +sub log_to_path { my ($ctx, $path) = @_; $ctx->log_cb (sub { @@ -923,30 +938,259 @@ *log = \&AnyEvent::Log::_log; *logger = \&AnyEvent::Log::_logger; -1; +=back + +=cut + +package AnyEvent::Log; + +=head1 CONFIGURATION VIA $ENV{PERL_ANYEVENT_LOG} + +Logging can also be configured by setting the environment variable +C (or C). + +The value consists of one or more logging context specifications separated +by C<:> or whitespace. Each logging specification in turn starts with a +context name, followed by C<=>, followed by zero or more comma-separated +configuration directives, here are some examples: + + # set default logging level + filter=warn + + # log to file instead of to stderr + log=file=/tmp/mylog + + # log to file in addition to stderr + log=+%file:%file=file=/tmp/mylog + + # enable debug log messages, log warnings and above to syslog + filter=debug:log=+%warnings:%warnings=warn,syslog=LOG_LOCAL0 + + # log trace messages (only) from AnyEvent::Debug to file + AnyEvent::Debug=+%trace:%trace=only,trace,file=/tmp/tracelog + +A context name in the log specification can be any of the following: + +=over 4 + +=item C, C, C + +Correspond to the three predefined C<$AnyEvent::Log::COLLECT>, +C and C<$AnyEvent::Log::LOG> contexts. + +=item C<%name> + +Context names starting with a C<%> are anonymous contexts created when the +name is first mentioned. The difference to package contexts is that by +default they have no attached slaves. + +=item a perl package name + +Any other string references the logging context associated with the given +Perl C. In the unlikely case where you want to specify a package +context that matches on of the other context name forms, you can add a +C<::> to the package name to force interpretation as a package. =back +The configuration specifications can be any number of the following: + +=over 4 + +=item C + +Configures the context to use Perl's C function (which typically +logs to C). Works like C. + +=item CI + +Configures the context to log to a file with the given path. Works like +C. + +=item CI + +Configures the context to log to a file with the given path. Works like +C. + +=item C or CI + +Configures the context to log to syslog. If I is given, then it is +evaluated in the L package, so you could use: + + log=syslog=LOG_LOCAL0 + +=item C + +Configures the context to not log anything by itself, which is the +default. Same as C<< $ctx->log_cb (undef) >>. + +=item C<0> or C + +Sets the logging level of the context ot C<0>, i.e. all messages will be +filtered out. + +=item C + +Enables all logging levels, i.e. filtering will effectively be switched +off (the default). + +=item C + +Disables all logging levels, and changes the interpretation of following +level specifications to enable the specified level only. + +Example: only enable debug messages for a context. + + context=only,debug + +=item C + +Enables all logging levels, and changes the interpretation of following +level specifications to disable that level. Rarely used. + +Example: enable all logging levels except fatal and trace (this is rather +nonsensical). + + filter=exept,fatal,trace + +=item C + +Enables all logging levels, and changes the interpretation of following +level specifications to be "that level or any higher priority +message". This is the default. + +Example: log anything at or above warn level. + + filter=warn + + # or, more verbose + filter=only,level,warn + +=item C<1>..C<9> or a logging level name (C, C etc.) + +A numeric loglevel or the name of a loglevel will be interpreted according +to the most recent C, C or C directive. By default, +specifying a logging level enables that and any higher priority messages. + +=item C<+>I + +Attaches the named context as slave to the context. + +=item C<+> + +A line C<+> detaches all contexts, i.e. clears the slave list from the +context. Anonymous (C<%name>) contexts have no attached slaves by default, +but package contexts have the parent context as slave by default. + +Example: log messages from My::Module to a file, do not send them to the +default log collector. + + My::Module=+,file=/tmp/mymodulelog + +=back + +Any character can be escaped by prefixing it with a C<\> (backslash), as +usual, so to log to a file containing a comma, colon, backslash and space in the +filename, you would do this: + + PERL_ANYEVENT_LOG='log=file=/some\ \:file\ with\,\ \\-escapes' + +Since whitespace (which includes newlines) is allowed, it is fine to +specify multiple lines in C, e.g.: + + PERL_ANYEVENT_LOG=" + filter=warn + AnyEvent::Debug=+%trace + %trace=only,trace,+log + " myprog + +Also, in the unlikely case when you want to concatenate specifications, +use whitespace as separator, as C<::> will be interpreted as part of a +module name, an empty spec with two separators: + + PERL_ANYEVENT_LOG="$PERL_ANYEVENT_LOG MyMod=debug" + +=cut + +for (my $spec = $ENV{PERL_ANYEVENT_LOG}) { + my %anon; + + my $pkg = sub { + $_[0] eq "log" ? $LOG + : $_[0] eq "filter" ? $FILTER + : $_[0] eq "collect" ? $COLLECT + : $_[0] =~ /^%(.+)$/ ? ($anon{$1} ||= ctx undef) + : $_[0] =~ /^(.*?)(?:::)?$/ ? ctx "$1" # egad :/ + : die # never reached? + }; + + /\G[[:space:]]+/gc; # skip initial whitespace + + while (/\G((?:[^:=[:space:]]+|::|\\.)+)=/gc) { + my $ctx = $pkg->($1); + my $level = "level"; + + while (/\G((?:[^,:[:space:]]+|::|\\.)+)/gc) { + for ("$1") { + if ($_ eq "stderr" ) { $ctx->log_to_warn; + } elsif (/^file=(.+)/ ) { $ctx->log_to_file ("$1"); + } elsif (/^path=(.+)/ ) { $ctx->log_to_path ("$1"); + } elsif (/syslog(?:=(.*))?/ ) { require Sys::Syslog; $ctx->log_to_syslog (eval "package Sys::Syslog; $1"); + } elsif ($_ eq "nolog" ) { $ctx->log_cb (undef); + } elsif (/^\+(.+)$/ ) { $ctx->attach ($pkg->("$1")); + } elsif ($_ eq "+" ) { $ctx->slaves; + } elsif ($_ eq "off" or $_ eq "0") { $ctx->level (0); + } elsif ($_ eq "all" ) { $ctx->level ("all"); + } elsif ($_ eq "level" ) { $ctx->level ("all"); $level = "level"; + } elsif ($_ eq "only" ) { $ctx->level ("off"); $level = "enable"; + } elsif ($_ eq "except" ) { $ctx->level ("all"); $level = "disable"; + } elsif (/^\d$/ ) { $ctx->$level ($_); + } elsif (exists $STR2LEVEL{$_} ) { $ctx->$level ($_); + } else { die "PERL_ANYEVENT_LOG ($spec): parse error at '$_'\n"; + } + } + + /\G,/gc or last; + } + + /\G[:[:space:]]+/gc or last; + } + + /\G[[:space:]]+/gc; # skip trailing whitespace + + if (/\G(.+)/g) { + die "PERL_ANYEVENT_LOG ($spec): parse error at '$1'\n"; + } +} + +1; + =head1 EXAMPLES -This section shows some common configurations. +This section shows some common configurations, both as code, and as +C string. =over 4 =item Setting the global logging level. -Either put PERL_ANYEVENT_VERBOSE= into your environment before -running your program, or modify the log level of the root context: +Either put C into your environment before +running your program, use C or modify the log level of +the root context at runtime: PERL_ANYEVENT_VERBOSE=5 ./myprog + PERL_ANYEVENT_LOG=log=warn + $AnyEvent::Log::FILTER->level ("warn"); =item Append all messages to a file instead of sending them to STDERR. This is affected by the global logging level. - $AnyEvent::Log::LOG->log_to_file ($path); (sub { + $AnyEvent::Log::LOG->log_to_file ($path); + + PERL_ANYEVENT_LOG=log=file=/some/path =item Write all messages with priority C and higher to a file. @@ -957,6 +1201,8 @@ $AnyEvent::Log::FILTER->attach new AnyEvent::Log::Ctx log_to_file => $path); + PERL_ANYEVENT_LOG=filter=+%filelogger:%filelogger=file=/some/path + This writes them regardless of the global logging level, because it is attached to the toplevel context, which receives all messages I the global filtering. @@ -964,6 +1210,8 @@ $AnyEvent::Log::COLLECT->attach ( new AnyEvent::Log::Ctx log_to_file => $path); + PERL_ANYEVENT_LOG=%filelogger=file=/some/path:collect=+%filelogger + In both cases, messages are still written to STDERR. =item Write trace messages (only) from L to the default logging target(s). @@ -974,6 +1222,8 @@ my $debug = AnyEvent::Debug->AnyEvent::Log::ctx; $debug->attach ($AnyEvent::Log::LOG); + PERL_ANYEVENT_LOG=AnyEvent::Debug=+log + This of course works for any package, not just L, but assumes the log level for AnyEvent::Debug hasn't been changed from the default. @@ -986,3 +1236,4 @@ http://home.schmorp.de/ =cut +