linux服务器日志分析,内入提取过滤再编辑[grep,sed,sort,awk]

495 查看

概述

linux环境下,日常应用中都离不开日志。日志是我们在排查问题的一个重要依据。但是日志并不是写了就好了,当你想查看日志的时候,你会发现线上日志堆积的长度已经超越了你一行行浏览的耐性的极限了。于是,很有必要通过一些手段来高效地辅助你来快速的从日志中找到你要找的问题。

最基础的日志查看命令

tail -100f duweixin.log #监控最后100行日志文件的变化 等与 tail -n 100 -f (-f参数是实时)  
less duweixin.log #查看日志文件,支持上下滚屏,查找功能  
uniq -c duweixin.log  #标记该行重复的数量,不重复值为1  

查找关键字:grep
浏览了日志文件后你会发现,日志文件成千上万行,用grep来进行日志的关键行提取

规则:grep [选项]...模式 [文件]... (模式是正则表达式)

e.g.

grep 'duweixin.net' duweixin.log #在文件duweixin.log中查找所有包行duweixin.net的行

e.g.
-o选项只提取 http://www.duweixin.net/weixi... 的内容(而不是一整行),并输出到屏幕上

grep -o 'http://duweixin.net/ [a−A]+' duweixin.log

e.g.
输出文件duweixin.log中查找所有包行ERROR的行的数量

grep -c 'ERROR' duweixin.log

e.g.
查找不含"ERROR"的行

grep -v 'ERROR' duweixin.log

精简日志内容:sed

从n多行的日志文件中提取到一定数量的行后,可能你还会觉得有些功能不够,比如你每行并不需要有哪个类抛出的描述,比如你不需要日志时间,或者要把时间格式换个形式展示等等,这时候你就可以通过sed的替换命令来进行对日志文件提取具体内容了。

sed简单用法:

-n选项是默认不输出信息,除非使用了p命令或者是s命令的p标志符;-e是表明空格后面接的是一个命令

sed -n '命令' 文件

这个用法是把命令写在脚本里

sed [-n] -f 脚本 文件

»'命令'的格式: [地址1[,地址2]][!] 指令 [参数]
» 地址的格式:用行号标识(1 表明匹配第一行),或者用正则表达式匹配('^INFO'表明该地址匹配以INFO打头的行)
» 指令的例子:p打印指令,s替换指令,d删除指令等等

e.g.
输出duweixin.log中的某个日期中的ERROR的行

sed -n '/^2016-06-21.*ERROR/p' duweixin.log

输出结果:

2016-06-21 19:57:30,709 [] ERROR bo.CommodityCerOrderBO - order-fix.curr_id:10117,status:添加属性id,但由于认证分类参数有误默认取匹配属性名称的第一个属性id:100104
2016-06-21 19:57:31,721 [] ERROR bo.CommodityCerOrderBO - order-fix.curr_id:10117,status:添加属性id,但由于认证分类参数有误默认取匹配属性名称的第一个属性id:100105
2016-06-21 19:57:32,727 [] ERROR bo.CommodityCerOrderBO - order-fix.curr_id:10117,status:添加属性id,但由于认证分类参数有误默认取匹配属性名称的第一个属性id:100107

提取duweixin.log中的日期,日志级别,订单id和状态。

sed -f demo.sed2 demo.log

http://www.reddragonfly.org/a...

对记录进行排序 sort

经过了日志文件的精炼后,我们可能不想对日志进行时间排序,这时候我们就可以用sort进行排序。

基本使用

sort [options] [file...]

对于demo.log,经过了上面的sed提取后,我希望先用id进行排序,然后再用日志级别倒序进行排序,最后才是日期排序

排序功能
-t表示用@作为分割符,
-k表示用分割出来的第几个域排序

sed -f test.sed duweixin.log | sort -t@ -k2,2n -k3,3r -k1,1

n为按数字排序,r为倒序

统计日志相关记录数 awk

现在日志已经比较清晰了,但是如果我想对不同日志进行统计怎么办,比如我要统计所有ERROR的日志记录书,或者要统计每个订单有多少个ERROR?这就需要我们的awk帮忙了。

awk简单使用:

awk [-v 变量名=变量值] [-Fre] [--] '模式 { 语句 }' 变量名=变量值 文件名  
awk [-v 变量名=变量值] [-Fre] -f 脚本文件 [--] 变量名=变量值 文件名  

打印日志中的第2,3列
BEGIN中预处理的是,把@号作为行的列分割符,把分割后的行的第2,3列输出

awk 'BEGIN{FS="@"} {print $2,$3}' duweixin.log_after_sort   

统计日志中INFO,ERROR出现的总数,以及总记录数

下面的例子是作为命令行输入的,利用单引号作为换行标记,这样就不用另外把脚本写进文件调用了

awk '  
BEGIN {  
  FS="@"  
}  
  
{  
  if ($3 == "INFO") {info_count++}  
  if ($3 == "ERROR") {error_count++}  
  
}  
  
