Customizing Exception, but only when an error is raised

From: Robert Klemme [mailto:bob.news@gmx.net]
Sent: Friday, October 07, 2005 6:52 AM
To: ruby-talk ML
Subject: Re: Customizing Exception, but only when an error is raised

<snip>

It would be interesting to learn what real world problem
Daniel wants to solve...

Kind regards

    robert

I was trying to emulate Perl's $SIG{__DIE__} handler for reports running
via cron where, whenever an error occurred, an email would be sent to me
(or whoever). While I can log errors to a file, I still have to check
the log files periodically to make sure they ran as expected. Every
once in a while something goes awry - network glitch, database glitch -
whatever.

No, I don't want to run a separate program to tail log files looking for
errors. :slight_smile:

My initial plan (which I had nearly complete) was to do something like
this:

# At the top of your program
require "exception/mail"
Exception.mail_host = "mailhost.foo.com"
Exception.mail_to = "person@foo.com"

Then, whenever an error was raised anywhere in my program an email would
be sent out. There are plenty of other options you could configure,
btw, though the others all have reasonable defaults.

The problem, I realized, was that even if I wanted to ignore an error
(or handle it some other way), an email would still be sent out. Now I'm
thinking that maybe I should redefine Kernel#raise instead.

Regards,

Dan

···

-----Original Message-----

Berger, Daniel wrote:

From: Robert Klemme [mailto:bob.news@gmx.net]
Sent: Friday, October 07, 2005 6:52 AM
To: ruby-talk ML
Subject: Re: Customizing Exception, but only when an error is raised

<snip>

It would be interesting to learn what real world problem
Daniel wants to solve...

Kind regards

    robert

I was trying to emulate Perl's $SIG{__DIE__} handler for reports
running via cron where, whenever an error occurred, an email would be
sent to me (or whoever).

I'm not too familiar with $SIG{__DIE__} but I assume it's called whenever
the program executes a die. If this is the case, there are several
alternatives in Ruby:

1. wrap your complete script in begin;rescue;end and handle the exception
appropriately at this top level.

1b. like 1 but catch only SystemExit

2. You can use at_exit to set an exit handler
http://www.ruby-doc.org/core/classes/Process.html#M001763

3. Use Signal.trap
http://www.ruby-doc.org/core/classes/Signal.html#M001593

4. In certain circumstances you might be able to use END {}

While I can log errors to a file, I still
have to check the log files periodically to make sure they ran as
expected. Every once in a while something goes awry - network
glitch, database glitch - whatever.

No, I don't want to run a separate program to tail log files looking
for errors. :slight_smile:

My initial plan (which I had nearly complete) was to do something like
this:

# At the top of your program
require "exception/mail"
Exception.mail_host = "mailhost.foo.com"
Exception.mail_to = "person@foo.com"

Then, whenever an error was raised anywhere in my program an email
would be sent out. There are plenty of other options you could
configure, btw, though the others all have reasonable defaults.

The problem, I realized, was that even if I wanted to ignore an error
(or handle it some other way), an email would still be sent out. Now
I'm thinking that maybe I should redefine Kernel#raise instead.

I would refrain from that and use one of the other approaches as fiddling
with such core features might lead to surprising effects (for example, if
you alias this method but 0/0 doesn't even invoke it but raises exceptions
differently) and incompatibilities.

Kind regards

    robert

···

-----Original Message-----

I was trying to emulate Perl's $SIG{__DIE__} handler for reports running
via cron where, whenever an error occurred, an email would be sent to me
(or whoever). While I can log errors to a file, I still have to check
the log files periodically to make sure they ran as expected. Every
once in a while something goes awry - network glitch, database glitch -
whatever.

I thought cron (at least Vixie cron) sends mail whenever a script has
console output. Generally, when I do cron stuff, I only have output
if something goes wrong. If Ruby throws an uncaught exception, the
output is pretty much what I would want to see anyways.

So why aren't you doing that? I think it's easier to have cron handle
it rather than have to write code in every script to send mail
whenever something bad happens.

The problem, I realized, was that even if I wanted to ignore an error
(or handle it some other way), an email would still be sent out. Now I'm
thinking that maybe I should redefine Kernel#raise instead.

You should definately not do that.

Robert's first option (begin/rescue/end around everything) is probably
the easiest and most direct method, if you can't use cron.

···

On 10/7/05, Berger, Daniel <Daniel.Berger@qwest.com> wrote:

--
Rob

