Logback最佳实践


Logback最佳实践

Logback架构

Logback分为三个模块:Logback-core,Logback-classic和Logback-access。

Logback-core是其他两个模块实现的基础。Logback-classic相当于是Log4j的改进版,并且实现了Slf4J接口。Logback-access与Servlet容器集成,以提供HTTP访问日志功能(这个模块实际开发中很少用到)。

Logback核心对象

Logback有三个核心对象:LoggerAppenderLayout

Logger 位于 logback-classic 模块中, 而 Appender 和 Layout 位于 logback-core 中,这意味着, Appender 和 Layout 并不关心 Logger 的存在,不依赖于 Logger,同时也能看出, Logger 会依赖于 Appender 和 Layout 的协助,日志信息才能被正常打印出来。

  • Logger:日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。
  • Appender:主要用于指定日志输出的目的地,目的地可以是控制台、文件、远程套接字服务器、 数据库、 JMS和远程UNIX Syslog守护进程等。
  • Layout:把事件转换成字符串,格式化的日志信息的输出。

Logger

各个Logger 都被关联到一个 LoggerContext,LoggerContext负责制造Logger ,也负责以树结构排列各Logger 。其他所有logger也通过org.slf4j.LoggerFactory 类的静态方法getLogger取得。 getLogger方法以 Logger 名称为参数。用同一名字调用LoggerFactory.getLogger 方法所得到的永远都是同一个Logger 对象的引用。

层次命名规则

为了可以控制哪些信息需要输出,哪些信息不需要输出,Logback中引进了一个 分层 概念。每个 logger 都有一个 name,这个 name 的格式与 Java 语言中的包名格式相同。Logger 的 name 格式决定了多个 Logger 能够组成一个树状的结构,为了维护这个分层的树状结构,每个 logger 都被绑定到一个 Logger 上下文中,这个上下文负责厘清各个 logger 之间的关系。

例如,"com.foo""com.foo.Bar"的父Logger。同样, "java""java.util"的父Logger和"java.util.Vector"祖先Logger。

root Logger是所有Logger的祖先,这是 logback 内部维护的一个 logger,并非开发者自定义的 Logger 。

Logger有日志打印级别继承规则,对于一个未设置打印级别的Logger会向上寻找一个设置了日志打印级别的父Logger,直到root Logger。因此 必须为 root Logger指定日志打印级别。

Appender & Layout

Appender 是绑定在 logger 上的,同时,一个 logger 可以绑定多个 Appender,意味着一条信息可以同时打印到不同的目的地去。例如,常见的做法是,日志信息既输出到控制台,同时也记录到日志文件中,这就需要为 logger 绑定两个不同的 logger。

logger 有继承关系,因此一个 logger 打印信息时的目的地 Appender 需要参考它的父亲和祖先。在 logback 中,默认情况下,如果一个 logger 打印一条信息,那么这条信息首先会打印至它自己的 Appender,然后打印至它的父亲和父亲以上的祖先的 Appender,但如果它的父亲设置了 additivity = false,那么这个 logger 除了打印至它自己的 Appender 外,只会打印至其父亲的 Appender,因为它的父亲的 additivity 属性置为了 false,开始变得忘祖忘宗了,所以这个 logger 只认它父亲的 Appender;此外,对于这个 logger 的父亲来说,如果父亲的 logger 打印一条信息,那么它只会打印至自己的 Appender中(如果有的话),因为父亲已经忘记了爷爷及爷爷以上的那些父辈了。

打印的日志除了有打印的目的地外,还有日志信息的展示格式。在 logback 中,用 Layout 来代表日志打印格式。比如说,PatternLayout 能够识别以下这条格式:

%-4relative [%thread] %-5level %logger{32} - %msg%n

然后打印出来的格式效果是:

176  [main] DEBUG manual.architecture.HelloWorld2 - Hello world.

第一个字段是自程序启动以来经过的毫秒数。第二个字段是发出日志请求的线程。第三个字段是日志打印级别。第四个字段是Logger名称。“-”之后的文本是日志消息。

