动态调整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,进行相关处理。

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

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

相关推荐
IT_Octopus3 小时前
IntelliJ 本地日志路径自定义:`-DLOG_PATH=./logs` 为什么总“不听话“
java·log4j·intellij-idea
长征coder4 小时前
【无标题】
java·性能优化
新时代牛马4 小时前
PCI与PCIe 硬件原理、配置空间/BAR 与 Linux 驱动完整篇:从 LTSSM、TLP 到 ECAM 与probe
java·linux·服务器
Bs_MoneyMagnet5 小时前
基于springboot+vue的医疗健康便民服务平台的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring
sunshine22 girl5 小时前
Java学习一 环境配置2 Idea的入门使用和maven配置,项目启动脚本配置
java·maven
有点。6 小时前
*C++哈夫曼树与哈夫曼编码
java·c++·servlet
CRMEB系统商城7 小时前
CRMEB标准版系统(Java)v3.1正式发布
java·spring·微信小程序·php·教育电商
写后端的胖头鱼7 小时前
【高频面试题】Java 基础类型 + 包装类 + String 互相转换
java·开发语言
weixin_460443567 小时前
企业考试系统从.NET迁移到Java+Linux:数据库迁移、接口兼容与灰度切换实践
java·煤矿培训考试系统·煤矿在线考试系统·煤矿安全培训·煤矿考试系统·一人一档
香菜TTT8 小时前
Redis的哨兵机制
java·数据库·redis