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

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

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 小结

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

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

你更喜欢哪一种方式呢?

相关推荐
鱼跃鹰飞2 分钟前
经典面试题:K8S的自动缩扩容和崩溃恢复
java·容器·kubernetes
Coder_Boy_7 分钟前
Spring Boot 事务回滚异常 UnexpectedRollbackException 详解(常见问题集合)
java·spring boot·后端
青云交8 分钟前
Java 大视界 -- 基于 Java+Redis Cluster 构建分布式缓存系统:实战与一致性保障(444)
java·redis·缓存·缓存穿透·分布式缓存·一致性保障·java+redis clus
风象南8 分钟前
SpringBoot 实现网络限速
后端
不知疲倦的仄仄10 分钟前
第五天:深度解密 Netty ByteBuf:高性能 IO 的基石
java·开源·github
xiaobaishuoAI13 分钟前
后端工程化实战指南:从规范到自动化,打造高效协作体系
java·大数据·运维·人工智能·maven·devops·geo
源代码•宸13 分钟前
Golang语法进阶(定时器)
开发语言·经验分享·后端·算法·golang·timer·ticker
期待のcode15 分钟前
TransactionManager
java·开发语言·spring boot
Hello.Reader16 分钟前
PyFlink JAR、Python 包、requirements、虚拟环境、模型文件,远程集群怎么一次搞定?
java·python·jar
计算机学姐17 分钟前
基于SpringBoot的汽车租赁系统【个性化推荐算法+数据可视化统计】
java·vue.js·spring boot·后端·spring·汽车·推荐算法