Logback加载流程

  1. Logback尝试在classpath下找到一个名为logback-test.xml 的文件 。(xml配置)
  2. 如果找不到上述文件,尝试在classpath找到一个名为logback.groovy 的文件 。(groovy配置)
  3. 如果找不到上述文件,尝试在classpath中找到一个名为logback.xml的文件。
  4. 如果上述的文件都找不到,则 logback 会使用 JDK 的 SPI 机制查找 META-INF/services/ch.qos.logback.classic.spi.Configurator 中的 logback 配置实现类,这个实现类必须实现 Configuration 接口,使用它的实现来进行配置。(编程式配置)
  5. 如果以上方法均未成功,则Logback将使用自带的 BasicConfigurator 进行自动配置。该配置构造一个ConsoleAppender用于向控制台输出日志,默认日志输出格式为”%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n“,日志打印级别为Debug

在Maven工程中,一般将 logback-test.xml放在src / test / resources 文件夹下,Maven将确保它不会包含在生成的工件中。因此,您可以在测试期间使用其他配置文件,即logback-test.xml,在生产环境中使用另一个文件,即logback.xml

logback 启动的时候解析配置文件大概需要 100 毫秒的时间,如果希望更快启动,可以采用 SPI 的方式。

Logback的默认配置

BasicConfigurator 默认配置可以使用以下配置文件表述:

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
      <pattern>%d&#123;HH:mm:ss.SSS&#125; [%thread] %-5level %logger&#123;36&#125; - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

Logback配置文件语法

配置文件的最基本结构可以描述为<configuration>元素,包含零个或多个<appender>元素,然后是零个或多个<logger>元素,然后是最多一个<root>元素。

<!--
    scan 设置为true(默认值) 配置文件变更会重新加载
    scanPeriod 检测日志是否修改时间间隔
    debug 是否打印logback内部日志
-->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <!--
        property 自定义定义变量值
            name  变量名
            value 变量值
           scope 作用域
                  local 作用域在配置文件内有效
                   context 作用域的有效范围延伸至 logger context
                   system 作用域的范围最广,整个 JVM 内都有效。    
    -->
    <property scope="context" name="glmapper-name" value="glmapper-demo" />
    <!-- 
        也可以引用外部配置文件,配置文件必须为 key-value 型。
    -->
    <property resource="resource1.properties" />
    <!--
        contextName logger上下文 用于区分不同应用程序的记录,默认为default
    -->
    <contextName>$&#123;glmapper-name&#125;</contextName>

    <!--
        appender 负责写日志的组件
    -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <Pattern>%d&#123;HH:mm:ss.SSS&#125; %-5level %logger&#123;80&#125; - %msg%n</Pattern>
        </encoder>
    </appender>


    <!--
        用于设置某一个包或某个类日志打印级别以及制定的appender
    -->
    <logger>
    </logger>

    <!--
        根logger 只有一个level属性,默认为debug
    -->
    <root level="info">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

配置Logger

使用<logger>元素配置Logger,Logger配置需注意Logger的级别继承特性。

<logger name="com.ning.test"
        level="info" additivity="false">
    <appender-ref ref="GLMAPPER-LOGGERONE" />
</logger>
  • name:强制性属性,指定Logger名称。可以是某个包或者具体的某个类。
  • level:日志打印级别(TRACE, DEBUG, INFO, WARN, ERROR, ALLOFF)。特殊值INHERITED或者同义词NULL,代表强制执行父级的级别。如果没有设置此属性,那么当前Logger 将会继承父级的级别。
  • addtivity:是否向上级Logger传递打印信息。默认是true
  • appender-ref:指定具体的appender。可包含0个或多个appender

配置根Logger

使用<root>元素配置根Logger。

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>

<root>只支持单个属性,即日志打印级别属性,可选值有不区分大小写的字符串TRACE,DEBUG,INFO,WARN,ERROR,ALL或OFF吗,默认值为DEBUG。

配置Appender

