背景
服务器有敏感数据,不允许直接登陆服务器查看日志文件,也不允许使用如ELK等日志功能收集日志,所以只能使用最简单的办法,只将错误日志进行收集,然后通过应用的webhook进行收集。
思路
两种思路:
- 周期轮询
- 实时抓取
周期轮询
每隔一分钟去抓取一次,错误日志的内容上送webhook。
这样的好处是不会抓到过多的错误日志,但是也有个问题,有可能会错过关键的错误日志。
假如每一分钟一轮询,这一次查询刚好没有错误日志产生,而这一个轮询时刻的一分钟内产生了错误日志,就会错过。
如果到到轮询时刻去统计这一分钟到上一分钟之间的错误日志,是可以,但是如果错误在轮询完成后的这一刻发生,需要等到一分钟之后才会告警出来,缺乏实时性,如果对实时性要求不高可以使用这种方式。
实时抓取
这个思路很简单,就是实时抓取ERROR日志,有ERROR就推送webhook。
实现思路:
- 使用 tail 查询日志
- 倒序获取第一条
- 关键字可指定
- 过滤关键字
下面这个脚本实现以上的几个思路,算是一种简单的实现,我一直觉得脚本这东西不要写的太复杂,需要考虑后面的人维护的成本。另外脚本尽量使用python而不是shell,python更好维护,也利于扩展。写shell是因为历史原因。
三个关键的文件
errorword.txt 是错误关键字
exclude.txt 是排除的关键字
error_test.log 是错误日志
如果测试的话,使用 echo >> 重定向进去,如果是使用vim编辑保存的话,tail 会抓会量日志,不是脚本有问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #!/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
|
改进版本
让任务在后台执行,上一个版本是用来验证这个功能,实际使用当中需要放到后台当中持续运行。
使用方式:
sh alert.sh start | stop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| #!/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
|
总结
尽量使用简单的脚本,不要让脚本变的复杂。