Logback介绍

一、写作目的:

    之前项目中使用到了 logback ,但是由于项目时间比较紧,所以没有太关注配置。

    现在稍微清闲一点,把相关的知识点总结一下

    本例子只是将中文文档中的内容复制后稍微修改,如有需要可以去原文档查看原文

二、参考地址:

官网英文文档:http://logback.qos.ch/manual/index.html
中文文档:http://www.logback.cn/

三、简介

    Logback 继承自 log4j。

    Logback 的架构非常的通用,适用不同的使用场景。Logback 被分成三个不同的模块:logback-core,logback-classic,logback-access。

    logback-core 是其它两个模块的基础。logback-classic 模块可以看作是 log4j 的一个优化版本,它天然的支持 SLF4J,所以你可以随意的从其它日志框架(例如:log4j 或者 java.util.logging)切回到 logack。

    logback-access 可以与 Servlet 容器进行整合,例如:Tomcat、Jetty。它提供了 http 访问日志的功能。

3.1、hello…

    logback 继承自 log4j,它建立在有十年工业经验的日志系统之上。它比其它所有的日志系统更快并且更小,包含了许多独特并且有用的特性。

依赖的添加:

    需要在项目工程中添加 slf4j-api.jarlogback-core.jar 以及 logback-classic.jar(例子中使用到logback版本为:1.2.3,slf4j版本为:1.7.29)。

    <dependencies>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
    </dependencies>
代码示例一:
package com.milory.logback;

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

public class HelloLogBack {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger("com.milory.logback.HelloLogBack");
        logger.debug("hello world");
    }
}

代码详解:

    类 HelloLogBack 定义在包 com.milory.logback 中,它导入了两个类 LoggerLoggerFactory,这两个类定义在 SLF4J API 中,在 org.slf4j 包中。

    在这个例子中,main() 包含了一个 DEBUG 级别的日志语句,输出信息为 “hello world”

    运行 HelloLogBack 将会在控制台看到一行日志。由于 logback 默认的配置策略:当没有默认的配置时,logback 将会在 root logger 中新增一个 ConsoleAppender

11:49:52.443 [main] DEBUG com.milory.logback.HelloLogBack - hello world

    Logback 通过一个内部的状态系统来报告它本身的状态信息。发生在 logback 生命周期中的事件可以通过 StatusManager 来获取。详情可参考下面的例子。

代码示例二:
package com.milory.logback;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.util.StatusPrinter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloLogBack2 {

    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger("com.milory.logback.HelloLogBack2");
        logger.debug("Hello world");

        // 打印内部的状态
        LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory();
        StatusPrinter.print(lc);
    }
}

代码详解:

    运行结果如下:

11:54:59.785 [main] DEBUG com.milory.logback.HelloLogBack2 - Hello world
11:54:59,725 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
11:54:59,725 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
11:54:59,725 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.xml]
11:54:59,728 |-INFO in ch.qos.logback.classic.BasicConfigurator@31221be2 - Setting up default configuration.

    根据日志打印信息可以看到:Logback 没有找到 logback-test.xml 和 logback.xml 配置文件,所以通过默认的配置策略-添加一个基本的 ConsoleAppender 来进行配置。

    Appender 类被看作为输出的目的地,Appenders 包括 console,files,Syslog,TCP Sockets,JMS 等等其它的日志输出目的地。用户可以根据自己的情况轻松的创建自己的 Appender。

    如果发生了错误,logback 会自动在控制台打印自己内部的状态信息。

    前面给的例子相对简单。实际上在大型的应用中,日志记录不会有太大的区别。日志记录的一般形式不会有改变,只是配置方式会有不同。可能你想要根据自己的需求来配置 logback,接下来的章节中将会介绍如何进行配置。

    在上面的例子中,我们通过 StatusPrinter.print() 打印了 logback 自身的内部状态。logback 的内部状态对查找 logback 相关的问题非常的有用。

    通过如下的三个步骤可以启用 logback 来记录日志:

  1. 配置 logback 环境。你可以通过简单或者复杂的方式来做,这个在后面会叙述到。
  2. 如果你想在每个类中打印日志,那么你需要将当前类的全称或者当前类当作参数,调用 org.slf4j.LoggerFactory.getLogger() 方法。
  3. 使用实例 logger 来调用不同的方法来打印日志。例:debug(),info(),warn(),error()。通过这些方法将会在配置好的 appender 中输出日志。

3.2、logback 的架构

