Log::Dispatch::File::Rolling - Object for logging to date/time/pid


Log-Dispatch-File-Rolling documentation Contained in the Log-Dispatch-File-Rolling distribution.

Index


Code Index:

NAME

Top

Log::Dispatch::File::Rolling - Object for logging to date/time/pid stamped files

SYNOPSIS

Top

  use Log::Dispatch::File::Rolling;

  my $file = Log::Dispatch::File::Rolling->new(
                             name      => 'file1',
                             min_level => 'info',
                             filename  => 'Somefile%d{yyyyMMdd}.log',
                             mode      => 'append' );

  $file->log( level => 'emerg',
              message => "I've fallen and I can't get up\n" );

ABSTRACT

Top

This module provides an object for logging to files under the Log::Dispatch::* system.

DESCRIPTION

Top

This module subclasses Log::Dispatch::File for logging to date/time stamped files. See Log::Dispatch::File for instructions on usage. This module differs only on the following three points:

fork()-safe

This module will close and re-open the logfile after a fork.

multitasking-safe

This module uses flock() to lock the file while writing to it.

stamped filenames

This module supports a special tag in the filename that will expand to the current date/time/pid.

It is the same tag Log::Log4perl::Layout::PatternLayout uses, see Log::Log4perl::Layout::PatternLayout, chapter "Fine-tune the date". In short: Include a "%d{...}" in the filename where "..." is a format string according to the SimpleDateFormat in the Java World (http://java.sun.com/j2se/1.3/docs/api/java/text/SimpleDateFormat.html). See also Log::Log4perl::DateFormat for information about further restrictions.

In addition to the format provided by Log::Log4perl::DateFormat this module also supports '$' for inserting the PID. Repeat the character to define how many character wide the field should be. This should not be needed regularly as this module also supports logfile sharing between processes, but if you've got a high load on your logfile or a system that doesn't support flock()...

METHODS

Top

new()

See Log::Dispatch::File and chapter DESCRIPTION above.

log_message()

See Log::Dispatch::File and chapter DESCRIPTION above.

HISTORY

Top

0.99

Original version; created by h2xs 1.22 with options

	-A
	-C
	-X
	-b5.6.1
	-nLog::Dispatch::File::Rolling
	--skip-exporter
	-v0.99

1.00

Initial coding

1.01

Someone once said "Never feed them after midnight!"---Ok, let's append: "Never submit any code after midnight..."

Now it is working, I also included 4 tests.

1.02

No code change, just updated Makefile.PL to include correct author information and prerequisites.

1.03

Changed the syntax of the '$' format character because I noticed some problems while making Log::Dispatch::File::Alerts. You need to change your configuration!

1.04

Got a bug report where the file handle got closed in mid-execution somehow. Added a additional check to re-open it instead of writing to a closed handle.

1.05

Updated packaging for newer standards. No changes to the coding.

1.06

Fixed a subtle bug that prevented us from locking the logfile after a fork if no PID was used in the filename.

Also disabled forced double opening of the logfile at startup. It was in place because I didn't trust Log::Dispatch::File to really open the file at the right moment.

Thanks to Peter Lobsinger for the patch. Please always wrap non-standard Test::* modules in eval and make your testfile clean up after itself... ;)

SEE ALSO

Top

Log::Dispatch::File, Log::Log4perl::Layout::PatternLayout, http://java.sun.com/j2se/1.3/docs/api/java/text/SimpleDateFormat.html, Log::Log4perl::DateFormat, 'perldoc -f flock', 'perldoc -f fork'.

AUTHOR

Top

M. Jacob, <jacob@j-e-b.net>

COPYRIGHT AND LICENSE

Top


Log-Dispatch-File-Rolling documentation Contained in the Log-Dispatch-File-Rolling distribution.

## no critic
package Log::Dispatch::File::Rolling;

use 5.006001;
use strict;
use warnings;

use Log::Dispatch::File;
use Log::Log4perl::DateFormat;
use Fcntl ':flock'; # import LOCK_* constants

our @ISA = qw(Log::Dispatch::File);

our $VERSION = '1.06';

our $TIME_HIRES_AVAILABLE = undef;

