基于日志服务数据加工分析Java异常日志

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 采集并脱敏了整个5月份的项目异常日志,准备使用日志服务数据加工做数据清洗以及分析。本案例是基于使用阿里云相关产品(OSS,RDS,SLS等)的SDK展开自身业务。需要对异常日志做解析,将原始日志中时间、错误码、错误信息、状态码、产品信息、请求方法、出错行号提取出来。然后根据提取出来的不同产品信息做多目标分发处理。对清洗后的数据做异常日志数据分析。

1. 本文要点(regex_findall()、e_regex()、e_output()专题):

  • 正则解析复杂异常日志(一次性解析不出全部格式异常日志怎么办?)
  • 根据不同产品的Exception做数据清洗以及多目标分发
  • 对不同的产品的异常日志做数据分析

2. 背景

业务背景:
采集并脱敏了整个5月份的项目异常日志,准备使用日志服务数据加工做数据清洗以及分析。本案例是基于使用阿里云相关产品(OSS,RDS,SLS等)的SDK展开自身业务。
业务需求:
需要对异常日志做解析,将原始日志中时间、错误码、错误信息、状态码、产品信息、请求方法、出错行号提取出来。然后根据提取出来的不同产品信息做多目标分发处理。对清洗后的数据做异常日志数据分析。

3. 案例总体流程

本案例中的所有异常日志全部是通过使用日志服务SDK上传到cloud_product_error_log的logstore中,然后再经过数据加工清洗数据后投递到各自产品的logstore中,最后针对各个产品的异常日志做分析。具体流程如下:
image.png

4. 原始日志样例

image.png

原始日志中只有错误日志信息message,需要将其message中的一些重要信息解析出来。

5. 加工需求

(1) 将message错误信息中的时间、错误码、状态码、产品信息、错误码信息、请求方法、出错行号提取出来
(2) 根据提取出的产品信息对清洗后的数据进行多目标分发

具体需求流程如下图所示:
image.png

6. 对异常日志数据进行解析和投递

6.1 加工逻辑分析

下图展示的是如何从原始异常日志经过数据加工得到解析后的日志信息(原始日志形式,预期日志形式),然后投递到不同logstore中,最后对不同产品的异常日志做数据分析。本案例中只对SLS异常日志和OSS异常日志做加工分析。
image.png

6.2 加工操作

经过以上对异常日志的分析以及加工流程的剖析,此时我们可以对异常日志加工进行实操(对于不熟悉如何使用日志服务数据加工的同学可以参考基于日志服务(SLS)实现电商数据加工与分析)。以下是具体的加工语法:

e_switch(
    regex_match(v("message"), r"LogException"),e_compose(e_regex("message", "(?P<data_time>\S+\s\S+)\s(?P<error_code>[a-zA-Z]+)\s(?P<status>[0-9]+)\scom\.aliyun\.openservices\.log\.exception\.(?P<product_exception>[a-zA-Z]+)\:(?P<error_message>[a-zA-Z0-9:,\-\s]+)\.(\s+\S+\s\S+){5}\s+\S+\scom\.aliyun\.openservices\.log\.Client\.(?P<method>[a-zA-Z]+)\S+\s+\S+\stransformEvent\.main\(transformEvent\.java\:(?P<error_line>[0-9]+)\)"),e_drop_fields("message"),e_output("sls-error")),
    regex_match(v("message"), r"OSSException"),e_compose(e_regex("message", "(?P<data_time>\S+\s\S+)\scom\.aliyun\.oss\.(?P<product_exception>[a-zA-Z]+)\:(?P<error_message>[a-zA-Z0-9,\s]+)\.\n\[ErrorCode\]\:\s(?P<error_code>[a-zA-Z]+)\n\[RequestId\]\:\s(?P<request_id>[a-zA-Z0-9]+)\n\[HostId\]\:\s(?P<host_id>[a-zA-Z-.]+)\n\S+\n\S+(\s\S+){3}\n\s+\S+\s+(.+)(\s+\S+){24}\scom\.aliyun\.oss\.OSSClient\.(?P<method>[a-zA-Z]+)\S+\s+\S+\stransformEvent\.main\(transformEvent\.java:(?P<error_line>[0-9]+)\)"),e_drop_fields("message"),e_output("oss-error"))
)