Logger, Appender 和 Layouts:

    Logback 构建在三个主要的类上:Logger,Appender 和 Layouts。这三个不同类型的组件一起作用能够让开发者根据消息的类型以及日志的级别来打印日志。

    Logger 类作为 logback-classic 模块的一部分。Appender 与 Layouts 接口作为 logback-core 的一部分。作为一个通用的模块,logback-core 没有 logger 的概念。

Logger 上下文:

    任何日志 API 的优势在于它能够禁止某些日志的输出,但是又不会妨碍另一些日志的输出。通过假定一个日志空间,这个空间包含所有可能的日志语句,这些日志语句根据开发人员设定的标准来进行分类。

    在 logback-classic 中,分类是 logger 的一部分,每一个 logger 都依附在 LoggerContext 上,它负责产生 logger,并且通过一个树状的层级结构来进行管理。

    一个 Logger 被当作为一个实体,它们的命名是大小写敏感的,并且遵循一下规则:

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

    例如:
            名为 com.foo 的 logger 是名为 com.foo.Bar 的 logger 的父级。
            名为 java 的 logger 是名为 java.util 的父级,也是名为 java.util.Vector 的祖先。

    root logger 作为 logger 层次结构的最高层。它是一个特殊的 logger,因为它是每一个层次结构的一部分。每一个 logger 都可以通过它的名字去获取。

Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME)

    所有其它的 logger 通过 org.slf4j.LoggerFactory 类的静态方法 getLogger 去获取,这个方法需要传入一个 logger 的名字。下面是 Logger 接口一些基本的方法:

package org.slf4j; 
public interface Logger { 
	//	这里可以看到内置的变量,用来获取层级的最高层
    String ROOT_LOGGER_NAME = "ROOT";
    public void trace(String message);
    public void debug(String message);
    public void info(String message); 
    public void warn(String message); 
    public void error(String message); 
    // 省略其它重载方法
}
有效级别又称为级别继承:

    Logger 能够被分成不同的等级。不同的等级(TRACE, DEBUG, INFO, WARN, ERROR)定义在 ch.qos.logback.classic.Level 类中。在 logback 中,类 Level 使用 final 修饰的,所以它不能用来被继承。一种更灵活的方式是使用 Marker 对象。

    如果一个给定的 logger 没有指定一个层级,那么它就会继承离它最近的一个祖先的层级。更正式的说法是:

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

    为了确保所有的 logger 都有一个层级,root logger 会有一个默认层级 — DEBUG

    以下四个例子指定不同的层级,以及根据继承规则得到的最终有效层级

例子一:

logger 的名字指定的级别有效级别
rootDEBUGDEBUG
XnoneDEBUG
X.YnoneDEBUG
X.Y.ZnoneDEBUG

    在这个例子中,只有 root logger 被指定了级别,所以 logger X,X.Y,X.Y.Z 的有效级别都是 DEBUG。

例子二:

logger的名字指定的级别有效级别
rootERRORERROR
XINFOINFO
X.YDEBUGDEBUG
X.Y.ZWARNWARN

    在这个例子中,每个 logger 都分配了级别,所以有效层级就是指定的级别。

例子三:

logger的名字指定的级别有效级别
rootDEBUGDEBUG
XINFOINFO
X.YnoneINFO
X.Y.ZERRORERROR

    在这个例子中,logger root,X,X.Y.Z 都分别分配了级别。logger X.Y 继承它的父级 logger X。

例子四:

logger的名字指定的级别有效级别
rootDEBUGDEBUG
XINFOINFO
X.YnoneINFO
X.Y.ZnoneINFO

    在这个例子中,logger root,X 都分配了级别。logger X.Y,X.Y.Z 的级别继承它们最近的父级 X。

方法打印以及基本选择规则:

    根据定义,打印的方法决定的日志的级别。例如:L 是一个 logger 实例,L.info("…") 的日志级别就是 INFO。

    如果一条的日志的打印级别大于 logger 的有效级别,该条日志才可以被打印出来。这条规则总结如下:

基本选择规则
日志的打印级别为 p,Logger 实例的级别为 q,如果 p >= q,则该条日志可以打印出来。

    这条规则是 logbakc 的核心。各级别的排序为:TRACE < DEBUG < INFO < WARN < ERROR。

    在下面的表格中,第一列表示的是日志的打印级别,用 p 表示。第一行表示的是 logger 的有效级别,用 q 表示。行列交叉处的结果表示由基本选择规则得出的结果。

