少し前に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