How to make Delayed_Job notify Airbrake when an ActionMailer runs into an error?
Asked Answered
I

4

12

The DelayedJob docs mention hooks, including an error hook, but only in the context of custom Job subclasses.

This similar question (with no answers) says adding the same hook to the mailer class did not work.

What's the trick?

Update:

In general, I'd like to see how to add hooks to jobs that are triggered using the object.delay.action() syntax, where I don't see an obvious link to a ____Job class.

Imprest answered 2/10, 2012 at 1:28 Comment(1)
Updated and simpler answer here: #11766868Cataplasia
H
20

I was just searching for a solution to this problem too, and I found this gist.

I don't know where it comes from (found it on Google), but well, it seems to do the job pretty well, is quite simple, and seems to follow a DelayedJob's plugin system I was not even aware of...

Here is a lightly improved one using parts of previous monkey-patch code:

# https://gist.github.com/2223758
# modified

module Delayed
  module Plugins
    class Airbrake < Plugin
      module Notify
        def error(job, error)
          ::Airbrake.notify_or_ignore(
            :error_class   => error.class.name,
            :error_message => "#{error.class.name}: #{error.message}",
            :parameters    => {
              :failed_job => job.inspect,
            }
          )
          super if defined?(super)
        end
      end

      callbacks do |lifecycle|
        lifecycle.before(:invoke_job) do |job|
          payload = job.payload_object
          payload = payload.object if payload.is_a? Delayed::PerformableMethod
          payload.extend Notify
        end
      end
    end
  end
end

Delayed::Worker.plugins << Delayed::Plugins::Airbrake

It will add the error's message and payload so that it's available in Airbrake.

Hapte answered 5/1, 2013 at 13:16 Comment(3)
I was not aware of this plugin system, either. I'll take a look. If this works in my system, I'll be switching the accepted answer to yours soon.Imprest
I really like this approach. I couldn't find a version released as a gem, so in thanks for sharing this, I packaged it as a gem for others to easily use. If you'd like write access, etc, please let me know! github.com/benjaminoakes/delayed-plugins-airbrakeBeulabeulah
This is the best answer because it works on all jobs, it's not a monkeypatch, and you don't have to remember to add a hook when you add a new usage of delay. Also, I would use @benjamin-oakes gem except that I'm using party_foul instead of airbrake.Anticatalyst
D
2

There's a DJ-honeybadger gem. The gem is of course actively maintained which makes it much better than using a monkey patch. Granted, when the monkey patch came out, there was no gem to do this.

https://github.com/honeybadger-io/delayed_job_honeybadger

Disqualify answered 21/2, 2014 at 0:58 Comment(3)
Thanks, The question was for Airbrake, but this is a similar, good answer. (There's also an Airbrake gem, now). github.com/benjaminoakes/delayed-plugins-airbrakeImprest
You're right @JordanFeldstein the question was for Airbrake. Funnily I used the same hack for honeybadger and it worked pretty well. I guess Airbrake and Honeybadger have APIs that respond to the same calls (very good thing)Disqualify
This plugin is now deprecated for the latest version of Honeybadger.Epicritic
I
1

The best method would be to use Global Hooks. Someone proposed this in 2011, but doesn't look like they've been implemented yet.

In the mean time, this works to monkey patch things:

# Patch delayed job to report runtime errors to Airbrake
module Delayed
  class Worker

    protected

    def handle_failed_job_with_airbrake(job, error)
      ::Airbrake.notify(
        :error_class   => error.class.name,
        :error_message => "#{error.class.name}: #{error.message}",
        :parameters    => {
          :failed_job => job.inspect,
        }
      )

      handle_failed_job_without_airbrake(job, error)
    end

    alias_method_chain :handle_failed_job, :airbrake

  end
end
Imprest answered 19/11, 2012 at 18:4 Comment(2)
Because of the way Airbrake shows parameters, I'd recommend not sending job.inspect but instead pass a hash. First parse the contents of the failed job: failed_job = YAML.load(job.handler). Then parse it's instance parameters into a hash, which you can pass to the :parameters for airbrake: failed_job.instance_variables.inject({}) { |hsh,v| hsh[v.to_s.gsub(/[^\w]/, '')] = failed_job.instance_variable_get(v); hsh }Duron
You might also want to include the class name of the job in the :parameters too: :class => failed_job.class.nameDuron
A
1

Probably the simplest way to get delayed_job to send through an alert to Airbrake when the job fails is to monkey-patch delayed_job. This allows you to hook into delayed_job's internals, and modify it slightly to alert Airbrake when something goes wrong.

Unfortunately, exactly how to do this will depend on which version of delayed_job you are using, and which version of Airbrake you are using; it will also depend, perhaps, on exactly where within the delayed_job processing you want to hook into the system.

However, probably the simplest example I have seen for how to do what you want is to monkey-patch the handle_failed_job method, as seen here. Note though that this example uses the old Hoptoad system for the alerting, so if you're using a modern Airbrake gem, you'll need to change the code that does the actual notifying to be as described here.

Artamas answered 12/12, 2012 at 17:9 Comment(1)
THanks for reminding me. I eventually came across the same monkey patch solution. Code snipped above.Imprest

© 2022 - 2024 — McMap. All rights reserved.