🔖
Slackでリアルタイムにログを確認する
これはなに?
アプリのバッチを動かすときに、私はSlackでログを見たくなります。
Slackで見れることでPCが見れない環境下でも状況を確認できますし、メールなど他チャネルに分散せず一箇所で確認・議論・対応できる点がお気に入りです。
バッチ監視の従来の手法
古来からサーバーに潜ってログファイルをtailして確認したし、最近だとCloudWatch Logsでtailしたりしますよね。
Slackでのバッチログについてバッチ開始時と終了時にpostするくらいがよくある形かと思いますが、Slackのメッセージ更新APIを使えばリアルタイムにバッチの状況を確認できます。
本記事はそういった使い方の共有です。
利用イメージ
後述のようにloggerを用意することで、このような形でバッチの途中経過が更新されます。
使い方
log_filepath = "log/sample.log"
logger = SlackLogger.new(logger: Jets::Logger.new(log_filepath))
logger.post!("Start.")
(:A .. :E).each do |alphabet|
logger.post!("#{alphabet}の処理 開始")
sleep(1)
# 大量に出力されるものはリアルタイムにSlack通知せず、ファイルにバッファして終了時にスレッドにポスト
1000.times do |i|
logger.write!("#{alphabet} - #{i}回目")
end
logger.post!("#{alphabet}の処理 終了")
sleep(1)
end
# ログファイルをポスト
logger.post_thread_file!(File.new(log_filepath))
# アタッチメントカラーを緑に
logger.good!("Completed.")
なお、かなりの数のバッチをこれで長年更新していますが、(本ロガーの件で)Slackの制限に引っかかったことはありません。
ロガークラスの実装
class SlackLogger
attr_accessor :color
def initialize(pretext = nil, *args)
pretext ||= begin
c = caller_locations(2, 1).first
"#{c.path.gsub(Jets.root.to_s, '')} - #{c.label}"
end
@options = args.extract_options!
@options[:channel] ||= :test
@options[:username] ||= "バッチログ"
@options[:username] += "(開発環境)" if Rails.env.development?
@logs = []
@pretext = pretext
@color = :default
@post_to_slack = true
@logger = @options.delete(:logger) || Rails.logger
end
def post!(text)
begin
full_text = "#{Time.current.strftime('%T')} - #{text}"
@logger.info(text)
unless @post_to_slack
puts full_text
return
end
@logs << full_text
attachments = [{
pretext: @pretext,
color: @color,
text: @logs.reverse.join("\n"),
}]
# 初回はpost、2回目からはupdate
if @message.nil?
@message = Slack.client.chat_postMessage(@options.merge({
attachments: attachments.to_json,
}))
else
Slack.client.chat_update(@options.merge({
channel: @message["channel"],
ts: @message["ts"],
attachments: attachments.to_json,
}))
end
rescue => e
@logger.error("fail. \n#{e.message}\n" + %!#{e.backtrace[0..3].join("\n")}!)
end
end
def good!(text)
@color = :good
post!(text)
end
def danger!(text)
@color = :danger
post!(text)
end
# 頻度が多い場合はpost!せずwrite!でファイルに溜めて、処理の終了時にログファイルをpost_thread_file!する
def write!(text)
@logger.info(text)
end
# スレッドにファイルをpostする
def post_thread_file!(text_file, filetype: "text", mime_type: "text/plain", filename: "tmp")
ret = Slack.client.files_upload({
channels: @message['channel'],
thread_ts: @message['ts'],
filename: filename.to_s,
filetype: filetype.to_s,
file: Faraday::UploadIO.new(text_file, mime_type),
})
end
end
バッチに余計な処理をはさみたくないというケースもあるかと思いますが、長年使っても安定していてここでトラブったことはないので快調ではあります。
(とはいえデリケートな処理の箇所ではタイムアウト設定や、一度でもタイムアウトしたらその日は更新しないなどの制御が必要です。)
誰かのお役に立てば幸いです!
Discussion