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

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

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

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

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

你更喜欢哪一种方式呢?

相关推荐
熊文豪14 分钟前
保姆级Maven安装与配置教程(Windows版)
java·windows·maven·maven安装教程·maven配置教程·maven安装与配置教程
dreams_dream28 分钟前
django错误记录
后端·python·django
AI大模型34 分钟前
AI老爱“瞎编”?给它请个RAG“私人秘书”,从此只说真话!
chatgpt·程序员·llm
怀旧,1 小时前
【C++】 9. vector
java·c++·算法
Tony Bai1 小时前
泛型重塑 Go 错误检查:errors.As 的下一站 AsA?
开发语言·后端·golang
AI大模型1 小时前
30天快速入门AI大模型:从理论到实践的详细学习方案
程序员·llm·ai编程
猿java1 小时前
Elasticsearch有哪几种分页方式?该如何选择?
后端·elasticsearch·架构
渣哥1 小时前
震惊!Java注解背后的实现原理,竟然如此简单又高深!
java
绝无仅有1 小时前
服务器Docker 安装和常用命令总结
后端·面试·github