SpringBoot动态修改日志级别

本文基于SpringBoot2.5.2 来讲解SpringBoot中如何动态修改日志级别

为什么要动态修改日志
为了避免大量日志对磁盘的占用,大量的日志甚至会影响系统性能,项目上线后一般会将日志级别设置成warn或者error级别。但是有时候为了查找bug,需要查看更低级别的日志信息,如果是修改日志级别重启,这是一个比较繁琐的过程,bug也不一定能够复现,因此需要能够在线上动态的修改日志级别。

其实spring-boot-starter-actuator已经提供了日志级别动态变更功能,和本文所讲实现原理其实是一样的,都是从Spring的上下文中拿到LoggingSystem,通过LoggingSystem来调用修改日志级别。但是如果仅仅是为了实现该功能去依赖spring-boot-starter-actuator,会使得工程显得有点笨重。

actuator中的实现参考如下两个类
org.springframework.boot.actuate.autoconfigure.logging.LoggersEndpointAutoConfiguration#loggersEndpoint
org.springframework.boot.actuate.logging.LoggersEndpoint

如下是本文所讲,SpringBoot动态修改日志级别的具体实现

为什么实现代码如下,建议先看明白SpringBoot中的 org.springframework.boot.context.logging.LoggingApplicationListener

这个只能针对当前单进程有效,示例代码只是用来了解其背后的原理,要想在分布式系统的所有进程中生效,可以对接配置中心来监听配置变更,利用LoggingSystem来动态修改日志级别.

@RequestMapping("/log")
@RestController
public class LoggerLevelController {

    private static final Map<String, LogLevel> LEVELS = new HashMap<>();

    static {
        LEVELS.put("trace", LogLevel.TRACE);
        LEVELS.put("debug", LogLevel.DEBUG);
        LEVELS.put("info", LogLevel.INFO);
        LEVELS.put("warn", LogLevel.WARN);
        LEVELS.put("error", LogLevel.ERROR);
        LEVELS.put("off", LogLevel.OFF);
    }

    @Autowired
    private ApplicationContext context;

    /** 设置日志级别 */
    @RequestMapping("/level/{loggerConf}")
    public List<String> setLevel(@PathVariable(name = "loggerConf") String loggerConf) {
        String[] logger = loggerConf.split("=");
        String loggerName = logger[0];
        String level = logger[1];

        LogLevel logLevel = LEVELS.getOrDefault(level, LogLevel.INFO);

        LoggingSystem loggingSystem = context.getBean(LoggingApplicationListener.LOGGING_SYSTEM_BEAN_NAME, LoggingSystem.class);

        //按分组设置日志级别
        LoggerGroups loggerGroups = context.getBean(LoggingApplicationListener.LOGGER_GROUPS_BEAN_NAME, LoggerGroups.class);
        LoggerGroup group = loggerGroups.get(loggerName);
        if (group != null && group.hasMembers()) {
            group.configureLogLevel(logLevel, loggingSystem::setLogLevel);

            List<String> loggerConfigs = group.getMembers().stream()
                    .map(loggingSystem::getLoggerConfiguration)
                    .sorted(Comparator.comparing(LoggerConfiguration::getName))
                    .map(LoggerConfiguration::toString)
                    .collect(Collectors.toList());
            return loggerConfigs;
        }
        //只针对单个loggerName设置日志级别
        LoggerConfiguration loggerConfiguration = loggingSystem.getLoggerConfiguration(loggerName);
        //如果loggerName对应的Logger不存在不应该修改其日志级别,如果日志使用的是logback,可能导致内存泄漏
        //参见 ch.qos.logback.classic.LoggerContext.getLogger(java.lang.String) L142
        //在SpringBoot2.0.9中这个判断不生效,即使loggerName不存在同样能够获取的LoggerConfiguration,注意测试
        if (loggerConfiguration == null) {
            return Collections.singletonList("不存在loggerName=" + loggerName);
        }
        loggingSystem.setLogLevel(loggerName, logLevel);

        return Collections.singletonList(loggingSystem.getLoggerConfiguration(loggerName).toString());
    }

    /** 获取所有的日志级别设置 */
    @RequestMapping("/confs")
    public List<LoggerConfiguration> allLoggerConfigs() {
        LoggingSystem loggingSystem = context.getBean(LoggingApplicationListener.LOGGING_SYSTEM_BEAN_NAME, LoggingSystem.class);
        return loggingSystem.getLoggerConfigurations()
                .stream()
                .sorted(Comparator.comparing(LoggerConfiguration::getName))
                .collect(Collectors.toList());
    }

    @RequestMapping("/set")
    public String log(String level) {
        log.debug("debug");
        log.info("info");
        log.warn("warn");
        log.error("error");
        return level;
    }
}

修改日志级别

http://localhost:8080/log/level/ROOT=info

在这里插入图片描述

查看日志级别

http://localhost:8080/log/confs
  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值