
目前应用程序日志的自动收集归集工具很多,比如著名的ELK工具套件。既可以收集,存储日志,也可以图形化展示。虽然ELK套件功能很强大,但是对于小型应用系统来说,使用ELK套件有牛刀杀鸡的感觉,一般使用shell脚本进行日志的自动化归集即可。
1 日志归集介绍
本文介绍使用shell脚本结合linux定时crontab进行定时日志归集,应用服务日志归集架构如下图:

日志归集架构示意了三个日志生成节点和一个日志归集节点,在日志生成节点部署fetch.sh脚本用于该节点的日志合并,在日志归集节点部署merge.sh脚本用于所有节点日志的合并归集。
本文的日志生成节点运行着多个应用服务器实例,每个应用服务器实例每次重启会产生一个日志文件,文件如下所示:
(←支持左右滑动→)
-rw-r--r-- 1 app app 351622454 Jan 7 00:05 app1_node3-20190106.log -rw-r--r-- 1 app app 399054758 Jan 7 22:57 app1_node3-20190107.log -rw-r--r-- 1 app app 331461924 Jan 6 00:05 app1_node4-20190105.log -rw-r--r-- 1 app app 342231931 Jan 7 00:05 app1_node4-20190106.log -rw-r--r-- 1 app app 400589586 Jan 7 22:57 app1_node4-20190107.log -rw-r--r-- 1 app app 338635212 Jan 6 00:05 app1_node5-20190105.log -rw-r--r-- 1 app app 344124368 Jan 7 00:05 app1_node5-20190106.log -rw-r--r-- 1 app app 390395925 Jan 7 22:57 app1_node5-20190107.log -rw-r--r-- 1 app app 344337568 Jan 6 00:05 app1_node6-20190105.log -rw-r--r-- 1 app app 309255519 Jan 7 00:05 app1_node6-20190106.log
日志文件的内容如下所示:
(←支持左右滑动→)
01-07 23:06:23[INFO ]·LoginVerifyInterceptor·-----登陆成功-class.com.mall.sell.order.OrderAllocationAction---- 01-07 23:06:26[INFO ]·LoginVerifyInterceptor·-----登陆成功-class com.mall.sell.order.OrderAllocationAction---- 01-07 23:06:28[INFO ]·LoginVerifyInterceptor·-----登陆成功-class com.mall.sell.order.OrderAllocationAction---- 01-07 23:06:30[INFO ]·LoginVerifyInterceptor·-----登陆成功-class com.mall.sell.order.OrderAllocationAction---- 01-07 23:06:33[INFO ]·LoginVerifyInterceptor·-----登陆成功-class com.mall.sell.order.OrderAllocationAction---- 01-07 23:06:36[INFO ]·LoginVerifyInterceptor·-----登陆成功-class com.mall.sell.order.OrderAllocationAction---- 01-07 23:06:42[INFO ]·LoginVerifyInterceptor·-----登陆成功-class com.mall.sell.order.OrderAllocationAction----
主要包括日志产生的时间,日志产生的代码文件,以及日志内容等信息。
2 单节点日志合并
单节点日志合并fetch.sh脚本,其作用是对多个日志文件进行合并,按日志产生时间升序排序,取最近固定间隔时间(本文固定间隔为10分钟)的日志作为增量,将日志增量输出到日志文件,远程跨节点拷贝到日志归集节点。
单节点日志合并脚本fetch.sh内容如下:
(←支持左右滑动→)
[app@localtest logs]$ more fetch.sh
#! /bin/bash
logdir="/app/logs/www/"
#2017-04-26 11:04
set `date -d ’-10 minute’ "+%Y-%m-%d %H:%M"` #【1】
time=$1" "$2
#2017-04-26 11:0
findstr=${time:0:15}
#
output="/app/logs/daily/app1-"$1":"$2".log.txt"
#tmp file
tmp="/app/logs/daily/log.tmp"
logname=app*_node*-*.log
ls ${logdir}$logname | gawk -F’/’ -v tmpfile=$tmp ’ #【2】
BEGIN{
node="";
file="";
}
{ #【3】
curFile=$0
curNode=substr($6,1,10);
if(curNode !=node) {
if(node != "") {
print(file) >> tmpfile;
}
file=curFile;
node=curNode;
} else {
if(curFile > file) {
file=curFile;
}
}
}
END{
if(file!="") {
print(file) >> tmpfile;
}
}’
set `date -d ’-10 minute’ "+%Y- %m-%d"`
year=$1
md=$2
files=`cat $tmp` #【4】
cat ${files} | gawk -F’[’ -v year=$year -v md=$md ’{ #【5】
idx=index($1,md);
if(idx == 1) {
print(year""$0);
} else {
print($0);
}
}’ | grep "^${findstr}" |sort -t [ > $output #【6】
rm -f $tmp
scp app@10.***.***.105:${output} app@10.***.***.105:/app/logs/daily/logs/ #【7】
rm -f $output #【8】
set `date "+%Y-%m-%d.%H:%M:%S"`
echo $1" finish to fetch logs."
脚本解析
【1】获取10分钟之前的日期和时间,将年月日赋值到$1中,时分赋值到$2中;
【2】gawk的-v参数,用于向命令内容传入参数;
【3】gawk中间代码段,用于获取每个node日志的最新日志文件,文件名输出到tmp文件中;
【4】将临时文件中的文件名输出到files变量中;
【5】该gawk命令是对日志文件内容中不包含年月的日志文本行前面添加年月字符
【6】grep找出包含最近10分钟的日志文本,按照日志时间升序排序,结果重定向到输出文件output中;
【7】使用scp命令,将该增量日志文件远程拷贝到日志归集服务器上;
【8】删除临时生成的增量日志文件。
3 日志归集
日志归集就是将所有日志生成节点的增量日志合并排序,其脚本merge.sh内容如下:
(←支持左右滑动→)
[app@localtest daily]$ more merge.sh #! /bin/bash logdir="/app/logs/daily/logs/" bakdir="/app/logs/daily/logs/bak/" #2017-04-26 16:23 set `date -d ’-10 minute’ "+%Y-%m-%d %H:%M"` #【1】 merge="/app/logs/daily/logs/"$1".log.txt" #【2】 logfiles="/app/logs/daily/logs/app*$1*.log.txt" #【3】 files=`ls $logfiles` #【4】 cat $files | sort -t . >> $merge #【5】 #mv $files $bakdir rm -f $files echo "rm "$files set `date "+%Y-%m-%d.%H:%M:%S"` echo $1" finish to merge logs."
脚本解析
【1】获取10分钟之前的时间,将年月日赋值到$1,时分赋值到$2;
【2】获取归集合并文件名merge,包含"2019-01-09"字样;
【3】带通配符的待合并的文件名,赋值给logfiles变量;
【4】将待合并的日志文件列表赋值给files变量;
【5】将待合并的日志文件按日志生产时间升序排序,输出追加到归集文件merge中。
4 备份文件删除
由于节点磁盘空间有限,会保存一定天数的日志文件,需要进行定时删除超时的日志文件。日志文件名称如下:
(←支持左右滑动→)
-rw-r--r-- 1 app app 1152868284 Oct 17 04:03 2018-10-09.log.txt.zip -rw-r--r-- 1 app app 1180321236 Oct 18 04:03 2018-10-10.log.txt.zip -rw-r--r-- 1 app app 1101144558 Oct 19 04:03 2018-10-11.log.txt.zip -rw-r--r-- 1 app app 1045207204 Oct 20 04:02 2018-10-12.log.txt.zip -rw-r--r-- 1 app app 1155900029 Oct 21 04:03 2018-10-13.log.txt.zip -rw-r--r-- 1 app app 1061351650 Oct 22 04:02 2018-10-14.log.txt.zip -rw-r--r-- 1 app app 1259379498 Oct 23 04:03 2018-10-15.log.txt.zip -rw-r--r-- 1 app app 1241618627 Oct 24 04:03 2018-10-16.log.txt.zip -rw-r--r-- 1 app app 1285573845 Oct 25 04:03 2018-10-17.log.txt.zip
日志删除脚本delete.sh内容如下:
(←支持左右滑动→)
[app@localhost scripts]$ more delete.sh
#/bin/bash
#日志文件,保留当天日志文件
logpath=/app/2iOrders/app_logs
cd $logpath #【1】
files=`ls *.log.txt` #【2】
for file in $files #【3】
do
#echo $file
zip $file".zip" $file #【4】
rm $file
done
scp_day=`date -d "90 days ago" +"%Y-%m-%d" ` #【5】
files=`ls *.log.txt.zip` #【6】
for file in $files
do
#2017-07-20.log.txt.zip
day=${file:0:10} #【7】
#echo $day
if [ $scp_day \> $day ] ; then #【8】
#echo gt
rm $file #【9】
fi
done
脚本解析
【1】进入日志备份目录;
【2】将日志备份目录的日志文件名赋值给files变量;
【3】对files循环变量每个文件;
【4】压缩日志文件为zip格式;
【5】将90天前的年月日字符串赋值给scp_day变量;
【6】将日志备份目录所有zip文件赋值给files变量;
【7】获取日志文件名中的年月日字符串,赋值给day变量;
【8】进行字符串比较,判断日志文件是否超过90天;
【9】如果超过90天,删除该日志文件。
5 定时执行
定时使用linux系统自带的crontab命令,crontab -e可以编辑定时任务,crontab -l可以查看定时任务。
定时执行脚本包括fetch.sh,merge.sh和delete.sh,定时参数设置如下:
(←支持左右滑动→)
00 04 * * * /app/2iOrders/app_logs/scripts/delete.sh >> /dev/null 01,11,21,31,41,51 * * * * /app/logs/daily/fetch.sh >> /dev/null 05,15,25,35,45,55 * * * * /app/logs/daily/merge.sh >> /dev/null
说明:
00 04第一个00表示0分时刻,第二个04表示4点;
05,15,...,55表示05,15,...,55分时刻,后面第一个*表示每小时;
01,11,21,...,51同05,15,...,55类似。