点击预览效果如下所示:
image.png

上图表面加工语法能够正常解析异常日志,接下来需要进行保存加工任务操作。具体加工配置如下图:
image.png

注意:
以上语法中的e_output中的name参数需要与加工配置规则中的目标名称一一对应。更多用法请参考e_output语法注意事项

6.3 语法详解

其中涉及到的正则语法分组相关知识请移步正则表达式-分组
(1) 首先使用regex_match函数匹配出此条日志中是否有LogException如果匹配上则走解析sls异常日志的规则分支,如果匹配上OSSException则走解析OSS异常日志的规则分支。
(2) 其次使用e_regex正则解析函数解析相应的异常日志。
(3) 最后删除原字段message信息,并投递到相应产品的logstore中。

其中本案例中正则解析日志详解是以解析SLS异常日志为例,具体如下
image.png

7. 数据加工诊断

当我们在解析日志的时候,时常会遇到一次性解析不出全部格式异常日志的情况。并且在加工预览页面只会预览一定量的数据,因此在加工预览页面不一定会提示出该正则解析不出的日志的情况。
综上,我们可以通过查看数据加工概览(具体如何查看请参考加工概览)的方式查到具体是那一条日志没有解析出来,然后再调整我们的正则表达式。解析失败样例,如下图所示:
image.png

具体异常详情的logging如下:

{
    "message": "transform_regex: field value \"2020-05-04 08:45:07 ServerBusy 505 com.aliyun.openservices.log.exception.LogException:The server is busy, please try again later.\n    at com.aliyun.openservices.log.Client.ErrorCheck(Client.java:2161)\n    at com.aliyun.openservices.log.Client.SendData(Client.java:2312)\n    at com.aliyun.openservices.log.Client.CreateConsumerGroup(Client.java:2190)\n    at com.aliyun.openservices.log.Client.SendData(Client.java:2265)\n    at com.aliyun.openservices.log.Client.GetCursor(Client.java:1123)\n    at com.aliyun.openservices.log.Client.CreateConsumerGroup(Client.java:1100)\n    at transformEvent.main(transformEvent.java:1950)\" cannot extract value with config \"(?P<data_time>\\S+\\s\\S+)\\s(?P<error_code>[a-zA-Z]+)\\s(?P<status>[0-9]+)\\scom\\.aliyun\\.openservices\\.log\\.exception\\.(?P<__topic__>[a-zA-Z]+)\\:(?P<error_message>[a-zA-Z0-9\\s:-]+)\\.(\\s+\\S+\\s\\S+){5}\\s+\\S+\\scom\\.aliyun\\.openservices\\.log\\.Client\\.(?P<method>[a-zA-Z]+)\\S+\\s+\\S+\\stransformEvent\\.main\\(transformEvent\\.java\\:(?P<error_line>[0-9]+)\\)\"",
    "processName": "MainProcess",
    "process": "1",
    "thread": "139873923098368",
    "levelname": "WARNING",
    "module": "regex_impl",
    "threadName": "ThreadPoolExecutor-0_1",
    "funcName": "__call__"
}

从以上图例和具体信息可以看到,如果正则解析失败会报WARNING级别日志。此时的加工处理规则是跳过加工失败的日志继续往下加工,此过程中并不会影响到整个加工任务的执行(只有ERROR级别的会影响到整个加工任务的消费)。
接下来主要关注message中的日志信息,看具体是那条日志或者那一类异常日志导致现有的正则规则加工失败。
其message中的解析失败的日志和正则(logging中的正则表达式需要自己去掉多加的转义反斜杠)。

