The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

 Fault::Logger - A message logger proxy.

SYNOPSIS

 use Fault::Logger;
 $proxy        = Fault::Logger->new (@delegates);
 $proxy        = Fault::Logger->new;
 @delegates    = Fault::Logger->delegates;
 @delegates    = $proxy->delegates;
 $one          = Fault::Logger->add_delegates (@delegates);
 $one          = $proxy->add_delegates (@delegates);

 $msg          = Fault::Logger->message;
 $msg          = $proxy->message;
                 Fault::Logger->clr_message;
                 $proxy->clr_message;

                 Fault::Logger->clr_log_once;
                 $proxy->clr_log_once;

 $didlog       = Fault::Logger->log               ($m,$t,$p,$o,@rest);
 $didlog       = $proxy->log                      ($m,$t,$p,$o,@rest);
                 Fault::Logger->crash             ($m,$t,$p,$o,@rest);
                 $proxy->crash                    ($m,$t,$p,$o,@rest);
 $firsttime    = Fault::Logger->log_once          ($m,$t,$p,$o,@rest);
 $firsttime    = $proxy->log_once                 ($m,$t,$p,$o,@rest);

 $notfault     = Fault::Logger->fault_check       ($c,$tag,$m,$t,$p,$o,@rest);
 $notfault     = $proxy->fault_check              ($c,$tag,$m,$t,$p,$o,@rest);
 $notfault     = Fault::Logger->assertion_check   ($c,$tag,$m,$t,$p,$o,@rest);
 $notfault     = $proxy->assertion_check          ($c,$tag,$m,$t,$p,$o,@rest);

 $notfault     = Fault::Logger->arg_check_isalnum ($v,$varname,$t,$p,$o,@rest);
 $notfault     = $proxy->arg_check_isalnum        ($v,$varname,$t,$p,$o,@rest);

 $notfault     = Fault::Logger->arg_check_isdigit ($v,$varname,$t,$p,$o,@rest);
 $notfault     = $proxy->arg_check_isdigit        ($v,$varname,$t,$p,$o,@rest);

 $notfault     = Fault::Logger->arg_check_noref   ($v,$varname,$t,$p,$o,@rest);
 $notfault     = $proxy->arg_check_noref          ($v,$varname,$t,$p,$o,@rest);

 $notfault     = Fault::Logger->arg_check_isa ($v,$class,$varname,$t,$p,$o,@rest);
 $notfault     = $proxy->arg_check_isa        ($v,$class,$varname,$t,$p,$o,@rest);

 $notfault     = Fault::Logger->bug_check         ($c,$m,$t,$p,$o,@rest);
 $notfault     = $proxy->bug_check                ($c,$m,$t,$p,$o,@rest);

Inheritance

 Base Class

Description

This Class does not have instance objects, only a single 'Class Object'. As it may be referenced by class name, it is very easy for code at any level or location within a system to find it and thus send messages to a central logging point. The actual logging is handled by a user specified and easily changed list of delegates so the logging behavior and destinations of your entire program is modifiable at run-time.

Since the actual logging is handled by a user delegate, you may ask, then what is the point of Logger? Logger is a controller. It provides the structure within which more sophisticated logging may be done.

Defaulting is central to the philosophy of the design. A mistake in the args to your rarely used log or fault call should not prevent at least something from being printed to let you know something happened. Crashing is not an option.

Logger currently provides four different types of logging:

Simple logging

This is what most people have in mind. You call a routine, and it sends a message somewhere. What Logger adds to this most basic process is the ability to use different destinations in different part of your program or to mix and match them as you wish. If you provide a delegate that handles output to files, you log to file; if it sets up syslog, you log the same message to syslog; if you set up a MySQL table then your delegate can log to that. All you need to handle in your delegate code is the moving of a message from your input onto one or more outputs. Logger passes through arguments unique to your delegate.

The user's program must of course have write privileges to where their object intends to log, whether it be file, syslog, database table or whatever.

