原有日志降级实现方式为使用LogUtil工具类,在工具类中判断ACM(Nacos)状态实现日志降级,缺点很明显,打印日志中的行号是LogUtil方法的行号,而不是原始代码的行号。
@Slf4j
public class LogUtil {
public static void logInfo(String format, Object... arguments){
if(!AcmSwitch.logInfoSwitchON){
return;
}
log.info(format,arguments);
}
public static void logWarn(String format, Object... arguments){
if(!AcmSwitch.logWarnSwitchON){
return;
}
log.warn(format,arguments);
}
public static void logError(String format, Object... arguments){
if(!AcmSwitch.logErrorSwitchON){
return;
}
log.error(format,arguments);
}
}
改造后通过Log4j2的Filter机制实现ACM(Nacos)动态日志降级,打印日志时,直接使用Logger,日志的行号保留原始的代码行号。
Log4j2 Filter实现ACM日志降级
1. 扩展Filter接口(或者AbstractFilter),实现Filter接口的方法
@Plugin(name = "AcmLogFilter", category = "Core", elementType = "filter", printObject = false)
public class AcmLogFilter extends AbstractFilter {
public AcmLogFilter() {
super(null, null);
}
public AcmLogFilter(Result onMatch, Result onMismatch) {
super(onMatch, onMismatch);
}
@Override
public Result filter(final LogEvent event) {
return filterSub(event.getLevel());
}
@Override
public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
final Throwable t) {
return filterSub(level);
}
@Override
public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
final Throwable t) {
return filterSub(level);
}
@Override
public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
final Object... params) {
return filterSub(level);
}
@Override
public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
final Object p0) {
return filterSub(level);
}
@Override
public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
final Object p0, final Object p1) {
return filterSub(level);
}
@Override
public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
final Object p0, final Object p1, final Object p2) {
return filterSub(level);
}
@Override
public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
final Object p0, final Object p1, final Object p2, final Object p3) {
return filterSub(level);
}
@Override
public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
final Object p0, final Object p1, final Object p2, final Object p3,
final Object p4) {
return filterSub(level);
}
@Override
public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
final Object p0, final Object p1, final Object p2, final Object p3,
final Object p4, final Object p5) {
return filterSub(level);
}
@Override
public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
final Object p0, final Object p1, final Object p2, final Object p3,
final Object p4, final Object p5, final Object p6) {
return filterSub(level);
}
@Override
public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
final Object p0, final Object p1, final Object p2, final Object p3,
final Object p4, final Object p5, final Object p6,
final Object p7) {
return filterSub(level);
}
@Override
public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
final Object p0, final Object p1, final Object p2, final Object p3,
final Object p4, final Object p5, final Object p6,
final Object p7, final Object p8) {
return filterSub(level);
}
@Override
public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
final Object p0, final Object p1, final Object p2, final Object p3,
final Object p4, final Object p5, final Object p6,
final Object p7, final Object p8, final Object p9) {
return filterSub(level);
}
private Result filterSub(Level level) {
if (Level.INFO.equals(level)) {
if (!AcmServiceSwitch.logInfoSwitchON) {
return onMismatch;
}
} else if (Level.WARN.equals(level)) {
if (!AcmServiceSwitch.logWarnSwitchON) {
return onMismatch;
}
} else if (Level.ERROR.equals(level)) {
if (!AcmServiceSwitch.logErrorSwitchON) {
return onMismatch;
}
}
return onMatch;
}
@PluginFactory
public static AcmLogFilter createFilter(@PluginAttribute(value = "onMatch", defaultString = "NEUTRAL") Result onMatch,
@PluginAttribute(value = "onMismatch", defaultString = "DENY") Result onMismatch) {
return new AcmLogFilter(onMatch, onMismatch);
}
}
关键代码为filterSub方法中,会调用下面的方法,方法中根据ACM配置决定是否log日志。
private Result filterSub(Level level) {
if (Level.INFO.equals(level)) {
if (!AcmProductServiceSwitch.logInfoSwitchON) {
return onMismatch;
}
} else if (Level.WARN.equals(level)) {
if (!AcmProductServiceSwitch.logWarnSwitchON) {
return onMismatch;
}
} else if (Level.ERROR.equals(level)) {
if (!AcmProductServiceSwitch.logErrorSwitchON) {
return onMismatch;
}
}
return onMatch;
}
2. 配置log4j2.xml
Filter可以配置在多个位置:
- Context-wide(全局)
- Logger
- Appender
- Appender Reference
例如,全局配置可以在 `<Configuration>`元素下增加以下内容,即可对全局的log进行过滤。
<Filters>
<AcmLogFilter></AcmLogFilter>
</Filters>
3. log
log时,不需要使用LogUtil,直接使用slf4j或log4j2的Logger方法即可。