【Rails】エラーを自動でSlackに通知する方法

この記事では、Railsアプリのエラーを自動でSlackに通知する方法について解説します。
今回は、簡単なTODOアプリに通知機能を追加し、実践していきます。




# 目次
[1. Slack側の設定](#1-slack側の設定)
[2. Railsアプリに記述を加えていく](#2-railsアプリに記述を加えていく)
[3. エラーを通知する](#3-エラーを通知する)
[4. 通知フォーマットのカスタマイズ](#4-通知フォーマットのカスタマイズ)
[5. まとめ](#5-まとめ)
[6. 参考資料](#6-参考資料)


# 今回の完成イメージ
こちらを実装していきます！
![スクリーンショット 2024-10-15 0.13.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634386/9a0b4495-bcc8-e8d9-2b5c-2b8d82381f32.png)

# 1. Slack側の設定
まずはSlackチャンネルに通知を送信するためのSlack Appを作成します。

この記事で手順がとても丁寧にまとめられていますので、参考にしながらSlackの通知アプリを作成してください。

https://qiita.com/to3izo/items/c2d16f8b3e52b09e543e

これでslackの通知アプリが作成できます。

最後の動作確認が少し難しいですが、
その場合は下の画像にある `curl -X` から始まるコマンドをコピーしてターミナルで実行してください。正しく設定できていれば、Slackに「Hello, World!」と通知が届くはずです。


![スクリーンショット 2024-10-16 17.07.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634386/2b580076-f2b7-2795-5cf6-15b696c10cdf.png)



```bash:ターミナル
例：
% curl -X POST -H 'Content-type: application/json' --data '{"text":"Hello, World!"}' https://hooks.slack.com/services/~~~~~~

```
ターミナルで実行すると、、、　通知が来た！

![スクリーンショット 2024-10-14 22.14.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634386/d3bf7cc9-087f-850a-88bf-2a1f23ba18de.png)


Slackへの通知が確認できたら、次に進みます。

# 2. Railsアプリに記述を加えていく
この簡単なTODOアプリを使って実践していきます。
エラーが発生した際にSlackに通知が届くように設定してみましょう。

![スクリーンショット 2024-10-15 0.58.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634386/80620bac-9aae-fb29-20ef-baedf468397a.png)

以下が、プロジェクトのディレクトリ構造です。

```bash:ディレクトリ構成図
todo_app/
├── app/
│   ├── controllers/
│   │   ├── application_controller.rb  # カスタム通知の設定
│   │   └── todos_controller.rb        # タスクの作成機能を実装
├── config/
│   ├── initializers/
│   │   └── exception_notification.rb  # エラー通知の設定を担当
├── .env                               # SlackのWebhook URLを設定
└── Gemfile                            # 使用するGemのリスト
```


## 必要なGemをインストール
まず、`slack-notifier`と`dotenv-rails`を`Gemfile`に追加します。

```ruby:Gemfile
gem 'slack-notifier'
gem 'dotenv-rails'
```
追加後、以下のコマンドでGemをインストールします。

```bash:ターミナル
bundle install
```

* `slack-notifier` はSlackに通知を送る際に使います
* `dotenv-rails` は環境変数を `.env` ファイルで管理するためのGemです

今回はSlackのWebHooksURLを環境変数で管理する際に利用します。

## 環境変数にWebhook URLを設定
先ほど取得したWebhook URLを `.env` ファイルに保存します。
プロジェクトのルートディレクトリに `.env` ファイルを作成し、次のようにWebhook URLを設定します。

```ruby:.env
SLACK_WEBHOOK_URL=https://hooks.slack.com~~~~~ # 取得したWebHooksURL
```

`.env`ファイルはGitに含めないように、`.gitignore`ファイルに追加しておきましょう。
```ruby:.gitignore
/.env
```


## Slack通知の設定
次に、`slack-notifier`を使ってSlackにメッセージを送信するコードを追加します。
今回はcreateアクションに追記して、新規投稿の際にslack通知が発生するようにします。
```diff_ruby:todos_controller.rb
  def create
    @todo = Todo.new(todo_params)
    if @todo.save
      redirect_to todos_path
    else
      render :index
    end

+   notifier = Slack::Notifier.new(
+     ENV['SLACK_WEBHOOK_URL']
+   )
+   notifier.ping '新しいタスクを作成しました。'
  end
```

<details><summary>コードの解説</summary>

```ruby
notifier = Slack::Notifier.new(
  ENV['SLACK_WEBHOOK_URL']
)
```

この部分では、`slack-notifier` Gemを使ってSlackに通知を送るための `notifier` オブジェクトを作成しています。

* `Slack::Notifier.new`: 新しい `Notifier` オブジェクトを作成します。
* `ENV['SLACK_WEBHOOK_URL']`: Webhook URL `.env` ファイルで環境変数として管理されており、ここで呼び出しています。このURLを使って、どのSlackチャンネルに通知を送るかを指定しています。


```ruby
notifier.ping '新しいタスクを作成しました。'
```
* `notifier.ping`: 実際にSlackにメッセージを送るためのメソッドです。
* `新しいタスクを作成しました。`: Slackに送信されるメッセージの内容です。この部分は自由にカスタマイズ可能で、タスクの内容や他の情報を含めることもできます。
</details>

これで、タスク作成時にSlackに通知が届くようになります。

ちなみに新規投稿が成功しようが失敗しようが通知はされますが、
今回は練習なので特に気にせず行きましょう。

#### 実際にタスクを作成してみると、
![スクリーンショット 2024-10-14 21.16.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634386/194b9a0b-8411-f666-b00a-268d22d87770.png)

Slackへの通知が無事に届きました！



# 3. エラーを通知する
ここまでで、任意のアクションが動作した際にSlackに通知されるようになりました。
次にRailsアプリでエラーが発生した際に自動で通知を送信する方法を紹介します。

## 必要なGemをインストール
`exception_notification`を`Gemfile`に追加します。

```ruby:Gemfile
gem 'exception_notification'
```
* `exception_notification`は、Railsアプリで発生した例外を自動的に通知するためのGemです。このGemを使うと、例外が発生した際にSlack以外にもメール、Webhooks、Campfireなどでエラーの詳細を送信することができます。



忘れずにGemをインストールします。

```bash:ターミナル
bundle install
```

次に、以下のコマンドで設定ファイルを作成します。

```ruby:ターミナル
$ rails g exception_notification:install
```
作成された `config/initializers/exception_notification.rb` に、Slack通知用の設定を追加します。

`exception_notification` Gemを使用して、例外発生時に自動で通知が行われるようにします。以下がその設定コードです。

```ruby:config/initializers/exception_notification.rb
  require 'exception_notification/rails'
 
  ExceptionNotification.configure do |config|
    config.add_notifier :slack, {
      :webhook_url => ENV['SLACK_WEBHOOK_URL']
    }
  end
```

<details><summary>コードの解説</summary>

```ruby
require 'exception_notification/rails'
```
まず、この行で `exception_notification` のライブラリがRails環境に読み込まれます。このライブラリには、Railsの例外処理をフックして、例外が発生したときに通知を行う仕組みが組み込まれています。

:::note info
Railsのミドルウェアが例外をキャッチ
Railsには例外処理を行うためのミドルウェアが組み込まれており、このミドルウェアがリクエストのライフサイクルで発生した例外をキャッチします。通常、このミドルウェアは未処理の例外をキャッチすると、500エラーとしてレスポンスを返しますが、exception_notification がインストールされると、この例外キャッチのフックに exception_notification の通知処理が追加されます。
:::


```ruby
ExceptionNotification.configure do |config|
```
この部分では、`exception_notification` Gemの設定を行います。`configure` メソッドを使って、通知の設定を定義します。

```ruby
config.add_notifier :slack, {
  :webhook_url => ENV['SLACK_WEBHOOK_URL'],
}
```
* `config.add_notifier :slack`: ここでは、Slackに通知を送るための設定を追加しています。:slack というキーを使ってSlack通知の設定を行います。
* `webhook_url`、Slack通知を送信するために必要な情報です。



</details>

この設定を追加することで、Railsアプリケーション内でエラー（例外）が発生した際に、Slackにそのエラー内容が自動で通知されます。

## 実際にエラーが発生した際の通知
検証！

意図的にエラーを発生させて、通知が送信されるか確認します。
`todos_controller.rb` の `create` アクション内で、`@todo` を `@todos` に変更し、NoMethodエラーを発生させます。
```diff_ruby:todos_controller.rb
  def create
    @todo = Todo.new(todo_params)
-   if @todo.save
+   if @todos.save
      redirect_to todos_path
    else
      render :index
    end
    
    # ここはもう必要ない！
    # notifier = Slack::Notifier.new(
    #   ENV['SACK_WEBHOOK_URL']
    # )
    # notifier.ping '新しいタスクを作成しました。'
  end
```
エラーが発生すると、次のようにSlackに通知が届きます。

![スクリーンショット 2024-10-14 21.22.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634386/92701675-c388-0be2-be87-6f798f1b7c96.png)


#### これで、エラーが発生した際に自動でSlackに通知される仕組みが完成しました！

# 4. 通知フォーマットのカスタマイズ

ただし、このファイルだけでは通知内容（例外のメッセージ、リクエストのパラメータなど）をカスタマイズすることはできません。

## カスタム通知の設定を追加
通知にエラーメッセージやリクエストパラメータを含めたい場合には、
`ApplicationController` に次のようなコードを追加することでSlack通知の内容を自由にカスタマイズできます。

```ruby
class ApplicationController < ActionController::Base

  # すべての例外をキャッチして、カスタム通知を送信する
  rescue_from Exception, with: :server_error

  def server_error(e) # このeは、rescue_fromでキャッチされた例外が自動的に渡される
    # ExceptionNotifierを使ってカスタム通知を送信
    ExceptionNotifier.notify_exception(
      e,
      env: request.env,  # リクエストの環境情報を渡す
      data: {
        message: "500エラーが発生しました",       # 自由なメッセージ
        exception_class: e.class.to_s,         # エラーのクラス名
        exception_message: e.message,          # エラーのメッセージ
        request_path: request.path,            # リクエストのパス
        params: request.params                 # リクエストのパラメータ
      }
    )

    # 必要に応じて500エラー画面を表示
    render file: "#{Rails.root}/public/500.html", status: 500
  end
end
```

* `ApplicationController` 等で `rescue_from` を使って例外をキャッチし、 `ExceptionNotifier.notify_exception` を呼び出して通知します。

* `rescue_from`とはRailsでコントローラ内の例外をハンドリング（処理）するためのメソッドです。

#### エラーを発生させてみると、、、

![スクリーンショット 2024-10-15 0.13.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634386/9a0b4495-bcc8-e8d9-2b5c-2b8d82381f32.png)


エラーが発生した際により詳しいSlack通知を送信できるようになりました！！


# 5. まとめ
この記事では、Railsアプリでエラーが発生した際にSlackに自動で通知を送信する方法を紹介しました。簡単なTODOアプリに実装する形で手順を解説しましたので、実際のプロジェクトにも応用できます。

基本的な例外通知の設定は `config/initializers/exception_notification.rb` で行いますが、通知の内容を細かくカスタマイズしたい場合は、`ApplicationController` に例外をキャッチする処理を追加することで、より柔軟に対応できます。これで開発や運用中に発生するエラーにも、素早く対応できるようになります。

# 6. 参考資料

https://qiita.com/f-gdggr/items/19140ece6ad399350a36

https://qiita.com/to3izo/items/c2d16f8b3e52b09e543e

https://blog.nakamu.me/posts/rails-slack-exception_notification