返回顶部
首页 > 资讯 > 后端开发 > JAVA >【Logback】<logger>、<root>标签详解
  • 788
分享到

【Logback】<logger>、<root>标签详解

logback链表java 2023-09-24 21:09:37 788人浏览 八月长安
摘要

文章目录 背景一、\使用1.1、使用示例1.1、属性配置说明 & 演示1.1.1、name1.1.2、level1.1.3、additivity1.1.3.1、效果演示:additivity=

文章目录

背景

排查一个项目的问题,发现打印了一堆重复日志…,为避免日志爆盘被投诉,写一下logback的使用和解析。 因为重复日志问题引发的,所以本文优先标签,其余的后续在写…

版本:logback-1.2.3

一、使用

如果需要定制指定模块或者类的日志信息,可以通过标签来实现,通过其name属性指定包或者类全路径即可

是有父子关系的
是一个特殊的 ,其name="ROOT",是所有的祖先节点,先了解,下面会讲。
父子关系是根据name属性的层级来确定的

,其父子关系如下(先了解结论,下面分析中会讲):


└─
│ └─
│ │ └─
│ │ │ └─

1.1、使用示例

<logger name="com.qbhj" level="INFO" additivity="true">        <appender-ref ref="CONSOLE"/>    <appender-ref ref="appender1"/>    <appender-ref ref="appender1"/>logger><logger name="com.qbhj.logback" additivity="true">    <appender-ref ref="CONSOLE"/>logger><logger name="com.qbhj.logback.empty" additivity="true">logger>

Tips:

可以配置 0-N
2、属性子元素2个部分构成,日志输出是所引用的来执行的
3、本身是一个链表,有一个父节点和N个子节点,具体在1.3、解析中详细说明

1.1、属性配置说明 & 演示

属性名属性值是否必填说明
name包或者类路径如: com.qbhj 或 com.qbhj.Test.class
levelOFF、ERROR、WARN、INFO、DEBUG、TRACE、ALL日志级别 ,默认继承父的日志级别
additivitytrue | false是否叠加,默认true ,建议配置为false,见日志规范

1.1.1、name

指定包或者类全路径,则该的配置就对其生效,默认使用的日志配置

1.1.2、level

日志级别
Tips:

日志级别level属性是有继承关系,其优先级如下(此表摘自logback官网):

Logger nameAssigned LevelEffective Level
rootDEBUGDEBUG
chapters.configurationINFOINFO
chapters.configuration.MyApp3nullINFO
chapters.configuration.FooDEBUGDEBUG

总结
若自身没设置 level 则使用其父Logger的level
若父级都没设置,则使用节点的,不配置则其默认级别level为DEBUG

1.1.3、additivity

是否允许父级打印自身name指定范围内的日志。若允许,则会重复打印日志。

1.1.3.1、效果演示:additivity=true

1)、打印日志代码

package com.qbhj.logback.controller;// import .....@Slf4j@RestControllerpublic class LoGController {    @GetMapping("/additivity")    public void additivity(@RequestParam(defaultValue = "1") Integer num) {        System.out.println("###################################### num: " + num);        for (int i = 0; i < num; i++) {            log.info("/additivity......");        }    }}

1)、日志配置,只配置root,无

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

2)、结果,日志只打印了一遍

###################################### num: 120:36:06.170  INFO --- [     Http-NIO-8080-exec-1] c.qbhj.logback.controller.LogController  : /additivity......

3)、说明

所有Logger都没有配置appender,所以无日志输出
root配置了appender=CONSOLE,所以控制台会打印日志

Loggerappenderadditivity(默认true打印日志是否被调用
CONSOLEtrue
└─true
│ └─true
│ │ └─true
│ │ │ └─true

1)、增加配置name=com.qbhj.logback.controller中都有配置控制台(CONSOLE)输出,共配置了2遍

    <logger name="com.qbhj.logback.controller" level="INFO" additivity="true">                <appender-ref ref="CONSOLE"/>    logger>    <root level="INFO">                <appender-ref ref="CONSOLE"/>    root>

2)、结果,日志打了2遍,是各自打一遍

###################################### num: 120:54:51.165  INFO --- [     http-nio-8080-exec-2] c.qbhj.logback.controller.LogController  : /additivity......20:54:51.165  INFO --- [     http-nio-8080-exec-2] c.qbhj.logback.controller.LogController  : /additivity......

3)、说明

配置了appender=CONSOLE ,控制台打印日志
root配置了appender=CONSOLE,所以控制台会打印日志

