使用SLF4J和LOGBACK (二 :核心组件 )

在正式使用和配置logback之前,先来了解一下logback中的三个核心组件,日后的使用,我们配置的也是这三类核心组件。所以有必要先对它们有个基础了解。

1. 日志记录器(Logger)

Logger

Logger是一个命名实体,并遵循分层命名规则,它的层次由命名中的.符号表示。例如一个logger名为:com.foo,它就是com.foo.bar的父logger,二者有继承关系。

RootLogger

RootLogger是一个特殊的logger实体,它由logback自身提供,无需我们定义,同时它也是logger继承结构中的顶级父对象(类似于Java中的Object),所有未定义层级
关系的logger默认都是RootLogger的子集。

Logger的继承关系

如果一个给定的Logger未指定级别,那么它将从具有明确级别的最近的父类中继承到父类的日志级别,上边说了,RootLogger是这个级别中顶级的存在,
如果给定的Logger没有其他父级,那么最终将继承RootLogger的日志级别,默认为:DEBUG

Logger名称 指定的级别 有效级别
root DEBUG DEBUG
X none DEBUG
X.Y none DEBUG
X.Y.Z none DEBUG

在这个表中root代表RootLogger,它的默认级别是DEBUG,剩下三行的X  X.Y  X.Y.Z均未明确指定级别,根据继承关系,它们的有效级别都为DEBUG,由RootLogger继承而来

Logger名称 指定的级别 有效级别
root DEBUG DEBUG
X INFO INFO
X.Y none INFO
X.Y.Z none INFO

在这个表格中,X指定了为INFO级别,而X.Y和X.Y.Z均未指定级别,但是它们的有效级别为INFO,是因为距离它们最近的父级X指定了级别为INFO,所以它们继承了离它们最近的父级日志的级别。

通过上述两个表格可以明白logger日志等级的继承关系,下边来说一下当使用logger.xxx()方法打印日志时的输出有效规则,所谓的输出有效规则就是对应级别的日志是否会输出。

Logger打印输出规则

按照日志级别由低到高有五个级别,依次为:TRACE, DEBUG, INFO, WARN, ERROR,对应的Logger实体中有五个打印方法:trace, debug, info, warn, error
我们要打印info级别的日志,需要使用logger.info()方法,但是日志是否输出有其自身的规则。在这里我们将Logger对象的五个级别设为q,而对应的五个打印方法的级别设为p,则有如下规则:

打印方法(p) Logger级别(q)
  TRACE DEBUG INFO WARN ERROR
trace YES NO NO NO NO
debug YES YES NO NO NO
info YES YES YES NO NO
warn YES YES YES YES NO
error YES YES YES YES YES

上述表格用一句话总结:打印方法的级别(p)必须>=Logger的级别(q),如果Logger的级别为info,那么debug和trace方法都不会输出,因为这两个级别低于info,不满足p >= q的条件

关于logger的继承关系和打印级别的输出,我写了一个简单的demo做为上述表格的演示,代码如下:

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import org.slf4j.LoggerFactory;

public class SimpleLogLevel {
    //注意此处的logger对象为:ch.qos.logback.classic.Logger,为了下边设置日志级别
    private static Logger loggerX = (Logger) LoggerFactory.getLogger("X");
    private static Logger loggerXY = (Logger) LoggerFactory.getLogger("X.Y");
    public static void main(String[] args) {
        //loggerX未设置日志级别,因此从RootLogger继承到默认的DEBUG级别
        //trace日志不会输出,因为loggerX级别为debug,只会输出级别>=debug的日志
        loggerX.info("-------------loggerX level from RootLogger------------");
        loggerX.trace("loggerX trace");
        loggerX.debug("loggerX debug");
        loggerX.info("loggerX info");

        //将loggerX显示设置为info级别,所以低于info级别的debug和trace都不会输出
        loggerX.info("-------------loggerX level from self------------");
        loggerX.setLevel(Level.INFO);
        //这里的debug日志不会输出,理由同上
        loggerX.debug("loggerX-INFO debug log");
        loggerX.info("loggerX-INFO info log");
        loggerX.warn("loggerX-INFO warn log");

        //loggerXY未配置级别,它会继承距离它最近的父级日志级别,这里就是loggerX,所以loggerXY的级别也为INFO
        //debug日志不会输出
        loggerX.info("-------------loggerXY level from loggerX------------");
        loggerXY.debug("loggerXY-debug debug log");
        loggerXY.info("loggerXY-info info log");
        loggerXY.warn("loggerXY-warn warn log");
    }
}

