背景
服务器有敏感数据,不允许直接登陆服务器查看日志文件,也不允许使用如ELK等日志功能收集日志,所以只能使用最简单的办法,只将错误日志进行收集,然后通过应用的webhook进行收集。
思路
两种思路:
- 周期轮询
- 实时抓取
周期轮询
每隔一分钟去抓取一次,错误日志的内容上送webhook。 这样的好处是不会抓到过多的错误日志,但是也有个问题,有可能会错过关键的错误日志。 假如每一分钟一轮询,这一次查询刚好没有错误日志产生,而这一个轮询时刻的一分钟内产生了错误日志,就会错过。
如果到到轮询时刻去统计这一分钟到上一分钟之间的错误日志,是可以,但是如果错误在轮询完成后的这一刻发生,需要等到一分钟之后才会告警出来,缺乏实时性,如果对实时性要求不高可以使用这种方式。
实时抓取
这个思路很简单,就是实时抓取ERROR日志,有ERROR就推送webhook。 实现思路:
- 使用 tail 查询日志
- 倒序获取第一条
- 关键字可指定
- 过滤关键字
下面这个脚本实现以上的几个思路,算是一种简单的实现,我一直觉得脚本这东西不要写的太复杂,需要考虑后面的人维护的成本。另外脚本尽量使用python而不是shell,python更好维护,也利于扩展。写shell是因为历史原因。
三个关键的文件 errorword.txt 是错误关键字 exclude.txt 是排除的关键字 error_test.log 是错误日志
如果测试的话,使用 echo >> 重定向进去,如果是使用vim编辑保存的话,tail 会抓会量日志,不是脚本有问题。
代码语言:javascript复制#!/bin/bash
IP=`echo $(curl -s ifconfig.me)`
ERROR_WORD=/data/errorword.txt
EXCLUDE=/data/exclude.txt
SLACK=https://hooks.slack.com/services/test_webhook
LOG_DIR=/Users/liukai/workspaces/temp/shell/alert/error_test.log
tail -Fn0 $LOG_DIR|
while read line;do
echo $line | grep -i -f $ERROR_WORD >/dev/null
if [ $? -eq 0 ];then
echo $line | grep -i -f $EXCLUDE >/dev/null
## white list
if [ $? -gt 0 ]; then
new_line=`echo $line | sed s/"//g`
message='{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "服务异常日志, IP: '"$IP"' n>'$new_line'n"
}
}
]
}'
alert=`echo $message | sed s/'//g`
#echo "$IP,$(date),$line" >> /Users/liukai/workspaces/temp/shell/alert/error_report.log
curl -H "Content-Type: application/json" -X POST -d "$alert" $SLACK
fi
fi
done
改进版本
让任务在后台执行,上一个版本是用来验证这个功能,实际使用当中需要放到后台当中持续运行。 使用方式:
代码语言:javascript复制sh alert.sh start | stop
#!/bin/bash
IP=`echo $(curl -s ifconfig.me)`
OPT=$1
ALERT_DIR=/data/alert
ERROR_WORD=$ALERT_DIR/errorword.txt
EXCLUDE=$ALERT_DIR/exclude.txt
LOG_DIR=/data/logs/tron.log
SLACK=https://hooks.slack.com/services/test_webhook
start() {
nohup tail -Fn0 $LOG_DIR|
while read line;do
echo $line | grep -i -f $ERROR_WORD >/dev/null
if [ $? -eq 0 ];then
echo $line | grep -i -f $EXCLUDE >/dev/null
## white list
if [ $? -gt 0 ]; then
new_line=`echo $line | sed s/"//g`
message='{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "服务异常日志, IP: '"$IP"' n>'$new_line'n"
}
}
]
}'
alert=`echo $message | sed s/'//g`
curl -H "Content-Type: application/json" -X POST -d "$alert" $SLACK
fi
fi
done &
}
stop() {
kill -9 `ps -ef |grep 'tail -Fn0'|grep -v 'grep' |awk {'print $2'}`
}
if [[ $OPT == 'start' ]]; then
start
if [[ !? -eq 0 ]]; then
echo "start alert"
else
echo "start fail"
fi
elif [[ $OPT == 'stop' ]];then
stop
echo "stop alert"
fi
总结
尽量使用简单的脚本,不要让脚本变的复杂。