日志打印级别P有效的级别q
TRACE DEBUG INFO WARN ERROROFF
TRACE YESNONONONONO
DEBUG YESYESNONONONO
INFO YESYESYESNONONO
WARN YESYESYESYESNONO
ERRORYESYESYESYESYESNO

代码示例

package com.milory.logback;

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

public class SelectionRule {

    public static void main(String[] args) {
        // ch.qos.logback.classic.Logger 可以设置日志的级别
        // 获取一个名为 "com.foo" 的 logger 实例
        ch.qos.logback.classic.Logger logger =
                (ch.qos.logback.classic.Logger)LoggerFactory.getLogger("com.foo");
        // 设置 logger 的级别为 INFO
        logger.setLevel(Level.INFO);

        // 这条日志可以打印,因为 WARN >= INFO
        logger.warn("警告信息");
        // 这条日志不会打印,因为 DEBUG < INFO
        logger.debug("调试信息");

        // "com.foo.bar" 会继承 "com.foo" 的有效级别
        Logger barLogger = LoggerFactory.getLogger("com.foo.bar");
        // 这条日志会打印,因为 INFO >= INFO
        barLogger.info("子级信息");
        // 这条日志不会打印,因为 DEBUG < INFO
        barLogger.debug("子级调试信息");
    }
}

获取 Logger

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

package com.milory.logback;

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

public class LogBackEqual {

    public static void main(String[] args) {
        //  获取名字相同的两个 logger实例
        Logger x = LoggerFactory.getLogger("wombat");
        Logger y = LoggerFactory.getLogger("wombat");

        //  打印日志信息
        System.err.println(x);
        System.err.println(y);

        //  判断两个对象是否为同一个对象
        System.err.println(x == y);
    }
}

输出如下信息:

Logger[wombat]
Logger[wombat]
true

代码分析

    x,y 是同一个 logger 对象。

    可以通过配置一个 logger,然后在其它地方获取,而不需要传递引用。父级 logger 会自动寻找并关联子级 logger,即使父级 logger 在子级 logger 之后实例化。

    logback 环境的配置会在应用初始化的时候完成。最优的方式是通过读取配置文件。

    在每个类里面通过指定全限定类名为 logger 的名字来实例化一个 logger 是最好也是最简单的方式。因为日志能够输出这个 logger 的名字,所以这个命名策略能够看出日志的来源是哪里。虽然这是命名 logger 常见的策略,但是 logback 不会严格限制 logger 的命名,你完全可以根据自己的喜好来,你开心就好。

    但是,根据类的全限定名来对 logger 进行命名,是目前最好的方式,没有之一。

Appender(输出的目的地) 与 Layout(日志的格式):

    有选择的启用或者禁用日志的输出只是 logger 的一部分功能。logback 允许日志在多个地方进行输出。站在 logback 的角度来说,输出目的地叫做 appender。appender 包括console、file、remote socket server、MySQL、PostgreSQL、Oracle 或者其它的数据库、JMS、remote UNIX Syslog daemons 中。

    一个 logger 可以有多个 appender。

    logger 通过 addAppender 方法来新增一个 appender。对于给定的 logger,每一个允许输出的日志都会被转发到该 logger 的所有 appender 中去。换句话说,appender 从 logger 的层级结构中去继承叠加性。

    例如:如果 root logger 添加了一个 console appender,所有允许输出的日志至少会在控制台打印出来。如果再给一个叫做 L 的 logger 添加了一个 file appender,那么 L 以及 L 的子级 logger 都可以在文件和控制台打印日志。可以通过设置 additivity = false 来改写默认的设置,这样 appender 将不再具有叠加性。

appender 的叠加性规则如下:

  • logger L 的日志输出语句会遍历 L 和它的子级中所有的 appender。这就是所谓的 appender 叠加性(appender additivity)
  • 如果 L 的子级 logger 为 P,且 P 设置了 additivity = false,那么 L 的日志会在 L 所有 的 appender 包括 P 本身的 appender 中输出,但是不会在 P 的子级 appender 中输出。
  • logger 默认设置 additivity = true。

介绍

