美文网首页
Logback学习笔记

Logback学习笔记

作者: 无知者云 | 来源:发表于2019-01-17 10:37 被阅读0次

知识点

  • logback在会先加载classpath下的logback-test.xml,如果没有找到再加载logback.xml。
  • Logback中三大核心概念:Logger、Appender和Layout。
  • Logger采用层级架构,采用类似java的package的命名方式,名为a.b的Logger是a.b.c的parent,而后者则为前者的child,如果两个logger之间间隔了几层,比如a.ba.b.c.d,那么a.ba.b.c.d的ancestor,反之a.b.c.da.b的descendant。
  • Logger本身可以设置Level,Level从高到低有:ERROR,WARN,INFO,DEBUG,TRACE。除了设置Level之外,在实际打日志时通过logger.info()其实指定的是请求Level,只有当请求Level高于Logger的设置Level时,该Logger才生效。这个不难理解,因为Level级别越高,表示越应该记录下该Logger。比如,当设置Level为INFO时,表示输入一些正常的日志事件,但是如果系统中出现了错误,即通过logger.error()输入日志,很明显这个错误是我们更应该关心的,因此该日志时需要记录下来的。
  • 多数时候,我们没有必要为每个logger设置level,此时并不代表logger没有level,而是继承了离其最近的ancestor,此处Logger的层级架构便起作用了。在层级的最上层,有个固定的名为ROOT的logger,表示任何logger如果没有设置的ancestor,那么其Level便继承自ROOT。Logback的ROOT的默认Level为DEBUG。
  • Logger在输出日志时,是通过Appender来完成的,有的Appender将日志输出到命令行,有的输出到文件,有的则输出到数据库或者消息队列。一个Logger可以有多个Appender,也即该Logger的日志会输出到多个地方。
  • 对于Appender,Logback有个重要的性质:如果某个Logger得到了启用(即请求Level高于其有效设置Level),那么除了该Logger自身所拥有的Appender会输出日志之外,该Logger的所有ancestor的Appender也会输出日志。这也是为什么在通常情况下,我们只需要为ROOT设置appender的原因。Logabck的这个性质其实依赖于Logback的additivity属性,事实上是该属性确定了是否将日志请求向上级Logger的appender发送。在默认情况下,additivity=true,则表示日志会输送到所有上级Logger的appender。当然,我们可以设置为false,使某个Logger的日志只是输出到某些特定的地方。比如,我们希望只将性能测试的日志输入到某个文件中,而将系统中的所有其他日志输出到命令行中。
  • 需要注意的是,additivity与level无关,就是说即便child logger的level比parent logger的低,child logger的additivity=true,那么只要child logger被启用,那么其日志依然会上传到parent logger的appender中。

案例项目

└── src
    └── main
        ├── java
        │   └── davenkin
        │       └── parent
        │           ├── Parent.java
        │           ├── child1
        │           │   └── Child1.java
        │           └── child2
        │               └── Child2.java
        └── resources
            └── logback.xml

Github地址: https://github.com/davenkin/logback-learning

其中Parent中main函数分别输出Parent,Child1和Child2的日志。

  • logback.xml:
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="ROOT_FILE" class="ch.qos.logback.core.FileAppender">
        <file>root.log</file>
        <append>true</append>
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="ROOT_FILE"/>
    </root>
</configuration>
  • Parent:
package davenkin.parent;

import davenkin.parent.child1.Child1;
import davenkin.parent.child2.Child2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Parent {
    private static final Logger logger = LoggerFactory.getLogger(Parent.class);

    public static void main(String[] args) {
        logger.info("info from parent");
        logger.debug("debug from parent");
        new Child1().hello();
        new Child2().hello();
    }

}

  • Child1:
package davenkin.parent.child1;

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

public class Child1 {
    private static final Logger logger = LoggerFactory.getLogger(Child1.class);

    public void hello() {
        logger.info("info from child1");
        logger.debug("debug from child1");
    }
}
  • Child2:
package davenkin.parent.child2;

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

public class Child2 {
    private static final Logger logger = LoggerFactory.getLogger(Child2.class);

    public void hello() {
        logger.info("info from child2");
        logger.debug("debug from child2");
    }
}

常用配置

以上例子是一个常见的日志配置,其中:没有为任何Logger显式配置Level与additivity属性,而只是配置了一个root logger,即表示所有日志最终都会打到root logger所对应的appender中,日志同时打到了命令行和文件中。root logger的Level设置成了INFO,表示debug的日志请求不会得到输出。这样的配置常见于生产环境,因为很多第三方类库都会输出大量的debug日志,这样会使得我们正常的INFO级别的业务记录日志淹没在debug日志中。

此时的命令行输出日志如下:

09:03:01.304 INFO  davenkin.parent.Parent - info from parent
09:03:01.306 INFO  davenkin.parent.child1.Child1 - info from child1
09:03:01.306 INFO  davenkin.parent.child2.Child2 - info from child2

为某个package下的日志设置单独的Level

有时为了调试方便,我们可能需要将某个package下的debug或者trace日志输出,比如在用Spring的WebserviceTemplate时,我们希望打印出请求和返回的XML数据,那么此时便可以显式地为对应logger设置Level:

   <logger name="org.springframework.ws.client.MessageTracing">
    <level value="TRACE"/> 
   </logger>
   <logger name="org.springframework.ws.server.MessageTracing">
    <level value="TRACE"/> 
   </logger>

对应到上面的案例项目,如果我们希望child1包下能够打印出debug级别的日志,那么可以修改logback.xml文件,在其中显式配置logger:

  <logger name="davenkin.parent.child1">
        <level value="DEBUG"/>
    </logger>

