RailsでエラーをSlackに通知する

Rails標準のLoggerを活用して、エラーログをSlackのチャンネル上に表示する方法について説明いたします。

Slack Notifierのインストール

まず、RailsからSlackへのメッセージの送信に関しては、slack-notifierという便利なgemが公開されておりますので、こちらを利用させていただきます。

https://github.com/stevenosloan/slack-notifier

Slack Notifierをインストールするには

gem "slack-notifier"

をGemfileに追加し、

$ bundle install

を実行します。

Slack通知用のLoggerの作成

次に、Slack Notifierを利用してSlackにメッセージを通知するためのLoggerを、Rails標準のLoggerを継承して作成します。

class SlackLogger < ActiveSupport::Logger
  def initialize
    super(SlackDevice.new)
  end

  class SlackDevice
    def initialize
      webhook_url = '<Webhook用のURL>'
      channel = '<チャンネル名(#含む)>'
      username = '<投稿ユーザー名>'
      @notifier = Slack::Notifier.new webhook_url, channel: channel, username: username
    end

    def write(message)
      @notifier.ping message
    end

    def close
      # Do nothing
    end
  end
end

SlackLoggerにはオーバーライドしたコンストラクタ(initialize)とSlackDeviceというサブクラスを持たせています。

コンストラクタでは、SlackDeviceを初期化し、そのインスタンスを継承元のコンストラクタに渡しています。

通常、継承元のActiveSupport::Loggerの初期化時(内部ではRuby標準のLoggerを呼んでいます。)には、その引数にログを書き込むファイルパスかIOオブジェクトを指定します。ただ、ソースコードを追ってみると、writeメソッドとcloseメソッドを持つオブジェクトであれば、とりあえず動作することが分かります。(将来的にこの挙動が続く保証はありませんので、Rubyのバージョンアップにはご注意ください。)

https://github.com/ruby/logger/blob/master/lib/logger/log_device.rb

今回はその特性を利用し、ファイルパスやIOオブジェクトの代わりに、writeメソッドとcloseメソッドを持つSlackDeviceというクラスを定義し、そのオブジェクトを渡しています。

SlackDeviceはコンストラクタにおいて、先ほどインストールしたSlack Notifierを初期化しています。その際、引数としてSlackへの接続情報を渡します。(実際には、接続情報はハードコードせず、設定ファイルから渡すと良いと思います。)

Webhook用のURLの取得の仕方はSlackの公式ドキュメントをご参照ください。

https://slack.com/intl/ja-jp/help/articles/115005265063-Slack-%E3%81%A7%E3%81%AE-Incoming-Webhook-%E3%81%AE%E5%88%A9%E7%94%A8

また、SlackDeviceのwriteメソッドではSlack Notifierオブジェクトのpingメソッドを実行しています。closeメソッドは特になにもしません。

Loggerの設定

Railsでは標準でプロジェクトルートのlogフォルダ以下に<環境名>.logという名前のログファイルを作成します。

その挙動を維持したまま、Slackへの通知を追加するには、次のコードをapp/config/environments/<環境名>.rbに追加します。(環境別に分ける必要がない場合は、app/config/application.rbでも大丈夫です。)

logger = ActiveSupport::Logger.new("#{Rails.root}/log/#{Rails.env}.log")

# Slack連携用Logger
slack_logger = SlackLogger.new
extended_logger = logger.extend(ActiveSupport::Logger.broadcast(slack_logger))

config.logger = extended_logger

非同期処理への対応

Slackへの通知は同期的に行われるため、ログレベルをdebug(config.log_level = :debug)やinfoに設定していると、画面表示に非常に時間が掛かるようになり、現実的ではありません。ログレベルをerror以上に設定するか、または、Sidekiq等を利用して、Slackへの通知を非同期的に処理する仕組みを入れるのが良いかと思います。

コメントを残す

Required fields are marked *.


このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

Top