Loggerappenderadditivity(默认true打印日志是否被调用
CONSOLEtrue
└─true
│ └─true
│ │ └─true
│ │ │ └─CONSOLEtrue

1)、再增加配置name=com.qbhj.logback logback包肯定包含 LogController类的,即中都有配置控制台(CONSOLE)输出,共配置了3遍

    <logger name="com.qbhj.logback.controller" level="INFO">                <appender-ref ref="CONSOLE"/>    logger>    <logger name="com.qbhj.logback" level="INFO">                <appender-ref ref="CONSOLE"/>    logger>    <root level="INFO">                <appender-ref ref="CONSOLE"/>    root>

2)、结果,日志打了3遍,2个各自打一遍

###################################### num: 120:58:23.914  INFO --- [     http-nio-8080-exec-1] c.qbhj.logback.controller.LogController  : /additivity......20:58:23.914  INFO --- [     http-nio-8080-exec-1] c.qbhj.logback.controller.LogController  : /additivity......20:58:23.914  INFO --- [     http-nio-8080-exec-1] c.qbhj.logback.controller.LogController  : /additivity......

3)、说明

2个 配置了appender=CONSOLE ,控制台打印日志2遍
root配置了appender=CONSOLE,控制台会打印日志

Loggerappenderadditivity(默认true打印日志是否被调用
CONSOLEtrue
└─true
│ └─true
│ │ └─CONSOLEtrue
│ │ │ └─CONSOLEtrue
1.1.3.1、效果演示:additivity=“false”

1)、同样的配置,全部改为additivity=“false”

    <logger name="com.qbhj.logback.controller" level="INFO" additivity="false">                <appender-ref ref="CONSOLE"/>    logger>    <logger name="com.qbhj.logback" level="INFO" additivity="false">                <appender-ref ref="CONSOLE"/>    logger>    <root level="INFO">                <appender-ref ref="CONSOLE"/>    root>

2)、结果:日志只打印了一遍

###################################### num: 121:00:24.598  INFO --- [     http-nio-8080-exec-1] c.qbhj.logback.controller.LogController  : /additivity......

3)、说明

,配置了appender=CONSOLE所以打印日志。其additivity=“false”,不调用父级,结束

Loggerappenderadditivity(默认true打印日志是否被调用
CONSOLEtrue
└─true
│ └─true
│ │ └─CONSOLEfalse
│ │ │ └─CONSOLEfalse

1.2 appender-ref

示例

<logger name="com.qbhj" level="INFO" additivity="false">        <appender-ref ref="CONSOLE"/>    <appender-ref ref="appender1"/>    <appender-ref ref="appender1"/>logger><logger name="com.qbhj.logback" additivity="false">    <appender-ref ref="CONSOLE"/>logger><logger name="com.qbhj.logback.empty" additivity="false">logger>

每个logger可以配置任意(含0)个
2、语法:ref=appender[name],声明 name属性值

二、使用

2.1、属性

属性名属性值是否必填说明
levelOFF、ERROR、WARN、INFO、DEBUG、TRACE、ALL日志级别,默认DEBUG

的本质是一个名为ROOT的特殊logger,即
root是所有logger的根节点

<root level="DEBUG">        <appender-ref ref="CONSOLE"/>        <appender-ref ref="FILE_ALL"/>    <appender-ref ref="FILE_WARN"/>    <appender-ref ref="FILE_ERROR"/>root>

三、解析

3.1、链表

是一个链表,有父节点和子节点
2、除父节点为null外,所有logger一定有父节点,为所有logger的根节点

ch.qos.logback.classic.Logger

package ch.qos.logback.classic;//..........public final class Logger implements org.slf4j.Logger, LocationAwareLogger, AppenderAttachable<ILoggingEvent>, Serializable {    // ..........        private String name;    // The assigned levelInt of this logger. Can be null.    transient private Level level;    // The effective levelInt is the assigned levelInt and if null, a levelInt is    // inherited fORM a parent.    transient private int effectiveLevelInt;// root 为所有logger的祖先(根)节点        transient private Logger parent;        transient private List<Logger> childrenList;// additive 默认 true    transient private boolean additive = true;    final transient LoggerContext loggerContext;    // ..........        private boolean isRootLogger() {      // only the root logger has a null parent// 只有root的parent为null      return parent == null;    }       // .......... }

3.2、root是一个名为 ROOT 的特殊logger,其 parent 为 null

LoggerContext 初始化的时候就在其构造函数中创建了root了
root默认日志级别为 DEBUG

ch.qos.logback.classic.LoggerContext

public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle {        public static final boolean DEFAULT_PACKAGING_DATA = false;    final Logger root;    private int size;    private int noAppenderWarning = 0;    final private List<LoggerContextListener> loggerContextListenerList = new ArrayList<LoggerContextListener>();    private Map<String, Logger> loggerCache;    // .............    public LoggerContext() {        super();        this.loggerCache = new ConcurrentHashMap<String, Logger>();        this.loggerContextRemoteView = new LoggerContextVO(this);// 初始化                 this.root = new Logger(Logger.ROOT_LOGGER_NAME, null, this);// ROOT的默认日志级别 DEBUG        this.root.setLevel(Level.DEBUG);        loggerCache.put(Logger.ROOT_LOGGER_NAME, root);        initEvaluatorMap();        size = 1;        this.frameworkPackages = new ArrayList<String>();    }    // .......... }

3.3、 name属性和继承关系

会根据name属性的值,逐级创建子logger,如com.qbhj.Test.class中 log.info(“print log…”),一共会创建3个logger

1)、com
2)、com.qbhj
3)、com.qbhj.Test