BEGIN { # borrowed from Log::Log4perl::Layout::PatternLayout, Thanks!
	# Check if we've got Time::HiRes. If not, don't make a big fuss,
	# just set a flag so we know later on that we can't have fine-grained
	# time stamps

	eval { require Time::HiRes; };
	if ($@) {
		$TIME_HIRES_AVAILABLE = 0;
	} else {
		$TIME_HIRES_AVAILABLE = 1;
	}
}

# Preloaded methods go here.

sub new {
	my $proto = shift;
	my $class = ref $proto || $proto;

	my %p = @_;

	my $self = bless {}, $class;

	# only append mode is supported
	$p{mode} = 'append';

	# base class initialization
	$self->_basic_init(%p);

	# split pathname into path, basename, extension
	if ($p{filename} =~ /^(.*)\%d\{([^\}]*)\}(.*)$/) {
		$self->{rolling_filename_prefix}  = $1;
		$self->{rolling_filename_postfix} = $3;
		$self->{rolling_filename_format}  = Log::Log4perl::DateFormat->new($2);
		$p{filename} = $self->_createFilename();
	} elsif ($p{filename} =~ /^(.*)(\.[^\.]+)$/) {
		$self->{rolling_filename_prefix}  = $1;
		$self->{rolling_filename_postfix} = $2;
		$self->{rolling_filename_format}  = Log::Log4perl::DateFormat->new('-yyyy-MM-dd');
		$p{filename} = $self->_createFilename();
	} else {
		$self->{rolling_filename_prefix}  = $p{filename};
		$self->{rolling_filename_postfix} = '';
		$self->{rolling_filename_format}  = Log::Log4perl::DateFormat->new('.yyyy-MM-dd');
		$p{filename} = $self->_createFilename();
	}

	$self->{rolling_fh_pid} = $$;
	$self->_make_handle(%p);

	return $self;
}

sub log_message { # parts borrowed from Log::Dispatch::FileRotate, Thanks!
	my $self = shift;
	my %p = @_;

	my $filename = $self->_createFilename();
	if ($filename ne $self->{filename}) {
		$self->{filename} = $filename;
		$self->{rolling_fh_pid} = 'x'; # force reopen
	}

	if ( $self->{close} ) {
		$self->_open_file;
		$self->_lock();
		my $fh = $self->{fh};
		print $fh $p{message};
		$self->_unlock();
		close($fh);
		$self->{fh} = undef;
	} elsif (defined $self->{fh} and ($self->{rolling_fh_pid}||'') eq $$ and defined fileno $self->{fh}) { # flock won't work after a fork()
		my $inode  = (stat($self->{fh}))[1];         # get real inode
		my $finode = (stat($self->{filename}))[1];   # Stat the name for comparision
		if(!defined($finode) || $inode != $finode) { # Oops someone moved things on us. So just reopen our log
			$self->_open_file;
		}
		$self->_lock();
		my $fh = $self->{fh};
		print $fh $p{message};
		$self->_unlock();
	} else {
		$self->{rolling_fh_pid} = $$;
		$self->_open_file;
		$self->_lock();
		my $fh = $self->{fh};
		print $fh $p{message};
		$self->_unlock();
	}
}

sub _lock { # borrowed from Log::Dispatch::FileRotate, Thanks!
	my $self = shift;
	flock($self->{fh},LOCK_EX);
	# Make sure we are at the EOF
	seek($self->{fh}, 0, 2);
	return 1;
}

sub _unlock { # borrowed from Log::Dispatch::FileRotate, Thanks!
	my $self = shift;
	flock($self->{fh},LOCK_UN);
	return 1;
}

sub _current_time { # borrowed from Log::Log4perl::Layout::PatternLayout, Thanks!
	# Return secs and optionally msecs if we have Time::HiRes
	if($TIME_HIRES_AVAILABLE) {
		return (Time::HiRes::gettimeofday());
	} else {
		return (time(), 0);
	}
}

sub _createFilename {
	my $self = shift;
	return $self->{rolling_filename_prefix}
	     . $self->_format()
	     . $self->{rolling_filename_postfix};
}

sub _format {
	my $self = shift;
	my $result = $self->{rolling_filename_format}->format($self->_current_time());
	$result =~ s/(\$+)/sprintf('%0'.length($1).'.'.length($1).'u', $$)/eg;
	return $result;
}

1;
__END__