5

I have been happily using the DelayedJob idiom:

foo.send_later(:bar)

This calls the method bar on the object foo in the DelayedJob process.

And I've been using DaemonSpawn to kick off the DelayedJob process on my server.

But... if foo throws an exception Hoptoad doesn't catch it.

Is this a bug in any of these packages... or do I need to change some configuration... or do I need to insert some exception handling in DS or DJ that will call the Hoptoad notifier?


In response to the first comment below.

class DelayedJobWorker < DaemonSpawn::Base
def start(args)
  ENV['RAILS_ENV'] ||= args.first || 'development'
  Dir.chdir RAILS_ROOT
  require File.join('config', 'environment')

  Delayed::Worker.new.start
end
Simone Carletti
  • 173,507
  • 49
  • 363
  • 364

4 Answers4

5

Try monkeypatching Delayed::Worker#handle_failed_job :

# lib/delayed_job_airbrake.rb

module Delayed
  class Worker

    protected

    def handle_failed_job_with_airbrake(job, error)
      say "Delayed job failed -- logging to Airbrake"
      HoptoadNotifier.notify(error)
      handle_failed_job_without_airbrake(job, error)
    end

    alias_method_chain :handle_failed_job, :airbrake

  end
end

This worked for me.

(in a Rails 3.0.10 app using delayed_job 2.1.4 and hoptoad_notifier 2.4.11)

bonkydog
  • 2,012
  • 20
  • 11
  • Thanks for specifying the versions! – Jonathan R. Wallace Oct 26 '11 at 19:22
  • 1
    Where is `handle_failed_job_without_airbrake` being defined? Sort of new to this and trying to understand this patch. We already have this code but I'm tweaking it because we're upgrading from hoptoad_notifier-2.4.11 to airbrake_notifier-3.0.9 – Bradley Jan 12 '12 at 21:54
  • 2
    It's created by metaprogramming in alias_method_chain. When you say "alias_method_chain :handle_failed_job, :airbrake", alias_method_chain essentially renames "handle_failed_job" to "handle_failed_job_without_airbrake" and renames "handle_failed_job_with_airbrake" to handle_failed_job. This lets you wrap the original implementation and do things before or after. Take a look at the code if you're curious: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/module/aliasing.rb – bonkydog Jan 12 '12 at 22:08
3

Check out the source for Delayed::Job... there's a snippet like:

# This is a good hook if you need to report job processing errors in additional or different ways
def log_exception(error)
  logger.error "* [JOB] #{name} failed with #{error.class.name}: #{error.message} - #{attempts} failed attempts"
  logger.error(error)
end

I haven't tried it, but I think you could do something like:

class Delayed::Job
  def log_exception_with_hoptoad(error)
    log_exception_without_hoptoad(error)
    HoptoadNotifier.notify(error)
  end

  alias_method_chain :log_exception, :hoptoad
end
Ryan McGeary
  • 235,892
  • 13
  • 95
  • 104
1

Hoptoad uses the Rails rescue_action_in_public hook method to intercept exceptions and log them. This method is only executed when the request is dispatched by a Rails controller. For this reason, Hoptoad is completely unaware of any exception generated, for example, by rake tasks or the rails script/runner.

If you want to have Hoptoad tracking your exception, you should manually integrate it. It should be quite straightforward. The following code fragment demonstrates how Hoptoad is invoked

def rescue_action_in_public_with_hoptoad exception
  notify_hoptoad(exception) unless ignore?(exception) || ignore_user_agent?
  rescue_action_in_public_without_hoptoad(exception)
end

Just include Hoptoad library in your environment and call notify_hoptoad(exception) should work. Make sure your environment provides the same API of a Rails controller or Hoptoad might complain.

Simone Carletti
  • 173,507
  • 49
  • 363
  • 364
0

Just throwing it out there - your daemon should require the rails environment that you're working on. It should look something along the lines of:

RAILS_ENV = ARGV.first || ENV['RAILS_ENV'] || 'production'
require File.join('config', 'environment')

This way you can specify environment in which daemon is called.

Since it runs delayed job chances are daemon already does that (it needs activerecord), but maybe you're only requiring minimal activerecord to make delayed_job happy without rails.

Max Chernyak
  • 37,015
  • 6
  • 38
  • 43
  • That's a good answer, but DJ does do that when it starts. DJ starts from script/delayed_job and contains this code: class DelayedJobWorker < DaemonSpawn::Base def start(args) ENV['RAILS_ENV'] ||= args.first || 'development' Dir.chdir RAILS_ROOT require File.join('config', 'environment') Delayed::Worker.new.start end ...etc... So yes the full environment is loaded, I just don't know why the Hoptoad notification code isn't automagically plugging itself in here. –  Aug 15 '09 at 05:09