"""解析失败日志"""
2020-05-04 08:45:07 ServerBusy 505 com.aliyun.openservices.log.exception.LogException:The server is busy, please try again later.\n    at com.aliyun.openservices.log.Client.ErrorCheck(Client.java:2161)\n    at com.aliyun.openservices.log.Client.SendData(Client.java:2312)\n    at com.aliyun.openservices.log.Client.CreateConsumerGroup(Client.java:2190)\n    at com.aliyun.openservices.log.Client.SendData(Client.java:2265)\n    at com.aliyun.openservices.log.Client.GetCursor(Client.java:1123)\n    at com.aliyun.openservices.log.Client.CreateConsumerGroup(Client.java:1100)\n    at transformEvent.main(transformEvent.java:1950)
"""正则表达式"""
(?P<data_time>\S+\s\S+)\s(?P<error_code>[a-zA-Z]+)\s(?P<status>[0-9]+)\scom\.aliyun\.openservices\.log\.exception\.(?P<product_exception>[a-zA-Z]+)\:(?P<error_message>[a-zA-Z0-9\s:-]+)\.(\s+\S+\s\S+){5}\s+\S+\scom\.aliyun\.openservices\.log\.Client\.(?P<method>[a-zA-Z]+)\S+\s+\S+\stransformEvent\.main\(transformEvent\.java\:(?P<error_line>[0-9]+)\)

经过一步一步的查找验证,得出在解析error_message中缺少了对有逗号情景的解析,因此此地方正则需要改善,具体改善如下:

"""解析失败error_message正则"""
(?P<error_message>[a-zA-Z0-9\s:-]+)
"""完善后解析error_message正则"""
(?P<error_message>[a-zA-Z0-9:,\-\s]+)

综上,经过我们不断的完善正则表达式,目前是能够成功解析各个不同类型的异常日志。接下来我们将进入数据分析阶段。

8. 异常日志数据分析

下图是通过sql查询所展示数据仪表大盘:
image.png

基于加工后的异常日志(以SLS错误日志为例分析),在这里我们的需求有以下几方面:
(1) 各个方法异常统计
(2) 各个方法异常占比统计图
(3) PutLogs错误信息
(4) 各个方法调用报错每日level气泡图
(5) 各个ErrorCode统计分析
(6) 各个方法报错时间轴

8.1 各个方法异常统计和各个方法异常占比统计图

为了方便查看那个方法出现错误次数最高和帮助我们定位业务调用的具体方法,因此我们先对各个方法出现的异常情况做一个统计。

* | SELECT COUNT(method) as m_ct, method GROUP BY method

这条语句表示的是统计每个调用方法出现异常的数量。
下图表示的是在查询框里输入以上sql分析语句之后进入统计图表,然后点击条形图进行配置,method为X轴,m_ct为Y轴。
image.png

通过上图分析可知总体情况,PutLogs调用出现异常的次数最多,其中GetCursor和DeleteConsumerGroup是出现异常的次数是最少的。
此外,为了方便我们查看各个方法异常占比情况,我们可以基于以上的SQL做一个占比统计图,点击漏斗图,然后以method为分组列,m_ct为数值列具体操作如下。
image.png

8.2 PutLogs错误信息

为了方便我们查看哪一类的错误信息是占比最大,帮助我们分析:
(1) 需要自身业务需要改进的
(2) 需要SLS帮忙调整的(如调整quota)
(3) 需要深入项目中具体排查错误的(具体的error line)

* | SELECT error_message,COUNT(error_message) as ct_msg, method WHERE method LIKE 'PutLogs' GROUP BY error_message,method

点击矩形树图,然后以error_message为分类和ct_msg为数值列。
image.png

从上图中我们可知:
(1) consumer group already exist、consumer group not exist、Logs must be less than or equal to 3 MB and 4096 entries等这些都是属于需要在项目中具体排查的错误
(2) Read/ Write quota is exceeded这些是需要SLS调整quota的
(3) 还有一些是需要调整自身业务的比如logstore/ project not exist看是否是因为一些业务原因而导致这些project被取消等等。

8.3 各个方法调用报错每日level气泡图

为了方面我们查看每一天的各个调用方法出错频次,我们需要有一个直观的图来查看每一天的调用出错程度,这样我们可以更加直观的去看某一天的具体调用出错细节。

