Rails 4 メールでのエラー通知


Rails で、エラーが出たときにメールが送信されるようにしました。

環境

  • Ubuntu 15.05 (64bit)
  • Ruby 2.2.2
  • Rails 4.1.8

昔のやり方

Rails 3 のときは、 ApplicationControllerrescue_in_public にメール送信のコードを見たことがあります。

current_user は ログインしているユーザです。 devise を利用しています。

しかし同じことをやっても意図した通りにメールは送信されませんでした。

解決法

Rails 3 のときにもあった rescue_from を使いました。

rescue_from Exception とすることで、コントローラ内の処理で発生したすべての例外を拾います。 これが rescue_from StandardError だと 単純に raise "exception!" のように例外を起こしたときの RuntimeError が拾えません。 また、0 = 1 の時に起きるような syntax error は拾いません。 拾うのはプログラムとして動いた上でのエラーです。 そして、コントローラに入る前の処理は拾いません。

重要なのは handle_exception の中の raise exception です。 handle_exception ではエラーをもみ消したいわけではなく、メールを送りたいだけです。 AOP の考え方です。 今回は簡易的に application_controller.rb にメソッドを追加しています。

send_error_mail の中で使われている current_user は gem devise で用意されているもので、ログインしているユーザを返します。 適宜変更してください。 self はどのコントローラで例外が発生したのかをメールに載せるために渡しています。 request はリクエストのURLなどを表示するのに使います。 また !Rails.env.development? で評価を行っているところがありますが、 config に書くようにできれば一番いいです。 Rails 4.2 になったらカスタムコンフィギュレーションが使えるそうなので、アップグレード後の課題としました。

注意点

コントローラに入った後の例外しか処理しないので、 コントローラに移る前の例外は別の方法で補足する必要があります。

それならばコントローラの中ではなく外側で例外処理を書けばいいという考え方もあるのですが、コントローラの中でしか参照できない値や、コントローラの中で使いやすくなっているオブジェクトなどがあるので、 applicatoin_controller.rb に処理を書きました。

送信メールの作成

送信メールは次のようにして作ります。

メーラクラス

まずは、 rails g mailer SystemMailer system_error を実行して SystemMailer クラス を作ります。 作成された メールのクラスは次のように変更します。

単純にインスタンス変数に渡しているだけです。 インスタンス変数の情報をメール本文に盛り込みます。

デフォルトの送信先は recipient@email.com にしています。 これはメールごとに変更することができます。 実際の送信先は、 開発チームが Google のメールサービスを使っていれば Google グループ でメーリングリストを作成してそちらに送信するのもいいですし、 さくらレンタルサーバを使って入ればそちらでメーリングリストを作成して送るのがいいと思います。 送信先をデータベースで管理して、必要に応じて変えるというやり方もあります。

メール本文

上で実行したコマンドによって、 app/views/system_mailer/system_error.text.erb ができているのでそれを編集します。 html 形式 がいいなら .html.erb となっているものを使いましょう。 私は html 形式 のビューは消しました。

header の値を出力しているところで force_encoding を使っています。 force_encoding を行わないと、 リクエストパラメータに日本語が含まれている場合に 次のようなエラーが出ます。

ActionView::Template::Error (incompatible character encodings: ASCII-8BIT and UTF-8)

ビューでの処理は避けるべきだという考え方からすると、 Time.now のところと hostname.strip のところはまだまだ改善が必要です。

またこのメールでは、ユーザが入力したパスワードなども見えてしまうので、(メールの送信経路が危険な場合は)部分的にヘッダデータを隠すか SSL を用いてメールを送信するか、 メールに詳細を記述せずにメール本文を連絡のみに(とど)めるなどして対策を行ってください。

私の場合は、メール通知に加えてロギングを行うため、 本記事のメソッドをもう少し変えて実装しました。