shell脚本在大文件日志中按照时间段快速搜索日志

问题描述:

在大流量线上服务中,日志系统会产生数量庞大的日志,动辄就是几十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介绍的方法。

下面就是我的脚本,姑且称它为探针法:

  1. #!/bin/bash
  2. #读入文件名
  3. file_name= $1
  4. #读入查询起始时间
  5. start= $2
  6. #读取文件大小
  7. file_size=$( stat --format=%s $file_name)
  8. #设置探针步长,一般为文件大小的百分之一到千分之一
  9. step=500000000
  10. #如果文件大小小于步长,则size为文件大小,否则size为步长
  11. [ $file_size -lt $step ] && size= $file_size || size= $step
  12. #初始化探针时间
  13. test_time= "00:00:00"
  14. #循环检测,直到探针时间大于查询时间,停止
  15. while [[ ${test_time} < ${start} ]]
  16. do size=$(( $size+ $step))
  17. test_time=$(dd if= $file_name skip=$(( $size/10000)) ibs=10000 count=1 2>/dev/null | sed -n "2p" | awk '{print $3}')
  18. done
  19. #读取此时的查询size,使用tail -c命令即从目标时间开始查日志。
  20. 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越小越精确,而探针尝试次数越多。


-----------------------------------------------------分割线------------------------------------------------------------

以上第一版方法效率并不理想,而且只有开始时间,没有结束时间,所以对脚本做优化改进如下:

1.探针法改为二分查找,精确定位到目标时间

2.增加了结束时间的设置

PS:  

脚本中没有为dd设置输出文件of参数,默认输出到终端,如有需要可以添加该参数。

在GetLocation方法中,使用awk提取时间戳,可根据日志格式的不同修改。

  1. #!/bin/bash
  2. if [[ $# -lt 3 ]]; then
  3. echo "$0 FILE STARTTIME ENDTIME"
  4. echo "STARTTIME and ENDTIME should be in format like 09:00:00"
  5. echo
  6. exit 1
  7. fi
  8. file_name= $1
  9. start= $2
  10. end= $3
  11. file_size=$( stat --format=%s $file_name)
  12. function GetLocation
  13. {
  14. echo $(dd if= $file_name skip=$(( $location/10000)) ibs=10000 count=1 2>/dev/null | sed -n "2p" | awk '{print substr($3, 1,8)}')
  15. }
  16. function BinSearch
  17. {
  18. low=0
  19. hight= $file_size
  20. location=0
  21. while [[ $low -le $hight ]]
  22. do
  23. mid=$((( $low+ $hight)/2));
  24. location= $mid
  25. tmp_time=`GetLocation`
  26. if [[ $1 = $tmp_time ]]
  27. then
  28. echo $location
  29. break
  30. fi
  31. if [[ $1 < $tmp_time ]]
  32. then
  33. hight= $mid
  34. fi
  35. if [[ $1 > $tmp_time ]]
  36. then
  37. low= $mid
  38. fi
  39. done
  40. }
  41. start_local=`BinSearch $2`
  42. echo $start_local
  43. end_local=`BinSearch $3`
  44. echo $end_local
  45. length=$(( $end_local- $start_local))
  46. echo $length
  47. dd if= $file_name bs=1000 skip=$(( $start_local/1000)) count=$(( $length/1000)) 2>/dev/null



 

原创,转发请注明。 By LZJing

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值