logback使用详解

news/2024/6/18 22:41:21 标签: java

文章目录

  • 一. 概览
  • 二. 快速入门
  • 三. 基础
  • 四. logback 配置

一. 概览

Logback 主要由三个模块组成:

  • logback-core
  • logback-classic
  • logback-access

logback-core 是其它模块的基础设施,其它模块基于它构建,显然,logback-core 提供了一些关键的通用机制。logback-classic 的地位和作用等同于 Log4J,它也被认为是 Log4J 的一个改进版,并且它实现了简单日志门面 SLF4J;而 logback-access 主要作为一个与 Servlet 容器交互的模块,比如说 tomcat 或者 jetty,提供一些与 HTTP 访问相关的功能。

二. 快速入门

引入依赖: slf4j-api,logback-core,logback-classic。其中 slf4j-api 并不是 Logback 的一部分,是另外一个项目,但是强烈建议将 slf4j 与 Logback 结合使用

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.5</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.0.11</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.0.11</version>
        </dependency>

Demo:

java">package com.test.wsl.logback;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Demo {
 
    private static final Logger logger = LoggerFactory.getLogger(Demo.class);
 
    public static void main(String[] args) {
        logger.info("Hello world");
    }
}

这里并没有引用任何一个跟 Logback 相关的类,而是引用了 SLF4J 相关的类,这样在需要将日志框架切换为其它日志框架时,无需改动代码。LoggerFactorygetLogger() 方法接收一个参数,以这个参数决定 logger 的名字,这里传入了 Demo 这个类的 Class 实例,那么 logger 的名字便是 Demo 这个类的全限定类名:com.test.wsl.logback.Demo

三. 基础

  1. 重要API

在 logback 里,最重要的三个类分别是

  • Loggr
  • Appender
  • Layout

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

  1. 分层命名规则

为了可以控制哪些信息需要输出,哪些信息不需要输出,logback 中引进了一个 分层 概念。每个 logger 都有一个 name,这个 name 的格式与 Java 语言中的包名格式相同。这也是前面的例子中直接把一个 class 对象传进 LoggerFactory.getLogger() 方法作为参数的原因。

logger 的 name 格式决定了多个 logger 能够组成一个树状的结构,为了维护这个分层的树状结构,每个 logger 都被绑定到一个 logger 上下文中,这个上下文负责厘清各个 logger 之间的关系。

例如, 命名为 com.wsl 的 logger,是命名为 com.wsl.test 的 logger 的父亲,是命名为 com.wsl.test.logback 的 logger 的祖先。

在 logger 上下文中,有一个 root logger,作为所有 logger 的祖先,这是 logback 内部维护的一个 logger,并非开发者自定义的 logger。

可通过以下方式获得这个 logger :

java">Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);

同样,通过 logger 的 name,就能获得对应的其它 logger 实例。

  1. 日志打印级别

日志级别优先级: TRACE < DEBUG < INFO < WARN < ERROR
打印规则: 指定某个level级别, 该级别及以上级别都会打印

日志级别的缺省继承:某个 logger 打印级别是可选的,可以指定打印级别也可以不指定。
如果未指定,那么它将继承离他最近的一个有指定打印级别的祖先的打印级别。这里有一个容易混淆想不清楚的地方,如果 logger 先找它的父亲,而它的父亲没有指定打印级别,那么它会立即忽略它的父亲,往上继续寻找它爷爷,直到它找到 root logger。因此,也能看出来,要使用 logback, 必须为 root logger 指定日志打印级别。

  1. Appender 和 Layout

在 logback 的世界中,日志信息不仅仅可以打印至 console,也可以打印至文件,甚至输出到网络流中,日志打印的目的地由 Appender 来决定,不同的 Appender 能将日志信息打印到不同的目的地去。

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

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

打印的日志除了有打印的目的地外,还有日志信息的展示格式。在 logback 中,用 Layout 来代表日志打印格式。比如说,PatternLayout 能够识别以下这条格式(详情参考logback日志pattern的每个参数的含义):
%-4relative [%thread] %-5level %logger{32} - %msg%n

  1. 参数化打印日志

经常能看到打印日志的时候,使用以下这种方式打印日志:

logger.debug("the message is " + msg + " from " + somebody);

这种打印日志的方式有个缺点,就是无论日志级别是什么,程序总要先执行 "the message is " + msg + " from " + somebody 这段字符串的拼接操作。当 logger 设置的日志级别为比 DEBUG 级别更高级别时,DEBUG 级别的信息不回被打印出来的,显然,字符串拼接的操作是不必要的,当要拼接的字符串很大时,这无疑会带来很大的性能白白损耗。

于是,一种改进的打印日志方式被人们发现了:

if(logger.isDebugEnabled()) { 
  logger.debug("the message is " + msg + " from " + somebody);
}

这样的方式确实能避免字符串拼接的不必要损耗,但这也不是最好的方法,当日志级别为 DEBUG 时,那么打印这行消息,需要判断两次日志级别。一次是logger.isDebugEnabled(),另一次是 logger.debug() 方法内部也会做的判断。这样也会带来一点点效率问题,如果能找到更好的方法,谁愿意无视白白消耗的效率。

