Logback 问题点总结

  |  
阅读次数
  |  
字数 2,181
  |  
时长 ≈ 8 分钟

LogbackLog4j 一样都是基于 slf4j 的日志实现框架,但前者较后者后者更多的性能优势因而被作为Log4j的替代品被广泛进行使用。

本次我们就来简单总结一下Logback常见的一些问题:

1.工作原理

先看一张图:

当调用logger.trace/debug/info/warn/error()时

1.1.获取过滤链(filter chain)

这里会获取到所有的过滤链,这里我们以LevelFilter为例。
如果我们设置了LevelFilter这个filter

1.2.过滤记录

这时根据当前Filter所设置的level来匹配当前记录的级别,

然后根据onMatch/onMissMatch(是否匹配)的布尔值来执行设置的操作。

如果过滤链的match的结果是 FilterReply.DENY,则记录请求被抛弃。

如果结果是 FilterReply.ACCEPT,则进入 LoggingEvent 对象的创建。

1.3.创建 LoggingEvent 对象

记录请求到了这一步后,logback 会创建一个 ch.qos.logback.classic.LoggingEvent 对 象,该对象包含所有与请求相关的参数,比如请求用的 logger、请求级别、消息、 请求携带的异常、当前时间、当前线程、执行记录请求的类的各种数据,还有 MDC。 注意有些成员是延迟初始化的,只有当它们真正被使用时才会被初始化。MDC 用 来为记录请求添加额外的上下文信息。

1.4.调用 appender

创建了 LoggingEvent 对象后,logback 将调用所有可用 appender 的 doAppend()方法,

这就是说,appender 继承 logger 的上下文。

所有 appender 都继承 AppenderBase 抽象类,AppenderBase 在一个同步块里实现了 doAppend 方以确保线程安全。AppenderBase 的 doAppender()方法也调用 appender 关联的自定义 过滤器, 如果它们 存在的话。 自定义过 滤器能被 动态地关 联到任何 appender

1.5.格式化输出

那些被调用了的 appender 负责对记录事件(LoggingEvent)进行格式化。然而,有 些但不是全部 appender 把格式化记录事件的工作委托给 layout。Layout 对 LoggingEvent 实例进行格式化,然后把结果以字符串的形式返回。注意有些 appender,比如 SocketAppender,把记录事件进行序列化而不是转换成字符串,所 以它们不需要也没有 layout。

1.6.发送记录事件(LoggingEvent)

记录事件被格式化后,被各个 appender 发送到各自的目的地。

2.配置

Logback 采取下面的步骤进行自我配置:

  1. 尝试在 classpath 下查找文件 logback-test.xml;
  2. 如果文件不存在,则查找文件 logback.xml;
  3. 如果两个文件都不存在,logback 用 BasicConfigurator 自动对自己进行配置,这会导致记录输出到控制台(ConsoleAppender)。

第三步也是最后一步是为了在缺少配置文件时提供默认(但基本的)记录功能。

前面提到过,如果 classpath 里有 logback-test.xml 或 logback.xml,logback 会试图用它进行自我配置。

配置以<configuration>开头,后面有零 个或多个<appender>元素,有零个或多个<logger>元素,有最多一个<root>元素(不区分大小写),如下图所示:

2.1.配置元素,设置logger的日志级别

<logger>元素有且仅有一个 name 属性、一个可选的level 属性和一个可选的 additivity 属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!--encoders are assigned by default the type ch.qos.logback.classic.encoder.PatternLayoutEncoder-->
<encoder>
<pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<logger name="chapters.configuration" level="INFO" />
<!-- Strictly speaking, the level attribute is not necessary since -->
<!-- the level of the root level is set to DEBUG by default. -->
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>

以上例子设置了再一个rootLogger的level为debug级别的时候,层次小于等于chapters.configuration的所有logger最多只打印INFO以上的级别日志。

2.2.配置元素

<root>元素是根logger,该元素有一个 level 属性。没有 name 属性,因为已经被命名为“ROOT”

日志级别可以看下图:

级别 说明 使用场景
FATAL 严重 严重的错误事件,将会导致应用程序的退出,需要运维管理人员马上介入,需慎用。
ERROR 错误 错误事件,影响正常使用,但仍然不影响系统的继续运行。
WARN 警告 预期之外的运行状况,可能会出现潜在错误的情形,比如大量时延过大等;一般是由系统资源等技术原因触发。
INFO 提示 粗粒度记录应用程序的正常运行过程中的关键信息。
DEBUG 调试 细粒度记录应用程序的正常运行过程中的信息,帮助调试和诊断应用程序。产品稳定后删除绝大部分。
TRACE 跟踪 细粒度记录应用程序的正常运行过程中的信息,帮助调试和诊断应用程序,比 DEBUG 更细粒度。产品稳定后删除绝大部分。

2.3.配置元素

该元素必要属性 name 和 class,name 属性指定 appender 的名称,class 属性指定 appender 类的全限定名。

<appender>元素可以包含零个或多个<layout>元素、零个或多个<encoder>元素和零个或 多个<filter>元素。除了这三个常用元素之外,还可以包含 appender 类的任意数量的 javabean 属性。下图演示了常用结构,注意对 javabean 属性的支持在图中不可见。

2.3.1.layout

对日志进行格式化

2.3.2.encoder

encoder 是0.9.19版本之后引进的,以前的版本使用 layout ,logback极力推荐的是使用 encoder 而不是 layout

1
2
3
<encoder charset="UTF-8">
<pattern>%d{yy-MM-dd HH:mm:ss} %p %t %c{2}.%M\\(%L\\) | %m%n</pattern>
</encoder>

这里%d等价于%date用于制定一个日期,后面是一个具体的日期格式

Conversion Pattern Result
%d 2006-10-20 14:06:49,812
%date 2006-10-20 14:06:49,812
%date{ISO8601} 2006-10-20 14:06:49,812
%date{HH:mm:ss.SSS} 14:06:49.812
%date{dd MMM yyyy;HH:mm:ss.SSS} 20 oct. 2006;14:06:49.812

其他更多表达式请看:Chapter 6: Layouts

2.3.3.filter

对日志进行过滤

rollingPolicy:滚动策略
logback有好几种文件滚动策略,滚动的方式各有不同。
滚动策略有好几个基本属性

  1. fileNamePattern:
    定义滚动(归档)记录文件的名字。
    其值应当包 含文件名及“%d” 格式转换 符。“%d”可 以包含一个 java.text.SimpleDateFormat 指定的日期时间模式。如果
    没有指定日期时间模式,则默认为 yyyy-MM-dd。如果使用SizeAndTimeBasedRollingPolicy作为滚动策略的话,需要另外设置%i,表示当前日期的第几个滚动的文件。
  2. maxHistory
    控制被保留的归档文件的最大数量,超出数量就删除旧 文件。
    例如,假设每月滚动,且 maxHistory 是 6,则只 保留最近 6 个月的归档文件,删除之前的文件。
    注意当 删除旧归档文件时,那些为了归档而创建的目录也会被 删除。
  3. maxFileSize
    一个日志文件大小的最大值,达到该大小则根据规则自动生成新的日志文件。
  4. totalSizeCap
    该属性表示在maxHistory时间内,所允许的总文件大小最大值,如果日志在该时间段内累计超过最大值,则从最老的文件开始删除,腾出空间。

3.注意点

3.1.

任何方法调用都会涉及“隐藏的” 参数构造消耗,例如,对于 logger x,

1
x.debug("Entry number: " + i + "is " + entry[i]);

把整数 i 和 entry[i]都转换为字符串和连接各字符串会造成消息参数构造消耗,不管 消息是否被记录。

参数构造消耗可以变得非常高,同时也跟参数大小有关。利用 SLF4J 的参数化记 录可以避免这种消耗。

1
x.debug("Entry number: {} is {}", i, entry[i]);

这种方式不会造成参数构造消耗。与前面的 debug()方法相比,这种方法快得多。

只有当请求在被发送给 appender 时,消息才会被格式化。

在格式化的时候,负责格式化消息的组件性能很高,不会对整个过程造成负面影响。

格式化 1 个和 3 个参 数分别需要 2 和 4 微妙。

请注意,无论如何,应当避免在紧密循环里或者非常频繁地调用记录语句,因为很可能降低性能。

即使记录被禁用,在紧密循环里作记录仍然会拖慢应用程序,如果记录被启用,就会产生大量(也是无用的)输出。