What's the best way to send an email if any error occurs in a Perl script?
I am working on a script that does many things, and if at any point during its execution a 'die' gets executed or an error occurs, I'd like to send an email message to myself.
I'm currently using the END block to check the exit status, and if it is greater than 0 call a error email subroutine, but I am wondering if there is a better way. The fla开发者_C百科w with this current implementation is that if an error occurs in the email subroutine, an infinite loop will occur.
We've really got two separate questions here:
How do you (reliably) execute code when the program
die
s without creating an infinite loop if this code also hits a fatal error?What's the best way to send email from Perl code?
So, to answer each separately:
Rather than an
END
block, I would use a$SIG{__DIE__}
handler (untested code, but should be right):$SIG{__DIE__} = \&error_handler; sub error_handler { die @_ if $^S; # Do nothing if we died in an eval{} delete $SIG{__DIE__}; # Clear handler in case following code also dies print STDERR "Blue Wizard is about to die!\n"; }
There are, of course, many excellent mail-sending modules on CPAN. My personal preference from among them is MIME::Lite, which will send mail using
sendmail
by default, but, if you're on Windows or some other system with nosendmail
command, you can useMIME::Lite->send("smtp");
to send via SMTP directly without going through a local MTA. There is also support for routing mail through an outgoing mail server (handy if your upstream provider blocks port 25!) and handling common authentication methods on that server if needed.
I use eval
with sendmail (since it's almost always installed):
eval
{
# do something
};
if ( $@ )
{
my $ip = `ifconfig`
or die "Can't ifconfig: $!\n";
my @now = localtime;
my $now = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $now[5]+1900, $now[4]+1, $now[3], $now[2], $now[1], $now[0] );
open ( SENDMAIL, "|/usr/sbin/sendmail -oi -t -fyou\@yourdomain.com" )
or die "Can't fork for sendmail: $!\n";
print SENDMAIL << "EOF";
To: Your Name <you\@yourdomain.com>
Subject: ERROR: Exception
$now
$@
$ip
EOF
close ( SENDMAIL )
or warn "Sendmail didn't close nicely";
};
This will fix your infinite loop, but won't send an email in the unlikely event that the email block dies first. I think @jcomeau_ictx gets the bulletproof award.
I also have no idea how to do this in Windows. Hope this helps!
if you're on a Unix system, including (probably) Mac OS/X: ./myscript.pl 2>/tmp/myscript.pl.log || mail -s 'myscript.pl failed' me@example.com < /tmp/myscript.pl.log
on Windows I don't know.
My personal preference is the Net::SMTP perl module. Somewhat verbose but very full-featured.
my $mail = Net::SMTP->new($smtp_host);
$mail->mail("$login\@foo.com");
$mail->to("$login\@foo.com");
$mail->data();
#Generate email headers
$mail->datasend("From: Root <root\@foo.com>");
$mail->datasend("Subject: Status");
$mail->datasend("To: root\@foo.com");
$mail->datasend("\n") # Need one newline between headers and body
# Report data
$mail->datasend("Mail body here");
$mail->datasend("More mail body, repeat $mail->datasend() as needed");
# Then close connection
$mail->quit();
精彩评论