Logger NameAttached AppendersAdditivity属性值目标输出地备注
rootA1不适用A1root logger 为 logger 层级中的最高层,additivity 对它不适用
xA-x1, A-x2trueA1, A-x1, A-x2x 与 root 的 appender
x.ynonetrueA1, A-x1, A-x2x 与 root 的 appender
x.y.zA-xyz1trueA1, A-x1, A-x2, A-xyz1x 与 x.y 与 root 的 appender
securityA-secfalseA-sec因为 additivity = false,所以只有 A-sec 这个 appender
security.accessnodetrueA-sec因为它的父级 logger security 设置了 additivity = false,所以只有 A-sec 这一个 appender

    通常,用户既想自定义日志的输出地,也想自定义日志的输出格式。通过给 appender 添加一个 layout 可以做到。

    layout 的作用是将日志格式化,而 appender 的作用是将格式化后的日志输出到指定的目的地。PatternLayout 能够根据用户指定的格式来格式化日志,类似于 C 语言的 printf 函数。

    例:PatternLayout 通过格式化串 “%-4relative [%thread] %-5level %logger{32} - %msg%n” 会将日志格式化成如下结果:

176  [main] DEBUG manual.architecture.HelloWorld2 - Hello world.
  • “%-4relative”: 参数表示程序启动以来的耗时,单位为毫秒。
  • “%thread”: 参数表示当前的线程号。
  • %-5level: 参数表示当前日志的级别。
  • "%logger{32} : 表示输出类全路径的长度(类的名称不会被简写,即使类名称长度比“length”的值要大。其它的部分可能会被缩短为一个字符,但是永不会被移除),例如配置的参数为:%logger{15},类的全称为:mainPackage.sub.sample.Bar,则显示的数据为:m.s.sample.Bar
  • %msg: 参数表示程序输出的信息
  • %n: 参数表示输出换行符
参数化日志:

    一般情况对于参数进行输出(通过字符串进行拼接):

logger.debug("Entry number: " + i + " is " + num);

    但是有一种更好的方式去格式化日志信息,假设 entry 是一个 Object 对象:

Object entry = new SomeObject();
logger.debug("The entry is {}", entry);

    只有在需要打印 debug 信息的时候,才会去格式化日志信息,将 ‘{}’ 替换成 entry 的字符串形式。也就是说在这种情况下,如果禁止了日志的打印,也不会有构建参数上的性能消耗。

    下面两行输出的结果是一样的,但是一旦禁止日志打印,第二个变量的性能至少比第一个变量好上 30 倍。

logger.debug("The new entry is " + entry + ".");
logger.debug("The new entry is {}", entry);

    使用两个参数的例子如下:

logger.debug("The new entry is {}, It replaces {}.", entry, oldEntry);

    如果需要使用三个或三个以上的参数,可以采用如下的形式:

Object[] paramArray = {newVal, below, above};
logger.debug("Value {} was inserted between {} and {}.", paramArray);

3.3、配置 logback

    在应用程序当中使用日志语句需要耗费大量的精力。根据调查,大约有百分之四的代码用于打印日志。即使在一个中型应用的代码当中也有成千上万条日志的打印语句。考虑到这种情况,我们需要使用工具来管理这些日志语句。

    可以通过编程或者配置 XML 脚本或者 Groovy 格式的方式来配置 logback。对于已经使用 log4j 的用户可以通过这个工具来把 log4j.properties 转换为 logback.xml。

以下是 logback 的初始化步骤

  1. logback 会在类路径下寻找名为 logback-test.xml 的文件。
  2. 如果没有找到,logback 会继续寻找名为 logback.groovy 的文件。
  3. 如果没有找到,logback 会继续寻找名为 logback.xml 的文件。
  4. 如果没有找到,将会通过 JDK 提供的 ServiceLoader 工具在类路径下寻找文件 META-INFO/services/ch.qos.logback.classic.spi.Configurator,该文件的内容为实现了 Configurator 接口的实现类的全限定类名。
  5. 如果以上都没有成功,logback 会通过 BasicConfigurator 为自己进行配置,并且日志将会全部在控制台打印出来。

    最后一步的目的是为了保证在所有的配置文件都没有被找到的情况下,提供一个默认的(但是是非常基础的)配置。

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

自动配置 logback

    最简单的方式配置 logback 是让它去加载默认的配置。

package com.milory.logback.config;

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

public class MyApp1 {
    public static final Logger LOGGER = LoggerFactory.getLogger(MyApp1.class);

    public static void main(String[] args) {
        LOGGER.info("Entering application.");

        Foo foo = new Foo();
        foo.doIt();
        LOGGER.info("Exiting application.");
    }
}

Foo的代码如下

package com.milory.logback.config;

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

public class Foo {
    public static final Logger LOGGER = LoggerFactory.getLogger(Foo.class);

