LogBack自定义过滤器实现
问题场景:
在做一个 WebService 项目的开发时,需要对调用服务端的参数进行验证,于是采用了验证框架,在调用参数不满足验证条件时就会产生异常。考虑到生产环境的情况,一定要把这种异常记录到日志中,便于发生问题时便于查找问题。于是开始干吧,加参数验证框架,加LogBack日志框架,等到都加好了做测试时,问题来了。
当调用服务端的参数不符合验证条件时,验证框架确实给出了异常提示,但同时也输出了很多WebService的信息,最要命的是这两种信息来自同一个类,都是DEBUG级别的。假如把这些信息都记到日志中去,将来查找问题估计会比较麻烦,需要人工过滤很多无用的信息。
按照常规的方法,用输出信息的包或类,和信息级别去筛选也无法将这两种信息分开。郁闷了。于是就想能不能靠要记录的日志信息的特征去过滤日志哪,比如要记录的日志是这样的:
[17:49:41.245][DEBUG][o. apache .cxf.phase.PhaseInterceptorChain][http-nio-8889-exec-1] Application {wsdl.bbzdmws.com}userService#{wsdl.bbzdmws.com}getUserName has thrown exception, unwinding now
org.apache.cxf.interceptor.Fault: getUserName.arg0.age: 年龄需要在0和100之间
at org.apache.cxf.service.invoker.AbstractInvoker.createFault(AbstractInvoker.java:162)
at org.apache.cxf.jaxws.AbstractJAXWSMethodInvoker.createFault(AbstractJAXWSMethodInvoker.java:267)
可以看到,日志是以“Application”开头的一串字符串,就认为其它不是以“Application”开头的都不用记录吧。在网上查找了资料后发现,自定义的过滤器可以实现这个想法,这里记录下实现过程。
实现步骤
- 写一个过滤器的实现类
- 在LogBack的配置文件中配置上这个类
过滤器的实现类
package com.bbzd.mws.logfilter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;
/**
* 自定义过滤器
* @date 2022/10/11 - 17:23
*/
public class InputCheckFilter extends Filter<ILoggingEvent> {
@Override
public FilterReply decide(ILoggingEvent iLoggingEvent) {
//在日志消息中包含"Application"字符串时,会记录消息,否则不记录
if(iLoggingEvent.getMessage().contains("Application")){
return FilterReply.ACCEPT;
}else{
return FilterReply.DENY;
}
}
}
LogBack的配置文件片断,其它按LogBack的常规配置就好,%ex{1}是去掉完整的异常调用栈信息,缩短记录的行数
<appender name="checkError" class="ch.qos.logback.core.rolling.Rolling File Appender">
<File>${LOG_HOME}checkError.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}checkError.log.%d{yyyy-MM-dd}.gz</fileNamePattern>
<maxHistory>10</maxHistory>
</rollingPolicy>
<encoder>
<pattern>[%d{HH:mm:ss.SSS}][%p][%c{40}][%t] %m%n %ex{1}</pattern>
</encoder>
<!-- 过滤器配置为自定义的过滤器 -->
<filter class="com.bbzd.mws.logfilter.InputCheckFilter" />
</appender>
实现效果:
[17:49:41.245][DEBUG][o.apache.cxf.phase.PhaseInterceptorChain][http-nio-8889-exec-1] Application {wsdl.bbzdmws.com}userService#{wsdl.bbzdmws.com}getUserName has thrown exception, unwinding now
org.apache.cxf.interceptor.Fault: getUserName.arg0.age: 年龄需要在0和100之间
at org.apache.cxf.service.invoker.AbstractInvoker.createFault(AbstractInvoker.java:162)
at org.apache.cxf.jaxws.AbstractJAXWSMethodInvoker.createFault(AbstractJAXWSMethodInvoker.java:267)
总结
- WebService框架居然把校验参数异常信息定义为DEBUG级别,我也是无语了
- 信息要做判断会影响效率,但这里是把判断的情况限定在由org.apache.cxf.phase包产生的前提下,应该影响不大,使用这种方式更多的是要看具体环境
附录:完整的LogBack的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<springProperty scope="context" name="LOG_HOME" source="logging.file.path"/>
<!-- 彩色日志配置 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){green} %clr([${PID:-}]){magenta} %clr([%25.25t]){yellow} %clr([${LOG_LEVEL_PATTERN:-%5p ]}){red} %clr(%-40.40logger{39}){blue} %clr(:){cyan} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
<!-- [%d{HH:mm:ss.SSS}][%p][%c{40}][%t] %m%n -->
${CONSOLE_LOG_PATTERN}
</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
</appender>
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${LOG_HOME}lmes.error.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}lmes.error.log.%d{yyyy-MM-dd}.gz</fileNamePattern>
<maxHistory>10</maxHistory>
</rollingPolicy>
<encoder>
<pattern>[%d{HH:mm:ss.SSS}][%p][%c{40}][%t] %m%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="checkError" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${LOG_HOME}checkError.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}checkError.log.%d{yyyy-MM-dd}.gz</fileNamePattern>
<maxHistory>10</maxHistory>
</rollingPolicy>
<encoder>
<pattern>[%d{HH:mm:ss.SSS}][%p][%c{40}][%t] %m%n %ex{1}</pattern>
</encoder>
<!-- 过滤器配置为自定义的过滤器 -->
<filter class="com.bbzd.mws.logfilter.InputCheckFilter" />
</appender>
<logger name="org.springframework">
<level value="INFO"></level>
</logger>
<logger name="org.apache.http">
<level value="INFO"></level>
</logger>
<logger name="org.hibernate.validator.internal.engine">
<level value="ERROR"></level>
</logger>
<logger name="org.apache.cxf.jaxb">
<level value="INFO"></level>
</logger>
<logger name="org.hibernate.validator.internal.xml">
<level value="INFO"></level>
</logger>
<logger name="org.apache.cxf.configuration.spring">
<level value="INFO"></level>
</logger>
<logger name="com.bbzd.mws">
<level value="ERROR"></level>
</logger>
<logger name="org.apache.cxf.phase" level="DEBUG" additivity="false">
<appender-ref ref="checkError"/>
<appender-ref ref="console"/>
</logger>
< root level="DEBUG">
<appender-ref ref="console"/>
<appender-ref ref="error"/>
</root>
</configuration>