Added OO-friendly logger object.

This commit is contained in:
Chris 2012-04-11 15:04:08 +01:00
parent 9605cdadfa
commit c93c9d6919

271
Logger.pm Normal file
View File

@ -0,0 +1,271 @@
## @file
# This file contains the implementation of a simple logging system like that
# provided in Logging, except that this supports verbosity control.
#
# @author Chris Page <chris@starforge.co.uk>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
## @class Logger
# A class to handle logging operations throughout a system. This collects
# together the various functions needed for displaying log messages and errors
# at various levels of verbosity, in an attempt to cut down on duplicate
# parameter passing throughout the rest of the system.
#
# @todo A lot of functionality is duplicated in Logging, but the interface
# is completely different. Modify this class to allow for both OO
# and direct calls to methods.
package Logger;
use strict;
use constant WARNING => 0;
use constant NOTICE => 1;
use constant DEBUG => 2;
use constant MAX_VERBOSITY => 2;
# ============================================================================
# Constructor
#
## @cmethod $ new(%args)
# Create a new Logging object for use around the system. This creates an object
# that provides functions for printing or storing log information during script
# execution. Meaningful options for this are:
#
# verbosity - One of the verbosity level constants, any messages over this will
# not be printed. If this is not specified, it defaults to DEBUG
# (the highest supported verbosity)
# fatalblargh - If set to true, any calls to the blargh function kill the
# script immediately, otherwise blarghs produce warning messages.
# Defaults to false.
# logname - If set, messages sent to warn_log and die_log will be appended
# to the specified log file. See start_log below for more details.
#
# @param args A hash of key, value pairs with which to initialise the object.
# @return A new Logging object.
sub new {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
my $self = {
"verbosity" => DEBUG,
"fatalblargh" => 0,
"outlevels" => [ "WARNING", "NOTICE", "DEBUG" ],
@_,
};
my $obj = bless $self, $class;
$obj -> start_log($self -> {"logname"}) if($self -> {"logname"});
return $obj;
}
## @method void set_verbosity($newlevel)
# Set the verbosity level of this logging object to the specified level. If the
# newlevel argument is not specified, or it is out of range, the object is set
# to the maximum supported verbosity.
#
# @param newlvel The new verbosity level for this logger.
sub set_verbosity {
my $self = shift;
my $newlevel = shift;
$newlevel = MAX_VERBOSITY if(!defined($newlevel) || $newlevel < 0 || $newlevel > MAX_VERBOSITY);
$self -> {"verbosity"} = $newlevel;
}
## @method void start_log($filename, $progname)
# Start logging warnings and errors to a file. If logging is already enabled,
# this will close the currently open log before opening the new one. The log
# file is appended to rather than truncated.
#
# @warning THIS SHOULD NOT BE CALLED IN PRODUCTION! This function should be used
# for testing only, otherwise you may run into <i>all kinds of fun</i>
# with attempts to concurrently append to the log file. If you decide
# to ignore this, don't complain to me when things blow up in your face.
#
# @param filename The name of the file to log to.
# @param progname A optional program name to show in the log. Defaults to $0
sub start_log {
my $self = shift;
my $filename = shift;
my $progname = shift || $0;
# Close the logfile if it has been opened already
$self -> end_log($progname) if($self -> {"logfile"});
my $logfile;
# Open in append mode
open($logfile, ">> $filename")
or die "Unable to open log file $filename: $!";
my $tm = scalar localtime;
print $logfile "\n----------= Starting $progname [pid: $$] at $tm =----------\n";
$self -> {"logfile"} = $logfile;
$self -> {"logtime"} = time();
}
## @method void end_log($progname)
# Stop logging warnings and errors to a file. This will write an indicator
# that logging is stopping to the file and then close it.
#
# @param progname A optional program name to show in the log. Defaults to $0
sub end_log {
my $self = shift;
my $progname = shift || $0;
if($self -> {"logfile"}) {
my $logfile = $self -> {"logfile"};
my $tm = scalar localtime;
my $elapsed = time() - $self -> {"logtime"};
print $logfile "----------= Completed $progname [pid: $$] at $tm, execution time $elapsed seconds =----------\n";
close($logfile);
# Make sure this is undefed so that we don't try to repeat close it.
$self -> {"logfile"} = undef;
}
}
# ============================================================================
# log printing
#
## @method $ fatal_setting($newstate)
# Get (and optionally set) the value that determines whether calls to blargh
# are fatal. If newstate is provided, the current state of blargh severity is
# set to the new state.
#
# @param newstate If specified, change the value that determines whether calls
# to blargh are fatal: if set to true, calls to blargh will exit
# the script immediately with an error, if set to 0 calls to
# blargh will generate warning messages.
# @return The current state of blargh fatality.
sub fatal_setting {
my $self = shift;
my $newstate = shift;
$self -> {"fatalblargh"} = $newstate if(defined($newstate));
return $self -> {"fatalblargh"};
}
## @method void print($level, $message, $newline)
# If the specified level is less than, or equal to, the current verbosity level,
# print the specified message to stdout. If the level is over the verbosity
# level the message is discarded.
#
# @param level The level of the message, should be one of WARNING, NOTICE, or DEBUG.
# @param message The message to print.
# @param newline Print a newline after the message. If set to falce, this will suppress
# the automatic addition of a newline after the message (although the
# message may still contain its own newlines). If set to true, or omitted,
# a newline is printed after the message.
sub print {
my $self = shift;
my $level = shift;
my $message = shift;
my $newline = shift;
$newline = 1 if(!defined($newline));
my $logfile = $self -> {"logfile"};
if($level <= $self -> {"verbosity"}) {
print $self -> {"outlevels"} -> [$level],": $message",($newline ? "\n" : "");
print $logfile $self -> {"outlevels"} -> [$level],": $message",($newline ? "\n" : "") if($logfile);
# flush stdout if needed to avoid log update delays
select((select(STDOUT), $| = 1)[0]) if($newline);
}
}
## @method void blargh($message)
# Generate a message indicating that a serious problem has occurred. If the logging
# object is set up such that blargh()s are fatal, this function will die with the
# specified message, otherwise the message will be printed as a warning.
#
# @param message The message to print.
sub blargh {
my $self = shift;
my $message = shift;
if($self -> {"fatalblargh"}) {
die "FATAL: $message\n";
} else {
$self -> print(WARNING, $message);
}
}
## @method void warn_log($ip, $message)
# Write a warning message to STDERR and to a log file if it is opened. Warnings
# are prepended with the process ID and an optional IP address, and entries
# written to the log file are timestamped.
#
# @note This method completely ignores all verbosity controls (unlike print()),
# it is not intended for use in situations where the user has control over
# verbosity levels.
#
# @param ip The IP address to log with the message. Defaults to 'unknown'
# @param message The message to write to the log
sub warn_log {
my $self = shift;
my $ip = shift || "unknown";
my $message = shift;
my $logfile = $self -> {"logfile"};
print $logfile scalar(localtime)," [$$:$ip]: $message\n"
if($logfile);
warn "[$$:$ip]: $message\n";
}
## @method void die_log($ip, $message)
# Write an error message a log file if it is opened, and then die. Errors
# are prepended with the process ID and an optional IP address, and entries
# written to the log file are timestamped.
#
# @note This method completely ignores all verbosity controls (unlike print()),
# it is not intended for use in situations where the user has control over
# verbosity levels.
#
# @param ip The IP address to log with the message. Defaults to 'unknown'
# @param message The message to write to the log
sub die_log {
my $self = shift;
my $ip = shift || "unknown";
my $message = shift;
my $logfile = $self -> {"logfile"};
print $logfile scalar(localtime)," [$$:$ip]: $message\n"
if($logfile);
die "[$$:$ip]: $message\n";
}
1;