目录
基础环境准备
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中,所以我使用如下方法触发定时任务的执行:
@PostConstruct
public void refreshLevel() {
RefreshLevel.refreshLevel();
}
测试效果
启动项目,调用一下 http://localhost:8080/test ,控制台打印如下:
此时我们修改redis缓存中的loggerLevel为debug,loggerEnable为true,等30秒后,就会发现debug日志也在控制台进行了打印,如下: