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への通知を非同期的に処理する仕組みを入れるのが良いかと思います。