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?
-
Interesting question, in looking I came across http://stackoverflow.com/questions/4104093/exception-notification-for-delayed-job and http://groups.google.com/group/delayed_job/browse_thread/thread/646314145a69360b?fwc=1&pli=1 – William May 12 '11 at 03:24
-
Yep, I've seen both of those but I think these solutions only work with Rails 2. – Alex Korban May 12 '11 at 03:40
6 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.

- 11,172
- 2
- 42
- 57
-
1I'd recommend using the official exception notification gem at https://github.com/smartinez87/exception_notification – Christopher Manning Dec 27 '11 at 03:13
-
Ah ok, will update to that. I was using the rails3 gem as there seamd to be some rails 3 issues with the official version at the time. – Mattias Wadman Dec 27 '11 at 14:47
-
+1 This approach works with Rails 3.2.6, Delayed Job 3.0.3, and Exception Notification 2.6.1. Initially I thought adding an error method using the built-in hook would be a cleaner approach, but that would have to be defined for each job class, and defining it generically for `PerformableMethod` didn't work due to how `delegate` is used. – Nathan Jul 07 '12 at 20:04
-
Nice. I actually updated to the same versions the other day but didn't remember to update the answer. – Mattias Wadman Jul 09 '12 at 11:13
-
3I've added a ```, :data => {:job => job}``` to the method call so that I have a bit more details… – mat Mar 11 '13 at 17:03
-
1To get notified ONLY when last attempt failed I add: `ExceptionNotifier.notify_exception(error) if job.attempts == Delayed::Worker.max_attempts` in place of: `ExceptionNotifier::Notifier.background_exception_notification(error)` – micred Oct 22 '15 at 12:42
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)

- 12,090
- 1
- 70
- 92
-
-
1@DaniëlZwijnenburg The method is being called by delayed_job, so yes – Alter Lagos Dec 24 '13 at 18:44
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

- 23,994
- 6
- 61
- 85
-
I think this is cleanest solution. BTW other solutions wont work in the future, because alias_method_chain is deprecated in Rails 5 – remo Feb 02 '17 at 14:37
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

- 1,541
- 19
- 17
simpler and updated answer:
# 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
ExceptionNotifier.notify_exception error,
data: {job: job, handler: job.handler} rescue nil
end
alias_method_chain :handle_failed_job, :notification
end
And test on console with:
Delayed::Job.enqueue (JS=Struct.new(:a){ def perform; raise 'here'; end }).new(1)

- 5,843
- 4
- 29
- 34