当前位置:网站首页>动态刷新日志级别
动态刷新日志级别
2022-08-02 14:01:00 【Ethan_199402】
概述
日志模块是每个项目中必须的,用来记录程序运行中的相关信息。一般在开发环境下使用DEBUG级别的日志输出,为了方便查看问题,而在线上一般都使用INFO级别的日志,主要记录业务操作的日志。那么问题来了,当线上环境出现问题希望输出DEBUG日志信息辅助排查的时候怎么办呢?修改配置文件,重新打包然后上传重启线上环境,但是这么做不优雅 而且可能会破坏现场。
本文介绍一种实现方案:通过Apollo配置中心来实现 动态调整线上日志级别。
日志级别
不同的日志框架支持不同的日志级别,其中比较常见的就是Log4j和Logback。
在Log4j中支持8种日志级别,优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。
Logback中支持7种日志级别,优先级从高到低分别是:OFF、ERROR、WARN、INFO、DEBUG、TRACE、ALL。
可以看到常见的ERROR、WARN、INFO、DEBUG,这两者都是支持的。
所谓设置日志的输出级别表示的是输出的日志的最低级别,也就是说,如果我们把级别设置成INFO,那么包括INFO在内以及比INFO优先级高的级别的日志都可以输出。
Spring Boot对日志的支持
Spring Boot 对log做了统一封装,代码在 org.springframework.boot.logging 包中,结构如下:
其中 org.springframework.boot.logging.LoggingSystem 是SpringBoot对日志系统的抽象,是一个顶层的抽象类,有很多具体的实现:
通过上图,我们可以发现目前SpringBoot目前支持3种类型的日志,分别是
- JDK内置的Log(JavaLoggingSystem)
- Log4j2(Log4J2LoggingSystem)
- Logback(LogbackLoggingSystem)。
static {
Map<String, String> systems = new LinkedHashMap<>();
systems.put("ch.qos.logback.core.Appender", "org.springframework.boot.logging.logback.LogbackLoggingSystem");
systems.put("org.apache.logging.log4j.core.impl.Log4jContextFactory",
"org.springframework.boot.logging.log4j2.Log4J2LoggingSystem");
systems.put("java.util.logging.LogManager", "org.springframework.boot.logging.java.JavaLoggingSystem");
SYSTEMS = Collections.unmodifiableMap(systems);
}
LoggingSystem是个抽象类,内部有这几个方法:
- beforeInitialize方法:日志系统初始化之前需要处理的事情。抽象方法,不同的日志架构进行不同的处理
- initialize方法:初始化日志系统。默认不进行任何处理,需子类进行初始化工作
- cleanUp方法:日志系统的清除工作。默认不进行任何处理,需子类进行清除工作
- getShutdownHandler方法:返回一个Runnable用于当jvm退出的时候处理日志系统关闭后需要进行的操作,默认返回null,也就是什么都不做
- getSupportedLogLevels: 返回日志系统实际支持的一组 LogLevel。
- setLogLevel方法:抽象方法,用于设置对应logger的级别
- get方法:检测并返回正在使用的日志系统。支持 Logback 和 Java 日志记录。
代码
我们可以将日志级别配置保存在Apollo配置中心中, 当日志级别发生变更时,我们需要通过监听该配置的变更,设置应用中的 Logger 的日志级别,从而后续的日志打印可以根据新的日志级别
@Slf4j
@Component
public class LoggingSystemAdjustListener {
/** * 日志配置项的前缀 */
private static final String LOGGER_PREFIX = "logging.level.";
@Resource
private LoggingSystem loggingSystem;
// By default only read config in "application"
@ApolloConfigChangeListener
public void onChange(ConfigChangeEvent changeEvent) throws Exception {
// <Y> 遍历配置集的每个配置项,判断是否是 logging.level 配置项
for (String key : changeEvent.changedKeys()) {
// 如果是 logging.level 配置项,则设置其对应的日志级别
if (key.startsWith(LOGGER_PREFIX)) {
String loggerName = key.replace(LOGGER_PREFIX, "");
//
LoggerConfiguration cfg = loggingSystem.getLoggerConfiguration(loggerName);
if (cfg == null) {
if (log.isErrorEnabled()) {
log.error("no loggerConfiguration with loggerName:{}", loggerName);
}
continue;
}
// 获得日志级别
ConfigChange change = changeEvent.getChange(key);
// the newLevel could be null if the config is deleted from apollo
// in this case we update it same as "root" level
String newLevel = change.getNewValue();
LogLevel level = null;
// config is deleted or kept as empty string
if (newLevel == null || newLevel.isEmpty()) {
level = getFallbackLogLevel(ROOT_LOGGER_NAME);
} else {
try {
level = LogLevel.valueOf(newLevel.toUpperCase());
} catch (IllegalArgumentException e) {
// do nothing
}
}
if (level == null) {
if (log.isErrorEnabled()) {
log.error("logger:[{}]current LogLevel is invalid:{}", loggerName, newLevel);
}
continue;
}
if (!isSupportLevel(level)) {
if (log.isErrorEnabled()) {
log.error("LoggingSystem:[] not support current LogLevel:{}",
loggingSystem.getClass().getName(), newLevel);
}
continue;
}
if (log.isInfoEnabled()) {
log.info("logger:[{}] current effective level:{}, to be changed to level:{}", loggerName,
cfg.getEffectiveLevel(), newLevel);
}
// 基于springboot的日志抽象类,设置日志级别到 LoggingSystem 中
loggingSystem.setLogLevel(loggerName, level);
}
}
}
private boolean isSupportLevel(LogLevel level) {
for (LogLevel ll : loggingSystem.getSupportedLogLevels()) {
if (ll == level) {
return true;
}
}
return false;
}
public LogLevel getFallbackLogLevel(String loggerName) {
LoggerConfiguration cfg = loggingSystem.getLoggerConfiguration(loggerName);
if (cfg == null) {
if (log.isErrorEnabled()) {
log.error("no loggerConfiguration with loggerName:{}", loggerName);
}
// use WARN as unexpected case
return LogLevel.WARN;
}
return cfg.getEffectiveLevel();
}
}
基于spring的日志支持,我们还可以在logback中通过logger标签对某一个包甚至类单独配置日志级别
<logger name="com.ethan.demo.log.controller" level="INFO" additivity="true">
<appender-ref ref="${CONSOLE_APPENDER}"/>
</logger>
边栏推荐
猜你喜欢
Object detection scene SSD-Mobilenetv1-FPN
A number of embassies and consulates abroad have issued reminders about travel to China, personal and property safety
攻防世界----unfinish
世界上最大的开源基金会 Apache 是如何运作的?
【ONE·Data || Getting Started with Sorting】
Interview | with questions to learn, Apache DolphinScheduler Wang Fuzheng
未来的金融服务永远不会停歇,牛市仍将继续 2021-05-28
配置zabbix自动发现和自动注册。
C语言提高篇(三)
logback源码阅读(二)日志打印,自定义appender,encoder,pattern,converter
随机推荐
文件加密软件有哪些?保障你的文件安全
You can't accept 60% slump, there is no eligible for gain of 6000% in 2021-05-27
deal!It's July 30th!
数值的整数次方
About the development forecast of the market outlook?2021-05-23
腾讯安全游戏行业研讨会:生态共建,护航游戏产业健康发展
微信小程序-最近动态滚动实现
FFmpeg AVPacket详解
网络安全第一次作业(2)
Configure zabbix auto-discovery and auto-registration.
目前想通过提取本地excel文件创建数据表,在sql语句这出了一些问题
logback源码阅读(一)获取ILoggerFactory、Logger
此次519暴跌的几点感触 2021-05-21
Sentinel源码(六)ParamFlowSlot热点参数限流
C语言提高篇(三)
Embedded system driver primary [2] - based on character device driver _ basic framework
关于Google词向量模型(googlenews-vectors-negative300.bin)的导入问题
RKMPP API安装使用总结
腾讯安全发布Tencent Cloud EdgeOne,为企业出海打造安全加速一体化服务
logback源码阅读(二)日志打印,自定义appender,encoder,pattern,converter