使用<appender> 元素配置appender。

    <!--
        appender种类      ConsoleAppender 打印到控制台
                         FileAppender   打印到文件
                         RollingFileAppender 上面的子类,滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。
    -->
    <appender name="NING-LOGGERONE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 文件追加 -->
        <append>true</append>
        <!--
            filter种类    ThresholdFilter 临界值过滤器 过滤掉低于指定临界值的日志
                         LevelFilter    级别过滤器,根据日志级别进行过滤
        -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <file>
            ./logs/ning-log-logback/ning-loggerone.log
        </file>
        <!--
            rollingPolicy滚动策略  只有appender为RollingFileAppender才有效 涉及文件的移动和重命名
                    TimeBasedRollingPolicy 时间滚动策略 常用
                    FixedWindowRollingPolicy 固定窗口算法重命名文件的滚动策略
        -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名:按天回滚 daily -->
            <FileNamePattern>./logs/ning-log-logback/ning-loggerone.log.%d&#123;yyyy-MM-dd&#125;</FileNamePattern>
            <!-- 保留30天 -->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <!--
            对记录事件进行格式化 把日志信息转换成字节数组 把字节数组写入到输出流
        -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d&#123;yyyy-MM-dd HH:mm:ss.SSS&#125; [%thread] %-5level %logger&#123;50&#125; - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
  • name:必要属性。指定appender名称。

  • class:指定appender类实例化的全限定名。

    • ConsoleAppender:打印到控制台
    • FileAppender:打印到文件的
    • RollingFileAppender:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。它是FileAppender的子类
  • 为true,日志文件追加。为false,覆写。默认为true。

  • :定义日志文件路径和文件名。

  • :对日志进行过滤器。可以添加多个filter,按配置顺序过滤。

    • ThresholdFilter:临界值过滤器,过滤掉低于指定临界值的日志。当日志级别等于或高于临界值时,过滤器返回NEUTRAL;当日志级别低于临界值时,日志会被拒绝。

    • LevelFilter:级别过滤器,根据日志级别进行过滤。如果日志级别等于配置级别,过滤器会根据onMath(用于配置符合过滤条件的操作) 和 onMismatch(用于配置不符合过滤条件的操作)接收或拒绝日志。

      <filter class="ch.qos.logback.classic.filter.LevelFilter">   
        <level>INFO</level>   
        <onMatch>ACCEPT</onMatch>   
        <onMismatch>DENY</onMismatch>   
      </filter> 
  • :滚动策略,只有当appender为RollingFileAppender时,此标签配置有效。涉及文件的移动和重命名。

    • TimeBasedRollingPolicy:最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责触发滚动。下面这段配置表明每天生成一个日志文件,保存30天的日志文件

      <rollingPolicy 
          class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
          <!--日志文件输出的文件名:按天回滚 daily -->
          <FileNamePattern>
              $&#123;logging.path&#125;/glmapper-spring-boot/glmapper-loggerone.log.%d&#123;yyyy-MM-dd&#125;
          </FileNamePattern>
          <!--日志文件保留天数-->
          <MaxHistory>30</MaxHistory>
      </rollingPolicy>
    • FixedWindowRollingPolicy:根据固定窗口算法重命名文件的滚动策略。

  • :对记录事件进行格式化。把日志信息转换成字节数组,再把字节数组写入到输出流。

LogBack运行流程

当用户调用名为com.wombat的Logger的info方法时,Logback内部运行流程如下。

  1. 获得过滤器链:过滤某些日志打印请求。过滤器返回结果为DENY时,取消打印请求。为NEUTRAL时,进入下一个过滤器。为ACCEPT时,直接跳到步骤3。
  2. 检查日志级别以决定是否继续打印:logback将Logger的有效级别与请求级别进行比较。
  3. 创建一个 LoggingEvent 对象:LoggingEvent对象包含请求的所有相关参数,例如请求的Logger,日志打印级别,日志消息本身,可能与请求一起传递的异常,当前时间,当前线程,有关发出日志记录请求的类的各种数据以及MDC
  4. 调用 Appenders:创建LoggingEvent对象后,logback将调用doAppend()所有适用的appender 的方法,即从logger上下文继承的appender。
  5. 进行日志信息格式化:被调用的Appender负责格式化日志记录事件。但是,一些(但不是全部)Appender将格式化日志记录事件的任务委托给Layout。Layout会格式化LoggingEvent实例,并以字符串形式返回结果。请注意,某些Appender(例如) SocketAppender不会将日志记录事件转换为字符串,而是将其序列化。因此,它们没有Layout,也不需要Layout。
  6. 发送 LoggingEvent 到对应的目的地:日志记录事件完全格式化后,每个Appender将其发送到其目的地。

文章作者: Ning0h2o
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Ning0h2o !
评论
评论
 本篇
Logback最佳实践 Logback最佳实践
Logback最佳实践Logback架构Logback分为三个模块:Logback-core,Logback-classic和Logback-access。 Logback-core是其他两个模块实现的基础。Logback-classic相
2020-09-20
下一篇 
Java日志体系概述 Java日志体系概述
Java日志体系概述平时自己写代码很少使用日志,对Java日志这方面的知识也有所欠缺。现在工作中要求在代码中使用合理地打上一些日志,这几天也有去了解Java日志有关的一些知识,这篇文章也是对最近几天的学习进行一些总结。 为什么要有Java日
2020-09-13
  目录