Simple logging methods are log and crash.

Log once

There are times when you want to see if a particular condition happens, but you know that if it does it will recur at a high rate. The log_once method does just this. It keeps track of each string passed through it for logging and if that string has already been seen it returns immediately without logging anything.

If you initialize the Logger via new it will also clear the list of logged messages kept up by log_once. You may also clear it with the clr_log_once method.

Conditional logging

It is quite often the case that you want to log a message every time some condition is true. This is the sort of thing which is done when you put diagnostic assertions into your code. You only want output if the assertion is true. For convenience we have assertion_check and a family of similar methods. They embed the condition flag (or an entire expression) in the method call so that you needn't construct a whole list of conditionals. In case you still require a conditional action, the subroutine returns the inverse of the value it tested. This will make it useful in common statements of the form: expression-a || expression-b.

Change of state logging

The most sophisticated use and one of the primary reasons for Logger is the management of 'edge-triggered' logging. The message text is used as a unique identifier. (It is thus not wise to do this sort of logging on messages with a non-repeatable component like the address of a variable). The full message is stored when first seen in conjunction with a true condition test; it is removed when the same text is seen with the condition test false. Changing from false to true causes the message to be logged as 'fault raised'; going from true to false logs a 'fault cleared' message.

The fault_check and bug_check methods are of this type.

There are also hooks supplied so that a user's delegate class may be called during initialization and at any or all transitions: false-false; false-true; true-true; true-false. You probably would only be interested in the false-true and true-false edge-transitions.

With this method you can construct systems to display and remove fault messages in real time as conditions occur and are fixed.

All of the Logger methods accept and pass through a target object pointer as the second argument. This allows a calling object to pass a callback pointer to itself through the Logger to the delegate object. The delegate object is then free to communicate whatever it wishes with the object which declared the error. It might write a copy of the log message into the target, or it might try to fix something. What happens is in the hands of the delegate writer. Logger only supplies the framework.

Logger also passes through a type argument in all calls, although it may be defaulted in most cases. To be truthful, this exists for my own database application, but it may be of use to others as well. It is intended to be used as a simple classifier of messages.

The definition of type names are left (mostly) to the user to define and utilize. Currently Logger only demands one type be recognized: "BUG". You will see this in your delegate if you use bug_check or default the type argument in fault_check.

Logger delegate protocol

We have made much of delegates in the previous discussion. But exactly what is a delegate? How do you write one?

Most basically, a Logger delegate is any instance of a Class that accepts a method call of the form:

        $didlog = $delegate->log ($msg,$o,@rest)

Where $msg is the Fault::Msg object being processed by Logger; $o is a callback pointer called the 'target', optionally passed in by the original caller of a Logger method; and @rest is any additional arguments which the Logger method received beyond those it uses itself.

It should return $didlog true if $msg is successfully logged and false if it was not. In the examples below, the Simple class implements this most minimal delegate.

This is a very useful capability. You can switch between using direct writes to logfiles to logging remotely, logging via a Unix socket to syslog, or even logging to a database table. The behavior is dependent on the capabilities of the delegate class passed to the Logger proxy.

In addition to the log method, delegates may define a number of other 'callback' or 'hook' methods. In Objective C on NeXT computers this sort of thing is called a protocol.

The user may seed the fault table with an initial set of messages (perhaps ones previously saved in a database) by providing an initfaults method:

        @list = $delegate->initfaults

The list should be a simple list of fault messages

        ("fault msg 1", "fault msg 2"...)
 

as previously captured via a trans01 method. The user may supply callbacks for any or all of the four possible fault transition states:

        $delegate->trans00 ($msg,$o,@rest)
        $delegate->trans01 ($msg,$o,@rest)
        $delegate->trans10 ($msg,$o,@rest)
        $delegate->trans11 ($msg,$o,@rest)

where $msg and $o are as described above and @rest are any private arguments the user passed into the logger call.

A method name will not be called unless it exists, so in most cases either none of the above or only trans01 and trans10 need be defined. The return value is not defined and will be ignored.

The meanings of the transitions are:

        00   No fault, no change.
        01   A new fault has occurred.
        10   An existing fault has cleared.
        11   Known fault, no change.

You may also wish to examine the code of the various Fault::Delegate classes provided as examples and a quick start.

Argument definitions

A number of arguments are standard and used in most of the callbacks defined by this delegate.

    A Type is a single arbitrary capitalized word. You may add your own, but this is the required subset.

            BUG     For programming faults.
            DATA    Anything to do with file data or directories.
            SRV     Server operational issues, startup, login,
                    initializing. Hardware failures.
            NET     Failure to connect to a host, connectivity issues.
            NOTE    Reporting things of interest. Restarts, normal 
                    operational info. 
            other   The user may define any additional single word tags
                    they desire and they will be treated equally to the
                    required set.

    If you use types not in this list, it is up to your web logger to accept them. You must accept any of the default list, but what you do with them or your own afterwards is up to you. Types help to categorize messages rather than define how important they are. You can have any 'type' of log messages reporting at any 'priority'.

    A priority must be one of the Unix syslog priorities:

            emerg   Off the scale.
            alert   A major subsystem is unuseable. 
            crit    A critical subsystem is not working entirely.
            err     Bugs, bad data, files not found, things that went
                    bump in the night.
            warning Something that should be attended to but that is not really
                    an error.
            notice  The standard reports people want to read.
            info    Ordinarily unneeded chatter that is useful if
                    trouble-shooting is needed after the fact.
            debug   Really boring diagnostic output.

    If a subclass has no means of doing anything with priority, it may be left out. All the arguments before it must be handled and if necessary defaulted to reasonable values by a subclass.

    If you do specify a type but not a priority in an arg list, for whatever reason, priority will default as follows:

            BUG     err
            DATA    warning
            SRV     warning
            NET     warning
            NOTE    info
            other   warning

    If there is no type both arguments will default, resulting in type equal 'BUG' and priority equal 'err'.

    A target is an object reference. If present it is passed unexamined to the subclass. A target could be used to return log state information to the site at which the log or fault occurred.

    As many additional subclass specific arguments as you wish may be added after the priority argument position in the calling sequences. They are passed straight through with no processing or checking.

Besides these explicit arguments the delegate checks for the existence of a global variable:

    $::PROCESS_NAME

If used, this should contain a single word name for your process. If the process name contains spaces, use underscore as a replacement for them. For example:

    $::PROCESS_NAME = "MyProcess";
    $::PROCESS_NAME = "My_Process";

If this global is undefined a default of "UnspecifiedProcess" is used as fault processing depends upon it. Further, the value is retrieved in each method just before use to cover the case of spawned processes whose names are different from that of the parent process.

Examples

Example 1: Default everything

 use Fault::Logger;
 Fault::Logger->log ("test logging");

Example 2: Multiple delegates

 use Fault::Logger;
 use Fault::Delegate::Stdout;
 use Fault::Delegate::Stderr;
 use Fault::Delegate::Syslog;
 use Fault::Delegate::File;

 my $delegate1  = Fault::Delegate::Stdout->new;
 my $delegate2  = Fault::Delegate::Syslog->new;
 my $delegate3  = Fault::Delegate::File->new ("/tmp/test.log");

 my @delegates  = ($delegate1,$delegate2,$delegate3);
                  Fault::Logger->new         (@delegates);
                  Fault::Logger->log         ("test logging",'NOTE','warning');

Example 3: Fault monitoring

 use Fault::Logger;
 use Fault::Delegate::DB;

 # Works only if you have the Log and Fault Tables set up in mydbname. 
 # [see Fault::Delegate::DB]
 my $delegate1  = Fault::Delegate::DB->new (undef,"mydbname","user","passwd");
                  Fault::Logger->new       ($delegate1);

 # Set a fault
 my $fail      = 0;
 Fault::Logger->fault_check 
     (!defined $foo,"Optional tag","No foo!",'BUG','err') or return $fail;

 # Clear a fault
 my $foo       = 1;
 Fault::Logger->fault_check 
     (!defined $foo,"Optional tag","No foo!",'BUG','err') or return $fail;
    

