QuXiao's Blog

Life && Tech && Thoughts

获取最近几分钟的日志

Written on

最近在整理对于各个模块的监控,需要有一定的实时性。比如,需要获取最近几分钟内的日志,然后看某些请求的数量以及响应时间是否符合要求。但是,线上服务的日志,通常都是按照小时粒度进行切分的,你不可能对一个文件进行直接的过滤操作。在此之前需要解决一个问题:在一份文件中,获取最近一段时间的日志

当然,还有一个最最基础的问题:你的日志内容里面是表示时间的字段的。(不打时间和请求ID的日志简直就是耍流氓!)

我一开始的想法是:估算平均请求压力下,每5分钟的日志会有多少条,然后直接将cat替换为tail -n XXX就可以了。虽然修改起来很方便,但是是有明显缺陷的:

  1. 随着流量变化,tail出来的日志的时间粒度是不一样的。如果用来监控实时请求响应时间还算能接受,用来监控请求量就不行了;
  2. 如果以后模块升级,增加或者减少了请求日志,tail出来的数字需要不断调整。

看来还是要精确的获取某个时间段的日志才行。其实思路还是比较清晰的:

  1. 计算出开始时间结束时间两个字段
  2. 提取日志行中的日志时间
  3. 比较三个值,如果日志行的时间符合要求,则将其打印,作为过滤程序的输入
  4. 执行数日志数量或者统计请求响应时间的命令

对于步骤1,使用date命令就可以获取,这个简单。

对于步骤2,一般日志中的时间都会比较在日志前面几个字段,比较好提取,也不难。

步骤4嘛,就看需求是什么了,如果是获取请求数目,直接用grepwc -l就OK了。如果涉及到提取日志字段,简单的也可以用cut搞定,复杂就得用grep或者awk了。

最关键是步骤3如何实现,我想到的是在awk中进行逻辑判断,获取日志中的时间字段不难,但是如果时间字段是通过多个字段拼接而来的,比如2014-05-0217: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--

comments powered by Disqus