动态刷新logback日志级别

目录

基础环境准备

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日志也在控制台进行了打印,如下:

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值