tmaly has asked for the wisdom of the Perl Monks concerning the following question:

I have a Log class in which I have an attribute of IO::File. I wanted to have a finish line written out to the file handle before the log class goes out of scope. I tried putting the code in the DESTROY of the Log class but this did not work. Then I tried changing the symbol table as in http://www.perlmonks.org/?node_id=394129 however this changed all of my instances of IO::File. Is there a good way to redefine the DESTROY that is called for just one instance of IO::File that would not affect any other instances? Best Regards Ty

Replies are listed 'Best First'.
Re: Redefine DESTROY in class instance
by Joost (Canon) on Aug 31, 2007 at 12:00 UTC
Re: Redefine DESTROY in class instance
by xdg (Monsignor) on Aug 31, 2007 at 16:19 UTC

    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.

      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.