* | select date_format(data_time, '%Y-%m-%d') as day,COUNT(method) as count_method, case 
when method = 'PullLogs' and COUNT(method) > 21800 then 3 when method = 'PullLogs' and COUNT(method)>21400 then 2 when method = 'PullLogs' and COUNT(method)>21100 then 1 
when method = 'CreateConsumerGroup' and COUNT(method) > 21900 then 3
when method = 'CreateConsumerGroup' and COUNT(method) > 21700 then 2 when method = 'CreateConsumerGroup' and COUNT(method) > 21550 then 1 
when method = 'PutLogs' and COUNT(method)>43900 then 3 when method = 'PutLogs' and COUNT(method)>43300 then 2 when method = 'PutLogs' and COUNT(method)>42900 then 1 
when method = 'DeleteConsumerGroup' and COUNT(method)>7440 then 3 when method = 'DeleteConsumerGroup' and COUNT(method)>7330 then 2 when method = 'DeleteConsumerGroup' and COUNT(method)>7320 then 1 
when method = 'GetCursor' and COUNT(method)>7350 then 3 when method = 'GetCursor' and COUNT(method)>7200 then 2 when method = 'GetCursor' and COUNT(method)>7150 then 1
else 0 end as level,  method group by day, method ORDER BY day asc

其中如果一天PullLogs出错次数大于21800则属于level3级别的,大于21400则属于level2级别,大于21100则属于level1,否则属于level0,然后再跟据每一天每个方法做聚合操作。
点击气泡图,其中以day为X轴,method为Y轴。

从上图中可以明显的知道每一天的每个方法的出现异常的level值,针对某一天level值高的可以对其做具体的错误查看到底是什么原因引起的。

8.4 各个ErrorCode统计分析

统计各个error_code出现的错误次数,能够更好的帮助我们查看哪些是因为ServerBusy,哪些是因为ParameterInvalid等不同的原因引起的异常,从而更好的定位异常原因。

* | SELECT error_code,COUNT(error_code) as count_code GROUP BY error_code

点击折线图,以error_code为X轴,以count_code为Y轴
image.png

基于上图可知WriteQuotaExceed出现异常次数最多,可以查查那个project下的logstore被限制,然后咨询相关的产品技术支持。

8.5 各个方法报错时间轴

设置报错时间轴,可以实时的查看调用接口异常信息。

* | SELECT date_format(data_time, '%Y-%m-%d %H:%m:%s') as date_time,status,product_exception,error_line, error_message,method ORDER BY date_time desc

image.png

基于上图所示,我们可以对重要的接口出现异常做标注高亮显示,以便我们能够快速识别出重要接口异常情况。

9. 总结

对于其他的产品的异常日志,我们也可以使用同样的方法加工清洗投递,然后SQL分析自己所关心的业务指标。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
3天前
|
存储 Oracle 关系型数据库
【赵渝强老师】MySQL InnoDB的数据文件与重做日志文件
本文介绍了MySQL InnoDB存储引擎中的数据文件和重做日志文件。数据文件包括`.ibd`和`ibdata`文件,用于存放InnoDB数据和索引。重做日志文件(redo log)确保数据的可靠性和事务的持久性,其大小和路径可由相关参数配置。文章还提供了视频讲解和示例代码。
70 10
【赵渝强老师】MySQL InnoDB的数据文件与重做日志文件
|
3天前
|
SQL Oracle 关系型数据库
【赵渝强老师】Oracle的联机重做日志文件与数据写入过程
在Oracle数据库中,联机重做日志文件记录了数据库的变化,用于实例恢复。每个数据库有多组联机重做日志,每组建议至少有两个成员。通过SQL语句可查看日志文件信息。视频讲解和示意图进一步解释了这一过程。
|
Java C语言 C++
Java 的数据类型划分(数据类型划分)| 学习笔记
快速学习 Java 的数据类型划分(数据类型划分)
119 0
Java 的数据类型划分(数据类型划分)| 学习笔记
|
Java 开发者 Windows
Java 数据类型划分(字符型)|学习笔记
快速学习 Java 数据类型划分(字符型)
126 0
|
Java 开发者
Java 数据类型划分(整型类型)|学习笔记
快速学习 Java 数据类型划分(整型类型)
|
Java 开发者
Java 数据类型划分(初见 String 类)|学习笔记
快速学习 Java 数据类型划分(初见 String 类)
|
Java 开发者
Java 数据类型划分(布尔型)|学习笔记
快速学习 Java 数据类型划分(布尔型)
102 0
|
4天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
13天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
|
3天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####

相关产品

  • 日志服务