最近在整理对于各个模块的监控,需要有一定的实时性。比如,需要获取最近几分钟内的日志,然后看某些请求的数量以及响应时间是否符合要求。但是,线上服务的日志,通常都是按照小时粒度进行切分
的,你不可能对一个文件进行直接的过滤操作。在此之前需要解决一个问题:在一份文件中,获取最近一段时间的日志。
当然,还有一个最最基础的问题:你的日志内容里面是表示时间的字段的。(不打时间和请求ID的日志简直就是耍流氓!)
我一开始的想法是:估算平均请求压力下,每5分钟的日志会有多少条,然后直接将cat
替换为tail -n XXX
就可以了。虽然修改起来很方便,但是是有明显缺陷的:
- 随着流量变化,
tail
出来的日志的时间粒度是不一样的。如果用来监控实时请求响应时间还算能接受,用来监控请求量就不行了; - 如果以后模块升级,增加或者减少了请求日志,
tail
出来的数字需要不断调整。
看来还是要精确的获取某个时间段的日志才行。其实思路还是比较清晰的:
- 计算出
开始时间
和结束时间
两个字段 - 提取日志行中的
日志时间
- 比较三个值,如果日志行的时间符合要求,则将其打印,作为过滤程序的输入
- 执行数日志数量或者统计请求响应时间的命令
对于步骤1,使用date
命令就可以获取,这个简单。
对于步骤2,一般日志中的时间都会比较在日志前面几个字段,比较好提取,也不难。
步骤4嘛,就看需求是什么了,如果是获取请求数目,直接用grep
和wc -l
就OK了。如果涉及到提取日志字段,简单的也可以用cut
搞定,复杂就得用grep
或者awk
了。
最关键是步骤3如何实现,我想到的是在awk
中进行逻辑判断,获取日志中的时间字段不难,但是如果时间字段是通过多个字段拼接而来的,比如2014-05-02
和17:25:00
,怎么把他们放到一个变量里面
呢?要是有像sprintf
这样的函数就好了,没想到,还真有!类似于下面这样:
cat xxx.log | awk '{t=sprintf("%s %s", $2, $3);}'
还有一个问题,就是如何将BASH中的开始时间
和结束时间
变量传入awk
呢?也有办法的!awk
里面有-v
选项,支持将外部变量传入其中。那么程序就类似于这样了:
start_time=`date -d"$last_minutes minutes ago" +"%Y-%m-%d %H:%M:%S"`
end_time=`date +"%Y-%m-%d %H:%M:%S"`
cat xxx.log | awk -v st="$start_time" -v et="$end_time" '{t=sprintf("%s %s", $2, $3); if(t>=st && t<=et){print $0}}'
最后,还有一个小问题,因为会定期切分日志,所以需要考虑临界时间点的情况,把当前时间段和上个时间段的日志同时作为输入即可。
这样,精确获取最近一段时间日志的需求就得到解决了。
–EOF–