在新线程调用PatternLayout.format()实现日志掩码时遇问题求助
日志掩码逻辑异步执行的问题
我们希望在新创建的Thread中调用PatternLayout类的format(LoggingEvent event)方法,独立执行自定义日志掩码逻辑且不影响原有功能,但遇到了以下错误:
- 编译器要求将LoggingEvent对象设置为final;
- 无法在run()方法中将掩码后的结果存入全局变量maskedOutput,而该变量需要从
format(LoggingEvent event)方法返回。
现有正常工作的代码
public class CardNumberFilteringLayout extends PatternLayout { private static final String MASK = "$1++++++++++++"; private static final Pattern PATTERN = Pattern.compile("([0-9]{4})([0-9]{9,15})"); @Override public String format(LoggingEvent event) { if (event.getMessage() instanceof String) { String message = event.getRenderedMessage(); Matcher matcher = PATTERN.matcher(message); if (matcher.find()) { String maskedMessage = matcher.replaceAll(MASK); @SuppressWarnings({ "ThrowableResultOfMethodCallIgnored" }) Throwable throwable = event.getThrowableInformation() != null ? event.getThrowableInformation().getThrowable() : null; LoggingEvent maskedEvent = new LoggingEvent(event.fqnOfCategoryClass, Logger.getLogger(event.getLoggerName()), event.timeStamp, event.getLevel(), maskedMessage, throwable); return super.format(maskedEvent); } } return super.format(event); } }
尝试修改后出现问题的代码
@Override public String format(LoggingEvent event) { String maskedOutput = null; new Thread(new Runnable() { @Override public void run() { maskedOutput = //Masking Logic(Same As Above) } }).start(); return maskedOutput; }
问题解决思路
1. 解决LoggingEvent需为final的问题
Java中匿名内部类(如这里的Runnable)访问外部方法的局部变量时,变量必须是final或实质不可变(Java 8+可省略final关键字,但变量不能被重新赋值)。只需给event添加final修饰符即可:
@Override public String format(final LoggingEvent event) { // 后续逻辑可正常访问event }
2. 解决无法给maskedOutput赋值的问题
匿名内部类不能直接修改外部方法的局部变量(局部变量存在于栈中,线程执行时可能已被销毁),可以用可变容器类(如AtomicReference)包装变量:
@Override public String format(final LoggingEvent event) { final AtomicReference<String> maskedOutput = new AtomicReference<>(); new Thread(new Runnable() { @Override public void run() { String result = super.format(event); if (event.getMessage() instanceof String) { String message = event.getRenderedMessage(); Matcher matcher = PATTERN.matcher(message); if (matcher.find()) { String maskedMessage = matcher.replaceAll(MASK); Throwable throwable = event.getThrowableInformation() != null ? event.getThrowableInformation().getThrowable() : null; LoggingEvent maskedEvent = new LoggingEvent(event.fqnOfCategoryClass, Logger.getLogger(event.getLoggerName()), event.timeStamp, event.getLevel(), maskedMessage, throwable); result = super.format(maskedEvent); } } maskedOutput.set(result); } }).start(); return maskedOutput.get(); }
关键注意:异步执行的致命缺陷
上述代码解决了编译错误,但实际运行时会返回null或未完成赋值的结果——因为format方法会在新线程执行完之前就返回。日志框架的format方法是同步调用的,必须立即返回格式化后的日志内容,异步执行掩码逻辑完全不符合该方法的设计意图。
如果想不影响原有日志流程,同时执行掩码逻辑(如单独记录掩码后的日志到其他目标),应改为“额外异步执行”的方式:
@Override public String format(final LoggingEvent event) { // 先执行原有同步逻辑,返回正常格式化结果,保证原有功能不受影响 String originalResult = super.format(event); // 异步执行掩码逻辑,做额外处理(比如单独记录到其他日志系统) new Thread(new Runnable() { @Override public void run() { if (event.getMessage() instanceof String) { String message = event.getRenderedMessage(); Matcher matcher = PATTERN.matcher(message); if (matcher.find()) { String maskedMessage = matcher.replaceAll(MASK); Throwable throwable = event.getThrowableInformation() != null ? event.getThrowableInformation().getThrowable() : null; LoggingEvent maskedEvent = new LoggingEvent(event.fqnOfCategoryClass, Logger.getLogger(event.getLoggerName()), event.timeStamp, event.getLevel(), maskedMessage, throwable); String maskedResult = super.format(maskedEvent); // 这里可将maskedResult发送到其他日志系统、存储等 // 示例:Logger.getLogger("MASKED_LOG").info(maskedResult); } } } }).start(); return originalResult; }
内容的提问来源于stack exchange,提问作者Sujit Kumar Mishra
相关产品推荐
相关产品推荐

