无需重启!动态修改日志级别的神技,运维开发都哭了

关注我的公众号:【编程朝花夕拾】,可获取首发内容。

01 引言

在软件开发与运维过程中,日志系统是我们监控应用状态、诊断问题的重要手段。然而,传统的日志系统往往需要重启应用才能更改日志级别,这在生产环境中通常是不可接受的。

动态修改日志级别的能力因此成为现代应用开发中的一项关键技术,它允许我们在运行时调整日志详细程度,既不中断服务又能精准获取所需信息。

02 痛点

为什么要动态修改日志级别?

程序员最讨厌别人不打日志,也最讨厌自己打日志。遇到线上问题时,总嫌弃日志打的太少无法定位问题。但是日志打印的多,运维这边又要收到报警了。源源不断的日志积累,瞬间沾满磁盘,甚至影响到正常的业务。

为了减少日志的打印,我们修改了日志的级别。不是很重要的日志设为DEBUG级别,被认定为调试日志。正常答应的为INFO级别,甚至WARN级别。

为了应该调试,安全等场景动态修改日志级别可以给我们带来以下几个优点:

  • 问题诊断与调试:当生产环境出现问题时,临时提高特定组件的日志级别可以获取更详细的执行信息,而无需重启服务
  • 性能优化:在高负载情况下,降低日志级别可以减少I/O操作和序列化开销,提升系统性能
  • 灵活监控:根据实际情况动态调整日志输出,平衡监控需求与存储成本
  • 安全合规:敏感操作期间提高日志级别以满足审计要求,完成后恢复原有级别

03 实现方案与技术栈

本节介绍三种动态修改日志级别的方案:

  • 自定义实现方案
  • 配置Actuator暴露接口
  • 集成SpringBoot Admin

Spring Boot默认级别是INFO,正常访问时Debug级别的日志是打印不出来的。我们定义一个专门打印日志的类:

java 复制代码
@RequestMapping("/printLog")
public String printLog() {
    log.debug("[DEBUG] Lever 日志:打印了......");
    log.info("[INFO] Lever 日志:打印了......");
    log.warn("[WARN] Lever 日志:打印了......");
    return "日志已打印!";
}

3.1 自定义实现方案

自定实方案可以定制修改,灵活性较大。

修改核心代码

java 复制代码
@RequestMapping("changeLogLever")
public String changeLogLever(String  packageName, String lever) {
    LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();

    Logger logger = loggerContext.getLogger(packageName);
    Level oldLevel = logger.getLevel();
    if (oldLevel == null) {
        oldLevel = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).getLevel();
    }

    logger.setLevel(Level.toLevel(lever));
    log.debug("【DEBUG】日志级别从[{}]修改成[{}]级别了......", oldLevel, lever);
    log.info("【INFO】日志级别从[{}]修改成[{}]级别了......",oldLevel, lever);
    log.warn("【WARN】日志级别从[{}]修改成[{}]级别了......",oldLevel, lever);

    return "日志级别修改成功.....";
}

关键类:ch.qos.logback.classic.LoggerContext

LoggerContext的获取是通过org.slf4j.LoggerFactory得到的。

测试

还有另外的一种写法,通过Spring Boot内部类来直接处理:

org.springframework.boot.logging.LoggingSystem

java 复制代码
@Autowired
private LoggingSystem loggingSystem;

@RequestMapping("changeLogLever2")
public String changeLogLever2(String  packageName, String lever) {
    String oldLevel = loggingSystem.getLoggerConfiguration(packageName).getEffectiveLevel().name();
    if (!StringUtils.hasText(oldLevel)) {
        oldLevel = loggingSystem.getLoggerConfiguration(Logger.ROOT_LOGGER_NAME).getEffectiveLevel().name();
    }

    loggingSystem.setLogLevel(packageName, LogLevel.valueOf(lever.toUpperCase()));
    log.debug("【DEBUG】日志级别从[{}]修改成[{}]级别了......", oldLevel, lever);
    log.info("【INFO】日志级别从[{}]修改成[{}]级别了......", oldLevel, lever);
    log.warn("【WARN】日志级别从[{}]修改成[{}]级别了......",  oldLevel, lever);
    System.out.println("------------------------------------------------------");

    return "日志级别修改成功.....";
}

3.2 配置Actuator暴露接口

actuator监控是Spring Boot自带的监控。开启监控有一定的风险,小编分享过一篇关于监控邮箱配置的线上故障的文章已经讲过。

Maven依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

暴漏接口

management.endpoints.web.exposure.include是用来暴露Spring Boot自带的一些Web接口的,可以用来查看和修改参数。

默认只有health

要精确配置可以配置loggers,这里为了测试,暴漏所有的接口。线上要小心配置。

properties 复制代码
management.endpoints.web.exposure.include=*

查看日志级别

通过GET请求http://127.0.0.1:8080/actuator/loggers,可以看到所有的包路径以及对应的日志级别:

查询具体的包的日志级别

只需要在连接后面跟上包名即可:

http://127.0.0.1:8080/actuator/loggers/包名

例如,我们要看com.simonking.boot.log.controller报下的日志级别:

这个是因为小编在自定义方案实现的时候修改成的DEBUG

修改日志级别

与查看的请求相同,只不过变成POST请求。并增加application/json参数:

json 复制代码
{"configuredLevel": "WARN"}

这里是要将日志级别变成WARN级别。

curl

sh 复制代码
curl --request POST \
  --url http://127.0.0.1:8080/actuator/loggers/com.simonking.boot.log.controller.LogLevelController \
  --data '{"configuredLevel": "WARN"}'

使用Apipost执行:

请求测试

结果就只打印出了Warn级别的日志。

3.3 集成SpringBoot Admin

spring-boot-admin并不是Spring官方提供的,而是有社区提供和维护的。

GitHub地址:github.com/codecentric...

文档地址:docs.spring-boot-admin.com/3.5.2/docs/...

spring-boot-admin由服务端和客户端组成:

  • 服务端:监控平台
  • 客户端:业务项目,引入Admin客户端,配置服务

spring-boot-admin的服务端需要自行搭建,这里就演示了,下一期,我们专门介绍该监控平台。

客户端配置

xml 复制代码
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
    <version>2.6.11</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
properties 复制代码
spring.boot.admin.client.url=http://127.0.0.1:8081
management.endpoints.web.exposure.include=*

客户端管理

直接访问业务系统的域名和端口,进入日志配置:

这里可以随意修改每一个包的日志级别,我们修改成DEBUG级别,顺便看下它调用的接口:

我们发现:这里调用的接口和配置Actuator的接口非常类似。其实分析之后的结果,他们调用的事一个接口,只不过多了服务端内部的转发而已。

我们验证结果却是也生效了。

04 小结

动态修改日志级别是现代应用开发中的重要能力,它极大提升了系统的可观测性和运维效率。通过合理使用日志框架提供的功能或自定义实现,我们可以在不影响服务的前提下,精准控制日志输出,更好地平衡监控需求与系统性能。

实现这一功能时,需要充分考虑安全性、资源管理和分布式一致性等问题,确保系统的稳定性和可靠性。随着云原生和微服务架构的普及,动态日志管理将继续演进,为复杂分布式系统的运维提供更强有力的支持。

你更喜欢哪一种方式呢?

相关推荐
葫芦和十三18 分钟前
图解 MongoDB 22|读写关注:持久性与一致性的档位选择
后端·mongodb·agent
葫芦和十三7 小时前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp7 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑8 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯9 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan11 小时前
多Agent之间的区别
后端
青石路13 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充13 小时前
1.面向对象设计思想
后端
IT_陈寒13 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro14 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端