跳到主要内容

LogBack

一、架构

Logger 上下文

任何日志 API 的优势在于它能够禁止某些日志的输出。

命名层次结构 如果一个 logger 的名字加上 . 作为另一个 logger 名字的前缀,那么该 logger 就是另一个 logger 的祖先。如果一个 logger 与另一个 logger 之间没有其他的 logger,那么该 logger 就是另一个 logger 的父级。

每一个 logger 都可以通过它的名字取获取。

    // 获取 root logger
Logger rootLogger = LoggerFactory.getLogger(ROOT_LOGGER_NAME);

有效级别

对于一个给定的名称为 L 的logger,它的有效级别为从自身一直回溯到 root logger, 直到找到第一个不为空的层级作为自己的层级。 root logger 有一个默认级别:DEBUG

基本选择规则

如果一条日志的打印级别大于 logger 的有效级别,该日志才可以被打印出来 日志的打印级别为 p, Logger 实例级别为 q, 如果 p >= q, 则该条日志会被输出。

TRANCE < DEBUG < INFO < WARN < ERROR

    ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(SetLoggerLevel.class);
// 将 logger 的有效级别设置为 warn
logger.setLevel(Level.WARN);
logger.trace("trance");
logger.info("info");
logger.warn("warn");
logger.error("error");

获取 logger

通过 LoggerFactory.getLogger(); 可以获取到具体的 logger,名字相同则返回的 logger 实例也相同。

Appender 与 Layout

Appender:输出目的地。可以将日志输出到console、file、remote socket server、MySQL、PostgreSQL、Oracle或者其他数据库,JMS、remote UNIX Syslog daemons 中。

一个 logger 可以有多个 appender。

appender 的叠加性 logger L 的日志输出语句会遍历 L 和他的子级中所有的 appender。 如果 L 的子级 looger P 设置了 additivity = false,那么 L 的日志会在 L 所有的(包括P本身) appender 中输出,但是不会在 P 的自己 appender 中输出。

参数化日志

问题1 ; logback 的可变参数是怎么实现的呢?

logback 的配置

初始化顺序

  1. logback-test.xml
  2. logback.groovy
  3. logback.xml
  4. META-INFO/services/ch.qos.logback.classic.spi.Configurator
  5. 以上文件都没找到的时候,会提供一个默认的配置

自动配置 logback

<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<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. 在 xml 配置错误的时候,控制台会自动打印出 logback 内部状态信息
  2. 使用 StatusPrinter 类打印出logback 内部状态信息
    LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusPrinter.print(context);
  1. 在 logback 的配置文件中的 configuration 元素上添加 debug 属性

debug 只与状态信息有关,不影响日志级别

<configuration debug = "true">
...
</configuration>
  1. 配置一个 OnConsoleStatusListener ,等同于配置 debug = "true"
<configuration>
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
...
</configuration>

logback.xml 被修改时,自动加载

在 configuration 标签上添加 scan = "true" 属性。默认 60s 扫描一次

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

配置文件语法

Appender

什么是 Appender

logback 将写入日志事件委托给一个叫做 appender 的组件。Appender 必须实现 Appender 接口。

doappend(E event) 是 Appender 中最重要的方法。接受一个泛型作为唯一的参数。它的职责是将日志事件进行格式化,然后输出到对应的设备上。
在 classic 中,接收 ILoggingEvent 类型,在 access 中接收 AccessEvent 类型。

AppenderBase

ch.qos.logback.core.UnsynchronizedAppenderBase

    
public void doAppend(E eventObject) {
// WARNING: The guard check MUST be the first statement in the
// doAppend() method.

// prevent re-entry.
if (Boolean.TRUE.equals(guard.get())) {
return;
}

try {
guard.set(Boolean.TRUE);

// UnsynchronizedAppenderBase 实现了 LifeCycle 接口
if (!this.started) {
// 当 appender 没有启动的时候在内部状态管理系统中发出一条警告信息,这条警告信息只会发 ALLOWED_REPEATS 条
if (statusRepeatCount++ < ALLOWED_REPEATS) {
addStatus(new WarnStatus("Attempted to append to non started appender [" + name + "].", this));
}
return;
}

if (getFilterChainDecision(eventObject) == FilterReply.DENY) {
return;
}

// ok, we now invoke derived class' implementation of append
this.append(eventObject);

} catch (Exception e) {
if (exceptionCount++ < ALLOWED_REPEATS) {
addError("Appender [" + name + "] failed to append.", e);
}
} finally {
guard.set(Boolean.FALSE);
}
}

