delayed_jobs による非同期処理時に、exception_notification によるエラー通知メール送信を行う

  • 20
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

delayed_jobs は極めて簡単に非同期処理を実現してくれる素敵gem。

see:
Railsで非同期処理が簡単に実装できる「Delayed_job」が便利すぎる
[ruby] delayed_job ちょっとだけ詳しく

exception_notification は Railsでの例外発生時に詳細を記述したエラー通知メールを送信してくれる素敵gem。

see:
Rails本番運用で役に立つプラグイン exception_notification
Rails3 で exception_notification プラグインを使う

exception_notification なしでは安心して眠れないくらいに頼りになるのですが、実は delayed_jobs の遅延処理タスクとして動作しているときは通知処理を行なってくれないという問題が。
これでは眠れなくなってしまうので、次のような記述を追加します。

config/initializer/delayed_jobs.rb
# exception_notifier にメソッドが存在することを確認。
[[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

# workrが失敗を検出した時のハンドラ(Delayed::Worker#handle_failed_job)にメール送信処理を追加
Delayed::Worker.class_eval do 
  def handle_failed_job_with_notification(job, error)
    handle_failed_job_without_notification(job, error)
    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 
  alias_method_chain :handle_failed_job, :notification 
end

これで非同期処理中のエラーも検出できるようになりました。
ただし、失敗時にはわりと短い時間で再試行するの、再現性のあるエラーの場合は同じようなメールが複数届いてしまってちょっと嫌な感じですが、あんまり気にしません。

以下の Q&A を参考にしました:
How to make ExceptionNotifier work with delayed_job in Rails 3?

追記

delayed_jobs 4.0 から ExceptionNotifier::Notifier.background_exception_notification(error) に代わって、ExceptionNotifier.notify_exception(error) を使うようになったので下記のようになります。

config/initializer/delayed_jobs.rb
# exception_notifier にメソッドが存在することを確認。
[[ExceptionNotifier, :notify_exception]].each do |object, method_name|
  raise NoMethodError, "undefined method `#{method_name}' for #{object.inspect}" unless object.respond_to?(method_name, true)
end

# workrが失敗を検出した時のハンドラ(Delayed::Worker#handle_failed_job)にメール送信処理を追加
Delayed::Worker.class_eval do
  def handle_failed_job_with_notification(job, error)
    handle_failed_job_without_notification(job, error)
    begin
      ExceptionNotifier.notify_exception(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

  alias_method_chain :handle_failed_job, :notification
end