此时输出日志中便包含了child1下的DEBUG日志,但是不包含其他package下的DEBUG日志:

09:09:10.935 INFO  davenkin.parent.Parent - info from parent
09:09:10.938 INFO  davenkin.parent.child1.Child1 - info from child1
09:09:10.938 DEBUG davenkin.parent.child1.Child1 - debug from child1
09:09:10.938 INFO  davenkin.parent.child2.Child2 - info from child2

将不同logger的日志打入到不同的文件中

有时我们希望将某些logger的日志输出到单独的文件中,比如在做性能测试时,需要将所有性能测试的日志输出到perfomance.log中,这时需要为性能测试专门创建一个logger,比如名为perfomance-logger,我们需要做3件事情:

  • 在代码中输出日志时,不能使用类名来命名logger,而是直接使用performance-logger:
Logger performanceLogger = LoggerFactory.getLogger("performance-logger");
  • 专门配置一个属于性能日志的appender:
   <appender name="PERFORMANCE_LOG" class="ch.qos.logback.core.FileAppender">
        <file>performance.log</file>
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>

  • 然后在配置logger时,设置perfomance-logger的additivity=false,这样便可防止性能日志输出到其他地方。
    <logger name="performance-logger" level="DEBUG" additivity="false">
        <appender-ref ref="PERFORMANCE_LOG"/>
    </logger>

对于,上面的例子项目,加入Child3:

package davenkin.parent.child3;

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

public class Child3 {
    private static final Logger logger = LoggerFactory.getLogger("performance-logger");

    public void hello() {
        logger.debug("debug from performance");
    }
}

在logback.xml中加入以下配置:


    <appender name="PERFORMANCE_LOG" class="ch.qos.logback.core.FileAppender">
        <file>performance.log</file>
        <append>true</append>
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>

    <logger name="performance-logger" level="DEBUG" additivity="false">
        <appender-ref ref="PERFORMANCE_LOG"/>
    </logger>

再修改Parent让其输出Child3的日志:

 public static void main(String[] args) {
        logger.info("info from parent");
        logger.debug("debug from parent");
        new Child1().hello();
        new Child2().hello();
        new Child3().hello();
    }

得到命令行输出:

09:38:58.301 INFO  davenkin.parent.Parent - info from parent
09:38:58.304 INFO  davenkin.parent.child1.Child1 - info from child1
09:38:58.304 DEBUG davenkin.parent.child1.Child1 - debug from child1
09:38:58.304 INFO  davenkin.parent.child2.Child2 - info from child2

可以看到,命令行中不包含performance日志,而在performance.log中得到了performance日志:

198  [main] DEBUG performance-logger - debug from performance

将不同Level的日志打到不同文件中

可以通过配置Filter的方式将不同Level的日志打到不同的文件中,Filter是配置在Appender中的,具体参考这里这里

最佳实践

  • log不应该包含敏感信息,比如用户密码等。
  • log中应该包含对应资源ID,比如在操作某个User时,应该记录该User的ID。
  • log中应该包含足够多的上下文信息。
  • 创建log时,采用static和final关键字:
    private static final Logger LOG = LoggerFactory.getLogger(Foo.class);
  • 对于异常日志应该统一处理,比如在Spring的ExceptionHandler中打印异常日志,而此时异常的message应该包含足够的上下文信息。
  • 发生异常时,不要打印日志然后再次抛出异常,这样导致日志输出2次:
catch (NoSuchMethodException e) { LOG.error("Blah", e); throw e; }
  • log中应该包含请求的ID,可以将请求ID放在MDC中。
  • log中应该包含操作者的ID,在Spring Security完成认证后,可以将操作者ID放在MDC中。
  • 在WriteApplicationService中,应该记录下业务日志,也即每次对系统一个业务操作,都应该有记录,而在ApplicationService中时最合适的,因此它对应了一次业务用例。
  • 在ReadApplicationService中,通常没有必要记录日志,除非此时系统产生了异常。

相关文章

  • Logback学习笔记

    知识点 logback在会先加载classpath下的logback-test.xml,如果没有找到再加载logb...

  • logback学习笔记

    一、logback的介绍Logback是由log4j创始人设计的另一个开源日志组件,官方网站: http://lo...

  • logback学习笔记

    目的:将项目中的log4j改为logback,并且使用门面日志slf4j进行打印日志。 1.删除log4j的依赖 ...

  • logback学习笔记(下)

    在logback学习笔记(上)中我们介绍了logback中的一些核心概念,在这篇文章中我们共同来学习一下如何利用配...

  • logback学习笔记(上)

    相信任何一位工程师都在代码中写过日志打印代码,也知道日志打印对项目的重要性,有人做过统计代码中的日志占到工程总代码...

  • 第3章 Logback的配置与使用

    Logback的主要模块 logback-access logback-classic logback-core ...

  • 三.Logback配置与使用

    logback日志介绍 主要模块 logback-access logback-classic logback-c...

  • Logback 日志第一章简介

    logback 分为三个模块:logback-core,logback-classic和logback-acces...

  • logback配置笔记

    LogBack的配置大概包括3部分,Appender,Logger,Root的配置。 Appender: 负责写日...

  • Springboot Logback笔记

    之前习惯了日志配置文件粘贴复制。今天闲下来的时候,学习了Logback的配置新姿势,以后不再盲目粘贴复制了。由于S...

网友评论

      本文标题:Logback学习笔记

      本文链接:https://www.haomeiwen.com/subject/ziqcoftx.html