    public void doIt() {
        LOGGER.debug("Did it again!");
    }
}

    假设配置文件 logback-test.xml 或者 logback.xml 不存在,logback 会调用 BasicConfigurator 进行最小的配置。

    最小的配置包含一个附加到 root logger 上的 ConsoleAppender,格式化输出使用 PatternLayoutEncoder 对模版 %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 进行格式化。root logger 默认的日志级别为 DEBUG。

输出信息

17:20:27.701 [main] INFO com.milory.logback.config.MyApp1 - Entering application.
17:20:27.705 [main] DEBUG com.milory.logback.config.Foo - Did it again!
17:20:27.705 [main] INFO com.milory.logback.config.MyApp1 - Exiting application.

    MyApp1 通过调用 org.slf4j.LoggerFactory 与 org.slf4j.Logger 这两个类与 logback 相关联,并检索会用到的 logger。

    除了配置 logback 的代码,客户端的代码不需要依赖 logback,因为 SLF4J 允许在它的抽象层下使用任何日志框架,所以非常容易将大量代码从一个框架迁移到另一个框架。

使用 logback-test.xml 或 logback.xml 自动配置

    下面的配置等同于通过 BasicConfigurator 进行配置。(你需要将上面的配置文件命名为 logback.xml 或 logback-test.xml)

<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>

    运行 MyApp1,你将会看到相同的结果(你要是不相信,你可以更改模版,看是否生效)。

在警告或错误的情况下自动打印状态信息

    如果在解析配置文件的过程当中发生了错误,logback 会在控制台打印出它的内部状态数据。如果用户明确的定义了状态监听器,为了避免重复,logback 将不会自动打印状态信息。

    在没有警告或错误的情况下,如果你想查看 logback 内部的状态信息,可以通过 StatusPrinter 类来调用 print() 方法查看具体的信息

package com.milory.logback.config;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.util.StatusPrinter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyApp2 {
    public static final Logger LOGGER = LoggerFactory.getLogger(MyApp2.class);

    public static void main(String[] args) {

        LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory();
        //  查看具体的信息
        StatusPrinter.print(lc);

        LOGGER.info("Entering application.");

        Foo foo = new Foo();
        foo.doIt();
        LOGGER.info("Exiting application.");
    }
}

输出信息如下:

20:14:54,218 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
20:14:54,218 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
20:14:54,218 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [file:/D:/Work/idea/2019/logback/modules/logback-hello/target/classes/logback.xml]
20:14:54,345 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
20:14:54,346 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
20:14:54,354 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
20:14:54,361 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
20:14:54,414 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to INFO
20:14:54,414 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
20:14:54,415 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
20:14:54,416 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@29444d75 - Registering current configuration as safe fallback point

20:14:54.420 [main] INFO  com.milory.logback.config.MyApp2 - Entering application.
20:14:54.427 [main] INFO  com.milory.logback.config.MyApp2 - Exiting application.

    在输出信息中,可以清楚的看到内部的状态信息,又称之为 Status 对象,可以很方便的获取 logback 的内部状态。

通过配置文件打印状态信息

    你可以通过构造一个配置文件来打印状态信息,而不需要通过编码的方式调用 StatusPrinter 去实现。只需要在 configuration 元素上添加 debug 属性。配置文件如下所示(需要将配置文件命令为 logback.xml 或 logback-test.xml,不然 logbak 找不到配置文件):

//<!-- debug 属性只跟状态信息有关,并不会影响 logback 的配置文件,也不会影响 logger 的日志级别。-->
<configuration debug="true">
    <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>

    如果配置文件的配置有问题,logback 会检测到这个错误并且在控制台打印它的内部状态。

    但是,如果配置文件没有被找到,logback 不会打印它的内部状态信息,因为没有检测到错误。通过编码方式调用 StatusPrinter.print() 方法会在任何情况下都打印状态信息。

    强制输出状态信息:在缺乏状态信息的情况下,要找一个有问题的配置文件很难,特别是在生产环境下。为了能够更好的定位到有问题的配置文件,可以通过系统属性 “logback.statusListenerClass” 来设置StatusListener 强制输出状态信息。系统属性 “logback.statusListenerClass” 也可以用来在遇到错误的情况下进行输出。

    设置 debug=“true” 完全等同于配置一个 OnConsoleStatusListener(效果完全一样)。具体示例如下:

<configuration>

    //<!-- 强制输出状态信息 -->
    <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />

    <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>
