如何使 ExceptionNotifier 在 Rails 3 中与延迟作业一起使用?

Posted

技术标签:

【中文标题】如何使 ExceptionNotifier 在 Rails 3 中与延迟作业一起使用?【英文标题】:How to make ExceptionNotifier work with delayed_job in Rails 3? 【发布时间】:2011-08-23 19:03:33 【问题描述】:

我希望 ExceptionNotifier 在延迟作业中发生异常时发送电子邮件,就像处理其他异常一样。我怎样才能做到这一点?

【问题讨论】:

有趣的问题,在寻找我遇到了***.com/questions/4104093/…和groups.google.com/group/delayed_job/browse_thread/thread/… 是的,这两种我都见过,但我认为这些解决方案只适用于 Rails 2。 【参考方案1】:

我用 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 无法发送邮件并且没有人挽救异常。现在代码拯救并记录它们。

【讨论】:

我建议使用github.com/smartinez87/exception_notification的官方异常通知gem 嗯,好的,将更新到那个。我使用的是 rails3 gem,因为当时官方版本存在一些 rails 3 问题。 +1 这种方法适用于 Rails 3.2.6、延迟作业 3.0.3 和异常通知 2.6.1。最初我认为使用内置钩子添加错误方法会是一种更简洁的方法,但是必须为每个作业类定义它,并且由于delegate 的方式而为PerformableMethod 通用定义它不起作用用过。 我在方法调用中添加了一个, :data => :job => job,这样我就有了更多的细节…… 仅在上次尝试失败时才收到通知,我添加:ExceptionNotifier.notify_exception(error) if job.attempts == Delayed::Worker.max_attempts 代替:ExceptionNotifier::Notifier.background_exception_notification(error)【参考方案2】:

添加到@MattiasWadman 的答案,因为 exception_notification 4.0 there's a new way to handle manual notify。所以而不是:

ExceptionNotifier::Notifier.background_exception_notification(error)

使用

ExceptionNotifier.notify_exception(error)

【讨论】:

这个也会在后台处理吗? @DaniëlZwijnenburg 该方法正在被delayed_job调用,所以是的【参考方案3】:

另一种处理异常的方式(作为初始化器):

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

【讨论】:

我认为这是最干净的解决方案。顺便说一句,其他解决方案将来不会起作用,因为在 Rails 5 中不推荐使用 alias_method_chain 【参考方案4】:

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

【讨论】:

这是对我有用的答案,运行 Rails 4.2。 Delayed::Worker.prepend CustomFailedJob可以直接使用【参考方案5】:

对于 exception_notification 3.0.0 更改:

ExceptionNotifier::Notifier.background_exception_notification(error)

到:

ExceptionNotifier::Notifier.background_exception_notification(error).deliver

【讨论】:

【参考方案6】:

更简单和更新的答案:

# 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)

【讨论】:

@AlterLagos 我认为你误解了 alias_method_chain。

以上是关于如何使 ExceptionNotifier 在 Rails 3 中与延迟作业一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

在 R 闪亮中,如何使“observeEvent”不会被“updateSelectizeInput”的更改触发

在 R 中,当循环的输出停止变化时,如何使循环停止?

如何使 R 闪亮图形中的模式栏永久显示?

R Shiny:如何使多个元素在添加/删除按钮上下文中具有反应性?

如何使R中无法访问全局环境中用户定义函数的源代码? (用于教育目的)

在R中,如何使函数内的变量可用于该函数内的较低级别的函数?(with,attach,environment)