问题描述:
在大流量线上服务中,日志系统会产生数量庞大的日志,动辄就是几十G。在如此之大的文件中快速搜索日志是运维人员经常遇见的问题。我们经常遇见的问题是查询一段时间内的某些条日志。比如,今天有一个访问失败了,大约是在上午9点,把这条日志找出来,然后查找失败原因。
常见处理方式及缺点:
1.如果文件比较小,100m以内使用grep、awk或者sed进行逐条匹配比较方便,但是文件非常大时,其查找效率是非常低的,运行时间长达几十分钟甚至上小时。
2.使用hadoop大数据处理,查询速度快,效率高。但是需要我们搭一套hadoop环境,需要巨大的计算量。感觉就是杀鸡焉用牛刀。
3.按照时间手动查找,使用tail -c size filename 命令 提取出一段日志,看看时间是否符合条件。如果不符合就调整size参数直到找到9点之前的时间段,然后从这里开始查起,使用grep、awk或者sed进行逐条匹配,直到找到目标日志然后ctrl+c停止,这样确实能省下很多时间,也是我以前经常用的一种方式。
解决方案:
很显然,手动查找不是一个程序员的理想解决方案,那么能不能写一个脚本自动按照时间段查找超大日志呢?常用的命令如,cat、head、tail、grep、more、less、sed、awk貌似都不能提供有效的方案。下面就是Blog介绍的方法。
下面就是我的脚本,姑且称它为探针法:
#!/bin/bash #读入文件名 file_name=$1 #读入查询起始时间 start=$2 #读取文件大小 file_size=$(stat --format=%s $file_name) #设置探针步长,一般为文件大小的百分之一到千分之一 step=500000000 #如果文件大小小于步长,则size为文件大小,否则size为步长 [ $file_size -lt $step ] && size=$file_size || size=$step #初始化探针时间 test_time="00:00:00" #循环检测,直到探针时间大于查询时间,停止 while [[ ${test_time} < ${start} ]] do size=$(($size+$step)) test_time=$(dd if=$file_name skip=$(($size/10000)) ibs=10000 count=1 2>/dev/null | sed -n "2p" | awk ‘{print $3}‘) done #读取此时的查询size,使用tail -c命令即从目标时间开始查日志。 tail -c $(($file_size-$size+$step)) $file_name
执行sh find.sh filename.log 09:00:00 , 然后再使用grep等命令过滤。
解释一下,这里主要用到了dd命令:拷贝一个文件,并按照参数对其处理转换。
具体的介绍可以查看相关文档,例如,http://blog.chinaunix.net/uid-24958038-id-3416169.html
这里利用dd命令从超大文件中复制出一小块,提取出时间,也就是test_time,判断test_time是否符合条件,不符合就使size增加一个步长然后继续提取时间,直到符合条件。因为dd命令有skip选项,可以直接跳过指定大小部分截取到一段,其效率是非常高的,循环500次耗时大于1s,也就是说探针测试500次的时间是1s左右。
dd命令截取出一段日志后使用sed取出完整的一行,然后用awk取出时间戳。
注意,设定时间为09:00:00,查询结果并不是严格从09:00:00开始的,一般会往前一点,这里受step参数影响。step越小越精确,而探针尝试次数越多。
原创,转发请注明。 By LZJing