Reputation: 6496
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 flaw with this current implementation is that if an error occurs in the email subroutine, an infinite loop will occur.
Upvotes: 2
Views: 3357
Reputation: 46207
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 no sendmail
command, you can use MIME::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.
Upvotes: 4
Reputation: 421
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();
Upvotes: 1
Reputation: 6606
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!
Upvotes: 3
Reputation: 38472
if you're on a Unix system, including (probably) Mac OS/X: ./myscript.pl 2>/tmp/myscript.pl.log || mail -s 'myscript.pl failed' [email protected] < /tmp/myscript.pl.log
on Windows I don't know.
Upvotes: 2