如何通过自定义工具类将指定类日志写入common.log(不修改log4j配置)
实现方案
1. 澄清配置类型
首先需要明确:你提供的配置是Logback(基于ch.qos.logback.core相关类)而非Log4j,后续实现基于Logback框架完成。
2. 实现线程安全的CustomUtility类
该类负责写入common.log,通过同步机制保证多线程环境下日志不混乱,同时保持与app.log一致的日志格式:
import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; public class CustomUtility { // 从系统变量读取common.log路径,与原有配置保持统一 private static final String COMMON_LOG_PATH = System.getProperty("home") + "/logs/common.log"; // 同步锁,避免多线程写入时日志错乱 private static final Object WRITE_LOCK = new Object(); // 复用原有日志时间格式 private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM.dd HH:mm:ss.SSS"); public static void writeCommonLog(String loggerName, String methodName, String level, String message) { synchronized (WRITE_LOCK) { try (PrintWriter writer = new PrintWriter(new FileWriter(COMMON_LOG_PATH, true))) { String timestamp = DATE_FORMAT.format(new Date()); // 严格对齐app.log的日志格式 String logContent = String.format("%s [%2.2t][%s.%s] %-5s - %s%n", timestamp, Thread.currentThread().getId(), loggerName.substring(loggerName.lastIndexOf('.') + 1), methodName, level, message); writer.write(logContent); } catch (IOException e) { // 写入失败时降级到原有日志系统记录错误,避免信息丢失 org.slf4j.LoggerFactory.getLogger(CustomUtility.class).error("Failed to write to common.log", e); } } } }
3. 配置Logback TurboFilter拦截指定类日志
添加自定义TurboFilter,拦截目标类的日志请求,调用CustomUtility写入common.log,同时阻止这些日志进入原有app.log的输出链路,无需新增appender或设置additivity="false":
<!-- 原有app.log的appender配置保持不变 --> <appender name="logFile" class="ch.qos.logback.core.rolling.RollingFileAppender"> <File>${home}/logs/app.log</File> <encoder> <pattern>%d{MM.dd HH:mm:ss.SSS} [%2.2t][%c{1}.%M] %-5p - %m%n</pattern> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> <maxIndex>20</maxIndex> <FileNamePattern>${home}/logs/app.log.%i</FileNamePattern> </rollingPolicy> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>25MB</MaxFileSize> </triggeringPolicy> </appender> <!-- 添加自定义TurboFilter,拦截指定类的日志 --> <turboFilter class="com.yourpackage.CommonLogInterceptor"> <!-- 配置需要拦截的目标类全限定名,多个类用逗号分隔 --> <targetClasses>com.yourpackage.TargetService,com.yourpackage.TargetDao</targetClasses> </turboFilter> <!-- 根日志配置保持原有指向logFile appender --> <root level="info"> <appender-ref ref="logFile"/> </root>
实现TurboFilter类
import ch.qos.logback.classic.Level; import ch.qos.logback.classic.turbo.TurboFilter; import ch.qos.logback.core.spi.FilterReply; import org.slf4j.Marker; public class CommonLogInterceptor extends TurboFilter { private String targetClasses; private String[] targetClassList; @Override public void start() { if (targetClasses == null || targetClasses.trim().isEmpty()) { addError("targetClasses property must be configured"); return; } // 拆分配置的目标类列表,去除空格 targetClassList = targetClasses.split(","); for (int i = 0; i < targetClassList.length; i++) { targetClassList[i] = targetClassList[i].trim(); } super.start(); } @Override public FilterReply decide(Marker marker, ch.qos.logback.classic.Logger logger, Level level, String format, Object[] params, Throwable t) { String loggerName = logger.getName(); // 判断当前日志是否来自目标类 for (String targetClass : targetClassList) { if (loggerName.equals(targetClass)) { // 从堆栈中获取日志调用的方法名 String methodName = extractCallingMethod(); // 调用CustomUtility写入common.log CustomUtility.writeCommonLog(loggerName, methodName, level.toString(), String.format(format, params)); // 返回DENY阻止日志进入原有appender return FilterReply.DENY; } } // 非目标类日志正常放行 return FilterReply.NEUTRAL; } private String extractCallingMethod() { // 遍历堆栈,跳过框架和工具类的调用,找到业务代码的方法名 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); for (StackTraceElement element : stackTrace) { String className = element.getClassName(); if (!className.equals(this.getClass().getName()) && !className.equals(CustomUtility.class.getName()) && !className.startsWith("ch.qos.logback") && !className.startsWith("org.slf4j")) { return element.getMethodName(); } } return "unknown"; } }
4. 关键注意事项
- 线程安全保障:CustomUtility通过
synchronized锁确保多线程写入common.log时不会出现日志串线,也可替换为FileLock或高性能线程安全日志组件优化。 - 格式一致性:严格对齐
app.log的日志格式,避免日志风格混乱。 - 错误降级:当
common.log写入失败时,自动降级到原有日志系统记录错误,防止日志丢失。 - 类名精确匹配:TurboFilter配置的
targetClasses必须是目标类的全限定名,多个类用逗号分隔。
内容的提问来源于stack exchange,提问作者Zia
相关产品推荐
相关产品推荐

