利用日志过滤器找到“消失的”异常
西瓜猿 12/11/2023 工作工作经验
利用日志过滤器找到“消失的”异常
# 一、需求背景
由于异常被捕获并处理,它的产生不会影响到接口的请求调用,使得外部调用者无法察觉到异常的存在。然而这种消失的异常可能会导致一些数据无法正常展示,从而导致问题难以被发现。 某些时候我们需要知道这种异常。
# 二、 “消失的”异常
所谓“消失的”异常是指在系统中隐藏且不容易被发现的异常。这种情况可能发生在接口出口有捕获异常的情况下,而内部的具体处理器具备充分的容错能力。由于异常被捕获并处理,它不会影响到接口的请求调用,使得外部调用者无法察觉到异常的存在。然而,这种消失的异常可能会导致一些数据无法正常展示,从而难以被发现。因为异常在内部部分逻辑中发生,但由于被捕获,没有被传播到外部。这可能导致系统在外部看起来正常运行,但实际上某些数据的处理或展示可能存在问题。 以下提供一种找到“消失的”异常的方案。
根据日志过滤器中的ACCEPT、DENY 和 NEUTRAL 事件
Filter.ACCEPT:表示接受该日志事件,允许它继续传递给下一个过滤器或者输出目标进行进一步处理。
Filter.DENY:表示拒绝该日志事件,停止它的传递,不再进行后续的处理和输出。
Filter.NEUTRAL:表示中立的过滤结果,不对该日志事件进行任何处理,将事件传递给下一个过滤器或者输出目标。
# 三、实现过程
# 3.1 在Log4j中配置自定义的过滤器
创建一个名为MyLoggingEventFilter的类,它继承了Filter接口,并实现了其中的decide()方法。在decide()方法中,你可以根据自己的需求编写自定义的过滤逻辑,并根据日志事件来决定是否接受、拒绝或中立处理该事件。这个自定义的过滤器,需要将它配置到 Log4j 配置文件中。
import org.apache.log4j.spi.Filter;
import org.apache.log4j.spi.LoggingEvent;
public class MyLoggingEventFilter extends Filter {
@Override
public int decide(LoggingEvent event) {
// 根据需要编写自定义的过滤逻辑
// 返回 Filter.ACCEPT、Filter.DENY 或 Filter.NEUTRAL
// 根据返回的结果决定是否接受、拒绝或中立处理该日志事件
return Filter.ACCEPT;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 3.2 日志配置文件
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- 其他配置 -->
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<!-- 其他 appender 配置 -->
<filter class="com.example.MyLoggingEventFilter" />
</appender>
<!-- 其他 appender 和 logger 配置 -->
</log4j:configuration>
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 四、项目中的实际应用
可以针对非业务异常,自定义告警,同时输出到指定文件。
# 4.1 项目中配置自定义的过滤器
import org.apache.log4j.spi.Filter;
import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ThrowableInformation;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.List;
public class ExceptionLogFilter extends Filter {
/**
* 异常前缀
*/
private String exceptionPrefix;
@Override
public int decide(LoggingEvent event) {
ThrowableInformation throwableInformation = event.getThrowableInformation();
if (throwableInformation == null) {
return Filter.DENY;
}
Throwable throwable = throwableInformation.getThrowable();
if (throwable == null) {
return Filter.DENY;
}
String className = throwable.getClass().getName();
String message = throwable.getMessage();
// 截取message信息
if (!StringUtils.isEmpty(message)) {
int end = Math.min(message.length(), 150);
message = message.substring(0, end) + "...";
}
List<String> exceptionPrefixList = Arrays.asList(exceptionPrefix.split(","));
boolean match = false;
for (String exceptionPrefix : exceptionPrefixList) {
if (className.startsWith(exceptionPrefix)) {
match = true;
break;
}
}
if (match) {
// 自定义告警
if (exceptionPrefixList.contains("java")) {
LocationInfo locationInformation = event.getLocationInformation();
StringBuilder sb = new StringBuilder();
String className1 = locationInformation.getClassName();
String methodName = locationInformation.getMethodName();
String lineNumber = locationInformation.getLineNumber();
sb.append("【非业务异常】")
.append("环境:").append(System.getProperty("profile.active")).append(",")
.append("类名:").append(className1).append(",")
.append("方法:").append(methodName).append(",")
.append("行号:").append(lineNumber).append(",")
.append("异常:").append(className).append(",")
.append("信息:").append(message);
//得到字符串进行其他处理
sb.toString();
}
return Filter.ACCEPT;
}
return Filter.DENY;
}
public String getExceptionPrefix() {
return exceptionPrefix;
}
public void setExceptionPrefix(String exceptionPrefix) {
this.exceptionPrefix = exceptionPrefix;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# 4.2 项目日志配置文件
<!-- 运行时异常日志 -->
<appender name="RUNTIME-EX" class="org.apache.log4j.RollingFileAppender">
<param name="file" value="${log.path}/runtimeEx.log"/>
<param name="Append" value="true" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} -%5p ${PID:- } [%X{traceId}][%X{pin}] --- [%X{PFTID}][%t] %-40.40logger{39}[%L] : %m%n"/>
</layout>
<filter class="filter.ExceptionLogFilter">
<param name="exceptionPrefix" value="java,org.apache.http.client,com.fasterxml.jackson,com.alibaba.fastjson"/>
</filter>
</appender>
<!-- 业务异常日志 -->
<appender name="BIZ-EX" class="org.apache.log4j.RollingFileAppender">
<param name="file" value="${log.path}/bizEx.log"/>
<param name="Append" value="true" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} -%5p ${PID:- } [%X{traceId}][%X{pin}] --- [%X{PFTID}][%t] %-40.40logger{39}[%L] : %m%n"/>
</layout>
<filter class="filter.ExceptionLogFilter">
<param name="exceptionPrefix" value="exception"/>
</filter>
</appender>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22