logback-core

logback-core 为 logback 其他组件的构建奠定了基础。

OutputStreamAppender

OutputStreamAppender 将事件附加到 java.io.OutputStream 上。这个类提供了其他 appender 构建的基础服务。

属性名属性值描述
ecoderEncoder决定通过哪种方式将事件写入 java.io.OutputStream
immediateFlushboolean(默认值为 true )是否立即刷新输出流,确保日志事件被立即写入,并且保证一旦 appender 没有正确关闭,日志事件也不会丢失

OutputStreamAppender 类图

ConsoleAppender

ConsoleAppender 将日志事件附加到控制台。通过 System.out 或者 System.error 进行输出,默认前者。(通过一个枚举类实现 ConsoleTarget)

属性名属性值描述
ecoderEncoder格式化输出
targetStringSystem.out 或者 System.err
withJansiboolean默认为 false,设置 withJansi 为 true 的时候需要添加 jansi jar包。
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<target>System.err</target>
<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>

可以在控制台看到使用 System.err 进行输出的日志

FileAppender

将日志事件输出到文件中,通过 file 指定目标文件。

属性名属性值描述
appendbooleantrue 表示,每次程序启动时,追加日志。false 表示:每次程序启动时,先清空再添加日志
ecoderEncoder格式化输出
fileString文件路径。C:/a.txt 或 C:\\c.txt
prudentboolean严谨模式
<configuration>
<appender name ="FILE" class="ch.qos.logback.core.FileAppender">
<append>false</append>
<file>F:/FileAppender.log</file>
<immediateFlush>true</immediateFlush>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>

文件唯一命名 时间戳 TimeStamp

配置

<?xml version="1.0" encoding="UTF-8"?>  
<!-- <configuration debug="true"> 调试模式下,可输出logback的内部日志信息 -->
<configuration>
<!-- 定义变量 -->
<property name="LOG_HOME" value="${user.dir}" />
<!-- 引用其它文件的设置 <property file="res/log_config.properties" /> -->
<!-- appender(输出端) 此处是控制台输出,用的是ConsoleAppender,STDOUT,此名字随意起,和root下的一致就行 -->
<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{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 文件输出1 -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<!-- 输出文件名 -->
<!-- <file>${LOG_HOME}/logs/mylog.log</file> -->
<!-- 输出文件名,引用其它文件的设置 -->
<file>${app_home}/mylog.log</file>
<encoder>
<!--%date日期 %level日志级别 [%thread]当前线程 %logger{50}输出日志的类,50代表包名加类名的总长度限制 [%file:%line]日志所在文件及行数 %msg%n消息及换行 -->
<pattern>%date %level [%thread] %logger{50} [%file:%line] %msg%n
</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE2" class="ch.qos.logback.core.rolling.RollingFileAppender">

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<FileNamePattern>${LOG_HOME}/ifproxy_%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- 最多日志文件数,必须和CleanHistoryOnStart一起使用 -->
<MaxHistory>3</MaxHistory>
<CleanHistoryOnStart>true</CleanHistoryOnStart>
</rollingPolicy>
<encoder>
<!--%date日期 %level日志级别 [%thread]当前线程 %logger{50}输出日志的类,50代表包名加类名的总长度限制 [%file:%line]日志所在文件及行数 %msg%n消息及换行 -->
<pattern>%date %level [%thread] %logger{50} [%file:%line] %msg%n
</pattern>
</encoder>
</appender>
<!-- 级别定义,输出端内容输出级别,大于或等于root定义的level,方能在输出端输出 -->
<root level="trace">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE2" />
</root>
<!-- 单独为某些类定输出级别 -->
<logger name="com.log.test.Test2" level="info"></logger>
<!-- 将指定类(mytest.lala.Test)的日志单独输出到指定的日志文件中(control2)
<appender name="FILE3" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_HOME}/%d{yyyyMM}/control2_%d{yyyy-MM-dd}.log</FileNamePattern>
</rollingPolicy>
<encoder charset="UTF-8">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
</pattern>
</encoder>
</appender>
<logger name="mytest.lala.Test">
<appender-ref ref="FILE3" />
</logger>
-->
</configuration>