ログファイルを監視して、slack などに通知したい。
ログを監視して Slackに投げる方法で、かんたんなのを考えた。
ログファイルに何か変更があって、それが特定のときに メールや slack のチャンネルに通知を出したら便利だと思う。
あれこれログ監視や通知のソフトウェアやパッケージがあるけれど、一番大事なのは手軽で何も知らなくても使えることだと思う。
ファイルを監視して grep して HTTP POST するだけなら、パイプで十分に使える。
ログ監視の仕組み
要は、次のコマンドパイプを呼び出せればいい。ただしいくつかポイントが有るので、それを見ておく
目指す形
このエントリでは次のようなコマンドを作る
tail -f path/to/file | grep some_thing | curl -X POST url
このままでは動かない。
コマンドの組合せならすぐ思いつきそうなパイプですが、単純にこのコマンドだけでは動きません。tai や grep や curl コマンドの特性上、このままではパイプで動かない。すこし変更が必要でした。
コマンドの問題点
tailf / tail -f
ではログがローテーションしたときにOpenしたファイルを追尾しない。新しいログファイルを開いてくれない。そのため、このままでは動きませんでした。
grepは、そのままでは次のパイプがリアルタイムに起動しませんでした。
curl コマンドは、この書き方すると標準入力行を全部受け取ってしまいます。
これらを防ぐために若干のコツというか、オプションを調べて見直したので、まとめておきます。
tail -F
でパスを監視する
tail コマンドには -F
というオプションがあり、これで該当PATHのファイル監視を出来る。 もし tail -f
で監視していると、ファイルが削除・移動後に追跡できない。そのため、tail を使った長期間ログ監視なら、次のコマンドが望ましい。
ログ監視の一番かんたんなコマンド
tail -F logs/lastest.log
追加された行だけを取得する
tail を起動すると、最終5行を表示して -f : follow モードに入る。そのため起動すると過去ログが何度も通知されるのが望ましくない。
tail -n0 -F logs/lastest.log
tail と grep の組合せ
tail -n0 -F logs/lastest.log | grep takuya
tail -f
とコマンドの組合せが便利。
パイプ先で必要情報だけを取出して、残りを捨てたフィルリングや書き換えが出来る。
tail と grep を組合せれば、ログをフィルタできる。また-f / -F
をつけたtail は、パイプがブロックIOになるので、ログが更新にリアルタイムで追従してくれる。
tail | grep
にさらにパイプをつなぐ
### リアルタイム動作しない tail -n0 -F logs/lastest.log | grep takuya | grep login
tail | grep
は正しくフィルタするが、 tail | grep | grep
は画面出力が得られない。 なぜか?。
grep
を tail -f
の組合せは、grep パイプ後パイプが動かない時がある。これはgrep がバッファリングしてしまう事が原因です。
実は grep が内部的にバッファリングして、高速化してくれる。そのため tail -f | grep | grep
では、中間のgrepがIOをバッファリングして、最後のgrepに即時にログが届かない。
line-buffered のオプション
次のオプションで、複数行バッファリングを一時停止(行単位で出力バッファ)するようにする。
man grep
--line-buffered Use line buffering on output. This can cause a performance penalty.
tail | grep | grep するときは次のようにする。
そのため tailf を grep して grep したり、sed したりする場合は、 次のようにする。
tail -F logs/lastest.log | grep --line-buffered takuya | grep --line-buffered login
ログを受け取ったら、何かする。
行単位でログがやってくる。
行単位で受け取ったらPOST処理をするとしたら、curl でPOSTしようとすると、行を標準入出力から受け取る必要がある.
出来ない
grep something | curl -X POST -d - ...
grep した結果をcurl で送信しようとしても、curl で標準入出力をパイプで扱うと、1度しか起動できない。行毎に起動するには xargs を使う
xargs で行が来るたびに送信する。
grep --line-buffered takuya | xargs -I@ curl -X POST http://example.com/ -d message="@"
xargs を -I で1行単位で起動するようにして、
@` という文字で指定して、1行をコマンドの文字列に展開する。
slack のURLを用意する
Slack にはPOST用の webhook が用意されていて、最近はチャンネル毎にPOST用のWebhook のURLを生成できる 生成したURLは次のように、ディレクトリが3段になっていた (2017/05/09)
https://hooks.slack.com/services/Axxx/Bxxx/Cxxx
ここに curl で post / json すれば、 Slack に拾われる。
curl -s \ https://hooks.slack.com/services/Axxx/Bxxx/Cxxx \ -X POST \ -H 'Content-type: application/json' \ --data '{"username":"curl", "text":"#{my message}"}'
パイプにまとめる。
ここまで見てきたそれぞれのコマンドとパイプを全部まとめると、次のようになる。
tail -n0 -F logs/lastest.log | grep --line-buffered takuya | xargs -I @ curl -s \ https://hooks.slack.com/services/Axxx/Bxxx/Cxxx \ -X POST \ -H 'Content-type: application/json' \ --data '{"username":"curl", "text":"@"}'
これで、ログが更新されたら、 grep で フィルタをかけ、条件にあったログが来たら Slack にPOSTすることが出来る。
さらにコレを関数か、シェルスクリプトファイルにする
このままでは、ファイルの指定とエンドポイントのPOSTのURLが変更が不便なのでシェルスクリプトにしておくと便利だと思います。
さらに、systemd に登録
これらのコマンド(シェルスクリプト)をsystemd.unitの service に登録して、Daemonと同時に起動しておけば、だいぶ楽だよね。
マインクラフトのログを監視する
たとえば、マインクラフトサーバーで「死んだ」人をSlackに晒し上げるには
ログファイルが lastest.log で、grep で slain を取れば死んだことが解り、最後にSlackで通知したら出来上がり。
tail -n0 -F logs/lastest.log | \ grep --line-buffered slain | \ xargs -I @ curl -s \ https://hooks.slack.com/services/Axxx/Bxxx/Cxxx \ -X POST \ -H 'Content-type: application/json' \ --data '{"username":"minecraft-serverl", "text":"@"}'
コマンドで出来てるので、たとえば、死んだ回数や死因を累計とか、ログイン・ログアウトを監視してプレイ時間を通知するとかね。
まとめ
tail と grep と curl だけで、ログを監視して通知することが出来ます。
tail -F で ファイルのパスの状態を監視できます
grep –line-buffered で行ごとにgrep をすることが出来ます。
xargs curl で行の内容をPOSTすることが出来ます。
個人的な
ちょっとしたログ監視を調べたら、ちょっとね・・・
「最強の○○のログ監視」と「ログ監視パッケージをdocker コンテナ」を導入してる 某キ◯タさんの意識高すぎるエントリに辟易して調べました。
開発とかちょっとしたことにわざわざDockerコンテナだのAWSとか、ちょっと重たすぎだよね。手順やリソースが多すぎる。
ログ監視ってのは単一のログファイルを見て何かするのと、「複数サーバー(インスタンス)」のログを一箇所に集めて何かするのとだいぶ違うと思うんだよね。
Slackに投稿したり、ログを投げたり、まとめたりっていろいろな方法がある。パッケージを入れてしまえばたしかにかんたんなのですが。後始末が面倒になる。後始末が面倒になってたくさんのインスタンスを使い潰したりコンテナから起動したりで、時間や学習コストは抑えられるのですが。金銭的に不利だったり、学習コストを抑えたために、応用が効かなかったり。。。うーんって思うことも有る。