[See example.pl for a bigger sample. It can be found either in eg/example.pl in your Perl package or /var/share/doc/libfault-perl/example.pl if installed from a debian package.]

Class Variables

 delegates  An object which satisfies a minimal logger delegate protocol.
            It must at the very least implement the log method.
 message    The mostly recently logged message. the null string if cleared
            or there has been none since the logger was last initialized.

Instance Variables

 None.

Class Methods

$proxy = Fault::Logger->new (@delegates)
$proxy = Fault::Logger->new

Initialize the logger proxy if it has never been called before and return a pointer to it in any case. There is only one logger object, a class object, and further calls simply return the same pointer. It can be accessed either by classname or the returned pointer.

By supplying a list of one or more delegate objects, you modify where and how your program will log and fault. The defaults is a Fault::Delegate::Stderr object if no delegate is supplied the first time new is called. On any subsequent calls, the default is to leave the delegate object as is.

Calling this routine re-initializes the logger object. it clears log once entries, previous log delegates and the internal fault table. If the any of the new delegates have initfaults methods, they are used to retrieve any active faults. If the delegate has a method of keeping persistant data, programs can be stopped and started without forgetting about active faults.

$one = Fault::Logger->add_delegates (@delegates)
$one = $proxy->add_delegates (@delegates)

Add zero or more logger delegates. A delegate object is ignored if it is already present.

$notfault = Fault::Logger->arg_check_isa ($val,$class,$name,$type,$priority,$target,@rest)
$notfault = $proxy->arg_check_isa ($val,$class,$name,$type,$priority,$target,@rest)

If the value $val of the variable named $name is undefined, is a not a reference or is not a member of $class or one of its subclasses, log an appropriate message. The message will contain the name of the subroutine or class and method of the caller. Class defaults to 'HASH' if not present. Other values default as documented in the Argument Description section.

This method is useful for checking subroutine args.

$notfault = Fault::Logger->arg_check_isalnum ($val,$name,$type,$priority,$target,@rest)
$notfault = $proxy->arg_check_isalnum ($val,$name,$type,$priority,$target,@rest)

If the value $val of the variable named $name is undefined, is a reference or contains a nonalphnumeric character, log an appropriate message. The message will contain the name of the subroutine or class and method of the caller. Type defaults to BUG if not present.

This method is useful for checking subroutine args.

$notfault = Fault::Logger->arg_check_isdigit ($val,$name,$type,$priority,$target,@rest)
$notfault = $proxy->arg_check_isdigit ($val,$name,$type,$priority,$target,@rest)

If the value $val of the variable named $name is undefined, is a reference or contains a non digit characters log an appropriate message. The message will contain the name of the subroutine or class and method of the caller. Type defaults to BUG if not present.

This method is useful for checking subroutine args.

$notfault = Fault::Logger->arg_check_noref ($val,$name,$type,$priority,$target,@rest)
$notfault = $proxy->arg_check_noref ($val,$name,$type,$priority,$target,@rest)

If the value $val of the variable named $name is undefined or is a reference or not alphanumeric, log an appropriate message. The message will contain the name of the subroutine or class and method of the caller. Type defaults to BUG if not present.

This method is useful for checking subroutine args.

$notfault = Fault::Logger->assertion_check ($cond,$tag,$msg,$type,$priority,$target,@rest)
$notfault = $proxy->assertion_check ($cond,$tag,$msg,$type,$priority,$target,@rest)

If the condition flag is true log the message. This is much like log except it encapsulates the condition test. This is useful if you want to log the testing of assertions sprinkled through your code. It does nothing if $cond is false or undefined.

$notfault = Fault::Logger->bug_check ($cond,$msg,$target,@rest)
$notfault = $proxy->bug_check ($cond,$msg,$target,@rest)