ch.qos.logback.classic.LoggerContext#getLogger(java.lang.Class)

    public final Logger getLogger(final Class<?> clazz) {        return getLogger(clazz.getName());    }    @Override    public final Logger getLogger(final String name) {        if (name == null) {            throw new IllegalArgumentException("name argument cannot be null");        }// root直接返回        // if we are asking for the root logger, then let us return it without        // wasting time        if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {            return root;        }// 非root,先拿到root,然后逐级创建其子logger        int i = 0;        Logger logger = root;        // check if the desired logger exists, if it does, return it        // without further ado.        Logger childLogger = (Logger) loggerCache.get(name);        // if we have the child, then let us return it without wasting time        if (childLogger != null) {            return childLogger;        }        // if the desired logger does not exist, them create all the loggers        // in between as well (if they don't already exist)        String childName;        while (true) {            int h = LoggerNameUtil.getSeparatorIndexOf(name, i);            if (h == -1) {                childName = name;            } else {                childName = name.substring(0, h);            }            // move i left of the last point            i = h + 1;            synchronized (logger) {  // 此logger为 root                childLogger = logger.getChildByName(childName);                if (childLogger == null) {// 调用Logger创建子节点                    childLogger = logger.createChildByName(childName);                    loggerCache.put(childName, childLogger);                    incSize();                }            }            logger = childLogger;            if (h == -1) {                return childLogger;            }        }    }// ch.qos.logback.classic.Logger#createChildByName    Logger createChildByName(final String childName) {        int i_index = LoggerNameUtil.getSeparatorIndexOf(childName, this.name.length() + 1);        if (i_index != -1) {            throw new IllegalArgumentException("For logger [" + this.name + "] child name [" + childName+ " passed as parameter, may not include '.' after index" + (this.name.length() + 1));        }        if (childrenList == null) {            childrenList = new CopyOnWriteArrayList<Logger>();        }        Logger childLogger;        childLogger = new Logger(childName, this, this.loggerContext);        childrenList.add(childLogger);        childLogger.effectiveLevelInt = this.effectiveLevelInt;        return childLogger;    }

3.3、level属性继承 和 优先级

若自身level为空,则继承父级Logger的level值

ch.qos.logback.classic.Logger#setLevel

    public synchronized void setLevel(Level newLevel) {        if (level == newLevel) {            // nothing to do;            return;        }        //  的 level 必填        if (newLevel == null && isRootLogger()) {            throw new IllegalArgumentException("The level of the root logger cannot be set to null");        }        level = newLevel;// 若level为空,则使用其父logger的有效level        if (newLevel == null) {            effectiveLevelInt = parent.effectiveLevelInt;            newLevel = parent.getEffectiveLevel();        } else {            effectiveLevelInt = newLevel.levelInt;        }        if (childrenList != null) {            int len = childrenList.size();            for (int i = 0; i < len; i++) {                Logger child = (Logger) childrenList.get(i);                // tell child to handle parent levelInt change                child.handleParentLevelChange(effectiveLevelInt);            }        }        // inform listeners        loggerContext.fireOnLevelChange(this, newLevel);    }

3.4、additivity属性

3.4.1、源码分析

调用自身的appender进行日志输出
2、若additive=false,则跳出循环,否则调用父logger的appender进行日志输出

ch.qos.logback.classic.Logger#callAppenders

// aai 就是中指定的 AppenderAttachableImpl    transient private AppenderAttachableImpl<ILoggingEvent> aai;// additive 默认为 true    transient private boolean additive = true;    // ............        public void callAppenders(ILoggingEvent event) {        int writes = 0;// 1、遍历当前logger的父logger        for (Logger l = this; l != null; l = l.parent) {// 2、若当前logger配置了append,则遍历append输出日志信息            writes += l.appendLoopOnAppenders(event);// 3、若当前logger的属性additive=false 则跳出循环,不再调用父级            if (!l.additive) {                break;            }        }        // No appenders in hierarchy        if (writes == 0) {            loggerContext.noAppenderDefinedWarning(this);        }    }    private int appendLoopOnAppenders(ILoggingEvent event) {// 若当前logger配置了append,则调用其进行日志输出        if (aai != null) {            return aai.appendLoopOnAppenders(event);        } else {            return 0;        }    }

在这里插入图片描述

3.4.2、演示

还是上述的例子

1)、只把name=“com.qbhj.logback.controller” 的logger,additivity属性改为"true"

    <logger name="com.qbhj.logback.controller" level="INFO" additivity="true">                <appender-ref ref="CONSOLE"/>    logger>    <logger name="com.qbhj.logback" level="INFO" additivity="false">        <appender-ref ref="CONSOLE"/>    logger>    <root level="INFO">                <appender-ref ref="CONSOLE"/>    root>

1)、结果:打印两遍

###################################### num: 121:02:30.800  INFO --- [     http-nio-8080-exec-2] c.qbhj.logback.controller.LogController  : /additivity......21:02:30.800  INFO --- [     http-nio-8080-exec-2] c.qbhj.logback.controller.LogController  : /additivity......

说明:

,且配置了appender=CONSOLE所以打印日志。其additivity=“true”,调用父级
,且配置了appender=CONSOLE,所以打印日志。其additivity=“false”,跳出循环,结束

Loggerappenderadditivity(默认true打印日志是否被调用
CONSOLEtrue
└─true
│ └─true
│ │ └─CONSOLEfalse
│ │ │ └─CONSOLEtrue

2)、只把name=“com.qbhj.logback.controller” 的logger,additivity属性改为"false", name="com.qbhj.logback"的logger改为false

    <logger name="com.qbhj.logback.controller" level="INFO" additivity="false">                <appender-ref ref="CONSOLE"/>    logger>    <logger name="com.qbhj.logback" level="INFO" additivity="true">        <appender-ref ref="CONSOLE"/>    logger>    <root level="INFO">                <appender-ref ref="CONSOLE"/>    root>

1)、结果:打印1遍

