【发布时间】:2011-08-23 19:03:33
【问题描述】:
我希望 ExceptionNotifier 在延迟作业中发生异常时发送电子邮件,就像处理其他异常一样。我怎样才能做到这一点?
【问题讨论】:
-
是的,这两种我都见过,但我认为这些解决方案只适用于 Rails 2。
标签: ruby-on-rails-3 delayed-job exception-notification
我希望 ExceptionNotifier 在延迟作业中发生异常时发送电子邮件,就像处理其他异常一样。我怎样才能做到这一点?
【问题讨论】:
标签: ruby-on-rails-3 delayed-job exception-notification
我用 Rails 3.2.6、delayed_job 3.0.3 和 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
在所有环境中加载此代码以在包更新等之后在它们进入生产之前捕获错误可能是一个好主意。我通过拥有一个 config/initializers/delayed_job.rb 文件来做到这一点,但您可以为每个 config/environments/* 环境复制代码。
另一个技巧是调整延迟的作业配置,默认情况下你可能会在作业失败时收到很多重复的异常邮件。
# In config/initializers/delayed_job_config.rb
Delayed::Worker.max_attempts = 3
更新我在delayed_job 守护进程静默退出时遇到了一些问题,结果是ExceptionNotifier 无法发送邮件并且没有人挽救异常。现在代码拯救并记录它们。
【讨论】:
delegate 的方式而为PerformableMethod 通用定义它不起作用用过。
, :data => {:job => job},这样我就有了更多的细节……
ExceptionNotifier.notify_exception(error) if job.attempts == Delayed::Worker.max_attempts 代替:ExceptionNotifier::Notifier.background_exception_notification(error)
添加到@MattiasWadman 的答案,因为 exception_notification 4.0 there's a new way to handle manual notify。所以而不是:
ExceptionNotifier::Notifier.background_exception_notification(error)
使用
ExceptionNotifier.notify_exception(error)
【讨论】:
另一种处理异常的方式(作为初始化器):
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
【讨论】:
alias_method_chain 在 Rails 5 中不再存在。
这是使用 Ruby 2 的 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
【讨论】:
Delayed::Worker.prepend CustomFailedJob可以直接使用
对于 exception_notification 3.0.0 更改:
ExceptionNotifier::Notifier.background_exception_notification(error)
到:
ExceptionNotifier::Notifier.background_exception_notification(error).deliver
【讨论】:
更简单和更新的答案:
# 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
并在控制台上测试:
Delayed::Job.enqueue (JS=Struct.new(:a){ def perform; raise 'here'; end }).new(1)
【讨论】: