Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make ExceptionNotifier work with delayed_job in Rails 3?

I'd like ExceptionNotifier to send out an email when an exception happens in a delayed job, just like for other exceptions. How can I achieve that?

like image 417
Alex Korban Avatar asked May 12 '11 03:05

Alex Korban


5 Answers

I do this with Rails 3.2.6, delayed_job 3.0.3 and exception_notification 2.6.1 gem

# In config/environments/production.rb or config/initializers/delayed_job.rb

# Optional but recommended for less future surprises.
# Fail at startup if method does not exist instead of later in a background job 
[[ExceptionNotifier::Notifier, :background_exception_notification]].each do |object, method_name|
  raise NoMethodError, "undefined method `#{method_name}' for #{object.inspect}" unless object.respond_to?(method_name, true)
end

# Chain delayed job's handle_failed_job method to do exception notification
Delayed::Worker.class_eval do 
  def handle_failed_job_with_notification(job, error)
    handle_failed_job_without_notification(job, error)
    # only actually send mail in production
    if Rails.env.production?
      # rescue if ExceptionNotifier fails for some reason
      begin
        ExceptionNotifier::Notifier.background_exception_notification(error)
      rescue Exception => e
        Rails.logger.error "ExceptionNotifier failed: #{e.class.name}: #{e.message}"
        e.backtrace.each do |f|
          Rails.logger.error "  #{f}"
        end
        Rails.logger.flush
      end
    end
  end 
  alias_method_chain :handle_failed_job, :notification 
end

It's probably a good idea to load this code in all environments to catch errors after bundle update etc before they reach production. I do this by having a config/initializers/delayed_job.rb file but you could duplicate the code for each config/environments/* environment.

Another tip is to tune the delayed job config a bit as default you may get a lot of duplicate exception mails when job fails.

# In config/initializers/delayed_job_config.rb
Delayed::Worker.max_attempts = 3

Update I had some problems with the delayed_job daemon silently exiting and it turned out to be when ExceptionNotifier fails to send mail and no one rescued the exception. Now the code rescues and log them.

like image 122
Mattias Wadman Avatar answered Oct 05 '22 14:10

Mattias Wadman


Adding to @MattiasWadman answer, since exception_notification 4.0 there's a new way to handle manual notify. So instead of:

ExceptionNotifier::Notifier.background_exception_notification(error)

use

ExceptionNotifier.notify_exception(error)
like image 27
Alter Lagos Avatar answered Oct 05 '22 13:10

Alter Lagos


Another way to handle exceptions (put as an initializer):

class DelayedErrorHandler < Delayed::Plugin

  callbacks do |lifecycle|

    lifecycle.around(:invoke_job) do |job, *args, &block|

      begin
        block.call(job, *args)
      rescue Exception => e

        # ...Process exception here...

        raise e
      end
    end
  end
end

Delayed::Worker.plugins << DelayedErrorHandler
like image 23
Christopher Oezbek Avatar answered Oct 05 '22 12:10

Christopher Oezbek


alias_method_chain no longer exists in Rails 5.

Here's the new (proper) way to do this using Ruby 2's prepend

# In config/initializers/delayed_job.rb
module CustomFailedJob
  def handle_failed_job(job, error)
    super
    ExceptionNotifier.notify_exception(error, data: {job: job})
  end
end

class Delayed::Worker
  prepend CustomFailedJob
end
like image 43
akaspick Avatar answered Oct 05 '22 12:10

akaspick


For exception_notification 3.0.0 change:

ExceptionNotifier::Notifier.background_exception_notification(error)

to:

ExceptionNotifier::Notifier.background_exception_notification(error).deliver
like image 23
Adrian Avatar answered Oct 05 '22 12:10

Adrian