in reply to How to print the backtrace in a "catch" block?

Have you looked at Carp::confess? You use the module, so I assume there is something that confess does that you don't want. Can you tell us how it fails for you to do what you want?

Replies are listed 'Best First'.
Re^2: How to print the backtrace in a "catch" block?
by tobyink (Canon) on Jan 16, 2012 at 16:41 UTC

    Carp is certainly one way to do it. What you have to be aware of is that Carp uses a concept of "safe calls". Essentially it assumes that when one sub calls another sub in the same package, the author of the package knows what he/she is doing, and doesn't include this call in the stack trace. So the stack trace only shows places where the call stack crosses package boundaries.

    Setting $Carp::Verbose to true switches off that behaviour, so stack traces will include all calls.

    A better solution though, is to notice that Perl doesn't just only support die($string) as a way of throwing an exception. die can be called on any scalar value, including blessed objects. There are a number of modules on CPAN providing useful classes that can be used for throwing exceptions. Throwable::Error is one of the best ones, but has a dependency on Moose. Exception::Class::Base and Error::Base are also good.

    Here's an example using Exception::Class::Base

    use Exception::Class ( 'My::Exception' => { description => 'simple exception'}, ); My::Exception->Trace(1); # enable catching full traces eval { My::Exception->throw('Foo'); }; if ($@) { my $e = $@; $e->show_trace(1); warn $e->as_string; }

    Exception::Class also offers some fairly nice syntactic sugar for exception catching...

    use Exception::Class ( 'My::Exception' => { description => 'simple exception'}, ); My::Exception->Trace(1); # enable catching full traces eval { My::Exception->throw('Foo'); }; if (my $e = Exception::Class->caught('My::Exception')) { $e->show_trace(1); warn $e->as_string; }

    Overall, I'd say the gold standard of exceptions for Perl is Throwable::Error combined with either TryCatch or Try::Tiny. These introduce some pretty heavy dependencies, but if you can afford them, the combination can't be beaten for maintainable, readable exception handling.

      Setting $Carp::Verbose to true switches off that behaviour, so stack traces will include all calls.

      I tried that, but the output is identical for my test script. The eval block somehow truncates my backtrace, and that's not what I want.

      Furthermore, I thank you for your effort, but I am not looking for modules that implement exception classes, as my errors tend to be runtime, i.e. thrown by Perl.

Re^2: How to print the backtrace in a "catch" block?
by Anonymous Monk on Jan 16, 2012 at 15:48 UTC
    Well, this is what I'm getting:
    runtime error at - line 6. the transaction failed at - line 23 main::main_sub() called at - line 27
    What I'm hoping for:
    runtime error at - line 6. the transaction failed at - line 23 main::sub3() called at - line 3 main::sub1() called at - line 13 main::main_sub() called at - line 27
    Or perhaps in Ruby code:
    def dying_sub raise RuntimeError, "reason here" end def main begin dying_sub() rescue RuntimeError => e STDERR.puts e.backtrace end end main() # Output: # test.rb:2:in `dying_sub' # test.rb:7:in `main' # test.rb:13

      You will need to replace the die in sub2 and/or sub3 with a call to confess to get at the stack trace. Maybe you can install a $SIG{__DIE__} handler that stashes away the stack trace for later retrieval. I think that Carp::Always does this, but I've never used it.

        $SIG{__DIE__} = sub { cluck shift; }
        did in fact do what I want (just need to make it look nicer). Thanks
        I looked at some of my code...
        It is possible to install a localized $SIG handler.
        In my case a stack trace means next to nothing..I want to know the exact DB record the failure happened on..that means something!

        In this code, I just have a __WARN__ handler, but I would presume that __DIE__ would be possible also. Of course $x has to be in scope for the $SIG handler!

        Anyway, installing a __DIE__ handler in the sub() might be easier and even better than a stack trace - although that could be done in the handler also. In this code, I just want to know which of 1M DB records caused the Warning - its not fatal for this code, but it is something I have to find and fix later. Just an idea....

        sub x { my ($x) = @_; # $x is an array ref to a DB record local $SIG{__WARN__} = sub { my $msg = shift; print STDERR "*******\n"; print STDERR $msg; print STDERR "current DB record: id=", $x->[ID]; }; # some stuff that might cause a Warning.... # some operations on the record pointed to by $x # maybe "abc123" is not numeric, etc. return $result; }

        Thanks, I suppose I'll try the signal approach. (I'm obviously not dying on purpose, but Perl is dying for me.) Was looking at Carp::Always's source code earlier, and that's what it uses.