IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 动态刷新日志级别 -> 正文阅读

[Java知识库]动态刷新日志级别

作者:https://csdnimg.cn/release/blogv2/dist/components/js/pc_wap_commontools-60a48feb57.min.js

概述

日志模块是每个项目中必须的,用来记录程序运行中的相关信息。一般在开发环境下使用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种类型的日志,分别是

  1. JDK内置的Log(JavaLoggingSystem)
  2. Log4j2(Log4J2LoggingSystem)
  3. 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是个抽象类,内部有这几个方法:

在这里插入图片描述

  1. beforeInitialize方法:日志系统初始化之前需要处理的事情。抽象方法,不同的日志架构进行不同的处理
  2. initialize方法:初始化日志系统。默认不进行任何处理,需子类进行初始化工作
  3. cleanUp方法:日志系统的清除工作。默认不进行任何处理,需子类进行清除工作
  4. getShutdownHandler方法:返回一个Runnable用于当jvm退出的时候处理日志系统关闭后需要进行的操作,默认返回null,也就是什么都不做
  5. getSupportedLogLevels: 返回日志系统实际支持的一组 LogLevel。
  6. setLogLevel方法:抽象方法,用于设置对应logger的级别
  7. 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>
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-07-21 21:22:54  更:2022-07-21 21:26:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:15:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码