###################################### num: 121:04:10.603  INFO --- [     http-nio-8080-exec-1] c.qbhj.logback.controller.LogController  : /additivity......

说明:

,且配置了appender=CONSOLE所以打印日志。其additivity=“false”,跳出循环,结束

Loggerappenderadditivity(默认true打印日志是否被调用
CONSOLEtrue
└─true
│ └─true
│ │ └─CONSOLEtrue
│ │ │ └─CONSOLEfalse

四、日志规范

阿里开发手册(黄山版)中约定如下:
二、异常日志
(三) 日志规约

【强制】避免重复打印日志,浪费磁盘空间,务必在日志配置文件中设置 additivity=false
正例:

五、参考资料

阿里开发手册(黄山版).pdf
https://logback.qos.ch/manual/configuration.html

来源地址:https://blog.csdn.net/weixin_43582081/article/details/129780600

--结束END--

本文标题: 【Logback】<logger>、<root>标签详解

本文链接: https://lsjlt.com/news/417101.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 【Logback】<logger>、<root>标签详解
    文章目录 背景一、\使用1.1、使用示例1.1、属性配置说明 & 演示1.1.1、name1.1.2、level1.1.3、additivity1.1.3.1、效果演示:additivity=...
    99+
    2023-09-24
    logback 链表 java
  • log4j2异步Logger(详解)
    1 异步Logger的意义之前的日志框架基本都实现了AsyncAppender,被证明对性能的提升作用非常明显。在log4j2日志框架中,增加了对Logger的异步实现。那么这一步的解耦,意义何在呢?如图,按我目前的理解:异步Logger是...
    99+
    2023-05-30
    log4j2 异步 logger
  • log4j中logger标签中additivity属性的用法说明
    目录log4j logger标签中additivity属性log4j.additivity用法和例子log4j logger标签中additivity属性 将logger中的 add...
    99+
    2024-04-02
  • resultMap标签中里的collection标签详解
    目录resultMap标签中的collection标签collection(一对多)collection标签中各属性的说明resultMap标签中的collection标签 coll...
    99+
    2024-04-02
  • Git Tag 标签详解
    Git Tag 标签 git tag 按字母排序显示标签 git tag v1.01 打上v1.01这个标签 git show v1.01 显示这个标签的详情 可以同时打多个tag指向同一个时间点上的版本 ...
    99+
    2022-06-04
    详解 标签 Git
  • HTML5中的新标签和常用标签详解
    本篇内容介绍了“HTML5中的新标签和常用标签详解”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!HTML5...
    99+
    2024-04-02
  • html中object标签详解
    HTML中的<object>标签用于嵌入外部资源,如图像、音频、视频、Flash等,并在页面上显示这些资源,提供了一种通用的方式来嵌入各种不同类型的媒体资源,标签语法为”<object data="UR...
    99+
    2024-01-25
    object标签 html
  • springmvc常用注解标签详解
     1、@Controller在SpringMVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model 返回...
    99+
    2023-05-31
    spring mvc 注解
  • dedecms [field:fulltitle/]标签详解(小结)
    我们在制作模版过程中为了美观通常会限制文章标题的长度,比如: {dede:arclist titlelen='30' row='10' } <li><a href="[field:ar...
    99+
    2022-06-12
    dedecms [field:fulltitle/]标签
  • golang日志包logger的用法详解
    1. logger包介绍 import "github.com/wonderivan/logger" 在我们开发go程序的过程中,发现记录程序日志已经不是fmt.print这...
    99+
    2024-04-02
  • Spring P标签的使用详解
    目录Spring P标签的使用本例设计对象Topic、Speech和Speakerspring配置p标签问题今天学习spring遇到这样的一个问题解决方法如下Spring P标签的使...
    99+
    2024-04-02
  • iframe标签属性说明 详解
    iframe标签是HTML中的一个标签,用于在当前HTML页面中嵌入另一个HTML页面。它有以下一些常用的属性:1. src:指定要...
    99+
    2023-09-20
    iframe
  • iframe标签属性说明详解
    `iframe`标签是HTML中的内联框架元素,用于在当前HTML文档中嵌入另一个HTML文档。它具有以下属性:1. `src`:指...
    99+
    2023-09-21
    iframe
  • html include标签的用法详解
    HTML的include标签是一种用于在HTML文件中包含其他文件内容的标签,它可以将一个外部文件的内容嵌入到当前的HTML文件中。...
    99+
    2023-08-29
    html
  • HTML中link标签属性详解
    在HTML中,link标签是一个自闭合元素,通常位于文档的head部分。它用于建立与外部资源的关联,如样式表、图标等。link标签具有多个属性,其中rel和href是最常用的。 re...
    99+
    2023-05-18
    HTML link标签属性 HTML中link标签 HTML link
  • HTML的iframe标签用法详解
    HTML的iframe标签用法详解 HTML中的iframe标签是用来在网页中嵌入其他网页或者图片等内容的一种方法。通过使用iframe标签,我们可以在一个网页中显示另一个网页的内容,...
    99+
    2024-02-22
    iframe标签 网页布局
  • python更加灵活的Logger日志详解
    用到的4个类: 1、Logger: 打印日志用的对象;  设置日志等级,添加移除handler,添加移除filter,设置下级Logger,使用各种方法打印日志; 创建方式...
    99+
    2024-04-02
  • Discuz!7.0标签聚合功能详解
    关键字描述:功能 详解 聚合 标签 显示 应用 安装 发布 论坛 出来   Discuz!7.0是康盛创想(Comsenz)公司于2008年12月份发布的一款论坛BBS建站产品。标签聚合功能是Discuz! 7.0的新...
    99+
    2022-06-12
    功能 详解 聚合 标签 显示 应用 论坛 发布 安装 出来
  • 帝国CMS的phomenewspic/ecmsinfo标签详解
    前两天刚接触帝国cms,它给我的感觉是,它确实很强大。内置非常灵活的万能调用标签:能实现各式各样的效果,满足各种网站的需要,适应于所有模型。这也就是今天所说的主题,关于标签。 至于为什么我要写这篇文章呢,我发现网上关于...
    99+
    2022-06-12
    phomenewspic ecmsinfo
  • 织梦DedeCMS [field:highlight/]标签详解(小结)
    我们在制作Tag标签模板时会遇到[field:highlight/]标签 {dede:tag row='60' sort='new'} <a href='[field:link/]' class='tagc[...
    99+
    2022-06-12
    DedeCMS [field:highlight/]标签
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作