AnyEvent

 view release on metacpan or  search on metacpan

lib/AnyEvent/Log.pm  view on Meta::CPAN

=head1 NAME

AnyEvent::Log - simple logging "framework"

=head1 SYNOPSIS

Simple uses:

   use AnyEvent;

   AE::log fatal => "No config found, cannot continue!"; # never returns
   AE::log alert => "The battery died!";
   AE::log crit  => "The battery is too hot!";
   AE::log error => "Division by zero attempted.";
   AE::log warn  => "Couldn't delete the file.";
   AE::log note  => "Attempted to create config, but config already exists.";
   AE::log info  => "File soandso successfully deleted.";
   AE::log debug => "the function returned 3";
   AE::log trace => "going to call function abc";

Log level overview:

   LVL NAME      SYSLOG   PERL  NOTE
    1  fatal     emerg    exit  system unusable, aborts program!
    2  alert                    failure in primary system
    3  critical  crit           failure in backup system
    4  error     err      die   non-urgent program errors, a bug
    5  warn      warning        possible problem, not necessarily error
    6  note      notice         unusual conditions
    7  info                     normal messages, no action required
    8  debug                    debugging messages for development
    9  trace                    copious tracing output

"Complex" uses (for speed sensitive code, e.g. trace/debug messages):

   use AnyEvent::Log;

   my $tracer = AnyEvent::Log::logger trace => \my $trace;

   $tracer->("i am here") if $trace;
   $tracer->(sub { "lots of data: " . Dumper $self }) if $trace;

Configuration (also look at the EXAMPLES section):

   # set default logging level to suppress anything below "notice"
   # i.e. enable logging at "notice" or above - the default is to
   # to not log anything at all.
   $AnyEvent::Log::FILTER->level ("notice");

   # set logging for the current package to errors and higher only
   AnyEvent::Log::ctx->level ("error");

   # enable logging for the current package, regardless of global logging level
   AnyEvent::Log::ctx->attach ($AnyEvent::Log::LOG);

   # enable debug logging for module some::mod and enable logging by default
   (AnyEvent::Log::ctx "some::mod")->level ("debug");
   (AnyEvent::Log::ctx "some::mod")->attach ($AnyEvent::Log::LOG);

   # send all critical and higher priority messages to syslog,
   # regardless of (most) other settings
   $AnyEvent::Log::COLLECT->attach (new AnyEvent::Log::Ctx
      level         => "critical",
      log_to_syslog => "user",
   );

=head1 DESCRIPTION

This module implements a relatively simple "logging framework". It doesn't
attempt to be "the" logging solution or even "a" logging solution for
AnyEvent - AnyEvent simply creates logging messages internally, and this
module more or less exposes the mechanism, with some extra spiff to allow
using it from other modules as well.

Remember that the default verbosity level is C<4> (C<error>), so only
errors and more important messages will be logged, unless you set
C<PERL_ANYEVENT_VERBOSE> to a higher number before starting your program
(C<AE_VERBOSE=5> is recommended during development), or change the logging
level at runtime with something like:

   use AnyEvent::Log;
   $AnyEvent::Log::FILTER->level ("info");

The design goal behind this module was to keep it simple (and small),
but make it powerful enough to be potentially useful for any module,
and extensive enough for the most common tasks, such as logging to
multiple targets, or being able to log into a database.

The module is also usable before AnyEvent itself is initialised, in which
case some of the functionality might be reduced.

The amount of documentation might indicate otherwise, but the runtime part
of the module is still just below 300 lines of code.

=head1 LOGGING LEVELS

Logging levels in this module range from C<1> (highest priority) to C<9>
(lowest priority). Note that the lowest numerical value is the highest
priority, so when this document says "higher priority" it means "lower
numerical value".

Instead of specifying levels by name you can also specify them by aliases:

   LVL NAME      SYSLOG   PERL  NOTE
    1  fatal     emerg    exit  system unusable, aborts program!
    2  alert                    failure in primary system
    3  critical  crit           failure in backup system
    4  error     err      die   non-urgent program errors, a bug
    5  warn      warning        possible problem, not necessarily error
    6  note      notice         unusual conditions
    7  info                     normal messages, no action required
    8  debug                    debugging messages for development
    9  trace                    copious tracing output

