What\'s the proper way to get error reports, when using a tool like AirBrake or ExceptionNotifier from mailing delayed jobs?
I tried to creating my own delayed job class
First, a module for inclusion in mailer and, possibly, other delayed jobs:
module Delayed
module Airbrake
# Send error via Airbrake
def error(job, e)
::Airbrake.notify(e, :component => job.name, :action => 'perform', :parameters => {:job => job.inspect})
end
end
end
then include it:
Delayed::PerformableMailer.send(:include, Delayed::Airbrake)
Overall the solution is quite simple. If you have are doing delayed_job on an Object (like MyClass.new.delay.some_method
), then you need to define error handling as an object method. If you're doing delayed_job on a Class (like MyTestMailer.test_email ...
), then you need to define error handling as a class method.
Let's say you have a mailer called TestMailer
. The solution is to define the error handling as a class method, not an object method:
# Your rails mailer
class TestMailer
# Whoa! error has to be a class method!
def self.error(job, e)
puts "I can now handle test mailer errors in delayed job!!!!"
end
end
Now the above def self.error
method will be used as the error callback in the delayed job!
Or if you want to be able to handle all action mailer errors,
class ActionMailer::Base
def self.error(job, e)
puts "I can now handle all mailer errors in delayed job!!!"
end
end
The reason is because of the way DelayedJob's internal PerformableMethod
handles errors. A PerformableMethod
has two things: a Target Object, and a Target Method. In Action Mailer's case, the Target Object is not an object, but your mailer class TestMailer
. The target method is the mail method that you use, say test_mail
. DelayedJob looks for all the hooks (error
, before
, after
, etc) on the Target Object. But in our case, the Target Object is the class itself. Hence the hooks have to be defined as class methods.
The way DelayedJob
handles ActionMailer mails is a little hacky. If you add an object method instead of a class method, it throws an unwanted exception. For example, here is the code:
# In <delayed-job-gem>/lib/delayed/performable_method.rb
module Delayed
class PerformableMethod
# line #7
delegate :method, :to => :object
Every object in ruby has a method
function, which is used to get a raw reference to a method inside that class. But in DelayedJob - this raw method
function has been kind of delegated to some other target object. This hack prevents us from normally using the def error
function for handling job errors.
Edit: Added footnote, minor clarification
Edit 2: Reordered answer