Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error reporting when sending emails with delayed_job

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, but the mail object created by Mailer.welcome() (or similar) is not serialized correctly. I also tried adding an error(job, exception) method to the PerformableMailer and PerformableMethod classes, but I got more errors generally related to serializing I believe. I tried both psych and sych for the serialization.

like image 474
pupeno Avatar asked Aug 01 '12 19:08

pupeno


2 Answers

Updated Solution

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

like image 200
Subhas Avatar answered Nov 12 '22 16:11

Subhas


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)
like image 35
Roman Avatar answered Nov 12 '22 17:11

Roman