少し前にPHPでFatal Errorやsintax errorがあったときにSwatchでメールで通知するをやったのですが、不便だったのでslackで実装しなおしました。
以下が不便だった理由。
- AWSの迷惑メール防止フィルタに引っ掛かる
- メール通知だと即時性が低い
- どのサーバからの通知かわからない
上から順に大きな理由です。
メール通知からslack通知に切り替えた理由
「1.AWSの迷惑メール防止フィルタに引っ掛かる」と「2.メール通知だと即時性が低い」の2つが理由として大きいです。
ちなみにAWSでもきちんと申請すればできるようです。
https://dev.classmethod.jp/beginners/ec2-port-25-throttle/
2.については正確に言うとメール通知の即時性が低いのではなく、常に見ている対象ではないのでエラーが起きた時に気付きにくいなと。 今の環境では社内SNSにslackを使っているので便利かなと思ったので。
実装の流れ
こんな感じで実装します。
- シェルスクリプトでslack通知
- Swatchの設定
- Swatchを常駐させる設定
シェルスクリプトでslack通知
以下の順で準備します。
- slackのIncoming Webhookを有効にする
- シェルスクリプトを作る
- シェルスクリプトに実行権限を与える
slackのIncoming Webhookを有効にする
この辺の情報は世の中にたくさんあるので割愛します。
このサイトがコードも汎用性が高くわかりやすかったです。
https://qiita.com/tt2004d/items/50d79d1569c0ace118d6
シェルスクリプトを作る
コードを先ほどのサイトから拝借。
https://qiita.com/tt2004d/items/50d79d1569c0ace118d6
パーミッションのユーザーはkusanagiでもいいけど、今のところ使う予定無いので一応rootで作りました。
$ sudo su - #ユーザーをrootに変更
「3.どのサーバからの通知かわからない」を解消するために以下を追加しました。
HOSTNAME=$(hostname -I)
/home/kusanagi/bin/slack.sh
#!/bin/sh set -eu #Incoming WebHooksのURL WEBHOOKURL="https://hooks.slack.com/services/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" #メッセージを保存する一時ファイル MESSAGEFILE=$(mktemp -t webhooks.XXXX) trap " rm ${MESSAGEFILE} " 0 usage_exit() { echo "Usage: $0 [-m message] [-c channel] [-i icon] [-n botname]" 1>&2 exit 0 } while getopts c:i:n:m: opts do case $opts in c) CHANNEL=$OPTARG ;; i) FACEICON=$OPTARG ;; n) BOTNAME=$OPTARG ;; m) MESSAGE=$OPTARG"\n" ;; \?) usage_exit ;; esac done #どのサーバからの通知かわかるようにip取得 HOSTNAME=$(hostname -I) #slack 送信チャンネル CHANNEL=${CHANNEL:-"#php_notification"} #slack 送信名 BOTNAME=${BOTNAME:-"Swatch"} #slack アイコン FACEICON=${FACEICON:-":sos:"} #見出しとなるようなメッセージ MESSAGE=${MESSAGE:-$HOSTNAME} if [ -p /dev/stdin ] ; then #改行コードをslack用に変換 cat - | tr '\n' '\r' | sed 's/'"$(printf '\r')"'/\\n/g' > ${MESSAGEFILE} else echo "nothing stdin" exit 1 fi WEBMESSAGE='```'`cat ${MESSAGEFILE}`'```' #Incoming WebHooks送信 curl -s -S -X POST --data-urlencode "payload={\ \"channel\": \"${CHANNEL}\",\ \"username\": \"${BOTNAME}\",\ \"icon_emoji\": \"${FACEICON}\",\ \"text\": \"${MESSAGE} ${WEBMESSAGE}\" }" ${WEBHOOKURL} >/dev/null #1>&2 #デバッグの時は1>&2をコメントアウトしないと、slackのwebhookのエラーステータスが表示されて便利
シェルスクリプトに実行権限を与える
$ chmod 700 /home/kusanagi/bin/slack.sh
Swatchの設定
Swatchのダウンロードや設定はこちらの記事を参考にしてください。
[blogcard id=”237″]
Error形式ごとに、kusanagiのlogの吐き出し方が違い、ここでかなりハマりました。
()と””の数や生で出さないようにしないといけない、みたいな工夫が必要です。
正直この辺は環境やlogを残す際の設定によって違うと思いますが、キモになるのは以下の2つです。
1.ダブルコーテーション"の数
swatchに引っ掛かった行のダブルコーテーションの数が奇数だと、「”が閉じてない!」と怒られます。
2.カッコ()が直で書かれてる
swatchに引っ掛かった行に()があると「()はエスケープしろ!」と怒られます。
/etc/swatch/swatch.conf
watchfor /PHP Fatal error/ echo red exec echo $_" | /home/kusanagi/bin/slack.sh #ダブルコーテーションが1つしかないので末尾に追加 watchfor /recv\(\)/ echo blue exec echo "$_" | /home/kusanagi/bin/slack.sh #行内に()があるため文字列ということを明示 watchfor /PHP Parse error/ echo yellow exec echo $_ | /home/kusanagi/bin/slack.sh # ダブルコーテーションできれいにくくられているために""をつけると逆にうまく出ないのでなにもつけない
logの時点でシングルコーテーション''をつけて出すようにするとかやりようはあるように思いますが、いい手段が見つからず対処療法的ならざるを得ませんでした…いい方法あれば教えてください。
Swatchを常駐させる設定
こちらも詳しくはこちらの記事を参考にしてください。
[blogcard id=”237″]
/etc/init.d/swatch
#!/bin/bash
#chkconfig: 345 80 20
#source function library.
. /etc/rc.d/init.d/functions
start(){
echo -n "starting Swatch: "
/usr/bin/swatch -c /etc/swatch/swatch.conf -t
/home/kusanagi/sitename/log/nginx/error.log & # -t 以降はログファイルの場所を設定 この行を追加すれば複数場所を監視可能
return 0
}
stop(){
killproc swatch
return 0
}
case "$1" in
start)
start
;;
stop)
stop
;;
esac
実行権限を与える
$ sudo su - #rootユーザーに変更 $ chmod 700 /etc/init.d/swatch #実行権限を付与
自動起動設定
$ sudo /etc/init.d/swatch start #daemonにswatchを追加 $ sudo chkconfig –-add swatch #chkconfigにswatchを追加 $ sudo chkconfig swatch on #chkconfigでswatchをonにする $ sudo chkconfig --list #chkconfigでswatchが動いてるか確認する
swatch.confの試行錯誤のコード
watchfor /PHP Parse error|PHP Fatal error|recv\(\) failed/
echo red
#exec /home/kusanagi/bin/slack5.sh -m "$*" > /dev/null 2>&1
#exec "echo kusanagi-stg PHP error | /home/kusanagi/bin/slack.sh"
#exec cat $0 | sed s/\"//g #ダメ
#exec echo $0" > /tmp/test.txt #動く
#exec echo $0 > /tmp/test.txt #閉じ"が無いとエラー出る
#exec echo $0" | sed s/\"//g #sedの"がダメ
#exec echo $0 | sed s/\"//g #sedの"がダメ
#pipe sed s/\"//g #sedの"がダメ
#exec echo $0" | sed "s/"//g" # sedの構文エラー
#pipe > /tmp/test.txt #エラーはないけどデータが入らない
#pipe echo $0" > /tmp/test.txt #動く
#exec sed s/\\"//g /tmp/test.txt | /home/kusanagi/bin/slack.sh #動く!
#exec sed s/\\"//g $0 | /home/kusanagi/bin/slack.sh #"が無いとエラーになる
#exec sed s/\\"//g $0" | /home/kusanagi/bin/slack.sh #実行しようとしてエラー
#exec /home/kusanagi/bin/slack.sh sed s/\\"//g $0" #nothing in std
#exec /home/kusanagi/bin/slack.sh < sed s/\\"//g $0" #nothing in std no sed such file
#exec echo $0" > /tmp/test.txt
#exec cat /tmp/test.txt | /home/kusanagi/bin/slack.sh
#exec echo $0" | /home/kusanagi/bin/slack.sh
#exec /tmp/test.sh $0"
#exec echo $0" | /tmp/test.sh #動いた
#exec echo $0" | sed s/\\"//g | sed s/\\(//g | /home/kusanagi/bin/slack.sh #動いたーーーーー
#exec echo $_" | sed s/\\"//g | sed s/\\(//g | /home/kusanagi/bin/slack.sh #動いたーーーーー2
#exec echo $_" | sed s/\\"//g | /home/kusanagi/bin/slack.sh #動いたーーーーー3
exec echo $_ | /home/kusanagi/bin/slack.sh #動いたーーーーー4