lib/AnyEvent/Log.pm  view on Meta::CPAN

The default implementation simply calls C<exit 1>.

In your main program (as opposed to in your module) you can override
the fatal exit function by loading this module and then redefining this
function. Make sure you don't return.

=back

=head1 LOGGING CONTEXTS

This module associates every log message with a so-called I<logging
context>, based on the package of the caller. Every perl package has its
own logging context.

A logging context has three major responsibilities: filtering, logging and
propagating the message.

For the first purpose, filtering, each context has a set of logging
levels, called the log level mask. Messages not in the set will be ignored
by this context (masked).

For logging, the context stores a formatting callback (which takes the
timestamp, context, level and string message and formats it in the way
it should be logged) and a logging callback (which is responsible for
actually logging the formatted message and telling C<AnyEvent::Log>
whether it has consumed the message, or whether it should be propagated).

For propagation, a context can have any number of attached I<slave
contexts>. Any message that is neither masked by the logging mask nor
masked by the logging callback returning true will be passed to all slave
contexts.

Each call to a logging function will log the message at most once per
context, so it does not matter (much) if there are cycles or if the
message can arrive at the same context via multiple paths.

=head2 DEFAULTS

By default, all logging contexts have an full set of log levels ("all"), a
disabled logging callback and the default formatting callback.

Package contexts have the package name as logging title by default.

They have exactly one slave - the context of the "parent" package. The
parent package is simply defined to be the package name without the last
component, i.e. C<AnyEvent::Debug::Wrapped> becomes C<AnyEvent::Debug>,
and C<AnyEvent> becomes ... C<$AnyEvent::Log::COLLECT> which is the
exception of the rule - just like the "parent" of any single-component
package name in Perl is C<main>, the default slave of any top-level
package context is C<$AnyEvent::Log::COLLECT>.

Since perl packages form only an approximate hierarchy, this slave
context can of course be removed.

All other (anonymous) contexts have no slaves and an empty title by
default.

When the module is loaded it creates the C<$AnyEvent::Log::LOG> logging
context that simply logs everything via C<warn>, without propagating
anything anywhere by default.  The purpose of this context is to provide
a convenient place to override the global logging target or to attach
additional log targets. It's not meant for filtering.

It then creates the C<$AnyEvent::Log::FILTER> context whose
purpose is to suppress all messages with priority higher
than C<$ENV{PERL_ANYEVENT_VERBOSE}>. It then attached the
C<$AnyEvent::Log::LOG> context to it. The purpose of the filter context
is to simply provide filtering according to some global log level.

Finally it creates the top-level package context C<$AnyEvent::Log::COLLECT>
and attaches the C<$AnyEvent::Log::FILTER> context to it, but otherwise
leaves it at default config. Its purpose is simply to collect all log
messages system-wide.

The hierarchy is then:

   any package, eventually -> $COLLECT -> $FILTER -> $LOG

The effect of all this is that log messages, by default, wander up to the
C<$AnyEvent::Log::COLLECT> context where all messages normally end up,
from there to C<$AnyEvent::Log::FILTER> where log messages with lower
priority then C<$ENV{PERL_ANYEVENT_VERBOSE}> will be filtered out and then
to the C<$AnyEvent::Log::LOG> context to be passed to C<warn>.

This makes it easy to set a global logging level (by modifying $FILTER),
but still allow other contexts to send, for example, their debug and trace
messages to the $LOG target despite the global logging level, or to attach
additional log targets that log messages, regardless of the global logging
level.

It also makes it easy to modify the default warn-logger ($LOG) to
something that logs to a file, or to attach additional logging targets
(such as loggign to a file) by attaching it to $FILTER.

=head2 CREATING/FINDING/DESTROYING CONTEXTS

=over 4

=item $ctx = AnyEvent::Log::ctx [$pkg]

