ryota21silvaの技術ブログ

Funna(ふんな)の技術ブログ

これまで学んだ技術の備忘録。未来の自分が救われることを信じて

【Rails】404、500エラーをハンドリング(slack通知も)

エラーハンドリングのメモが残ってたから、体系的な内容に纏めてみた。
どうせなのでコッチに移しておく。

エラーハンドリングをする

  • 404エラー時は、404エラー用のテンプレートを表示させる。
  • 500エラー時は例外(エラー)クラス、エラーメッセージ 、バックトレースをログに出力させ、500エラー用のテンプレートを表示させる。

application_controller.rbにエラー時の設定を書く
→どんなページを表示するか、どんなログを残すかなど。

(application_controller.rb)
class ApplicationController < ActionController::Base
  rescue_from ActiveRecord::RecordNotFound, with: :render_404
  rescue_from StandardError, with: :render500

  def render_404
    render file: Rails.root.join('public/404.html').to_s, status: :not_found, layout: false, content_type: 'text/html'
  end

  def render_500(error)
    logger.error("エラークラス: #{error.class}")
    logger.error("エラーメッセージ : #{error.message}")
    logger.error('バックトレース -------------')
    logger.error(error.backtrace.("\n"))
    logger.error('-------------')
    render file: Rails.root.join('public/500.html').to_s, status: :internal_server_error, layout: false, content_type: 'text/html'
  end
end

config.consider_all_requests_localをfalseにするだけで、development環境でエラーページの動作を確認できる

trueに設定すると、どのような種類のエラーが発生しても、詳細なデバッグ情報がHTTPレスポンスに出力される。
developmentとtest環境ではtrue、production環境ではfalseに設定されている。

(config/environments/development.rb)

# Show full error reports.
# falseにするだけでdevelopment環境でエラーページの動作を確認できる

config.consider_all_requests_local = false

意図的にエラーを発生させる

get '*path', to: 'application#render_500'
get '*path', to: 'application#render_404'

エラーログの参考資料

エラーログ

log/development.log
Started GET "/boards/599" for ::1 at 2020-04-29 11:58:10 +0900
Processing by BoardsController#show as HTML
 Parameters: {"id"=>"599"}
 [1m[36mUser Load (2.5ms)[0m  [1m[34mSELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?[0m  [["id", 43], ["LIMIT", 1]]
 ↳ vendor/bundle/ruby/2.6.0/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98
 [1m[36mBoard Load (0.2ms)[0m  [1m[34mSELECT  "boards".* FROM "boards" WHERE "boards"."id" = ? LIMIT ?[0m  [["id", 599], ["LIMIT", 1]]
 ↳ app/controllers/boards_controller.rb:26
 Rendering public/404.html
 Rendered public/404.html (4.7ms)
Completed 404 Not Found in 61ms (Views: 16.7ms | ActiveRecord: 2.7ms)
log/development.log
Started GET "/login" for ::1 at 2020-04-29 11:59:39 +0900
Processing by UserSessionsController#new as HTML
エラークラス: NameError
エラーメッセージ : undefined local variable or method `hello' for #<UserSessionsController:0x00007fd3cda92010>
バックトレース -------------
Completed 500 Internal Server Error in 219ms (ActiveRecord: 0.0ms)
NoMethodError (undefined method `call' for #<Array:0x00007fd3c77ebf88>):
app/controllers/application_controller.rb:20:in `error500'

エラーページを作成する

Railsは標準で、public/404.htmlとpublic/422.html、public/500.htmlが作成されているが、以下のサイトなどを使って自前のエラーページを作るのもロマンがあるというか、面白味があると思う。

たった1分で素敵なエラーページが作れるサイト「Better Error Pages」 | ライフハッカー[日本版]

エラー時にslack通知する

gemのインストール。

gem 'slack-notifier'
gem'exception_notification'

チャンネルの右上の「iマーク」を押す。 f:id:ryota21silva:20200916193556p:plain

詳細が表示されるから、その他→アプリを追加する。 f:id:ryota21silva:20200916193635p:plain

こんなページへ飛ぶ。 f:id:ryota21silva:20200916193714p:plain

チャンネルのWebhookURLを取得 f:id:ryota21silva:20200916194212p:plain

例外エラーをslackチャンネルに通知するよう設定

$ be rails g exception_notification:install                                                                                                             
      create  config/initializers/exception_notification.rb
(config/initializers/exception_notification.rb)
ExceptionNotification.configure do |config|
 config.add_notifier :slack, {
     webhook_url: Rails.application.credentials.dig(:slack, :webhook_url),
     channel: Settings.slack.exception_notification_channel
 }
end
config.add_notifier :slack,
                       webhook_url: Rails.application.credentials.slack[:webhook_url],
                       channel: チャンネル名

settingsファイルで設定もあり

(config/settings/development.yml)
slack:
 exception_notification_channel: '#基礎編通知'

※500番エラー(サーバー側の問題)が発生した時だけ、通知が来るようになっている
→通知が来ることで、改修などを行える
※404などの間違ったURLを入力した等のエラー(クライアント側の問題)で毎回通知が来る必要はない。

credentialsファイルに秘匿情報(チャンネルURL)を保存しておく。

$ EDITOR="vi" bin/rails credentials:edit
# credentialの中身 
slack:
  webhook_url: 取得したURL

config/credentials.yml.encに、暗号化した情報が入る(リモート上で管理したくない秘匿情報)。
→そしてmaster_keyに暗号を解くための鍵が入っており、情報の暗号化、複合を行ってくれる。

また、master_keyはバージョン管理対象外となっている。

.gitignore
# Ignore master key for decrypting credentials and more.
/config/master.key

こんな感じでslackに通知される。 f:id:ryota21silva:20200916194702p:plain

参考資料

Rackとは何か - Qiita

Railsアプリの例外ハンドリングとエラーページの表示についてまとめてみた - Qiita

Rails のエラー処理について知ってる範囲でまとめ - Qiita

rescue_fromの走査は下から上だった。 - Qiita

先輩と覚える HTTP ステータスコード · GitHub