logback源码解析及自定义Appender、自定义logback.xml标签

2023-11-04

本文基于slf4j 1.7.25

logback妙用之自定义Converter、异步打印日志
logback源码解析分为两条线:一、加载解析配置文件;二、使用Logger打印日志。

0. 基本概念介绍

  1. Appender:定义日志的格式和输出目的地。
  2. Logger:输出日志的对象,关联上Appender执行相关操作。(一个Logger可以关联多个Appender)

1. 简单实用示例

​ 一般我们会配置一个logback.xml,在程序中使用。

  1. 代码示例

    public class Test {
    
        private final static Logger LOGGER = LoggerFactory.getLogger(Test.class);
        
        public void test(){
            LOGGER.info("xxx");
        }
    }
    
  2. logback.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration scan="false">
        <springProperty scope="context" name="logLevel" source="logging.level.root"/>
        <property name="log.pattern"
                  value="[%d{yyyy-MM-dd HH:mm:ss:SSS}] [%t] [%level] [%logger{80}] [%X{transactionId}] [%X{spanId}] [%X{parentId}] [%X{serviceId}] [%X{protocol}] [%X{logType}] - %m%n"/>
        <property name="additivity.value" value="false"/>
    
        <!-- 标准 -->
        <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
            <layout class="ch.qos.logback.classic.PatternLayout">
                <pattern>${log.pattern}</pattern>
            </layout>
        </appender>
        
        <appender name="accessapender" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <File>${log.access}/access.log</File>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <FileNamePattern>
                    ${log.access}/access.log.%d{yyyy-MM-dd-HH}.%i
                </FileNamePattern>
                <TimeBasedFileNamingAndTriggeringPolicy
                                                        class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <MaxFileSize>5MB</MaxFileSize>
                </TimeBasedFileNamingAndTriggeringPolicy>
            </rollingPolicy>
            <encoder>
                <pattern>${log.pattern}</pattern>
                <charset>UTF-8</charset>
            </encoder>
        </appender>
    
        <logger name="access" additivity="${additivity.value}" level="${logLevel}">
            <appender-ref ref="accessapender"/>
        </logger>
        
        <root level="${logLevel}">
            <appender-ref ref="accessapender"/>
        </root>
        
    </configuration>
    

