
spring boot 3.x 中使用 log4j2 配置自定义日志级别(如 bsharing_log)时,若消息仅输出到控制台而未写入目标文件,根本原因在于 appender 缺少精准的 levelrangefilter 过滤规则,导致日志事件未被正确路由到对应 rollingfile。
spring boot 3.x 中使用 log4j2 配置自定义日志级别(如 bsharing_log)时,若消息仅输出到控制台而未写入目标文件,根本原因在于 appender 缺少精准的 levelrangefilter 过滤规则,导致日志事件未被正确路由到对应 rollingfile。
在 Log4j2 中,Logger 的 level 属性仅控制该 Logger 接收日志的最低级别阈值,不负责将日志分发给特定 Appender;真正的日志路由由 Appender 上的过滤器(Filter) 决定。原配置中:
<logger name="bsharinglogger" level="BSHARING_LOG" additivity="false"><appenderref ref="Console"></appenderref><appenderref ref="bsharingLogAppender"></appenderref></logger>
存在两个关键问题:
-
bsharingloggerLogger 实例并未在代码中实际获取(LogManager.getLogger(this.getClass())返回的是类全限定名 logger,而非"bsharinglogger"); - 即便显式使用
LogManager.getLogger("bsharinglogger"),bsharingLogAppender本身未配置任何过滤器,会默认接受所有传入日志(但受 Root Logger 级别限制),而appLog同样未排除BSHARING_LOG,造成日志重复或遗漏。
✅ 正确做法是:统一交由 Root Logger 管理日志分发,并为每个 Appender 显式配置 LevelRangeFilter,实现“按级别精准分流”。
✅ 推荐配置(log4j2.xml 核心片段)
<appenders><console name="Console" target="SYSTEM_OUT" follow="true"><patternlayout pattern="${LOG_PATTERN}"></patternlayout></console><!-- application.log:接收 INFO 及以上标准级别(排除 BSHARING_LOG) --><rollingfile name="appLog" filename="${APP_LOG_ROOT}/application.log" filepattern="${APP_LOG_ROOT}/application-%d{yyyy-MM-dd}-%i.log"><patternlayout pattern="${LOG_PATTERN}"></patternlayout><!-- 关键:拒绝 BSHARING_LOG,避免污染主日志 --><levelrangefilter minlevel="INFO" maxlevel="WARN" onmatch="ACCEPT" onmismatch="NEUTRAL"></levelrangefilter><levelrangefilter minlevel="ERROR" maxlevel="FATAL" onmatch="ACCEPT" onmismatch="DENY"></levelrangefilter><policies><sizebasedtriggeringpolicy size="19500KB"></sizebasedtriggeringpolicy></policies><defaultrolloverstrategy max="1"></defaultrolloverstrategy></rollingfile><!-- bsharing.log:仅接收 BSHARING_LOG 级别日志 --><rollingfile name="bsharingLogAppender" filename="${APP_LOG_ROOT}/bsharing.log" filepattern="${APP_LOG_ROOT}/bsharing-%d{yyyy-MM-dd}-%i.log"><patternlayout pattern="${LOG_PATTERN}"></patternlayout><!-- 关键:精确匹配自定义级别 --><levelrangefilter minlevel="BSHARING_LOG" maxlevel="BSHARING_LOG" onmatch="ACCEPT" onmismatch="DENY"></levelrangefilter><policies><sizebasedtriggeringpolicy size="19500KB"></sizebasedtriggeringpolicy></policies><defaultrolloverstrategy max="1"></defaultrolloverstrategy></rollingfile></appenders><loggers><!-- 所有日志统一由 Root 分发 --><root level="INFO"><appenderref ref="Console"></appenderref><appenderref ref="appLog"></appenderref><appenderref ref="bsharingLogAppender"></appenderref></root></loggers>
? Java 代码调用(无需新建 Logger 名称)
public class CustomSecurityFilter extends GenericFilterBean {
private static final Logger log = LogManager.getLogger(CustomSecurityFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 直接使用自定义 Level 发送日志(无需绑定特定 Logger 名)
log.log(Level.forName("BSHARING_LOG", 350), "test log message2");
log.log(Level.getLevel("BSHARING_LOG"), "test log message");
chain.doFilter(request, response);
}
}
⚠️ 注意事项
-
不要依赖
Logger name匹配:除非显式调用LogManager.getLogger("bsharinglogger"),否则this.getClass()获取的 logger 名为ru.bozza.Sharing.CustomSecurityFilter,与不匹配。 -
additivity="false"在 Root 下无效:Root Logger 是日志分发终点,additivity属性仅对非 Root Logger 有效。 -
确保
Level.forName()注册时机:CustomLevel 已在中声明,运行时可直接通过名称解析,无需额外注册。 -
验证日志目录权限:确保
logs/目录存在且应用有写入权限,否则 RollingFile 会静默失败(可通过status="DEBUG"临时开启 Configuration 调试日志排查)。
通过上述配置,BSHARING_LOG 日志将严格只写入 bsharing.log,标准级别日志归入 application.log,控制台保持全量输出,实现清晰、可控的日志分离策略。