harp:~ > cat a.rb
   class Exception
     class << self
       def __initialize__hooks__
         @@__initialize__hooks__ ||=
       end
     end

     alias_method '__initialize__', 'initialize'

     def initialize *a, &b
       klass = self.class
       if klass.__initialize__hooks__
         klass.__initialize__hooks__.each do |hook|
           hook.call self, a, b
         end
       end
       __initialize__ *a, &b
     end
   end

   def exception_intercept *handlers
     handlers.flatten!
     handlers.compact!
     n = handlers.size
     begin
       n.times{ Exception::__initialize__hooks__.push handlers.shift}
       yield
     ensure
       n.times{ Exception::__initialize__hooks__.pop }
     end
   end

   email = lambda{|exp, args, block| p ['mail', exp, args, block]}
   another = lambda{|exp, args, block| p ['another', exp, args, block]}

   exception_intercept(email, another) do
     raise ArgumentError, 'forty-two'
   end

   harp:~ > ruby a.rb
   ["mail", #<ArgumentError: ArgumentError>, ["forty-two"], nil]
   ["another", #<ArgumentError: ArgumentError>, ["forty-two"], nil]
   a.rb:38: forty-two (ArgumentError)
           from a.rb:37:in `exception_intercept'
           from a.rb:37

hth.

-a

···

On Fri, 7 Oct 2005, Berger, Daniel wrote:

-----Original Message-----
From: Robert Klemme [mailto:bob.news@gmx.net]
Sent: Friday, October 07, 2005 6:52 AM
To: ruby-talk ML
Subject: Re: Customizing Exception, but only when an error is raised

<snip>

It would be interesting to learn what real world problem
Daniel wants to solve...

Kind regards

    robert

I was trying to emulate Perl's $SIG{__DIE__} handler for reports running
via cron where, whenever an error occurred, an email would be sent to me
(or whoever). While I can log errors to a file, I still have to check
the log files periodically to make sure they ran as expected. Every
once in a while something goes awry - network glitch, database glitch -
whatever.

No, I don't want to run a separate program to tail log files looking for
errors. :slight_smile:

My initial plan (which I had nearly complete) was to do something like
this:

# At the top of your program
require "exception/mail"
Exception.mail_host = "mailhost.foo.com"
Exception.mail_to = "person@foo.com"

Then, whenever an error was raised anywhere in my program an email would
be sent out. There are plenty of other options you could configure,
btw, though the others all have reasonable defaults.

The problem, I realized, was that even if I wanted to ignore an error
(or handle it some other way), an email would still be sent out. Now I'm
thinking that maybe I should redefine Kernel#raise instead.

Regards,

Dan

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
Your life dwells amoung the causes of death
Like a lamp standing in a strong breeze. --Nagarjuna

===============================================================================

Rob Rypka wrote:

I thought cron (at least Vixie cron) sends mail whenever a script has
console output. Generally, when I do cron stuff, I only have output
if something goes wrong. If Ruby throws an uncaught exception, the
output is pretty much what I would want to see anyways.

The admin was getting irritated with the mail that would collect, so we redirected stderr to the log as well, per his request.

Also, sometimes that mail would simply disappear into the ether, or appear days later. It just wasn't reliable, for reasons unknown.

Regards,

Dan

Not that this code isn't a fine example of some of the things you can
do, but I was thinking more along the lines of this:

#! ruby

begin
  require ARGV.shift
rescue
  # send it to yourself
end

If we called this script 'cronmail', then you could put 'cronmail
real_script.rb [args 'n' stuff]' in the tab for any program for which
you wanted to mail those pesky errors.

···

On 10/7/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:

   harp:~ > cat a.rb
   class Exception
     class << self
       def __initialize__hooks__
         @@__initialize__hooks__ ||=
       end
     end

     alias_method '__initialize__', 'initialize'

     def initialize *a, &b
       klass = self.class
       if klass.__initialize__hooks__
         klass.__initialize__hooks__.each do |hook|
           hook.call self, a, b
         end
       end
       __initialize__ *a, &b
     end
   end

   def exception_intercept *handlers
     handlers.flatten!
     handlers.compact!
     n = handlers.size
     begin
       n.times{ Exception::__initialize__hooks__.push handlers.shift}
       yield
     ensure
       n.times{ Exception::__initialize__hooks__.pop }
     end
   end

   email = lambda{|exp, args, block| p ['mail', exp, args, block]}
   another = lambda{|exp, args, block| p ['another', exp, args, block]}

   exception_intercept(email, another) do
     raise ArgumentError, 'forty-two'
   end

   harp:~ > ruby a.rb
   ["mail", #<ArgumentError: ArgumentError>, ["forty-two"], nil]
   ["another", #<ArgumentError: ArgumentError>, ["forty-two"], nil]
   a.rb:38: forty-two (ArgumentError)
           from a.rb:37:in `exception_intercept'
           from a.rb:37

--
Rob (Simple-minded Solutions, Inc.)

btw. in cron you can set MAILTO to be any address. any program which prints
to stderr will have message sent to this address. then you can simply use
ruby mail tools to scan the message looking for anything relevant and deleting
the rest.

fyi.

-a

···

On Sat, 8 Oct 2005, Daniel Berger wrote:

Rob Rypka wrote:

I thought cron (at least Vixie cron) sends mail whenever a script has
console output. Generally, when I do cron stuff, I only have output
if something goes wrong. If Ruby throws an uncaught exception, the
output is pretty much what I would want to see anyways.

The admin was getting irritated with the mail that would collect, so we redirected stderr to the log as well, per his request.

Also, sometimes that mail would simply disappear into the ether, or appear days later. It just wasn't reliable, for reasons unknown.

Regards,

Dan

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
Your life dwells amoung the causes of death
Like a lamp standing in a strong breeze. --Nagarjuna

===============================================================================