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

Hello monks, I'm running into something that is confusing me quite a bit. I've borrowed a nice little logging class, er, package written by Jeff Rowan from: http://jrf.odpn.net/writings/Log.pm I seem to recall it was working fine for a short time (I got logs anyway, and still do actually), and after a number of modifications to my program it seems to have started complaining (maybe it always complained and the way I was starting my program masked that). I'm now getting a somewhat cryptic (to me) error which seems to indicate I'm not passing enough information to the object when it is constructed, but I don't see what could be missing or why. I'm creating a new log object:
my $log = new File::QuickLog(file => "logtaild.log");
And then using the print method like so:
$log->print("Starting in build mode.");
The error I'm getting is this:
(in cleanup) Can't call method "open" on an undefined value at ./logt +aild.pl line 334 during global destruction.
So I think what is happening is that the $log object no longer (or ever) had some piece of information needed for the open...that seems to make sense, except the information needed should all be there as far as I can tell:
my $self = bless { _logfile => $arg{'file'} ? $arg{'file'} : $arg[0], _fh => new FileHandle, _logstart => _datetime(), }, $class;
From this I'm thinking we've got _logfile from 'file' during construction, and _fh comes from the Filehandle object, and logstart is just a timestamp created by a small date object included in the log class.

The line that is complaining is here:

unless($self->{'_fh'}->open (">>".$self->{'_logfile'})){ croak "Failed to open \"".$self->{'_logfile'}."\""; }
It looks like we're calling open with the filehandle object that we constructed earlier, and the logfile name that was passed to the object constructor.

The object language/syntax of perl still baffles me, so I suspect my problems are more opaque to me than to someone who understands objects in perl better. If larger code snippets are required, let me know--the full module is linked above, and it is quite simple to understand (even for me, except for some of the object stuff).

Anyway, thanks for any pointers anyone can provide that will set me on the path. I'm running with Use strict and -w, of course...

Replies are listed 'Best First'.
Re: Objects and undefined values
by chromatic (Archbishop) on Feb 07, 2002 at 20:11 UTC
    "during global destruction" means that the problem comes when Perl is cleaning up right before the program ends. This shouldn't be a problem, as if it's cleaning up $log, $log should still have a reference to the appropriate FileHandle object.

    Do you have a log call in a DESTROY sub?

      Thanks chromatic, for the quick reply!

      Yes, the DESTROY is this:

      sub DESTROY { my ($self, @args) = @_; $self->print("End Logging"); undef $self->{'_fh'}; return 1; }
      And commenting out the $self->print line kills the error.

      Now to understand why... Shouldn't $self->print be the same as calling it as $log->print? The filehandle is still defined until the next line...Hmmm. This is why perl objects confuse me! ;-)

      Since DESTROY is being called implicitly, is the problem that it isn't getting all of the $log object, or perhaps there is a bug in the way the DESTROY object is handling the elements? Should I break out the elements as found in the 'new' method, like so:

      sub new{ my ($class, @arg) = @_; my %arg = @arg; ... }
      Where we're breaking the array args into a hash arg. Nope, guess not, as it doesn't fix the error.
        You also mention that $log is a global object. By the end of the program, are all references to it explicitly destroyed, or are you relying on Perl to clean it up for you as the main package goes out of scope?

        If it's the latter (and you're usually okay doing that, except for when you aren't), you're probably getting tripped up by Perl's unreliable destruction order. Instead of What We All Expect, where objects and variables are cleaned up in reverse order of their creation, there's a mad rush for the exit, and occasionally things get trampled underfoot. Unfortunately, that appears to mean the FileHandle is gone before $log is.

        You have two solutions -- move your final log message out of DESTROY, or explicitly undefined the last reference to the $log object before falling off the end of the program.

        Ok, replying to myself because I think I'm tracking down my problem or at least catching a glimpse of it.

        I am daemonizing my program in the normal case, so I fork && exit near the top in the primary subroutine that is called if we are to run in daemon mode--but this fork comes after the log object construction. So I'm creating two of the log object, I think...

        So I tried breaking out the check for starting in daemon mode, put the log object construction right after the check and before the actual daemonizing subroutine call. So I should have a global $log object throughout either daemon mode, or the non-daemon mode. But alas I still get the error when signalling the program to shutdown (but not immediately on run as I was before, thus the reason I think I'm catching a fleeting glimpse of what is going wrong).

        I've commented out the 'undef $log' at the end of the program as a test, but no result...and $log is not going out of scope until the end of the program because it is a global object. So does this mean the DESTROY method is called /after/ all objects are already out of scope? That just doesn't seem correct.

        So what am I missing? (I know I'm missing something, because it can't be that DESTROY is called after the object is already gone. Or am I just thinking too hard?)