Java如何实现仅Debug及以下级别日志携带Throwable异常栈
方案1:封装统一日志工具类(侵入性最小,无需修改日志框架配置)
把判断逻辑收拢到工具类中,配合IDE全局替换可以快速完成存量代码改造:
import org.slf4j.Logger; public class LogUtils { // warn级别条件输出异常栈 public static void warnWithConditionalStack(Logger logger, String msg, Throwable e) { if (logger.isDebugEnabled()) { logger.warn(msg, e); } else { logger.warn("{} | 异常信息: {}", msg, e.toString()); } } // 可按需扩展error、info等其他级别的对应方法 public static void errorWithConditionalStack(Logger logger, String msg, Throwable e) { if (logger.isDebugEnabled()) { logger.error(msg, e); } else { logger.error("{} | 异常信息: {}", msg, e.toString()); } } }
业务代码仅需替换原有日志打印语句即可:
try { // ... } catch (Exception e){ LogUtils.warnWithConditionalStack(logger, "ERR Occurs", e); }
方案2:自定义日志框架转换器(零业务代码侵入,推荐)
主流日志框架(Logback/Log4j2)都支持自定义格式转换器,只需修改框架配置即可全局生效,完全不需要改动业务代码。
以Logback为例实现逻辑如下:
- 自定义异常转换器类
import ch.qos.logback.classic.pattern.ThrowableProxyConverter; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.IThrowableProxy; import org.slf4j.event.Level; public class ConditionalStackTraceConverter extends ThrowableProxyConverter { @Override protected String throwableProxyToString(IThrowableProxy tp) { ILoggingEvent event = getEvent(); // 判断当前logger生效级别低于等于DEBUG时输出完整栈 if (event.getLoggerContextVO().getLevel().toInt() <= Level.DEBUG.toInt()) { return super.throwableProxyToString(tp); } // 高日志级别下仅输出异常类名和错误信息 return tp.getClassName() + ": " + tp.getMessage(); } }
- 修改Logback配置文件,注册转换器
<configuration> <!-- 注册自定义转换器 --> <conversionRule conversionWord="condEx" converterClass="com.yourpackage.ConditionalStackTraceConverter" /> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <!-- 用自定义的condEx替换原有%ex/%exception占位符 --> <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%condEx%n</pattern> </encoder> </appender> <!-- 其余原有配置保持不变 --> </configuration>
配置完成后业务代码正常写logger.warn("ERR Occurs", e)即可,框架会自动根据日志级别判断是否输出完整异常栈。Log4j2也有类似的ThrowablePatternConverter扩展机制,实现逻辑一致。
方案3:SLF4J 2.0+ Fluent API实现(适合新项目)
如果使用SLF4J 2.0及以上版本,可以用原生Fluent API实现条件化传入异常参数,代码更简洁:
try { // ... } catch (Exception e) { logger.atWarn() .setMessage("ERR Occurs") .setCause(logger.isDebugEnabled() ? e : null) .log(); }
内容的提问来源于stack exchange,提问作者KM H
相关产品推荐
相关产品推荐