2. 加载解析配置logback配置文件源码解析

  1. 我们先从private final static Logger LOGGER = LoggerFactory.getLogger(Test.class);跟进去

    LoggerFactory

    public final class LoggerFactory {
        
        public static Logger getLogger(Class<?> clazz) {
            //跟进去,内部方法
            Logger logger = getLogger(clazz.getName());
            return logger;
        }
    
        public static Logger getLogger(String name) {
            //获取日志工厂,我们看一下
            ILoggerFactory iLoggerFactory = getILoggerFactory();
            return iLoggerFactory.getLogger(name);
        }
    
        public static ILoggerFactory getILoggerFactory() {
            //判断状态,如果没初始化,先初始化,不过多关注
            if (INITIALIZATION_STATE == UNINITIALIZED) {
               //...
            }
            switch (INITIALIZATION_STATE) {
                case SUCCESSFUL_INITIALIZATION:
                    //初始化成功,获取日志工厂.我们看看StaticLoggerBinder这个单例类
                    return StaticLoggerBinder.getSingleton().getLoggerFactory();
                    //...
            }
            throw new IllegalStateException("Unreachable code");
        }
        
    }
    
  2. StaticLoggerBinder

    public class StaticLoggerBinder implements LoggerFactoryBinder {
        //日志上下文
        private LoggerContext defaultLoggerContext = new LoggerContext();
        
        //该类有静态代码块,先执行
        static {
            SINGLETON.init();
        }
        
        void init() {
    		//ContextInitializer,初始化日志上下文,解析logback.xml文件,我们看看autoConfig
            new ContextInitializer(defaultLoggerContext).autoConfig();
            contextSelectorBinder.init(defaultLoggerContext, KEY);
            initialized = true;
        }
    }
    
  3. ContextInitializer

    public class ContextInitializer {
    	public void autoConfig() throws JoranException {
            StatusListenerConfigHelper.installIfAsked(loggerContext);
            // 从classpath下获取logback配置文件的地址,默认按顺序查找logback-test.xml,logback.groovy,logback.xml
            // 如果为空,则默认返回一个(
            // SpringBoot的是自定义了一个查找解析配置文件的类,继承了logback的,所以此处初始化解析类流程有些许不同
            URL url = findURLOfDefaultConfigurationFile(true);
            if (url != null) {
                //加载解析配置文件,看看
                configureByResource(url);
            } else {
                //...
            }
        }
            
        public void configureByResource(URL url) throws JoranException {
      		//...        
            
            //如果是xml后缀的logback文件,利用JoranConfigurator来解析(SpringBoot是继承了该类来解析)
            else if (urlString.endsWith("xml")) {
                JoranConfigurator configurator = new JoranConfigurator();
                configurator.setContext(loggerContext);
                //我们就不进去看了
                configurator.doConfigure(url);
            }
        }
    }
    
  4. 加载解析配置文件后续不看了,但是我们总结一下加载解析配置文件(GenericConfigurator类):

    1. 用sax解析xml,并将解析事件转换成SaxEvent(StartEvent:开始标签,BodyEvent:标签属性,EndEvent:结束标签),放入saxEventList。

    2. 创建Interpreter,遍历saxEventList,匹配相应SaxEvent执行Interpreter中相应的动作(标签中的class实例化成对象,子标签会当成父标签的属性设置进去;标签里面的属性也会设置到标签对象中

    3. 不同的标签会对应不同的Action(AppenderAction,ConfigurationAction,LoggerAcction,RootLoggerAction,PropertyAction),JoranConfigurator中也注册了很多Action(保存在RuleStore中)。执行相应Action的操作,我们可以看到Action中begin、body、end方法就是对应开始标签、标签属性、结束标签执行的动作。

      public abstract class Action extends ContextAwareBase {
          
          public abstract void begin(InterpretationContext ic, String name, Attributes attributes) throws ActionException;
      
          public void body(InterpretationContext ic, String body) throws ActionException {
          }
      
          public abstract void end(InterpretationContext ic, String name) throws ActionException;
      
      }
      
  5. 所以到此结束后,logback.xml文件解析完了。其中定义的类实例化了,属性也赋值了 。

  6. 获取Logger,我们从获取LoggerFactory代码可以看到获取的LoggerFactory其实就是LoggerContext。然后从LoggerContext获取相应的Logger。

3. 加载解析配置文件拓展点

上面我们知道每个xml标签都可以对应一个Action来处理,如果我们要自定义自己的标签,也可以对应一个Action来处理。

3.1 标签对应复杂对象,默认用NestedComplexPropertyIA解析执行

  1. logback.xml。如下自定义Appender,自定义标签testService。TestService有一个属性url,TestAppender有一个属性TestService。

    <appender name="test" class="com.zeng.utils.TestAppender">
        <File>${log.collection}/spring.log</File>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
        <testService class="com.zeng.utils.TestService">
            <url>http://xxx</url>
        </testService>
    </appender>
    
  2. TestService

    public class TestService {
    
        private String url;
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    }
    
  3. TestAppender

    public class TestAppender extends RollingFileAppender {
    
        private TestService testService;
    
        public TestService getTestService() {
            return testService;
        }
    
        public void setTestService(TestService testService) {
            this.testService = testService;
        }
    }
    
  4. 不是应该在RuleStore中添加一个testService标签对应一个Action吗?其实也不一定需要。如果testService标签对应是一个复杂对象,logback默认会使用NestedComplexPropertyIA来解析该标签。我们可以看到如下图解析出来的对象都已经赋值好了。

    在这里插入图片描述

3.2 标签对应的是简单对象,默认用NestedBasicPropertyIA解析执行

​ 就不举例了,只需吧上面例子中TestService换成一个简单的String类型即可。

4. 使用Logger打印流程解析及拓展点

  1. 通过Logger打印日志,我们就不做解析了。画一个图解析一下

    在这里插入图片描述

我们通过上面的图EnCoder.doEncoder用来编码,Layout.doLayout用来格式化日志。我们重点关注红色框中的subAppend。因为我们如果要自定义一个Appender,主要逻辑也是在subAppend中实现

  1. 示例(我们把上面的例子改一下)

    TestService

    public class TestService {
    
        private String url;
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    
        //发送http请求,代码省略
        public void httpRequest() {
    
        }
    }
    
    

    TestAppender

    public class TestAppender extends RollingFileAppender {
    
        private TestService testService;
    
    
        public TestService getTestService() {
            return testService;
        }
    
        public void setTestService(TestService testService) {
            this.testService = testService;
        }
    
        //重写subAppend方法,写日志时,发送请求,event中包含日志相关信息。
        @Override
        protected void subAppend(Object event) {
            testService.httpRequest();
            super.subAppend(event);
        }
    }
    

5. 总结

我们通过以上的源码解析,大致了解了logback的配置解析加载,和logback打印日志。

清楚了如果自定义logback.xml标签和自定义Appender。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

logback源码解析及自定义Appender、自定义logback.xml标签 的相关文章

  • 是否可以同时读取和写入 java.net.Socket?

    是否可以同时从套接字读取和写入 我有一个连续读取套接字的线程 由于只有一个线程正在从套接字读取数据 因此读取操作是线程安全的 现在我有很多线程 比如100 写入套接字 因此 很明显 我必须通过这样做来使写操作线程安全 package com
  • 将 Spring 的 @Scheduled 注解与特定执行器一起使用

    如何告诉我的 Spring 计划方法使用特定的执行器运行 例如 这是我的 spring 调度程序方法之一 Scheduled fixedRate 1000 public void scheduleJobs doThese 这是我的 Java
  • 如何从 JAXB 编组 XML 文件中删除 xmlns:xsi 和 xsi:type

    我有一组 JAXB 生成的类 其中一些类具有接受的 setter 方法 对象 作为参数 例如 XmlAccessorType XmlAccessType FIELD XmlType name Car propOrder defaultCar
  • 什么 RoundingMode 常量 100% 与 Math.round 一样工作?

    我的意思是有没有舍入模式 https docs oracle com javase 8 docs api java math RoundingMode html常数准确地描述了什么Math round arg 做 据我所知 Math rou
  • 颜色资源 ID 返回错误值

    我试图在 onCreate 期间以编程方式从颜色资源设置文本颜色 但得到了一些奇怪的结果 我在 res colors xml 中定义了一个颜色资源
  • 使用 Netbeans 导出 JAR

    如何使用Netbeans将java项目导出到JAR 我找不到像 Eclipse 那样的选项 您需要启用该选项 Project Properties gt Build gt Packaging gt Build JAR after compi
  • 如何在 Java 中从任意基数转换为基数 10

    我是 Java 新手 我想编写一个程序 仅使用算术运算将基数 2 3 4 5 6 7 8 9 16 转换为基数 10 我已经完成了从键盘读取字符串 如果数字是十六进制 并将其转换为整数 之后我做了一个 while 循环 将数字分割为数字并将
  • 如何从c调用Java函数

    我被这个问题困扰了 我需要从 c c 调用 Java 函数 在示例和教程中 我只看到一个java应用程序调用一个c方法 并在同一个方法中调用另一个java方法 但我想做的是从代码的任何部分调用java方法 这就是我所拥有的 static J
  • 异步方法的默认 EJB 事务模式?

    当我有一个 AsynchronousEJB 中的方法 并且我没有指定 TransactionAttribute 那么容器到底是如何处理事务边界的呢 显然 它不能使用调用线程的事务 那么它是做什么的呢 同样的问题 但涉及由 TimerServ
  • 如何按照最初给出的时区存储和显示日期?

    我有一台服务器正在从不同时区的客户端提供数据 数据源包含人物 他们的出生日期和其他事件日期 出于我们的目的 如果我们可以将日期存储为给我们的日期 那就很方便了 例如 如果客户位于加利福尼亚州 并且告诉我们该人的出生日期是 5 月 31 日
  • netty 4.x 中 ServerBootstrap.option() 和 ServerBootstrap.childOption() 有什么区别

    根据文档4 0 中值得注意的新内容 http netty io wiki new and noteworthy in 4 0 html wiki h3 31 netty4提供了新的bootstrap API 文档给出了以下代码示例 publ
  • 为什么Java不支持C中的clrscr这样的函数?

    我有一个问题 对很多人来说可能听起来很愚蠢 但我不能停下来把它发布在这里 因为在互联网上找不到任何东西 为什么java没有我们在C中使用的clrscr之类的函数 如果我创建了一个基于用户输入反复迭代的 java 控制台应用程序 然后如果我想
  • 基本的 Swing 库? JGoodies,JFreeChart [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • Java ReentrantReadWriteLocks - 如何在读锁中安全地获取写锁?

    我现在在我的代码中使用可重入读写锁 http java sun com javase 6 docs api java util concurrent locks ReentrantReadWriteLock html同步对树状结构的访问 这
  • 在Java中解析包含multipart/form-data请求体的字符串

    问题陈述 我认为标题说明了一切 我正在寻找解析 a 的方法String包含 multipart form data HTTP 请求的正文部分 IE 字符串的内容看起来像这样 xyzseparator blah Content Disposi
  • 从邮件服务器读取发送的邮件

    我知道如何从 INBOX 文件夹中检索邮件 但现在我想从 SENT ITEMS 文件夹中检索邮件 我正在使用 imap 检索数据 让我知道我应该在此函数中传递什么参数才能从 SENT ITEMS 文件夹中获取邮件Folder folder
  • 根据 netbeans 中的单选按钮切换组件的“启用”属性

    我在按钮组中有两个单选按钮 在同一面板中我有一个文本框和一个按钮 我想仅在选择第二个按钮时启用文本框和按钮 并在选择另一个单选按钮时禁用文本框和按钮 我已经尝试过这个但没有成功 private void radio button2Actio
  • JavaFX - 当文本字段具有焦点时加速器不工作

    在我的应用程序中 我有一个使用加速器的屏幕 我正在使用功能键 F3 在我的应用程序中执行操作 它每次都工作正常 但是当我单击此屏幕上的任何文本字段时 功能键不会执行 这是我设置加速器的代码 scene getAccelerators put
  • SnakeYaml“无法找到属性错误”

    这是我的 config yml 的一部分 Authenctication AuthenticationConfig AuthencticationType LDAP LDAPConfig LDAPUrl ldap localhost 389
  • @Transactional 方法调用另一个没有 @Transactional 注解的方法?

    我在 Service 类中看到了一个方法 该方法被标记为 Transactional 但它还调用同一类中的一些其他方法 这些方法未标记为 Transactional 这是否意味着对单独方法的调用导致应用程序打开与数据库的单独连接或挂起父事务

随机推荐

  • Ubuntu18.04安装indicator-sysmonitor显示实时网速

    系统环境 Ubuntu18 04 6 LTS 1 添加源 sudo add apt repository ppa fossfreedom indicator sysmonitor 2 更新源 sudo apt get update 3 安装
  • 最长公共子序列(输出公共序列)

    给出两个字符串A B 求A与B的最长公共子序列 子序列不要求是连续的 比如两个串为 abcicba abdkscab ab是两个串的子序列 abc也是 abca也是 其中abca是这两个字符串最长的子序列 Input 第1行 字符串A 第2
  • python 基础篇 day 4 选择结构—— if 结构

    文章目录 if 基础结构 单 if 语句 if else 语句 if elif else 语句 嵌套的 if 语句 if 进阶用法 使用比较运算符 使用逻辑运算符 使用 in 关键字 range 函数 使用 is 关键字 使用 pass 语
  • 【数据库管理】十分钟了解啥是三级封锁协议、X锁和S锁

    一 为什么要用锁 在多用户共享系统中 许多事务可能同时对同一数据进行操作 称为 并发操作 此时数据库管理系统的并发控制子系统负责协调并发事务的执行 保证数据库的完整性不受破坏 同时避免用户得到不正确的数据 如果并发不受控制 则数据库的多个事
  • [HNOI2019]校园旅行(生成树+二分图+DP+回文性质)

    神仙好题啊 膜拜出题者 洛谷题目传送门 题目描述 给定一张无向图 每个点有权值v 0或1 q次询问 每次给出两个点 判断两点间是不是有一条路径 使得路径上经过的点的权值构成一个回文串 每个点 每条边可以重复走 解题思路 30pts 我们形式
  • 信息安全技术 信息安全风险评估规范

    风险处理计划 对不可接受的风险应根据导致该风险的脆弱性制定风险处理计划 风险处理计划中明确应采取的弥 补弱点的安全措施 预期效果 实施条件 进度安排 责任部门等 安全措施的选择应从管理与技术两 个方面考虑 安全措施的选择与实施应参照信息安全
  • Figma实用小技巧

    1 介绍 原作 草帽 视频地址 文字版整理 岩鸣杨子 2 快速拖拽调整数值 按住option可在数值上拖拽调整 不按option时只能在X Y W H 旋转 圆角等的图标上拖拽调整 不能调整透明度的数值 3 属性粘贴复制 shift X 互
  • java web选择题_Servlet、JSP选择题

    Java EE软件工程师认证考试 试题库 选择题 一 选择题 包括单选和双选 1 A 对于B S架构的工作流程 以下说法正确的是 选择一项 A 浏览器接受用户的输入 gt 浏览器向服务器端发送请求 gt 服务器端数据处理 gt 响应数据至客
  • shell命令:在windows的git bash执行ln -s 无效

    在windows的git bash执行ln s 无效 网上的方法试过 但是没用 仍然是创建文件及目录的拷贝而不是创建软链接 windows n WINDIR function common link if windows then echo
  • ImageLoader简单使用

    1 导包 implementation com nostra13 universalimageloader universal image loader 1 9 5 2 权限
  • kafka多个消费者消费一个topic_Kafka 为什么使用消费者组?

    1 消费者组的特点 这是 kafka 集群的典型部署模式 消费组保证了 一个分区只可以被消费组中的一个消费者所消费 一个消费组中的一个消费者可以消费多个分区 例如 C1 消费了 P0 P3 一个消费组中的不同消费者消费的分区一定不会重复 例
  • qt中插入数据库的数据类型是float类型时

    一般正常的插入数据库的类型为varchar类型时 我们可以在程序中QString类型来直接进行插入 如下数据库类型 2 但是如果你想要插入的是float类型的数据时 就会发现单纯使用QString是没办法进行插入的 如下数据库类型 3 这个
  • format 修改matlab数据显示的位宽

    修改matlab数据显示的位宽 format long maltab浮点数据显示一般默认小数点后四位 如 如果需要扩大显示的精度 可以使用format long 对于双精度数据显示15位小数位 对于单精度数据显示7位小数位 如 通过help
  • Spring中同一个service调用方法不回滚的解决方法

    在java配置类上添加注解 EnableAspectJAutoProxy exposeProxy true 方式暴漏代理对象 然后在service中通过代理对象AopContext currentProxy 去调用方法 Service pu
  • echarts 渐变

    series name 电流 A type line showBackground true itemStyle color 437EE0 areaStyle color type linear x 0 y 0 x2 0
  • 【毕业设计】深度学习指纹识别算法研究与实现 - python opencv

    文章目录 1 前言 2 指纹识别方式 2 1 电容式指纹识别 2 2 光学式指纹识别 3 指纹识别算法实现 3 1 指纹识别算法流程 3 2 指纹图像预处理 3 3 指纹图像目标提取 3 4 指纹图像增强 3 5 指纹特征提取 3 6 指纹
  • Java Web项目练习---信息管理系统

    一 项目简介 本次项目为Java Web的一个练习 是小白入门级较好的资料 文章如有不妥 请多多指教 1 技术需求 Servlet JSP MVC设计 JDBC MVC设计思想将整个项目分为三层结构 分别为model层 包括pojo层 da
  • <flutter>跨平台开发小白入坑 Dart Dio Pubspec 打包 MethodChannel 解析 Xcode hybrid

    1 资源文件和依赖三方包 pubspec yaml pubspec yaml文件可以说是和安卓的gradle文件差不多 它用来描述版本号 sdk 依赖等的 在资源导入方面同安卓不一样的是 flutter需要在pubspec yaml中声名
  • 大学生团体天梯赛L3-016 二叉搜索树的结构(非暴力解法,建树后O1查找)

    不难发现 里面绝大多数的查询都是跟父节点有关 所以我们每次插入的时候维护他和他的父节点 同层则再维护一个层节点 1 指针法 include
  • logback源码解析及自定义Appender、自定义logback.xml标签

    本文基于slf4j 1 7 25 目录 0 基本概念介绍 1 简单实用示例 2 加载解析配置logback配置文件源码解析 3 加载解析配置文件拓展点 3 1 标签对应复杂对象 默认用NestedComplexPropertyIA解析执行