Set or clear a bug fault report.

If $cond is defined and true, a fault defined by $tag and $msg is now active; it is false or undefined, that fault is now inactive.

The return value is the inverse of $cond: it is true if there was no fault and false if there was. This makes the function useful in statements like:

        Fault::Logger->bug_check(@arglist) || (return undef);
or
        return Fault::Logger->bug_check(@arglist);

Note that your methods will always receive type equal "BUG" and a priority of 'err' from this method. So...

Beware. For convenience the calling sequence of this method differs from that of all the other methods. You have been warned.

Fault::Logger->clr_log_once
$proxy->clr_log_once

Flush the 'log once' table. Doing this will allow those messages to be logged again. Sometimes useful in debugging. I can imagine running it once a day so as to see if some problems are still present or have gone away.

Fault::Logger->clr_message
$proxy->clr_message

Clear the most recently logged message by setting it to a null string.

Fault::Logger->crash ($msg,$type,$priority,$target,@rest)
$proxy->crash ($msg,$type,$priority,$target,@rest)

The message "Fatal error: $msg" is sent to the delegate and then calls die with the same message.

@delegates = Fault::Logger->delegates
@delegates = $proxy->delegates

Return the list of logger delegates.

$notfault = Fault::Logger->fault_check ($cond,$tag,$msg,$type,$priority,$target,@rest)
$notfault = $proxy->fault_check ($cond,$tag,$msg,$type,$priority,$target,@rest)

This method provides 'edge triggered' fault handling. It should be called every time an action is taken, not just when there is an error. $cond is an expression which tests your fault condition, where true means fault and anything else means there is no fault condition. When a new fault arises, a message of the form:

 [FAULT    RAISED] $msg

will be printed. When $cond is next false with the same message, the fault is considered cleared:

 [FAULT CLEARED] $msg

This is useful for monitoring of systems as it can keep track of many unique fault conditions at a low level with very little code overhead in the user's program. As an example:

 Fault::Logger->fault_check
    (((-e $fn) ? 1 : 0),  $self,
    "Ignored: \"$fn\" already exists.",  "NOTE",
    @rest);

the condition expression may be anything which can be interpreted as a logical value:

 (!open ($fd,"<myfile"))

If $cond is defined and true, a fault defined by $tag and $msg is now active; it is false or undefined, that fault is now inactive.

$waslogged = Fault::Logger->log ($msg,$type,$priority,$target,@rest)
$waslogged = $proxy->log ($msg,$type,$priority,$target,@rest)

All arguments are sent to the delegate object via its log method and the return value of the delegate method is the return value here. If the message cannot be logged (the delegate returns false), the message is sent to a default logger and false is returned.

$firsttime = Fault::Logger->log_once ($msg,$type,$priority,$target,@rest)
$firsttime = $proxy->log_once ($msg,$type,$priority,$target,@rest)

Log a message if it has never appeared before; otherwise ignore it. Returns true if this is the first time; false in all other cases.

$msg = Fault::Logger->message
$msg = $proxy->message

Return the most recently logged message or else the null message if nothing has been logged yet or it has been explicitly cleared.

Instance Methods

 None.

Private Class Methods

 None.

Private Instance Methods

 None.

Errors and Warnings

 None.

KNOWN BUGS

 See TODO.

SEE ALSO

Fault::Delegate, Fault:Delegate::Stdout, Fault:Delegate::Stderr, Fault:Delegate::File, Fault:Delegate::Syslog, Fault:Delegate::DB, Fault:Delegate::SimpleHttp, Fault::Delegate::List

AUTHOR

Dale Amon <amon@vnl.com>

3 POD Errors

The following errors were encountered while parsing the POD:

Around line 555:

=over should be: '=over' or '=over positive_number'

Around line 619:

=back doesn't take any parameters, but you said =back 4

Around line 889:

=back doesn't take any parameters, but you said =back 4