任务背景:
车联网业务TSP网关打印的日志接入es数据量过大,每小时基本有上亿条日志,快扛不住了。
本着降本增效的原则,需要实现tbox白名单日志打印功能。
框架选择
- logback
- log4j2
日志框架的选择网上已经有很多文章,我是参考了如下几篇文章,加上程序中原有框架也是log4j2,所以想着继续延用并在其基础上优化吧。
纠结哇!日志框架选型,Logback 还是 Log4j2? 转载
Logback与Log4j2日志框架性能对比与调优
Logback 对比 Log4j2
功能设计
- 程序启动时候加载数据库tbox白名单数据到程序内缓存,
- tbox白名单CRUD,数据库使用mysql,
- 每天凌晨定时刷新程序内白名单缓存,
- 对数据库tbox白名单做CRUD的时候,实时更新各分布式节点程序的缓存,
- 自定义日志过滤拦截器,
前面两点没啥好说的。
第三点是通过redis发布订阅实现的。每次对tbox白名单进行crud的时候向redis的channel “tbox_white_info” 发送一条指令数据,{“refresh”:“true”},代表需要刷新缓存了。网关程序订阅这个channel,有收到数据就进行刷新缓存的操作。这样就可以同步更新多个分布式订阅节点的数据了。
自定义日志拦截
接下来就是重点了,如何实现自定义log42的日志拦截器。有如下几个步骤
1、继承AbstractFilter
2、使用工厂模式创建你的Filter对象
3、重写filter方法,实现你的业务逻辑
4、定义注解,可以在log4j2.xml中使用
@Plugin(name = "TboxLogFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
@Slf4j
public class TboxLogFilter extends AbstractFilter {
private Result onMatch;
private Result onMisMatch;
//是否启用白名单日志打印功能
private Boolean enable = false;
public TboxLogFilter(boolean enable, Result onMatch, Result onMismatch) {
super();
this.enable = enable;
if(!enable){
log.info("白名单日志打印功能未启用。");
}else {
log.info("白名单日志打印功能开始启用。");
}
this.onMatch = onMatch;
this.onMisMatch = onMismatch;
}
//业务
@Override
public Result filter(LogEvent event) {
if(!enable){
//未开启功能
return this.onMatch;
}
String log = event.getMessage().toString();
if(!StringUtils.containsIgnoreCase(log,"tbox")
&& !StringUtils.containsIgnoreCase(log,"tboxsn")
&& !StringUtils.containsIgnoreCase(log,"sn")){
//不包含tbox,sn,tboxsn关键字
return this.onMatch;
}
if(GlobalSessionChannel.enableLog(log)){
return this.onMatch;
}
return this.onMisMatch;
}
@PluginFactory
public static TboxLogFilter createFilter(@PluginAttribute("enable") String enable,
@PluginAttribute("onMatch") final Result match,
@PluginAttribute("onMismatch") final Result mismatch) {
assert StringUtils.isNotBlank(enable);
final Result onMatch = match == null ? Result.NEUTRAL : match;
final Result onMismatch = mismatch == null ? Result.DENY : mismatch;
Boolean bo = Boolean.valueOf(enable);
return new TboxLogFilter(bo , onMatch, onMismatch);
}
@Override
public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
return super.filter(logger, level, marker, msg, t);
}
}
我这里对这个tbox白名单日志打印过滤功能留了个总开关,方便全局开启和关闭。
同时在过滤逻辑上做了优化,只有当日志中包含关键字“tbox”、“sn”,“tboxsn”(忽略大小写)的时候才会进入是否过滤的判定环节。
接下来是xml的配置了,有需要的同学可以直接使用。
<?xml version="1.0" encoding="UTF-8"?>
<!--设置log4j2的自身log级别为warn-->
<configuration status="warn">
<Properties>
<!--<property name="LOG_DIR" value="/opt/log/gateway" />-->
<property name="LOG_DIR" value="${sys:user.dir}/logs" />
<property name="PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%p] -%t -%c:%L - %m%n" />
<property name="ROLLING_LOG_SIZE" value="50 MB" />
<property name="ROLLING_LOG_MAX_NUM" value="10" />
<property name="ROLLING_LOG_AGE" value="7D" />
</Properties>
<appenders>
<console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${PATTERN}" charset="UTF-8"/>
</console>
<RollingFile name="RollingFileDebug" fileName="${LOG_DIR}/debug.log"
filePattern="${LOG_DIR}/debug-%d{yyyy-MM-dd}-%i.log">
<Filters>
<ThresholdFilter level="DEBUG"/>
<ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<PatternLayout pattern="${PATTERN}" charset="UTF-8"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="${ROLLING_LOG_SIZE}"/>
</Policies>
<!--归档文件设置-->
<DefaultRolloverStrategy max="${ROLLING_LOG_MAX_NUM}">
<Delete basePath="${LOG_DIR}" maxDepth="1">
<IfFileName glob="debug.*.log"/>
<IfLastModified age="${ROLLING_LOG_AGE}"/>
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
<RollingFile name="RollingFileInfo" fileName="${LOG_DIR}/info.log"
filePattern="${LOG_DIR}/info-%d{yyyy-MM-dd}-%i.log">
<Filters>
<ThresholdFilter level="INFO"/>
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<PatternLayout pattern="${PATTERN}" charset="UTF-8"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="${ROLLING_LOG_SIZE}"/>
</Policies>
<!--归档文件设置-->
<DefaultRolloverStrategy max="${ROLLING_LOG_MAX_NUM}">
<Delete basePath="${LOG_DIR}" maxDepth="1">
<IfFileName glob="info.*.log"/>
<IfLastModified age="${ROLLING_LOG_AGE}"/>
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
<RollingFile name="RollingFileWarn" fileName="${LOG_DIR}/warn.log"
filePattern="${LOG_DIR}/warn-%d{yyyy-MM-dd}-%i.log">
<Filters>
<ThresholdFilter level="WARN"/>
<ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<PatternLayout pattern="${PATTERN}" charset="UTF-8"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="${ROLLING_LOG_SIZE}"/>
</Policies>
<!--归档文件设置-->
<DefaultRolloverStrategy max="${ROLLING_LOG_MAX_NUM}">
<Delete basePath="${LOG_DIR}" maxDepth="1">
<IfFileName glob="warn.*.log"/>
<IfLastModified age="${ROLLING_LOG_AGE}"/>
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
<RollingFile name="RollingFileError" fileName="${LOG_DIR}/error.log"
filePattern="${LOG_DIR}/error-%d{yyyy-MM-dd}-%i.log">
<Filters>
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<PatternLayout pattern="${PATTERN}" charset="UTF-8"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="${ROLLING_LOG_SIZE}"/>
</Policies>
<!--归档文件设置-->
<DefaultRolloverStrategy max="${ROLLING_LOG_MAX_NUM}">
<Delete basePath="${LOG_DIR}" maxDepth="1">
<IfFileName glob="error.*.log"/>
<IfLastModified age="${ROLLING_LOG_AGE}"/>
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
</appenders>
<loggers>
<!--过滤掉spring和hibernate的一些无用的debug信息-->
<logger name="org.springframework" level="INFO" includeLocation="true" additivity="false"/>
<logger name="org.hibernate" level="INFO" includeLocation="true" additivity="false"/>
<logger name="com.netflix" level="WARN" includeLocation="true" additivity="false"/>
<logger name="io.netty" level="INFO" includeLocation="true" additivity="false"/>
<logger name="io.lettuce" level="WARN" includeLocation="true" additivity="false"/>
<logger name="org.apache" level="INFO" includeLocation="true" additivity="false"/>
<logger name="org.apache.kafka.clients.consumer" level="WARN" includeLocation="true" additivity="false"/>
<logger name="org.springframework.kafka.listener" level="WARN" includeLocation="true" additivity="false"/>
<logger name="cn.com.jit.assp.css.client.communication.RequestClient" level="WARN" includeLocation="true" additivity="false"/>
<root level="info" includeLocation="true" additivity="false">
<appender-ref ref="Console"/>
<!-- tbox过滤 -->
<TboxLogFilter enable="true" onMatch="NEUTRAL" onMismatch="DENY"/>
<appender-ref ref="RollingFileDebug"/>
<appender-ref ref="RollingFileInfo"/>
<appender-ref ref="RollingFileWarn"/>
<appender-ref ref="RollingFileError"/>
</root>
</loggers>
</configuration>
关键是下面一行的使用,enable=true的时候功能功能,onMatch的时候放行,onMismatch则直接丢弃这条日志。
<TboxLogFilter enable="true" onMatch="NEUTRAL" onMismatch="DENY"/>