package Log::Log4perl::Appender::File::fixed_size; ################################################## use base qw(Log::Log4perl::Appender::File); use constant _INTERNAL_DEBUG => 0; use warnings; use strict; use Fcntl; use Tie::File; ################################################## sub new { ################################################## my($class, @options) = @_; pop(@options) if(@options%2!=0); my $self={ max_lines => 0, name => "unknown name", umask => undef, owner => undef, group => undef, binmode => undef, utf8 => undef, recreate => 0, recreate_check_interval => 30, recreate_check_signal => undef, recreate_pid_write => undef, create_at_logtime => 0, header_text => undef, @options }; if($self->{create_at_logtime}) { $self->{recreate} = 1; } if(defined $self->{umask} and $self->{umask} =~ /^0/) { # umask value is a string, meant to be an oct value $self->{umask} = oct($self->{umask}); } if(defined $self->{header_text} && $self->{header_text} !~ /\n\Z/){ $self->{header_text} .= "\n"; } die "Mandatory parameter 'filename' missing" unless exists $self->{filename}; bless $self, $class; # This will die() if it fails $self->file_open() unless $self->{create_at_logtime}; return $self; } ################################################## sub file_open { ################################################## my($self) = @_; my $arrows = ">"; my $mode = (O_CREAT|O_RDWR); my $old_umask = umask(); umask($self->{umask}) if defined $self->{umask}; $self->{tied}=tie(@{$self->{log}}, 'Tie::File', $self->{filename}, mode => $mode); if( -f $self->{filename} and ( defined $self->{owner} or defined $self->{group} ) ) { eval { $self->perms_fix() }; if($@) { # Cleanup and re-throw $self->file_close(); die $@; } } if($self->{recreate}) { $self->{watcher} = Log::Log4perl::Config::Watch->new( file => $self->{filename}, (defined $self->{recreate_check_interval} ? (check_interval => $self->{recreate_check_interval}) : ()), (defined $self->{recreate_check_signal} ? (signal => $self->{recreate_check_signal}) : ()), ); } umask($old_umask) if defined $self->{umask}; if (defined $self->{binmode}) { binmode $self->{tied}->{fh}, $self->{binmode}; } if (defined $self->{utf8}) { binmode $self->{tied}->{fh}, ":utf8"; } } ################################################## sub file_close { ################################################## my($self) = @_; if(defined $self->{tied}) { delete($self->{tied}); untie(@{$self->{log}}); delete($self->{log}); } } ################################################## sub file_switch { ################################################## my($self, $new_filename) = @_; print "Switching file from $self->{filename} to $new_filename\n" if _INTERNAL_DEBUG; $self->file_close(); $self->{filename} = $new_filename; $self->file_open(); } ################################################## sub log { ################################################## my($self, %params) = @_; if($self->{recreate}) { if($self->{recreate_check_signal}) { if($self->{watcher}->{signal_caught}) { $self->{watcher}->{signal_caught} = 0; $self->file_switch($self->{filename}); } } else { if(!$self->{watcher} or $self->{watcher}->file_has_moved()) { $self->file_switch($self->{filename}); } } } shift(@{$self->{log}}) if($self->{max_lines} && defined $self->{header_text}); push(@{$self->{log}},$params{message}); shift(@{$self->{log}}) while($self->{max_lines} && @{$self->{log}}>$self->{max_lines}); unshift(@{$self->{log}}, $self->{header_text}) if($self->{max_lines} && defined $self->{header_text}); } ################################################## sub DESTROY { ################################################## $_[0]->file_close(); } 1;