目录
基础环境准备
pom依赖
Redis工具类
测试类
实现思路及过程
思路
redis中的系统参数
缓存工具类
动态更改日志级别
定时任务定时刷新日志级别
测试效果
最近我们开发了一个Java程序,并没有用到Springboot,对于Springboot程序想要实现动态刷新日志级别是很容易的,只要借助于LoggingSystem 类和 Nacos 就可以轻松实现,这里记录一下在借助于logback的日志的上下文对象LoggerContext来实现动态刷新日志级别,大致思路:通过LoggerFactory获取日志的上下文对象LoggerContext,然后再获取所有的日志类,最后遍历设置对应的日志级别即可。
希望对大家有帮助,如果我的博客对你有帮助,欢迎进行评论✏️✏️、点赞👍👍、收藏⭐️⭐️,满足一下我的虚荣心💖🙏🙏🙏 。
基础环境准备
pom依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.3.3.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>2.3.3.RELEASE</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version><scope>compile</scope></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.0</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.6.2</version></dependency>
Redis工具类
package com.yjh.learn.logbacklearn.utils;import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;/*** @author yjh*/
public class JedisUtil {private static JedisPool jedisPool;static {JedisPoolConfig config = new JedisPoolConfig();config.setMaxTotal(30);config.setMaxIdle(10);config.setMaxWaitMillis(5000L);config.setTestOnBorrow(true);config.setTestOnReturn(true);config.setTestWhileIdle(true);config.setMinEvictableIdleTimeMillis(60000L);config.setTimeBetweenEvictionRunsMillis(3000L);config.setNumTestsPerEvictionRun(-1);jedisPool = new JedisPool(config, "127.0.0.1", 6379, 60000, null, 0);}public static String get(String key) {String value = null;Jedis jedis = null;try {jedis = jedisPool.getResource();value = jedis.get(key);} catch (Exception e) {e.printStackTrace();} finally {close(jedis);}return value;}public static void close(Jedis jedis) {try {if (jedis != null) {jedis.close();}} catch (Exception e) {if (jedis.isConnected()) {jedis.quit();jedis.disconnect();}}}}
测试类
@RestController
public class TestController {private static final Logger logger = LoggerFactory.getLogger(TestController.class);@GetMapping("/test")public void test() throws Exception{while (true){Thread.sleep(2000);logger.debug("--- debug ---");logger.info("--- info ---");logger.warn("--- warn ---");}}
}
实现思路及过程
思路
将日志级别、对哪个目录生效等作为系统参数放到redis中,当需要更改项目的日志级别的时候,修改下redis中的日志级别参数,程序中会有一个读取缓存的工具类来获取日志级别,然后还需要一个定时器定时的将最新的日志级别更新到Logger类中,这样就完成了日志级别的动态修改。
redis中的系统参数
日志级别、生效包路径作为系统参数缓存到redis中,现在redis中有如下参数:
其中loggerLevel为默认日志级别值为info,loggerPackage为生效的包路径,值为我项目的包路径,另外我还加了一个loggerEnable参数,下面用到了再说。
缓存工具类
redis中已经有了我们想要的数据,现在需要一个读取redis数据的工具类,如下:
import org.apache.commons.lang3.StringUtils;/*** @author yjh*/
public class CacheUtil {private static final String SYS_PARAM_PREFIX = "sys:param:";private static final String LOGGERPACKAGE = "loggerPackage";private static final String LOGGERLEVEL = "loggerLevel";private static final String LOGGERENABLE = "loggerEnable";public static String loggerPackage = "com.yjh.learn.logbacklearn";public static String loggerLevel = "info";public static boolean loggerEnable = false;public static String getLoggerpackage() {String loggerPackageVal = JedisUtil.get(SYS_PARAM_PREFIX + LOGGERPACKAGE);if (StringUtils.isNotBlank(loggerPackageVal)) {loggerPackage = loggerPackageVal;}return loggerPackage;}public static boolean getLoggerenable() {String loggerEnableVal = JedisUtil.get(SYS_PARAM_PREFIX + LOGGERENABLE);if (StringUtils.isNotBlank(loggerEnableVal)) {loggerEnable = Boolean.parseBoolean(loggerEnableVal);}return loggerEnable;}public static String getLoggerlevel() {String loggerLevelVal = JedisUtil.get(SYS_PARAM_PREFIX + LOGGERLEVEL);if (StringUtils.isNotBlank(loggerLevelVal)) {loggerLevel = loggerLevelVal;}return loggerLevel;}
}
为了避免空指针,这里我将loggerPackage、loggerLevel、loggerEnable都设置了默认值。
动态更改日志级别
refreshLoggerLevel方法中有两个参数,loggerPackage为日志级别生效的包路径,, Level loggerLevel为要设置的日志级别,最终通过Logger类的setLevel方法更改日志级别。
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import org.slf4j.LoggerFactory;import java.util.List;
import java.util.stream.Collectors;/*** @author yjh*/
public class RefreshLoggerLevel {private static final String LEVELDEBUG = "debug";private static final String LEVELINFO = "info";private static final String LEVELWARN = "warn";private static final String LEVELERROR = "error";public static void refreshLoggerLevel(String loggerPackage, Level loggerLevel) {LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();List<ch.qos.logback.classic.Logger> loggerList = loggerContext.getLoggerList();List<ch.qos.logback.classic.Logger> packageLoggerList = loggerList.stream().filter(a -> a.getName().startsWith(loggerPackage)).collect(Collectors.toList());for (ch.qos.logback.classic.Logger logger : packageLoggerList) {logger.setLevel(loggerLevel);}}public static Level getLevel(String level) {if (LEVELDEBUG.equals(level)) {return Level.DEBUG;} else if (LEVELINFO.equals(level)) {return Level.INFO;} else if (LEVELWARN.equals(level)) {return Level.WARN;} else if (LEVELERROR.equals(level)) {return Level.ERROR;} else {return Level.INFO;}}}
定时任务定时刷新日志级别
import ch.qos.logback.classic.Level;
import com.yjh.learn.logbacklearn.extend.RefreshLoggerLevel;
import com.yjh.learn.logbacklearn.utils.CacheUtil;import javax.annotation.Resource;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;/*** @author yjh*/
@SuppressWarnings("all")
public class RefreshLevel {private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor();public static void refreshLevel() {EXECUTOR.scheduleAtFixedRate(() -> {boolean enable = CacheUtil.getLoggerenable();if (enable) {String level = CacheUtil.getLoggerlevel();System.out.println("refreshLevel level=" + level);Level lv = RefreshLoggerLevel.getLevel(level);RefreshLoggerLevel.refreshLoggerLevel(CacheUtil.getLoggerpackage(), lv);}}, 0, 30, TimeUnit.SECONDS);}
}
如上,我添加了一个定时任务,每30秒执行一次,并且只有当loggerEnable为true时才刷新日志级别,这个loggerEnable参数是可以去掉的,我这里加这个参数,是防止不小心对loggerLevel参数进行了改动,所以才用两个参数进行日志级别刷新的控制。
最后,别忘了触发一下这个定时任务的执行,如果你是在纯java项目中,需要在main方法中调用一下refreshLevel方法,我这里是在Springboot中,所以我使用如下方法触发定时任务的执行:
@PostConstructpublic void refreshLevel() {RefreshLevel.refreshLevel();}
测试效果
启动项目,调用一下 http://localhost:8080/test ,控制台打印如下:
此时我们修改redis缓存中的loggerLevel为debug,loggerEnable为true,等30秒后,就会发现debug日志也在控制台进行了打印,如下: