Error reporting when sending emails with delayed_j

2019-04-05 14:24发布

问题:

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.

回答1:

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



回答2:

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)