END {  
  print "order total count:"NR           #NR是awk内置变量,是遍历的当前行号,到了END区域自然行号就等于总数了  
  printf("INFO count:%d ERROR count:%d\n",info_count,error_count)  
} ' demo.log_after_sort  

输出:

order total count:22
INFO count:5 ERROR count:17

对指定时间范围内的日志进行统计,包括输出INFO,ERROR总数,记录总数,每个订单记录分类统计

下面的例子综合了前面sed和sort

sed -f duweixin.sed duweixinlog.log | sort -t@ -k2,2n -k3,3r -k1,1 | awk -f demo.awk  
#demo.awk  
BEGIN {  
  FS="@"  
  stime="2011-08-23 19:57:31"  
  etime="2011-08-23 19:57:37"  
}  
  
$1 > stime && $1 < etime {  
  if ($3 == "INFO") {info_count++}  
  if ($3 == "ERROR") {error_count++}  
  
  ++total  
  
  status[$2]=status[$2]"\t"$1"\t"$3"\t"$4"\n"  
  
}  
  
END {  
  for(i in status){  
      printf("id:%s:\n%s\n",i,status[i])  
  }  
  
  print "order total count:"total  
  printf("INFO count:%d ERROR count:%d\n",info_count,error_count)  
} <span style="font-size:18px;"><strong>  
</strong></span>  

这个例子只是举例说明awk的统计用法,实际运用中可能会统计超时的次数,页面访问次数等。

awk统计某个开始-结束范围内的关键字累加总数:


BEGIN {  
  running=0  
  count=0  
  startRow="begin =====>" id            #id,nextId是通过-v 参数从外部传入  
  endRow="begin =====>" nextId  
}  
  
$0 ~ startRow{    # ~是匹配运算符,判断$0是否满足startRow正则表达式  
  running = 1  
#  printf("start\n")  
}  
  
$0 ~ endRow {  
  running = 0  
#  printf("end\n")  
}  
  
{  
  if(running==1) {      # 仅在startRow 和 endRow 范围内统计  
    if($0 ~ "it show") {  
#        printf($0 "\n")  
        str=$0  
        sub(/^.*show times:/, "", str)  
        sub(/ .*$/, "", str)  
        printf(str "\n")  
        count = count + str  
    }  
  }  
}  
  
END {  
  printf("showTimeCount:"+count)  
} 

示例

100.109.192.199 - - [29/Jun/2016:03:10:38 +0800] "POST /user/device HTTP/1.0" 200 226 "-" "BetweenTheLines/1.5.2 (iPhone; iOS 9.3.2; Scale/3.00)" "114.111.167.93" "0.010" "0.010"
100.109.192.204 - - [29/Jun/2016:03:10:39 +0800] "POST /user/my_subscribe HTTP/1.0" 200 24494 "-" "BetweenTheLines/1.5.2 (iPhone; iOS 9.3.2; Scale/3.00)" "114.111.167.93" "0.222" "0.222"
100.109.192.197 - - [29/Jun/2016:03:10:41 +0800] "POST /user/personal_user HTTP/1.0" 200 651 "-" "BetweenTheLines/1.5.2 (iPhone; iOS 9.3.2; Scale/3.00)" "114.111.167.93" "0.018" "0.019"
100.109.192.200 - - [29/Jun/2016:03:10:48 +0800] "POST /user/creation_article HTTP/1.0" 200 97 "-" "BetweenTheLines/1.5.2 (iPhone; iOS 9.3.2; Scale/3.00)" "114.111.167.93" "0.136" "0.136"
100.109.192.196 - - [29/Jun/2016:03:10:51 +0800] "POST /user/creation_article HTTP/1.0" 200 97 "-" "BetweenTheLines/1.5.2 (iPhone; iOS 9.3.2; Scale/3.00)" "114.111.167.93" "0.135" "0.136"
100.109.192.202 - - [29/Jun/2016:03:10:53 +0800] "POST /article/delete HTTP/1.0" 200 24 "-" "BetweenTheLines/1.5.2 (iPhone; iOS 9.3.2; Scale/3.00)" 

要求:

获取请求接口和最后两列,并按列排序

cat access.log | grep -F "POST" | awk '{print $18,$19,$7}' > result.txt  #汇总过滤数据
sort -k1 result.txt  #按第一列排序

获取第一列

文档

军事@其它#bd4a9e23-6c5d-480e-9baf-7cb0e9f9d997
天气@其它#bd4a9e23-6c5d-480e-9baf-7cb0e9f9d997
广告@其它#bd4a9e23-6c5d-480e-9baf-7cb0e9f9d997
幽默搞笑@其它#bd4a9e23-6c5d-480e-9baf-7cb0e9f9d997
科学@其它#bd4a9e23-6c5d-480e-9baf-7cb0e9f9d997
星座@其它#bd4a9e23-6c5d-480e-9baf-7cb0e9f9d997
其他@其它#bd4a9e23-6c5d-480e-9baf-7cb0e9f9d997
未分类@其它#bd4a9e23-6c5d-480e-9baf-7cb0e9f9d997

awk -F "@" '{print $1}' classify_map