awk处理nginx日志

时间:2021-03-31 01:41:34

awk处理nginx日志

主要是将非结构化的日志处理成结构话数据并入库做统计,本场景是统计rest接口的调用情况:

  • nginx日志设置
  • awk抽取字段
  • awk日期格式化
  • 拼装sql与导入数据库
  • crontab自动执行
  • 配合log rotate
  • 统计分析
  • 邮件通知
  • 报表展示

nginx日志设置

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

抽取字段

代码:

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

crontab自动执行

/etc/crontab里,添加:

59 23 * * * root /soft/stat/invokeStat.sh
# 每天晚上12点前执行一次

配合log rotate

/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

crontab自动执行

/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

报表展示

todo list

  1. 集合echart与sql的字段组合,统计具体接口的调用情况
  2. 跟踪接口在不同时间的响应时间
  3. 优化慢请求
  4. 增量?