【Logback日志级别】动态调整Logback的日志级别
阅读原文时间:2021年08月04日阅读:1

一、导入

  Logback作为目前一个比较流行的日志框架,我们在实际项目经常使用到该框架来帮助我们打印日志,以便我们可以更快速地获取业务逻辑执行情况、定位系统问题。

  常用的日志打印一共有5种级别控制,优先级情况为:【TRACE】<【DEBUG】<【INFO】<【WARN】<【ERROR】。

  【TRACE】:trace是一种很低的日志级别,一般不会使用。目前,我只有在SpringBoot的启动之中,略微发现一些它的影子,表示的就是默认不打印的日志。

  【DEBUG】:debug是一种调试程序的日志级别,一般用于程序开发过程中打印一些调试日志、运行信息,可以比较随意的使用。

  【INFO】:info是用来输出程序的一些关键信息,强调业务逻辑的运行过程信息,不能随便打印。

  【WARN】:warn是用于输出一些警告提示信息,一般是系统进入到一种可恢复的状态时打印的信息,警告但不是严重错误。

  【ERROR】:error是系统已经发生错误的事件,比如发生了异常,但是不想影响系统的正常运行。可以打印一些错误信息,提示开发者关注或者定位问题。

  日志是打印在日志文件之中的,如果大量的打印会造成日志文件的骤增导致磁盘空间快速增长。但是,排查问题的时候,我们都尽可能希望日志级别可以足够的细致。没有问题的时候,我们希望日志文件可以尽量减少磁盘的占用。

  所以,如果我们可以做到动态地去控制日志级别,实现动态打印日志,那就可以完美解决上诉的需求。

二、敲代码

  1.先来个不同日志级别的打印类。

  每个打印类都只做一件事,判断一下当前级别,然后打印一行日志。(实际上,logback里面已经帮我们做了判断,我们在这里只是为了代码规范,减少日志拼接再打印的消耗,所以提前做了日志级别判断)

  TRACE级别:

package cn.lxw.trace;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @Title:
* @ClassName: cn.lxw.trace.TraceLogger.java
* @Description:
*
* @Author: luoxw
* @Date: 2021/8/3 19:13
*/
public class TraceLogger {
private static final Logger logger = LoggerFactory.getLogger(TraceLogger.class);

/\*\*  
 \* 功能描述: <br>  
 \* 〈Print trace-level log〉  
 \* @Param: \[\]  
 \* @Return: {@link Void}  
 \* @Author: luoxw  
 \* @Date: 2021/8/3 19:12  
 \*/  
public static void printLog(){  
    if(logger.isTraceEnabled()) {  
        logger.trace("print trace log");  
    }  
}

}

  DEBUG级别:

package cn.lxw.debug;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @Title:
* @ClassName: cn.lxw.debug.DebugLogger.java
* @Description:
*
* @Author: luoxw
* @Date: 2021/8/3 19:12
*/
public class DebugLogger {
private static final Logger logger = LoggerFactory.getLogger(DebugLogger.class);

/\*\*  
 \* 功能描述: <br>  
 \* 〈Print debug-level log〉  
 \* @Param: \[\]  
 \* @Return: {@link Void}  
 \* @Author: luoxw  
 \* @Date: 2021/8/3 19:12  
 \*/  
public static void printLog(){  
    if(logger.isDebugEnabled()) {  
        logger.debug("print debug log");  
    }  
}

}

  INFO级别:

package cn.lxw.info;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @Title:
* @ClassName: cn.lxw.info.InfoLogger.java
* @Description:
*
* @Author: luoxw
* @Date: 2021/8/3 19:13
*/
public class InfoLogger {
private static final Logger logger = LoggerFactory.getLogger(InfoLogger.class);

/\*\*  
 \* 功能描述: <br>  
 \* 〈Print info-level log〉  
 \* @Param: \[\]  
 \* @Return: {@link Void}  
 \* @Author: luoxw  
 \* @Date: 2021/8/3 19:12  
 \*/  
public static void printLog(){  
    if(logger.isInfoEnabled()){  
        logger.info("print info log");  
    }  
}

}

  WARN级别:

package cn.lxw.warn;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @Title:
* @ClassName: cn.lxw.warn.WarnLogger.java
* @Description:
*
* @Author: luoxw
* @Date: 2021/8/3 19:13
*/
public class WarnLogger {
private static final Logger logger = LoggerFactory.getLogger(WarnLogger.class);

/\*\*  
 \* 功能描述: <br>  
 \* 〈Print warn-level log〉  
 \* @Param: \[\]  
 \* @Return: {@link Void}  
 \* @Author: luoxw  
 \* @Date: 2021/8/3 19:12  
 \*/  
public static void printLog(){  
    if(logger.isWarnEnabled()) {  
        logger.warn("print warn log");  
    }  
}

}

  ERROR级别:

package cn.lxw.error;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @Title:
* @ClassName: cn.lxw.error.ErrorLogger.java
* @Description:
*
* @Author: luoxw
* @Date: 2021/8/3 19:13
*/
public class ErrorLogger {
private static final Logger logger = LoggerFactory.getLogger(ErrorLogger.class);

/\*\*  
 \* 功能描述: <br>  
 \* 〈Print error-level log〉  
 \* @Param: \[\]  
 \* @Return: {@link Void}  
 \* @Author: luoxw  
 \* @Date: 2021/8/3 19:12  
 \*/  
public static void printLog(){  
    if(logger.isErrorEnabled()) {  
        logger.error("print error log");  
    }  
}

}

  2.将这些日志打印封装到一个方法中。

