Log4j2中按需创建带类名的同步/异步日志器问题
解决方案:类名Logger绑定自定义同步/异步Appender组合
你的需求核心是让Logger名称为类名(满足%logger输出要求),同时根据选择灵活绑定同步/异步的文件/数据库Appender,不用再依赖配置里的固定名称Logger。下面是具体的实现方案:
第一步:修改log4j2.xml配置
先把原来的固定名称Logger替换为「基础父Logger」,每个父Logger对应一种同步/异步+目标的组合,这样所有类名Logger都可以继承这些父Logger的配置:
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Console name="console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%sn] [%t] %-5level %logger{36} - %msg%n" /> </Console> <RandomAccessFile name="randomAccessFile" fileName="temp\logs.log" immediateFlush="false" append="false"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </RandomAccessFile> <JDBC name="databaseAppender" tableName="app_logs" bufferSize="1000"> <ConnectionFactory class="com.mc.loggerProvider.ConnectionFactory" method="getDatabaseConnection" /> <Column name="LOG_ID" pattern="%u{TIME}" isUnicode="false" /> <Column name="LOG_TIMESTAMP" isEventTimestamp="true" isUnicode="false" /> <Column name="LOG_LEVEL" pattern="%level" isUnicode="false" /> <Column name="LOGGER_NAME" pattern="%logger" isUnicode="false" /> <Column name="MESSAGE" pattern="%msg %ex{full}" isUnicode="false" /> </JDBC> </Appenders> <Loggers> <!-- 基础父Logger:对应不同类型的同步/异步+Appender组合 --> <AsyncLogger name="base.asyncDB" level="trace" includeLocation="false" additivity="false"> <AppenderRef ref="databaseAppender" /> </AsyncLogger> <Logger name="base.syncDB" level="trace" includeLocation="false" additivity="false"> <AppenderRef ref="databaseAppender" /> </Logger> <AsyncLogger name="base.asyncFile" level="trace" includeLocation="false" additivity="false"> <AppenderRef ref="randomAccessFile" /> </AsyncLogger> <Logger name="base.syncFile" level="trace" includeLocation="false" additivity="false"> <AppenderRef ref="randomAccessFile" /> </Logger> <AsyncLogger name="base.asyncDBasyncFile" level="trace" includeLocation="false" additivity="false"> <AppenderRef ref="databaseAppender" /> <AppenderRef ref="randomAccessFile" /> </AsyncLogger> <Logger name="base.syncDBsyncFile" level="trace" includeLocation="false" additivity="false"> <AppenderRef ref="databaseAppender" /> <AppenderRef ref="randomAccessFile" /> </Logger> <Root level="info" includeLocation="false"> <AppenderRef ref="console" /> </Root> </Loggers> </Configuration>
第二步:改造LogProvider类
通过Log4j2的API动态给类名Logger绑定对应的父Logger,这样类名Logger会继承父Logger的同步/异步特性和Appender配置,同时保留类名作为Logger名称:
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.LoggerConfig; public class LogProvider { // 基础父Logger的前缀,和配置里的name对应 private static final String BASE_LOGGER_PREFIX = "base."; public static Logger registerFor(Class<?> clazz, AppendersType type) { // 用类的全限定名作为Logger名称 String loggerName = clazz.getName(); // 获取对应类型的基础父Logger名称 String parentLoggerName = BASE_LOGGER_PREFIX + getParentLoggerSuffix(type); // 获取Log4j2的全局上下文 LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false); // 获取类名对应的Logger配置(如果不存在则是默认的Root配置) LoggerConfig loggerConfig = loggerContext.getConfiguration().getLoggerConfig(loggerName); // 如果是默认配置,创建新的LoggerConfig绑定类名 if (loggerConfig.getName().equals(LoggerConfig.ROOT)) { loggerConfig = new LoggerConfig(loggerName, null, false); loggerContext.getConfiguration().addLogger(loggerName, loggerConfig); } // 获取基础父Logger的配置 LoggerConfig parentLoggerConfig = loggerContext.getConfiguration().getLoggerConfig(parentLoggerName); // 设置类名Logger的父Logger,继承其Appender和同步/异步特性 loggerConfig.setParent(parentLoggerConfig); // 关闭additivity,避免日志重复输出到Root的Appender(按需调整) loggerConfig.setAdditivity(false); // 更新上下文配置 loggerContext.updateLoggers(); // 返回类名对应的Logger return LogManager.getLogger(clazz); } // 映射AppendersType到基础父Logger的后缀 private static String getParentLoggerSuffix(AppendersType type) { return switch (type) { case DATABASE_ASYNC -> "asyncDB"; case DATABASE_SYNC -> "syncDB"; case FILE_ASYNC -> "asyncFile"; case FILE_SYNC -> "syncFile"; case FILE_DATABASE_SYNC -> "syncDBsyncFile"; case FILE_DATABASE_ASYNC -> "asyncDBasyncFile"; default -> throw new IllegalArgumentException("Unsupported AppendersType: " + type); }; } }
第三步:业务代码调用方式
现在你可以直接在业务类中按规范调用,同时指定需要的日志类型:
public class MyBusinessClass { // 选择异步数据库日志,Logger名称为MyBusinessClass private static final Logger logger = LogProvider.registerFor(MyBusinessClass.class, AppendersType.DATABASE_ASYNC); public void doBusiness() { // %logger会输出MyBusinessClass,日志异步写入数据库 logger.info("Processing business logic..."); } }
关键原理说明
- 基础父Logger:把不同同步/异步+Appender的组合抽象成父Logger,避免重复配置每个类的Logger。
- 动态绑定父Logger:通过Log4j2的API给类名Logger设置父Logger,让它继承父Logger的所有配置(包括异步特性、Appender、日志级别等)。
- 保留类名作为Logger名称:最终返回的Logger是用
LogManager.getLogger(clazz)获取的,所以%logger会输出类的全限定名,满足你的日志规范要求。
内容的提问来源于stack exchange,提问作者lovelyim92
相关产品推荐
相关产品推荐

