in reply to Redefine DESTROY in class instance

Put a DESTROY method in your Log class that writes out the line that you want when the log object is destroyed. Leave the IO::File class alone.

use IO::File; package Log; sub new { my ($class, $file) = @_; my $self = bless {}, $class; $self{fh} = IO::File->new( $file, ">>" ) or die $!; return $self; } sub log { my $self = shift; $self{fh}->print( map { scalar localtime() . " $_\n" } @_ ); } sub DESTROY { my $self = shift; $self{fh}->print scalar localtime(), "Finished\n"; $self{fh}->close; }

-xdg

Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Replies are listed 'Best First'.
Re^2: Redefine DESTROY in class instance
by tmaly (Monk) on Sep 04, 2007 at 13:22 UTC
    I have tried the writing to the file handle before in the DESTROY. However the IO::File object seems to go out of scope or has its DESTROY called before I am able to write to it in my Log objects DESTROY. Here is my code below. I create an object of this class in a script and let it fall off the end of the script to test if the Finished line is written during the DESTROY however it never gets written unless I explicitly call DESTROY
    package MyLog $VERSION = '0.0.1'; use strict; use Carp qw(croak longmess); use English; use File::Path; use File::Basename; use IO::File; use MyModules::Date::Holiday qw(today); our $home; our $username; BEGIN { $username = (getpwuid($<))[0]; $home = $ENV{HOME} ? $ENV{HOME} + : "/home/users/$username"; } { my $log; my $fh; # {{{ new sub new { # singleton pattern for the logger if($log) { return $log; } else { my $class = shift; my $self = {}; $log = bless($self, $class); $self->_init(@_); return $self->new(); } } # }}} # {{{ _init intialize all the inputs given to the constructor sub _init { my $self = shift; if(@_) { my %args = %{shift @_}; @$self{keys %args} = values %args; } if(!$self->{logfile}) { my $basedir = $home . '/log/' . today() . '/'; # if for some reason we do not have the dated log directory cr +eated, create one unless(-d $basedir) { eval { mkpath($basedir); }; if($@) { croak("$0 cannot create log directory $basedir : $@"); } } my ($f,$d,$s) = fileparse($0); $self->{'logfile'} = $basedir . $f . '.log'; } if(!$self->{fh}) { my $logfile = $self->{logfile}; if(-e $logfile) { # if the logfile is there then append to it $fh = new IO::File; if($fh->open(">> $logfile")) { $self->{'fh'} = $fh; } else { croak("$0 Could not open logfile $logfile for appendin +g $OS_ERROR\n"); } } else { # create a new logfile if one does not exist $fh = new IO::File; if($fh->open("> $logfile")) { $self->{'fh'} = $fh; } else { croak("$0 Could not create logfile $logfile $OS_ERROR\ +n"); } } } print {$self->{'fh'}} $self->get_time() . " INFO | Started \n"; } # }}} # {{{ log($level,$msg) sub log { my $self = shift; my $level = shift; my $msg = shift; my $fh = $self->{fh}; my $logentry = $self->get_time() . $level . ' | ' . "$msg"; # if it is an error, email it if($level =~ /(ERROR|WARN)/i) { if($self->{'to'}) { croak($logentry,$self->{'to'}); } else { croak($logentry); } } print {$fh} "$logentry\n"; } # }}} # {{{ info($msg) sub info { my $self = shift; my $msg = shift; $self->log("INFO",$msg); } # }}} # {{{ warn($msg) sub warn { my $self = shift; my $msg = shift; $self->log("WARN",$msg); } # }}} # {{{ fatal($msg) sub fatal { my $self = shift; my $msg = shift; $self->log("ERROR",longmess() . " $msg"); $self->DESTROY(); die($msg); } # }}} # {{{ get_time borrowed from Pavels LogFile.pm sub get_time { my ($sec, $min, $hour, $day, $mon, $year) = localtime(); my $date = sprintf "%4d.%02d.%02d %02d:%02d.%02d | ", $year + 1900, $mon + 1, $day, $hour, $min, $sec; return $date; } # }}} # {{{ log_file sub log_file { return shift->{'logfile'}; } # }}} # {{{ DESTROY sub DESTROY { my $self = shift; if($fh && fileno $fh) { print "Finish\n"; print {$fh} $self->get_time() . 'INFO | ' . "$0 Finished\n"; $fh->flush; $fh->close; } undef $fh; } # }}} 1; # Magic true value required at end of module __END__

      Does it work if you don't make it a singleton object?

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

        Due to the nature of the usage of this class, I needed it to be a singleton. I did test it without the singleton and it did not seem to work. I was able to see that the IO::Handle::DESTROY was being called multiple times when I changed the symbol table to redefine it. I was able to come up with a work around solution. I subclassed IO::File and defined a DESTROY in this subclass that wrote out to the log file, called SUPER::DESTROY then closed the filehandle. This worked like a charm. I want to say thank you for your help on this. Best Regards Ty