/\*\*  
 \* 功能描述: <br>  
 \* 〈print log with different logger levels.〉  
 \* @Param: \[\]  
 \* @Return: {@link Void}  
 \* @Author: luoxw  
 \* @Date: 2021/8/3 19:11  
 \*/  
private static void printLog() {  
    TraceLogger.printLog();  
    DebugLogger.printLog();  
    InfoLogger.printLog();  
    WarnLogger.printLog();  
    ErrorLogger.printLog();  
}

  3.写一个随机返回日志级别的方法

private static Level getRandomLevel(){  
    Level\[\] levelArr = new Level\[\]{Level.TRACE,Level.DEBUG,Level.INFO,Level.WARN,Level.ERROR};  
    int index = (int) (Math.random() \* 10) % 6 + 1;  
    if(index > levelArr.length - 1){  
        index = 0;  
    }  
    return levelArr\[index\];  
}

  4.核心修改来了,动态调整日志级别。这里主要是获取日志的上下文对象,是从ILoggerFactory转化来的,因为我们的日志上下文LoggerContext实现了ILoggerFactory接口。这里我的做法是获取所有的日志类,然后遍历出符合我们包名或者类名的条件,然后遍历设置对应的日志级别。

/\*\*  
 \* 功能描述: <br>  
 \* 〈refresh the level setting of the logger context〉  
 \* @Param: \[loggerPackage, loggerLevel\]  
 \* @Return: {@link Void}  
 \* @Author: luoxw  
 \* @Date: 2021/8/3 19:11  
 \*/  
private static void refreshLoggerLevel(String loggerPackage, Level loggerLevel) {  
    // #1.get logger context  
    ch.qos.logback.classic.LoggerContext loggerContext = (ch.qos.logback.classic.LoggerContext) LoggerFactory.getILoggerFactory();  
    List<ch.qos.logback.classic.Logger> loggerList = loggerContext.getLoggerList();  
    // #2.filter the Logger object  
    List<Logger> packageLoggerList = loggerList.stream().filter(a -> a.getName().startsWith(loggerPackage)).collect(Collectors.toList());  
    // #3.set level to logger  
    for (ch.qos.logback.classic.Logger logger : packageLoggerList) {  
        logger.setLevel(loggerLevel);  
    }  
}

  5.开始测试。开启一个定时线程池,然后打印日志,获取随机的一个日志级别,然后刷新日志上下文(设置日志级别)。下一个任务执行时间,继续重复上诉操作,观察变化。

/\*\*  
 \* 功能描述: <br>  
 \* 〈dynamic setting of Logback level start here.〉  
 \* @Param: \[args\]  
 \* @Return: {@link Void}  
 \* @Author: luoxw  
 \* @Date: 2021/8/3 19:06  
 \*/  
public static void main(String\[\] args) {  
    // you should know that the logger-level priority:  
    //      trace < debug < info < warn < error

    // At first,define a scheduled thread.  
    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);  
    scheduledExecutorService.scheduleAtFixedRate(() -> {  
        // Secondly, print log.  
        printLog();  
        // get logger-package and logger-level here(you can fetch the config from database or config-center)  
        // Thirdly, get logger level which is configured by you.  
        Level randomLevel = getRandomLevel();  
        System.out.println("logger level next time is : " + randomLevel);  
        // Finally,refresh the logger level which package is LOGGER\_PACKAGE  
        refreshLoggerLevel(LOGGER\_PACKAGE,randomLevel);  
    }, 2, 2, TimeUnit.SECONDS);  
}

  6.查看日志,符合预期。通过该调整,可以实现我们需要的功能操作。

20:16:04.360 [pool-1-thread-1] DEBUG cn.lxw.debug.DebugLogger - print debug log
20:16:04.364 [pool-1-thread-1] INFO cn.lxw.info.InfoLogger - print info log
20:16:04.366 [pool-1-thread-1] WARN cn.lxw.warn.WarnLogger - print warn log
20:16:04.367 [pool-1-thread-1] ERROR cn.lxw.error.ErrorLogger - print error log
logger level next time is : WARN
20:16:06.137 [pool-1-thread-1] WARN cn.lxw.warn.WarnLogger - print warn log
20:16:06.137 [pool-1-thread-1] ERROR cn.lxw.error.ErrorLogger - print error log
logger level next time is : WARN
20:16:08.138 [pool-1-thread-1] WARN cn.lxw.warn.WarnLogger - print warn log
20:16:08.138 [pool-1-thread-1] ERROR cn.lxw.error.ErrorLogger - print error log
logger level next time is : DEBUG
20:16:10.138 [pool-1-thread-1] DEBUG cn.lxw.debug.DebugLogger - print debug log
20:16:10.138 [pool-1-thread-1] INFO cn.lxw.info.InfoLogger - print info log
20:16:10.138 [pool-1-thread-1] WARN cn.lxw.warn.WarnLogger - print warn log
20:16:10.138 [pool-1-thread-1] ERROR cn.lxw.error.ErrorLogger - print error log
logger level next time is : ERROR
20:16:12.137 [pool-1-thread-1] ERROR cn.lxw.error.ErrorLogger - print error log

三、总结

  这个动态日志打印其实不难,只是网络上的资料太少,需要自己翻阅一些源码,找到里面核心的内容,然后通过一些框架提供的类按照我们的需求进行自定义。源码还是要多看的,学会其中思想,从实际出发,解决项目问题。好了,今天分享到这,谢谢大家的观看!

  项目地址:https://github.com/telephone6/java-collection/tree/main/frame/logback