动态调整Logback的Appender的解决方案

前言

在复杂的应用环境中,动态调整Logback的Appender是非常实用的功能。

日志系统对于监控和调试应用程序至关重要,尤其是在生产环境。当应用运行时,不同的场景可能需要不同的日志级别或日志输出策略。例如,在出现异常情况或进行性能调优时,可能需要临时增加日志详细度(如从INFO级别改为DEBUG级别),以便收集更详尽的运行信息;而在系统正常运行且资源受限时,则可能需要降低日志级别以减少磁盘I/O和提升系统性能。

另外,有时需要根据实际需求灵活地更改日志的存储位置、格式或者启用新的Appender来将日志发送到不同的目标(如追加到文件、输出到控制台、发送到远程服务器或消息队列等)。

通过动态调整Appender,开发人员可以在不重启应用的前提下即时响应变化,增强了系统的可维护性和灵活性。这对于运维人员快速定位问题、实时分析系统状态以及保证服务稳定运行具有重要价值。

配置示例

如下,是一个logback.xml定义,配置了两个appender:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 常规日志输出   -->
    <appender name="SIMPLE-CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%level %thread %msg \r\n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!-- json格式日志输出   -->
    <appender name="JSON-CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>{ "level":"%level", "thread": "%thread", "msg":"%msg" }\r\n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="SIMPLE-CONSOLE"/>
    </root>

    <logger name="com.xxd.demo" level="INFO" additivity="false">
        <appender-ref ref="SIMPLE-CONSOLE"/>  
        <appender-ref ref="JSON-CONSOLE"/>
    </logger>
</configuration>
  • SIMPLE-CONSOLE 输出纯文本行的日志
  • JSON-CONSOLE com.xxd.demo包下的,输出json格式的日志

看下效果,日志使用SIMPLE-CONSOLE和JSON-CONSOLE输出:

如果在项目运行过程中,想要把JSON-CONSOLE这个appender干掉,怎么办?

下面提供一个我自己摸索出来的解决思路,借助这个思路,如果有其它调整需求,可以参考一下。

Append调用位置

看上面图片右侧的调用栈,Logger.info()等方法,从info方法最终调用到appendLoopOnAppenders()方法,遍历配置的appender进行调用,输出日志。

如果我现在想要移除或增加某个appender,关键在于appenderList对象的处理。

解决思路

这个类里有两个关键的方法:

  • addAppender: 附加一个appender到appenderList。如果追加器已经在列表中,则不会再次添加。
  • detachAppender: 从appenderList移除一个appender

上面对应这几个方法的实现可以看源码。

现在需要解决的问题是如何调用到这两个方法。

很简单,我们创建的Logger对象有这个属性:AppenderAttachableImpl, AppenderAttachableImpl类有appenderList属性。

并且也提供了这几个方法,供我们操作:

如下是我们平常创建一个Logger对象

swift 复制代码
public static Logger log = LoggerFactory.getLogger(Application.class);

/**  
* 缓存每个logger对象配置的相关appender.  
*/  
public static Map<ch.qos.logback.classic.Logger, Appender<ILoggingEvent>> cache = new HashMap<>();

增加一个appender:

java 复制代码
    public static void addAppender() {
        ILoggerFactory factory = LoggerFactory.getILoggerFactory();
        // 强转为这个对象
        LoggerContext context = (LoggerContext) factory;
        List<ch.qos.logback.classic.Logger> loggerList = context.getLoggerList();
        loggerList.forEach(logger -> {
            // 遍历所有的logger实例
            if (cache.containsKey(logger)) {
                logger.addAppender(cache.get(logger));
                cache.remove(logger);
            }
        });
    }

移除appender:

java 复制代码
    public static void detachAppender() {
        ILoggerFactory factory = LoggerFactory.getILoggerFactory();
        LoggerContext context = (LoggerContext) factory;
        List<ch.qos.logback.classic.Logger> loggerList = context.getLoggerList();
        loggerList.forEach(logger -> {
            Appender<ILoggingEvent> appender = logger.getAppender("JSON-CONSOLE");
            if (appender != null) {
                cache.put(logger, appender);
                logger.detachAppender("JSON-CONSOLE");
            }
        });
    }

测试看下效果:

动态修改

目前已经实现了运行中的增、删appender,至于动态修改,方案比较多,这里提供一个思路:

对接配置中心,比如nacos,监听配置变动处理,在配置属性中配置是否隐藏或使用哪些appender,进行相关处理。

或者定时检查是否有配置变动等。

具体实现取决于自己项目实际情况。

相关推荐
向宇it14 分钟前
【从零开始入门unity游戏开发之——C#篇24】C#面向对象继承——万物之父(object)、装箱和拆箱、sealed 密封类
java·开发语言·unity·c#·游戏引擎
小蜗牛慢慢爬行16 分钟前
Hibernate、JPA、Spring DATA JPA、Hibernate 代理和架构
java·架构·hibernate
星河梦瑾1 小时前
SpringBoot相关漏洞学习资料
java·经验分享·spring boot·安全
黄名富1 小时前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua
love静思冥想1 小时前
JMeter 使用详解
java·jmeter
言、雲1 小时前
从tryLock()源码来出发,解析Redisson的重试机制和看门狗机制
java·开发语言·数据库
TT哇1 小时前
【数据结构练习题】链表与LinkedList
java·数据结构·链表
Yvemil72 小时前
《开启微服务之旅:Spring Boot 从入门到实践》(三)
java
Anna。。2 小时前
Java入门2-idea 第五章:IO流(java.io包中)
java·开发语言·intellij-idea
.生产的驴2 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven