背景

代码中添加了一些日志,只有在问题排查&过程跟踪时,才需要打印出来,其他时间不需要打印出来。

方案

  1. 写一个通用的Logger类,以及一个"日志级别"配置项,打印日志时,调用logger.log方法,方法内部通过判断日志级别来分别调用log.debuglog.info等方法。
  2. 打印日志方法不变(业务代码自己直接调用log.debuglog.info等方法),通过配置来动态修改指定业务类内的log对象的日志级别。

以上的方案1:通用的Logger类要把log的所有方法(log.debuglog.info等)都覆盖到,需要写很多个方法,太麻烦。
方案2:可以做到精细化管理,并且不需要对原先的代码做修改。

那假如不想管到那么精细,只想用通用的Logger类来简单管理呢?

实现

方案1

  1. 借鉴了以上方案2的核心代码:

    @Slf4j
    @Component
    public class DynamicLogger {
    
     /**
      * @param dynamicLevel
      * @author 
      * @date 2022/12/6 15:09
      */
     @Value("${log.dynamic.level:WARN}") // 通过配置系统配置和修改,也可以通过其他方式,但是核心代码一样
     public void setDynamicLevel(String dynamicLevel) {
         log.info("[动态日志]收到日志级别配置[{}]", dynamicLevel);
         try {
             String logType = StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr();
             if ("org.apache.logging.slf4j.Log4jLoggerFactory".equals(logType)) {
                 LoggerContext.getContext(false)
                         .getLogger(DynamicLogger.class.getName())
                         .setLevel(Level.toLevel(dynamicLevel));
             } if (xxx) { // logback等其他日志组件
                 
             } else {
                 log.error("[动态日志]Log框架类别[{}]无法识别: type{}", logType);
             }
         } catch (Exception e) {
             log.error("[动态日志]日志级别配置[{}]解析异常!", dynamicLevel, e);
         }
     }
    
     /**
      * @return org.slf4j.Logger
      * @author 
      * @date 2022/12/6 15:35
      */
     public static Logger getLogger() {
         return log;
     }
    
    }

    使用时,从原先的log.info改为DynamicLogger.getLogger.info

  2. 写一个通用的Logger类,以及一个"日志级别"配置项,打印日志时,从Logger类获取实际执行调用logger.log方法,方法内部通过判断日志级别来动态修改当前log对象的打印级别。

方案2

以上方案,需要对底层是使用哪个日志组件做判断,如果是使用Springboot框架,则Springboot提供了更上一层的抽象:

@Autowired
private LoggingSystem loggingSystem;

public void setDynamicLevel(String dynamicLevel, String loggerName) {
    log.info("[动态日志]收到日志级别配置[{}]", dynamicLevel);
    try {
        LoggerConfiguration loggerConfiguration = loggingSystem.getLoggerConfiguration(loggerName);
        if (loggerConfiguration == null) {
            log.error("[动态日志]没有获取到[{}]日志配置", loggerName);
            return;
        }
        List<String> supportLevels = loggingSystem.getSupportedLogLevels().stream().map(Enum::name).collect(Collectors.toList());
        if (!supportLevels.contains(dynamicLevel)) {
            log.error("[动态日志]不支持配置的[{}]日志级别", dynamicLevel);
            return;
        }
        if (!loggerConfiguration.getEffectiveLevel().name().equals(dynamicLevel)) {
            loggingSystem.setLogLevel(loggerName, LogLevel.valueOf(dynamicLevel));
        } else {
            log.info("[动态日志]当前已经是配置的[{}]日志级别", dynamicLevel);
        }
    } catch (Exception e) {
        log.error("[动态日志]日志级别配置[{}]解析异常!", dynamicLevel, e);
    }
}

但是这方案也有局限性,就是只能修改配置的Logger(比如xml文件里配置的Logger列表)。比如不能单独修改某个类下的Logger对象(比如不能像方案1那样修改的DynamicLogger类下的log对象的打印级别),只能修改配置Loggers里的Logger的级别,例子:如果项目用的是以下log4j2.xml文件,那loggerName只能是com.xxx或者ROOT

<Configuration status="DEBUG" updateCheck="false">      
    ……         
    <Loggers>
        <Logger name="com.xxx" level="INFO" additivity="false">
            <AppenderRef ref="InfoRollingFile" />
        </Logger>
        <Root level="INFO">
            <AppenderRef ref="Console" />
            <AppenderRef ref="InfoRollingFile" />
        </Root>
    </Loggers>     
</Configuration>    

对比

以上两个方案,调整的对象是互相反过来的。

  • 方案1:调整的是日志类的级别,如果将级别调整到XML配置的级别以下,就不会有日志;调整到配置的级别以上,就会有日志。
  • 方案2:调整的是XML配置的级别,将级别调整到日志类的打印级别以下,就会有日志;调整到日志类的打印级别以上,就不会有日志。
参考:
Java日志:日志级别动态调整
https://juejin.cn/post/7140939233518649357
求你了,别再随便打日志了,教你动态修改日志级别!

noname
314 声望47 粉丝

一只菜狗