主要是将非结构化的日志处理成结构话数据并入库做统计,本场景是统计rest接口的调用情况:
nginx.conf中:
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'$request_time $upstream_response_time $pipe'
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
代码:
awk '{print substr($4,2,11),substr($4,14,15),substr($6,2),$7,$12,$13}' /var/log/nginx/access.log > ~/stat.log
# 获取访问时间,请求方法,rest url,request time和response time
nginx的时间是这种格式[02/Oct/2016 04:50:11],先用date格式化的方法:
awk -v A=$(date +%Y%m%d) '{gsub("/"," ",$1); sprintf("date -d \"%s %s\" \"+%%Y-%%m-%%d %%H:%%M:%%S\"", $1,$2) | getline D;printf("insert interfaceInvoke(ddate,cdate,method,url,requesttime,responsetime) values(\"%s\",\"%s\",\"%s\",\"%s\",%f,%f);\n",A,D,$3,$4,$5,$6)}' ~/stat.log > ~/stat.sql
发现经常有too many open files错误,嵌套的date命令用掉太多文件句柄,7k条测试数据执行完成需要13s。
考虑了一下,去掉gsub和date,由于时间字符串的格式是固定的、每一部分的长度也是固定的,全部使用字符串处理:
awk -v A=$(date +%Y%m%d) 'BEGIN{ mon["Jan"] = 1; mon["Feb"] = 2; mon["Mar"] = 3; mon["Apr"] = 4; mon["May"] = 5; mon["Jun"] = 6; mon["Jul"] = 7; mon["Aug"] = 8; mon["Sep"] = 9; mon["Oct"] = 10; mon["Nov"] = 11; mon["Dec"] = 12; }; {printf("insert interfaceInvoke(ddate,cdate,method,url,requesttime,responsetime) values(\"%s\",\"%s-%s-%s %s\",\"%s\",\"%s\",%f,%f);\n",A,substr($1,8,4),mon[substr($1,4,3)],substr($1,1,2),$2 ,$3,$4,$5,$6)}' ~/stat.log > ~/stat.sql
这样就不需要执行gsub替换和嵌套date命令,7k条测试执行时间只有0.026s,也可以文件句柄的问题了。
invokeStat.sh全部代码如下:
awk '{print substr($4,2,11),substr($4,14,15),substr($6,2),$7,$12,$13}' /var/log/nginx/access.log > ~/stat.log
# insert into db
awk -v A=$(date +%Y%m%d) 'BEGIN{ mon["Jan"] = 1; mon["Feb"] = 2; mon["Mar"] = 3; mon["Apr"] = 4; mon["May"] = 5; mon["Jun"] = 6; mon["Jul"] = 7; mon["Aug"] = 8; mon["Sep"] = 9; mon["Oct"] = 10; mon["Nov"] = 11; mon["Dec"] = 12; }; {printf("insert tablename(ddate,cdate,method,url,requesttime,responsetime) values(\"%s\",\"%s-%s-%s %s\",\"%s\",\"%s\",%f,%f);\n",A,substr($1,8,4),mon[substr($1,4,3)],substr($1,1,2),$2 ,$3,$4,$5,$6)}' ~/stat.log > ~/stat.sql
mysql -h192.168.*.* -uXXX -pXXX dbname < ~/stat.sql
/etc/crontab里,添加:
59 23 * * * root /soft/stat/invokeStat.sh
# 每天晚上12点前执行一次
/etc/logrotate.d/nginx中:
/var/log/nginx/*.log {
daily
missingok
rotate 52
compress
delaycompress
notifempty
create 640 nginx adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
通过logrotate每天晚上切换一次日志,保证invokeStat.sh每天跑的都是最新的,而不是昨天的日志。其实也可以考虑这两个脚本合并成一个。invokeStat先执行。
主要先实现调用次数统计:cat file | wc -l
调用时间大于1s的统计: awk '{$13>1 {......
调用时间大于60s的统计: awk '{$13>60 {......
调用时间最长的10次统计:sort -k3nr ...... | head -n 10
/etc/crontab里,添加:
59 23 * * * root /soft/stat/invokeStat.sh
# 每天晚上12点前执行一次
先配置sendmail,然后使用mail命令:
cat ~/mystat.log | mail -s "[APP]执行时间超过1s的接口统计-"$CURDATE kimmking@163.com
如果发送的文件时html类型:
cat ~/mystat.log | mail -s "$(echo -e "[APP]执行时间超过1s的接口统计-$CURDATE\nContent-Type: text/html;charset=utf-8")" kimmking@163.com