通过系统属性指定默认的配置文件:

    通过系统属性 logback.configurationFile 可以指定默认的配置文件的路径。它的值可以是 URL,类路径下的文件或者是应用外部的文件(注意:文件类型只能是 “.xml” 或者 “.groovy”,其它的拓展文件将会被忽略。)。

java -Dlogback.configurationFile=/path/to/config.xml chapters.configuration.MyApp1

    如果是使用IDE开发工具可以设置“VM options”
通过IDE工具设置VM options
运行结果

09:06:32,476 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [./logback-abc.xml] at [file:/D:/Work/idea/2019/logback/modules/logback-hello/target/classes/logback-abc.xml]
09:06:32,599 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
09:06:32,603 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
09:06:32,610 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
09:06:32,646 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to INFO
09:06:32,647 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
09:06:32,647 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
09:06:32,648 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@29444d75 - Registering current configuration as safe fallback point
09:06:32.652 [main] INFO  com.milory.logback.config.MyApp1 - Entering application.
09:06:32.655 [main] INFO  com.milory.logback.config.MyApp1 - Exiting application.

    因为 logback.configureFile 是一个系统属性,所以也可以在应用内进行设置。但是必须在 logger 实例创建前进行设置。

package com.milory.logback.config;

import ch.qos.logback.classic.util.ContextInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyApp3 {

    static{
        System.setProperty(ContextInitializer.CONFIG_FILE_PROPERTY, "logback-abc.xml");
    }

    public static void main(String[] args) {
        //  如果把这个参数放在静态代码块之前的成员变量位置,则无效(系统属性必须在 logger 实例创建前进行设置才能生效)
        final Logger LOGGER = LoggerFactory.getLogger(MyApp3.class);

        LOGGER.info("Entering application.");

        Foo foo = new Foo();
        foo.doIt();
        LOGGER.info("Exiting application.");
    }
}

当配置文件更改时,自动加载

    为了让 logback 能够在配置文件改变的时候自动去扫描,需要在 <configuration> 标签上添加 scan=true 属性。

    例如:

<configuration scan="true">
    //	其它代码省略
</configuration>

    默认情况下,一分钟扫描一次配置文件,看是否有更改。通过 <configuration> 标签上的 scanPeriod 属性可以指定扫描周期。扫描周期的时间单位可以是毫秒、秒、分钟或者小时(如果没有指定时间单位,则默认为毫秒)。

    例如:

<configuration scan="true" scanPeriod="30 seconds"
   //	其它代码省略
</configuration>

    当设置了 scan=“true”,会新建一个 ReconfigureOnChangeTask 任务用于监视配置文件是否变化。ReconfigureOnChangeTask 也会自动监视外部文件的变化。

    如果更改后的配置文件有语法错误,则会回退到之前的配置文件。

在堆栈中展示包数据

    在 1.1.4 版本中,展示包数据是默认被禁用的。

    如果启用了展示包数据,logback 会在堆栈的每一行显示 jar 包的名字以及 jar 的版本号。展示包数据可以很好的解决 jar 版本冲突的问题。但是,这个的代价比较高,特别是在频繁报错的情况下。

14:28:48.835 [btpool0-7] INFO  c.q.l.demo.prime.PrimeAction - 99 is not a valid value
java.lang.Exception: 99 is invalid
  at ch.qos.logback.demo.prime.PrimeAction.execute(PrimeAction.java:28) [classes/:na]
  at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431) [struts-1.2.9.jar:1.2.9]
  at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236) [struts-1.2.9.jar:1.2.9]
  at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432) [struts-1.2.9.jar:1.2.9]
  at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) [servlet-api-2.5-6.1.12.jar:6.1.12]
  at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502) [jetty-6.1.12.jar:6.1.12]
  at ch.qos.logback.demo.UserServletFilter.doFilter(UserServletFilter.java:44) [classes/:na]
  at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115) [jetty-6.1.12.jar:6.1.12]
  at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:361) [jetty-6.1.12.jar:6.1.12]
  at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:417) [jetty-6.1.12.jar:6.1.12]
  at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230) [jetty-6.1.12.jar:6.1.12]

启用展示包数据:

<configuration packagingData="true">
    //	其它代码省略
</configuration>
直接调用 JoranConfigurator

    Logback 依赖的配置文件库为 Joran,是 logback-core 的一部分。

    logback 的默认配置机制为:通过 JoranConfigurator 在类路径上寻找默认的配置文件。你可以通过直接调用 JoranConfigurator 的方式来重写 logback 的默认配置机制。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值