有一种更好的方法,那就是提供占位符的方式,以参数化的方式打印日志,例如上述的语句,可以是这样的写法:

logger.debug("the message {} is from {}", msg, somebody);

这样的方式,避免了字符串拼接,也避免了多一次日志级别的判断。

  1. logback 内部运行流程

在这里插入图片描述

当应用程序发起一个记录日志的请求,例如 info() 时,logback 的内部运行流程如下所示

  1. 获得过滤器链条
  2. 检查日志级别以决定是否继续打印
  3. 创建一个 LoggingEvent 对象
  4. 调用 Appenders
  5. 进行日志信息格式化
  6. 发送 LoggingEvent 到对应的目的地

四. logback 配置

  1. 配置方式

logback 提供的配置方式有以下几种:

  • 编程式配置
  • xml 格式
  • groovy 格式

logback 在启动时,根以下步骤寻找配置文件:

  1. 在 classpath 中寻找 logback-test.xml文件
  2. 如果找不到 logback-test.xml,则在 classpath 中寻找 logback.groovy 文件
  3. 如果找不到 logback.groovy,则在 classpath 中寻找 logback.xml文件
  4. 如果上述的文件都找不到,则 logback 会使用 JDK 的 SPI 机制查找 META-INF/services/ch.qos.logback.classic.spi.Configurator 中的 logback 配置实现类,这个实现类必须实现 Configuration 接口,使用它的实现来进行配置
  5. 如果上述操作都不成功,logback 就会使用它自带的 BasicConfigurator 来配置,并将日志输出到 console

2. 默认的配置

前面有提到默认的配置,由 BasicConfiguator 类配置而成,这个类的配置可以用如下的配置文件来表示:

<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{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>
  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>
  1. 启动时打印状态信息

如果 logback 在启动时,解析配置文件时,出现了需要警告的信息或者错误信息,那 logback 会自动先打印出自身的状态信息。

如果希望正常情况下也打印出状态信息,则可以在代码里显式地调用使其输出:

public static void main(String[] args) {
  // assume SLF4J is bound to logback in the current environment
  LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
  // print logback's internal status
  StatusPrinter.print(lc);
  ...
}

也可以在配置文件中,指定 configuration 的 debug 属性为 true

<configuration debug="true">
  ... the rest of the configuration file  
</configuration>

还可以指定一个 Listener:

<configuration>

  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />  

  ... the rest of the configuration file  

</configuration>

重置默认的配置文件位置

设置 logback.configurationFile 系统变量,可以通过 -D 参数设置,所指定的文件名必须以 .xml 或者 .groovy 作为文件后缀,否则 logback 会忽略这些文件。

  1. 输出异常栈时 jar 包信息

这个属性默认是关闭,可通过以下方式开启:

<configuration packagingData="true">
  ...
</configuration>

也可以通过 LoggerContext 的 setPackagingDataEnabled(boolean) 方法来开启

java"> LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
 lc.setPackagingDataEnabled(true);
  1. 配置文件格式

基本格式: configuration节点下配置appender、logger、root

根节点是 configuration,可包含0个或多个 appender,0个或多个 logger,最多一个 root。

  • 配置 logger 节点

在配置文件中,logger 的配置在 标签中配置, 标签只有一个属性是一定要的,那就是 name,除了 name 属性,还有 level 属性,additivity 属性可以配置,不过它们是可选的。
level 的取值可以是 TRACE, DEBUG, INFO, WARN, ERROR, ALL, OFF, INHERITED, NULL, 其中 INHERITEDNULL 的作用是一样的,并不是不打印任何日志,而是强制这个 logger 必须从其父辈继承一个日志级别。
additivity 的取值是一个布尔值,true 或者 false。

标签下只有一种元素,那就是 ,可以有0个或多个,意味着绑定到这个 logger 上的 Appender。

  • 配置 root 节点

标签和 标签的配置类似,只不过 标签只允许一个属性,那就是 level 属性,并且它的取值范围只能取 TRACE, DEBUG, INFO, WARN, ERROR, ALL, OFF
标签下允许有0个或者多个 。

  • 配置 appender 节点

标签有两个必须填的属性,分别是 name 和 class,class 用来指定具体的实现类。 标签下可以包含至多一个 ,0个或多个 ,0个或多个 ,除了这些标签外, 下可以包含一些类似于 JavaBean 的配置标签。

包含了一个必须填写的属性 class,用来指定具体的实现类,不过,如果该实现类的类型是 PatternLayout 时,那么可以不用填写。 也和 一样,可以包含类似于 JavaBean 的配置标签。
标签包含一个必须填写的属性 class,用来指定具体的实现类,如果该类的类型是 PatternLayoutEncoder ,那么 class 属性可以不填。
如果想要往一个 logger 上绑定 appender,则使用以下方式:

<logger name="HELLO" level="debug">
    <appender-ref ref="FILE" />
    <appender-ref ref="STDOUT" />
</logger>
  • 变量替换

在 logback 中,支持以 ${varName} 来引用变量

定义变量

a. 可以直接在 logback.xml 中定义变量

<configuration>
 
  <property name="USER_HOME" value="/home/sebastien" />
 
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>${USER_HOME}/myApp.log</file>
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>
 
  <root level="debug">
    <appender-ref ref="FILE" />
  </root>
</configuration>

b. 也可以通过大D参数来定义

java -DUSER_HOME="/home/sebastien" MyApp2

c. 也可以通过外部文件来定义

<configuration>
 
  <property file="src/main/java/chapters/configuration/variables1.properties" />
 
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
     <file>${USER_HOME}/myApp.log</file>
     <encoder>
       <pattern>%msg%n</pattern>
     </encoder>
   </appender>
 
   <root level="debug">
     <appender-ref ref="FILE" />
   </root>
</configuration>

d. 外部文件也支持 classpath 中的文件

<configuration>
 
  <property resource="resource1.properties" />
 
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
     <file>${USER_HOME}/myApp.log</file>
     <encoder>
       <pattern>%msg%n</pattern>
     </encoder>
   </appender>
 
   <root level="debug">
     <appender-ref ref="FILE" />
   </root>
</configuration>

变量的作用域, 变量有三个作用域

  • local
  • context
  • system

local 作用域在配置文件内有效,context 作用域的有效范围延伸至 logger context,system 作用域的范围最广,整个 JVM 内都有效。

logback 在替换变量时,首先搜索 local 变量,然后搜索 context,然后搜索 system。

如何为变量指定 scope ?

<configuration>
 
  <property scope="context" name="nodeId" value="firstNode" />
 
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>/opt/${nodeId}/myApp.log</file>
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>
 
  <root level="debug">
    <appender-ref ref="FILE" />
  </root>
</configuration>

变量的默认值: 在引用一个变量时,如果该变量未定义,那么可以为其指定默认值,做法是:

${aName:-golden}

http://www.niftyadmin.cn/n/876072.html

相关文章

Optional方法详解

Optional方法详解1. 构建2. 空处理3. 转换4. 获取5. 场景本质: 内部包装了一个引用型对象, 封装了很多对null判断及转化的API 1. 构建 Optional of(T value) #value必须非null Optional ofNullable(T value) #value可以为null 2. 空处理 boolean isPresent() #判断是否为null…

JDK中的Exception

JDK中的Exception1. Throwable2. Exception3. RuntimeException4. 自定义异常1. Throwable 三个重要属性 Throwable cause; //getCause的返回,异常的起因String detailMessage; //getMessage()的返回信息, 初始化值为null, 如果不继承异常体系就是nullStackTraceElement[] st…

堆外缓存OHCache使用总结

一. 为什么用缓存 用户数和访问量越来越大并发量/吞吐量要求越来越高连接数或者文件读写存在瓶颈应用和数据库所做的计算也越来越多 如何能够有效利用有限的资源来提供尽可能大的吞吐量&#xff1f;一个有效的办法就是引入缓存 什么是缓存? 缓存&#xff08;cache&#xff…

Java8新特性之Lambda表达式、函数式接口、方法引用、Stream API、Optional类等的使用

Lambda表达式、函数式接口、方法引用、Stream API、Optional类等的使用Lambda表达式Lambda的具体使用无参、无返回值有参数、无返回值有参数&#xff0c;有返回值省略数据类型省略参数的小括号省略参数的小括号与Lambda体大括号函数式接口内置4大核心函数式接口其他接口使用Jav…

部署Seata服务环境以及基于Seata实现分布式事务

部署Seata服务环境以及基于Seata实现分布式事务Seata简介实现原理三个角色事务过程(生命周期)事务模式部署Seata服务环境安装Seata服务建库建表配置registry.conf文件配置file.conf文件重启Seata服务Seata的使用引入依赖配置seata创建过滤器创建拦截器取消过滤器注册A服务B服务…

Spring Cloud Stream的基本使用

Spring Cloud Stream的基本使用Spring Cloud Stream概述核心概念常用注解InputOutputStreamListenerSendToInboundChannelAdapterServiceActivatorTransformerSource与Sink内置接口生产者消费者自定义接口生产者消费者消息异常处理全局异常处理局部异常处理重试消费Spring Clou…

记Date类型的一次踩坑

记Date的一次踩坑一. 背景1. 线上问题2. 排查过程3. 问题的原因4. Date的时区确定一. 背景 1. 线上问题 新功能的营销活动时间23:59:59才结束, 但是16点多发现页面显示《活动已结束》 2. 排查过程 http抓数据包: activityEndTime为1654761599000, 转化为Date为2022-06-09 15:5…

解决git diff时的^M问题

目录1. 问题现象2. 解决方案1. 问题现象 git diff 时发现明明只改动了几行代码, 但是对比时却显示全部文档都修改过了, 而且到处都是^M字符, 导致看不出修改点, 达不到git diff的效果. 2. 解决方案 分析原因 ^M根据ASCII码表, 查出是\r, 即windows换行符\r\n的前一半. 猜…