【小知识】log4j2动态修改日志级别(轻量级)

背景

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

方案

  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
求你了,别再随便打日志了,教你动态修改日志级别!

一只菜狗

305 声望
41 粉丝
0 条评论
推荐阅读
Spring事务传播行为详解
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为。事务传播行为是Spring框架独有的事务增强特性,他不属于的事务实际提供方数据库行为。这是Spring为我们提供的强大的工具箱,使用事务传播行可...

JerryTse242阅读 122.6k评论 97

一文搞懂秒杀系统,欢迎参与开源,提交PR,提高竞争力。早日上岸,升职加薪。
前言秒杀和高并发是面试的高频考点,也是我们做电商项目必知必会的场景。欢迎大家参与我们的开源项目,提交PR,提高竞争力。早日上岸,升职加薪。知识点详解秒杀系统架构图秒杀流程图秒杀系统设计这篇文章一万多...

王中阳Go31阅读 2.4k评论 1

封面图
计算机网络连环炮40问
本文已经收录到Github仓库,该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~

程序员大彬13阅读 1.7k

万字详解,吃透 MongoDB!
MongoDB 是一个基于 分布式文件存储 的开源 NoSQL 数据库系统,由 C++ 编写的。MongoDB 提供了 面向文档 的存储方式,操作起来比较简单和容易,支持“无模式”的数据建模,可以存储比较复杂的数据类型,是一款非常...

JavaGuide8阅读 1.6k

封面图
花了半个小时基于 ChatGPT 搭建了一个微信机器人
相信大家最近被 ChatGPT 刷屏了,其实在差不多一个月前就火过一次,不会那会好像只在程序员的圈子里面火起来了,并没有被大众认知到,不知道最近是因为什么又火起来了,而且这次搞的人尽皆知。

Java极客技术12阅读 3k评论 3

封面图
数据结构与算法:二分查找
一、常见数据结构简单数据结构(必须理解和掌握)有序数据结构:栈、队列、链表。有序数据结构省空间(储存空间小)无序数据结构:集合、字典、散列表,无序数据结构省时间(读取时间快)复杂数据结构树、 堆图二...

白鲸鱼9阅读 5.2k

PHP转Go实践:xjson解析神器「开源工具集」
我和劲仔都是PHP转Go,身边越来越多做PHP的朋友也逐渐在用Go进行重构,重构过程中,会发现php的json解析操作(系列化与反序列化)是真的香,弱类型语言的各种隐式类型转换,很大程度的减低了程序的复杂度。

王中阳Go11阅读 2.7k评论 4

封面图

一只菜狗

305 声望
41 粉丝
宣传栏