上述代码产生的输出如下:

17:01:33.308 [main] INFO X - -------------loggerX level from RootLogger------------
17:01:33.310 [main] DEBUG X - loggerX debug
17:01:33.310 [main] INFO X - loggerX info
17:01:33.310 [main] INFO X - -------------loggerX level from self------------
17:01:33.310 [main] INFO X - loggerX-INFO info log
17:01:33.310 [main] WARN X - loggerX-INFO warn log
17:01:33.310 [main] INFO X - -------------loggerXY level from loggerX------------
17:01:33.310 [main] INFO X.Y - loggerXY-info info log
17:01:33.310 [main] WARN X.Y - loggerXY-warn warn log

相信结合代码和输出,应该可以充分理解logger级别的继承关系了。

2. Appender

Appender翻译过来就是附加器,它是附加在Logger对象上的。我们说的更直白一些,它就是指明日志输出到哪里的一个配置项,logback支持将日志输出到多种目标中,例如:控制台、文件、远程socket服务器,数据库、jmx等。当你配置了一个Appender,并将其附加到某个Logger对象时,就代表这个logger打印的日志会输出到Appender指定的目标中。

一个logger可以附加多个Appender,即一个logger对象打印的日志可以输出到多个目标中。

和logger一样,Appender也有其继承关系,总体来看Appender的继承关系其实就是logger的继承关系,只不过logger对象中有一个"附加标志位"的属性配置会影响Appender的继承传递性。下边我们也用一个表格来说明

Logger名称 附加的Appender 是否可附加标志位 输出目标
root A1 RootLogger作为根节点不支持该属性 A1
x A-x1, A-x2 true A-x1, A-x2
x.y none true A1, A-x1, A-x2
x.y.z A-xyz1 true A1, A-x1, A-x2, A-xyz1
security A-sec false A-sec
security.access none true A-sec

先解释一下"附加标志位"的概念,首先它是logger对象的属性,根据我个人的理解,它的作用是:是否阻断向上查找继承关系的开关。举个例子,有三个logger,a, a.b, a.b.c,从层级关系来说,它们具有继承关系,是祖孙三代。如果三个logger的"附加标志位"都是true,那么在查询继承关系时,a.b.c会先查找它的父级a.b,找到继承关系,在向上传递到a,最终继承a和a.b中所有的appender,但是如果此时a.b的"附加标志位"被配置为false了,那么a.b.c查找继承关系时,只会查找到a.b这一级,由于a.b的"附加标志位"是false,就不会再向上查找所以a.b.c了,只会继承a.b的Appender,而不会继承a的Appender了。

有了上述概念我们再来分析表格中的内容:

第一行的root表示根节点,根节点Logger作为继承结构的顶级节点,它没有附件标志位传递的概念,因此root节点的Logger只会输出到附件的A1 Appender上
第二行的x,因为它没有任何的明确的父级,因此继承了root节点,加上它自己的两个Appender,它会输出到3个Appender上
第三行x.y,从命名结构上来说,它继承自x,而自己没有任何Appender,所以它的继承关系向上传递到x,继承了两个Appender,再向上到root,共继承了三个Appender
第四行x.y.z,它直接继承x.y,间接继承x和root,再加上它自己的一个Appender,它共有四个Appender
第五行的可附加标志位为false,说明它不具有向上继承的传递性,因此它只有自己的一个A-sec Appender
第六行继承自security,但是由于security可附加标志位为false,因此它的继承关系只到security,不再向上传递,如果security的标志位是true,那么security会默认继承root,security.access也会向上传递继承到root的Appender。

还有一个Layout组件,这里我们一句话带过,后续用到的时候会具体说。它是一个布局器,用来配置日志输出格式布局的。可以支持类似C语言printf的语法来格式化输出。

posted @ 2022-09-07 17:30  框架搬运工  阅读(168)  评论(0编辑  收藏  举报