目标

通过分析 error.log 日志,统计出有哪些异常及其数量

分析

首先要看下异常日志的格式

2017-07-19 01:03:41,399 ERROR [qtp738355611-33898] [com.xxxxxx.apkfilemanage.web.AppInfoController] - appUpdate:java.lang.NumberFormatException: !hex 215at org.eclipse.jetty.util.TypeUtil.convertHexDigit(TypeUtil.java:375)at org.eclipse.jetty.util.UrlEncoded.decodeUtf8To(UrlEncoded.java:545)at org.eclipse.jetty.util.UrlEncoded.decodeTo(UrlEncoded.java:601)at org.eclipse.jetty.server.Request.extractParameters(Request.java:298)at org.eclipse.jetty.server.Request.getParameter(Request.java:708)at xxx.apkfilemanage.common.util.BuildBeanUtil.buildAppUpdate(BuildBeanUtil.java:905)at xxxx.apkfilemanage.web.AppInfoController.appUpdate(AppInfoController.java:204)at sun.reflect.GeneratedMethodAccessor132.invoke(Unknown Source)

经过分析,发现每次打印异常的格式如下

  • 第一行
    时间,日志级别,线程信息,类,异常消息(业务)
  • 第二行及后续
    异常堆栈信息,其中第二行是异常的类名及异常消息

那么分析了以上的日志后,我们就可以找到以下的解决思路了。

  • 找到有第三个域为 ERROR 的那一行
  • 该行的下一行就是我们需要统计分析的那一行

命令

zcat xxxxx.gz | awk -v line=0 '{if (line==1) print($0); if ($3=="ERROR") {line=1;} else {line=0;}}' | sort | uniq -c | sort -rn

命令执行结果如下:

 54 redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool 54 org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: 28 org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: org.eclipse.jetty.io.EofException; nested exception is com.google.gson.JsonIOException: org.eclipse.jetty.io.EofException 12 org.springframework.dao.DeadlockLoserDataAccessException:8 org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.7 java.lang.IllegalArgumentException: fromIndex(150) > toIndex(127)6 redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out4 org.springframework.dao.DataIntegrityViolationException:4 java.lang.IllegalStateException: Optional long parameter 'timestamp' is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type.3 org.eclipse.jetty.io.EofException3 java.lang.NullPointerException3 java.lang.IllegalArgumentException: fromIndex(150) > toIndex(131)3 com.alibaba.fastjson.JSONException: syntax error, pos 7201 org.springframework.dao.CannotAcquireLockException:1 java.lang.NumberFormatException: !hex 711 java.lang.NumberFormatException: !hex 341 java.lang.NumberFormatException: !hex 2151 java.lang.NumberFormatException: !hex 151 com.alibaba.fastjson.JSONException: unclosed string : �1 com.alibaba.fastjson.JSONException: unclosed string : ̂1 com.alibaba.fastjson.JSONException: syntax error, unexpect token error1 com.alibaba.fastjson.JSONException: not match ':' - ,1 com.alibaba.fastjson.JSONException: error parse false

zcat

和 cat 命令类似,不过作用的对象是压缩文件.zcat命令用于不真正解压缩文件,就能显示压缩包中文件的内容的场合。zcat是一个命令行实用程序,用于查看压缩文件的内容,而无需对其进行解压缩。 它将压缩文件扩展为标准输出,使您可以查看其内容。 另外,zcat与运行gunzip -c命令完全相同。

|

这是 linux 管道符,他的作用是把管道符左边命令的输出当作管道符右边命令的输入. demo如下:

ps aux | grep 'error'

awk

这个命令很复杂,这里解释一下前面统计异常数量的命令. awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。

awk -v line=0 '{if (line==1) print($0); if($3=="ERROR") {line=1;} else {line=0;}}'

我们知道,awk 命令会对输入的文本逐行进行处理,类似以下的处理过程.

while 当前行不为空处理 当前行移动到 下一行

那么上面的命令就可以这样理解:

# 指示当前行是否包含异常信息 line = 0while 当前行不为空if line == 1 输出当前行if $3 == 'ERROR'line = 1elseline = 0

这段代码的作用就是找到含有 ERROR 的行,并将其下一行打印输出

sort

以行为单位对文本进行排序

uniq

删除重复的行,参数
-c 在输出行前面加上每行在输入文件中出现的次数。
-d 仅显示重复行。
-u 仅显示不重复的行

一些常用的 awk 命令

统计各个接口的 pv .分析 nginx 的日志文件

awk '{if($14=="/proxy") {print $15} else {print $14}}' xxx_access.log|sort|uniq -c|sort -rn|head -n 20

统计慢接口,响应时间超过 1 秒的接口数

awk '{if($9>1)print $0}'|wc -l 

如果想要输出具体是哪些接口,可以结合上面命令

awk '{if($9>1)print $0}'|awk '{if($14=="/proxy") {print $15} else {print $14}}' |sort|uniq -c|sort -rn|head -n 20

如果要分时段统计

awk '{if($9>1)print $1}'|awk -F ':' '{print $1}'|uniq -c