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

I am writing a new type of logger. If the main program dies anywhere along the way, I would like to be able to capture the dying message, the class will already write to log on death, but it will not write the die <message>. Any one out there ever try this? I have tried $!, $^W, and $@, but none of these report the die message.
#The logger will write to the file when the logger is #destroyed, or by explicitly calling the flush() function. my $l = Loger->new(fileName=>'/tmp/log.log'); $l->add("Log line 1","cat to log"); die "Died for some reason..."; #More program below
I would like the log to read: timestamp::Log line 1::cat to log timestamp::Died for some reason

Replies are listed 'Best First'.
Re: Capturing Text from a die command
by blokhead (Monsignor) on May 14, 2004 at 18:03 UTC
    You can register a die handler in $SIG{__DIE__}. This example code probably won't work when you have multiple instances of Logger, and it will clobber any die handler the user has set up. But it's a good start:
    package Logger; sub new { my $self = ... ... $SIG{__DIE__} = sub { my $msg = shift; $self->add($msg); die $msg; }; return $self; }
    A more robust solution is to maintain a list of active Logger instances within the package, and set up just one die handler that writes to all of them.
    package Logger; our @instances; sub new { my $self = ...; ... push @instances, $self; return $self; } $SIG{__DIE__} = sub { my $msg = shift; $_->add($msg) for @instances; }
    You may also want to take care to not clobber existing die handlers, but I don't know a whole lot about that. I'll leave it to the pros ;)

    blokhead

      Works like a charm, I appreciate it. You also just gave me a new subject to read up on. Thanks!

      Note that die is often caught in eval, and you usually don't want to log such fake deaths. You can avoid that by checking $^S:

      $SIG{__DIE__} = sub { die @_ if $^S; ... };

      Note that the fake SIG handlers __DIE__ and __WARN__ have special handling: the handler is suspended while running it, so calling die() within the die handler does the right thing.

      This still won't necessarily do the right thing for the commonish idiom of 'load module if available':

      BEGIN { $module_loaded = eval { require Some::Module }; }
      since in that context $^S is set to undef; I'm not aware of any easy way to determine in that case whether or not the failed require is happening inside an eval.

      Hugo

Re: Capturing Text from a die command
by matija (Priest) on May 14, 2004 at 18:03 UTC
    If you're trying to catch a die message from a program you don't control, you could define $SIG{__DIE__} to point to a subroutine that will handle the messages for you.

    Note that if you want to capture the warnings, use $SIG{__WARN__}.

Re: Capturing Text from a die command
by kvale (Monsignor) on May 14, 2004 at 17:56 UTC
    If you want to log a death, simply put in a log entry for this right before you explicitly die:
    $l->add("Log line 1","cat to log"); #...intervening program $l->add("Died for some reason"); die "Died for some reason...";

    -Mark

      I understand that, I am trying to be a bit more elegant than that, I am not trying to write a wrapper, or duplicate work, that would not be a good class if I had to do that.
Re: Capturing Text from a die command
by saintmike (Vicar) on May 15, 2004 at 01:07 UTC
    Are you sure you want to write your own logger? The Log::Log4perl package provides everything you can probably think of, including your requirement. Check for logdie() in the documentation.