2006.10.11

Ruby Rails

RailsのProduction環境でErrorを管理者にメールで自動通知する方法(ErrorMail on Rails)

アプリケーションを本番環境に移行すると、ブラウザではエラーの詳細が表示されませんし、ユーザはエラーの発生を教えてくれる訳ではありません。そこでエラーが発生したときには、エラーメッセージの内容を、管理者へ自動的にメールしてくれると助かります。

今回はRailsでエラーメッセージを自動送信する方法についてです。内容はRails本に書いてある通りだけど。

RailsによるアジャイルWebアプリケーション開発
RailsによるアジャイルWebアプリケーション開発前田 修吾

オーム社 2006-02-25
売り上げランキング : 14017

Amazonで詳しく見る
by G-Tools
Joel on Software Life Hacks PRESS ~デジタル世代の「カイゼン」術~ ライド・オン・Rails Ruby on Railsを徹底攻略 プログラミングRuby―達人プログラマーガイド Rubyレシピブック 268の技

まず、Railsでエラーが発生した場合、必ず呼ばれるメソッドが、

rescue_action_in_public(exception)

です。こいつをオーバーライドすることで、エラーメッセージを送信できるようにしてやります。

まず、ApplicationControllerに以下のメソッドを追加します。(app/controllers/application_controller.rb)

def rescue_action_in_public(exception)
  case exception
  when ActiveRecord::RecordNotFound
    flash[:notice] = "404 NOT FOUND"
    render(:file => "#{RAILS_ROOT}/public/404.html", :status => "404 NOT FOUND")
    SystemNotifier.deliver_exception_notification(self, request, exception)
  else
    flash[:notice] = "500 ERROR"
    render(:file => "#{RAILS_ROOT}/public/500.html", :status => "500 ERROR")
    SystemNotifier.deliver_exception_notification(self, request, exception)
  end
end

この中の

SystemNotifier.deliver_exception_notification(self, request, exception)

とうやつが、エラーメッセージを送信する部分です。

次にSystemNotifierモデルを定義します。(app/models/system_notifiler.rb)

require "pathname"
 
class SystemNotifier < Iso2022jpMailer
  SYSTEM_EMAIL_ADDRESS = %{"YourApplication::ErrorNotifier" <エラーメッセージの送信元アドレス>}
  EXCEPTION_RECIPIENTS = %w{エラーメッセージの送信先アドレス}
  
  def exception_notification(controller, request, exception, sent_on=Time.now)
    @subject = sprintf("[ERROR on WANTED] %s#%s (%s) %s", controller.controller_name,
                                                controller.action_name,
                                                exception.class,
                                                exception.message.inspect)
    @body    = { :controller => controller,
                 :request    => request,
                 :exception  => exception,
                 :backtrace  => sanitize_backtrace(exception.backtrace),
                 :host       => request.env["HTTP_HOST"],
                 :rails_root => rails_root }
    @sent_on = sent_on
    @from    = SYSTEM_EMAIL_ADDRESS
    @recipients = EXCEPTION_RECIPIENTS
    @headers = {}
  end
  
  private
  
  def sanitize_backtrace(trace)
    re = Regexp.new(/^#{Regexp.escape(rails_root)}/)
    trace.map do |line|
      Pathname.new(line.gsub(re, "[RAILS_ROOT]")).cleanpath.to_s
    end
  end
  
  def rails_root
    @rails_root ||= Pathname.new(RAILS_ROOT).cleanpath.to_s
  end
end

それからエラーメッセージのテンプレートを用意します。(app/views/system_notifier/exception_notification.rhtml)

<% require 'pp' %>
A <%= @exception.class %> occured in 
  <%= @controller.controller_name %>#<%= @controller.action_name %>:
  <%= @exception.message %> <%= @backtrace.first %>
 
****
 リクエスト情報
     ****
  URL:<%= @request.protocol %><%= @host %><%= @request.request_uri %>
  パラメータ:<%= @request.parameters.inspect %>
  リファラ:<%= @request.referer %>
  Railsルート:<%= @rails_root %>
 
****
 セッションダンプ
     ****
<% for variable in @request.session.instance_variables -%>
<% next if variable =~ /^@db/ -%>
  <%= variable %>:
    <%= PP.pp(@request.session.instance_variable_get(variable), "").gsub(/\n/, "\n").strip %>
<% end -%>
 
****
 環境
     ****
<% for key, value in @request.env -%>
  <%= key %>:<%= value.to_s.strip %>
<% end -%>
 
****
 フル実行バックトレース
     ****
  <%= @backtrace.join "\n  " %>
 
 
エラー発生時刻 <%= Time.now %>

これでRailsアプリケーションで発生したエラーは自動的に設定した送信先にメールされます。ただしApplicationControllerまで処理が渡された場合のみですが。(ルーティングエラーなどは補足できないので、config/routes.rbでコントローラが見つからなかったりしてもメールは来ない)

注)ちなみに、こんなのもあります。Railsでエラーが起こったらメールで通知するプラグイン