This function creates or returns a logging context (which is an object).

If a package name is given, then the context for that package is
returned. If it is called without any arguments, then the context for the
callers package is returned (i.e. the same context as a C<AE::log> call
would use).

If C<undef> is given, then it creates a new anonymous context that is not
tied to any package and is destroyed when no longer referenced.

=cut

sub ctx(;$) {
   my $pkg = @_ ? shift : (caller)[0];

   ref $pkg
      ? $pkg
      : defined $pkg
         ? $CTX{$pkg} ||= AnyEvent::Log::_pkg_ctx $pkg
         : bless [undef, (1 << 10) - 1 - 1], "AnyEvent::Log::Ctx"
}

=item AnyEvent::Log::reset

Resets all package contexts and recreates the default hierarchy if
necessary, i.e. resets the logging subsystem to defaults, as much as
possible. This process keeps references to contexts held by other parts of
the program intact.

This can be used to implement config-file (re-)loading: before loading a
configuration, reset all contexts.

=cut

our $ORIG_VERBOSE = $AnyEvent::VERBOSE;
$AnyEvent::VERBOSE = 9;

sub reset {
   # hard to kill complex data structures
   # we "recreate" all package loggers and reset the hierarchy
   while (my ($k, $v) = each %CTX) {
      @$v = ($k, (1 << 10) - 1 - 1, { });

      $v->attach ($k =~ /^(.+)::/ ? $CTX{$1} : $AnyEvent::Log::COLLECT);
   }

   @$_ = ($_->[0], (1 << 10) - 1 - 1)
      for $LOG, $FILTER, $COLLECT;

lib/AnyEvent/Log.pm  view on Meta::CPAN


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} ||= do { my $ctx = ctx undef; $ctx->[0] = $_[0]; $ctx })
      : $_[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 ("$1");
            } elsif ($_ eq "nolog"           ) { $ctx->log_cb (undef);
            } elsif (/^cap=(.+)/             ) { $ctx->cap ("$1");
            } 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";
   }
}

=head1 EXAMPLES

This section shows some common configurations, both as code, and as
C<PERL_ANYEVENT_LOG> string.

=over 4

=item Setting the global logging level.

Either put C<PERL_ANYEVENT_VERBOSE=><number> into your environment before
running your program, use C<PERL_ANYEVENT_LOG> 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);

   PERL_ANYEVENT_LOG=log=file=/some/path

=item Write all messages with priority C<error> and higher to a file.

This writes them only when the global logging level allows it, because
it is attached to the default context which is invoked I<after> global
filtering.

   $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<before>
the global filtering.

   $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 Additionally log all messages with C<warn> and higher priority to
C<syslog>, but cap at C<error>.

This logs all messages to the default log target, but also logs messages
with priority C<warn> or higher (and not filtered otherwise) to syslog
facility C<user>. Messages with priority higher than C<error> will be
logged with level C<error>.

   $AnyEvent::Log::LOG->attach (
      new AnyEvent::Log::Ctx
         level  => "warn",
         cap    => "error",
         syslog => "user",
   );

   PERL_ANYEVENT_LOG=log=+%syslog:%syslog=warn,cap=error,syslog

=item Write trace messages (only) from L<AnyEvent::Debug> to the default logging target(s).

Attach the C<$AnyEvent::Log::LOG> context to the C<AnyEvent::Debug>
context - this simply circumvents the global filtering for trace messages.

   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<AnyEvent::Debug>, but
assumes the log level for AnyEvent::Debug hasn't been changed from the
default.

=back

=head1 ASYNCHRONOUS DISK I/O

This module uses L<AnyEvent::IO> to actually write log messages (in
C<log_to_file> and C<log_to_path>), so it doesn't block your program when
the disk is busy and a non-blocking L<AnyEvent::IO> backend is available.

=head1 AUTHOR

 Marc Lehmann <schmorp@schmorp.de>
 http://anyevent.schmorp.de

=cut

1



( run in 0.564 second using v1.01-cache-2.11-cpan-39bf76dae61 )