Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Improving the Error Handling of my Scripts

by Dru (Hermit)
on Mar 10, 2010 at 18:39 UTC ( [id://827859]=perlquestion: print w/replies, xml ) Need Help??

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

Greetings Monks,

Almost all of my scripts run non-interactively via cron. Sometimes they have problems and I do not find out for awhile. My error handling up to this point has been shoddy and I would like to improve this. I would like to have the scripts email me with a descriptive message of where they died, but also continue to have them die gracefully (and tell why) if I ever have to run them interactively (which I occasionally do). This is how I accomplish this task now (when it gets done):
use English '-no_match_vars'; opendir(DIR, $dir) or mail("Line 11: Could not open $dir at $PROGRAM_NAME: $OS_ERROR\n") and die("Line 11: Could not open $dir at $PROGRAM_NAME: $OS_ERROR\n") sub mail { my $error = $_[0]; ... }
And this is ok, but I do not like the fact that the line number is static and I have to remember to update it if I ever modify the script (I won't remember). Also, after reading Perl Best Practices, I have been converted to using the Carp module. It provides everything I need in the error message, but I can't figure out how to redirect the carp output to my mail subroutine. I would like to do something like this (which doesn't work of course, but I think you can get the gist of what I am attempting to accomplish):
use Carp; opendir(DIR, $dir) or ($error = carp("Could not open $dir"); (mail($error) and croak) if $error; sub mail { my $error = $_[0]; ... }
Is it possible to do something like this? I know croak dies immediately, which is why I am using carp first to catch the error.

Thanks,
Dru

Perl, the Leatherman of Programming languages. - qazwart

Replies are listed 'Best First'.
Re: Improving the Error Handling of my Scripts
by Corion (Patriarch) on Mar 10, 2010 at 19:01 UTC

    I use $SIG{__DIE__} to send me a mail:

    $SIG{__DIE__} = sub { my $err = $@; local $@; mail($@); };

    That way, whenever the script dies, the error gets mailed to me.

      I think the die-message is available in $@ only in case the die happens within an eval{} block (in which case I personally wouldn't want to be mailed).

      If the die happens outside of an eval, however (i.e. when the script atually dies), the message is only available in @_.   (Also, I suppose your code should be mail($err), as the localized $@ would always be empty...)

      In other words, I'd use something like

      #!/usr/bin/perl use strict; use warnings; use Carp; $SIG{__DIE__} = sub { die @_ if $^S; # skip handler for eval{} blocks mail(@_); }; sub mail { chomp(my $msg = shift); print qq(mailing "$msg"...\n\n); } sub test { eval { croak "bar"; }; # doesn't send mail croak "foo"; # sends mail (and dies) } test(); __END__ $ ./827866.pl mailing "foo at ./827866.pl line 19 main::test() called at ./827866.pl line 22"... foo at ./827866.pl line 19 main::test() called at ./827866.pl line 22
Re: Improving the Error Handling of my Scripts
by elTriberium (Friar) on Mar 10, 2010 at 19:44 UTC
    To avoid hard-coding any line numbers you can use the "caller" function:

    http://perldoc.perl.org/functions/caller.html

    Here's a simple example:
    #!/usr/bin/perl exit main(); sub main { print_error("test1"); print_error("test2"); print_error("test3"); return 0; } sub print_error { my ($error) = @_; my $line = (caller(0))[2]; print "line " , $line , ": " , $error , "\n"; }

    This will print:

    -bash:perl$ perl caller.pl line 6: test1 line 7: test2 line 8: test3
Re: Improving the Error Handling of my Scripts
by almut (Canon) on Mar 10, 2010 at 20:16 UTC
    but I can't figure out how to redirect the carp output

    Carp provides a routine shortmess, which just returns the string that carp would write to STDERR.  So you could say

    my $error; opendir(DIR, $dir) or $error = Carp::shortmess("Could not open $dir"); mail($error) and die $error if $error;

    I have no idea, though, why shortmess is no longer documented in 5.10.x (in 5.8.8 it was), despite the fact that it's still there and works...

    Actually, the implementation of carp is just sub carp { warn shortmess @_ }. And shortmess is also in @EXPORT_OK.

    P.S.:  Does anyone know if it has been deprecated (nothing related is mentioned in either the 5.10 or the 5.8 docs), or what else might be wrong with using it?

      I have no idea, though, why shortmess is no longer documented in 5.10.x (in 5.8.8 it was), despite the fact that it's still there and works... P.S.: Does anyone know if it has been deprecated (nothing related is mentioned in either the 5.10 or the 5.8 docs), or what else might be wrong with using it?
      I do not know of anything wrong with shortmess. A quick glance at delta docs does not reveal anything. I recommend you submit a patch to add this back into the POD using perlbug. Either it will be fixed, or you should get a response explaining why it will not be fixed.

      I have been planning to submit a patch for a minor POD bug in Carp, but I have been too Lazy.

Re: Improving the Error Handling of my Scripts
by Anonymous Monk on Mar 10, 2010 at 19:39 UTC

    Perhaps I'm missing something, but cron will mail a program's output to the owner's account. Just make sure you have the job owner's account correctly configured or in the 'aliases' file.

    Perhaps not a glamorous perl solution, but it keeps the arcane out of your scripts.

    If your script's STDOUT is already being mailed to a user other than you, have the cron job redirect it's STDERR to your mail.

Re: Improving the Error Handling of my Scripts
by Hue-Bond (Priest) on Mar 11, 2010 at 08:38 UTC
    I do not like the fact that the line number is static and I have to remember to update it if I ever modify the script (I won't remember).

    Use __LINE__ for that:

    use warnings; use strict; printf "I'm in line %d\n", __LINE__; printf "I'm in line %d\n", __LINE__; printf "I'm in line %d\n", __LINE__; __END__ I'm in line 4 I'm in line 5 I'm in line 6

    --
     David Serrano
     (Please treat my english text just like Perl code, i.e. feel free to notify me of any syntax, grammar, style and/or spelling error. Thank you!).

Re: Improving the Error Handling of my Scripts
by cpbills (Initiate) on Mar 11, 2010 at 04:33 UTC
    another option would be defining the $MAILTO variable in your crontab and not redirecting error output to /dev/null
Re: Improving the Error Handling of my Scripts
by Dru (Hermit) on Mar 11, 2010 at 17:00 UTC
    All,

    Thank you very much for the responses. I didn't even think about just having cron mail me the output. Very simple and elegant solution (which are always the best).

    Also, thank you for determining the current line number of one's script. I knew this had to be possible, and I thought there was a special variable for it, but I was not able to find it.

    Thanks,
    Dru

    Perl, the Leatherman of Programming languages. - qazwart

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://827859]
Approved by Corion
Front-paged by toolic
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (3)
As of 2024-04-24 18:17 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found