Spring Boot 篇五: Log的使用

Log在Spring的前世今生

Log对于任一系统的重要性不言而喻。

跟JPA类似,Spring中的Log也只是提供了接口定义,然后在项目中,需要项目自行选用Log的具体实现。

值得一提的是,Spring跟Spring Boot使用的Log接口与默认实现不同:

  • Spring的Log接口是JCL (Jakarta Commons Logging)。Spring框架采用了JUL(属于java.util.logging)来作为默认的Log实现。
  • Spring Boot的Log接口是SLF4J,Simple Logging Facade for Java。且使用Logback来作为标准实现。

另外一个有名的Log的实现是Log4j和Log4j2。当然最近的Log4j2的漏洞问题,导致Log4j2颇有些谈虎色变的味道。

log4j, logback和log4j2

log4j 是Apache的一个开源项目,已经结束了生命周期;
logback 官网上介绍说Logback is intended as a successor to the popular log4j project,也就是log4j 1.x的一个后续,基于SLF4J API的原生实现。
log4j2官网Log4j2是log4j 1.x和logback的改进版。其采用了一些新技术(无锁异步等),使得日志的吞吐量、性能比log4j 1.x提高了10倍,并解决了一些死锁的bug,而且配置更加简单灵活。

SLF4J API

不管是Logback还是log4j2,应用程序实际上通过SLF4J API再进行编程。

SLF4J

SLF4J Manual上有个非常基础的例子:

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

public class HelloWorld {

  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}

另外一个典型案例(同样来自SLF4J Manual):

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

public class Wombat {
    final Logger logger = LoggerFactory.getLogger(Wombat.class);
    Integer t;
    Integer oldT;
    
    public void setTemperature(Integer temperature) {
        oldT = t;        
        t = temperature;

        logger.debug("Temperature set to {}. Old value was {}.", t, oldT);

        if(temperature.intValue() > 50) {
            logger.info("Temperature has risen above 50 degrees.");
        }
    }
} 

还可以使用Fluent Logging API:

int newT = 15;
int oldT = 16;

// using traditional API
logger.debug("Temperature set to {}. Old value was {}.", newT, oldT);

// using fluent API, log message with arguments
logger.atDebug().log("Temperature set to {}. Old value was {}.", newT, oldT);
   
// using fluent API, add arguments one by one and then log message
logger.atDebug().setMessage("Temperature set to {}. Old value was {}.").addArgument(newT).addArgument(oldT).log();

// using fluent API, add one argument with a Supplier and then log message with one more argument.
// Assume the method t16() returns 16.
logger.atDebug().setMessage("Temperature set to {}. Old value was {}.").addArgument(() -> t16()).addArgument(oldT).log();
      

使用Logback

Logback是Spring Boot的默认实现,且默认的输出是Console。

想要更改到别的位置,需要修改application.properties来指定(一般来说,只需要指定其中一个):

  • logging.file (更高优先级),设置文件。
  • logging.path,设置目录,会在指定目录下创建spring.log文件并写入。

一个例子:

logging.path=/log/
logging.pattern.console=%d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
logging.pattern.file=%d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n

默认条件下,Logback会在%classpath%下查找一下文件:

  • logback-spring.xml
  • logback-spring.groovy
  • logback.xml
  • logback.groovy

xml

一个基础的logback-spring.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>
 
  <logger name="chapters.configuration" level="INFO"/>
 
  <root level="DEBUG">          
    <appender-ref ref="STDOUT" />
  </root>  
  
</configuration>

logger

logger,用来设置某一个包或者具体的某一个类的日志打印级别,并用来关联appender。

logger节点下有三个属性,和一个子节点。

  • name:用来指定受此logger约束的某一个包或者具体的某一个类。
  • level:用来设置打印级别(TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF),还有一个值INHERITED或者同义词NULL,代表强制执行上级的级别。如果没有设置此属性,那么当前logger将会继承上级的级别。
  • addtivity:是否向上级logger传递打印信息。默认是true

子节点appender-ref 则是用来指定具体appender的。

root

root是一种特殊的logger。它不支持nameaddtivity属性。

appender

appender节点,有两个属性。

  • name: 指定appender的名称,logger就通过该名称进行引用;
  • class:指定appender的类名。一般就三个类。
    1. ch.qos.logback.core.ConsoleAppender。用来写入Console。
    2. FIleAdapter。
    3. ch.qos.logback.core.rolling.RollingFileAppender。用来写入文件,通过rolling的方式来决定是否写入新的文件。

该节点有一些常用的子节点。

append

<append>true</append>

当这个子节点为true时,日志被追加到文件结尾。为false时,清空现存文件。默认是true

filter

filter子节点为appender添加过滤器,可以添加多个过滤器。当appender有多个过滤器时,按照配置顺序执行。

filter的返回值:

  • DENY:类似于循环中的break。日志将立即被抛弃,并且不经过剩余的过滤器;
  • NEUTRAL:类似于循环的continue。 并且会由列表里中的下个过滤器过接着处理日志;
  • ACCEPT:类似于循环中的return。日志会被立即处理,且不经过剩余的过滤器;

常用的filter有两种:

  • ThresholdFilter。临界值过滤器,用于过滤掉低于指定临界值的日志。当日志级别大于或等于临界值时,过滤器返回NEUTRAL;当日志级别低于临界值时,日志会被放弃。

下列示例会记录INFO及以上的Log:

<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
    <level>INFO</level>
</filter>
  • LevelFilter。 级别过滤器,根据日志级别进行过滤。如果日志级别等于配置级别,过滤器会根据onMath(用于配置符合过滤条件的操作) 和 onMismatch(用于配置不符合过滤条件的操作)接收或拒绝日志

下述示例只会记录INFO级别的LOG。

<filter class="ch.qos.logback.classic.filter.LevelFilter">   
  <level>INFO</level>   
  <onMatch>ACCEPT</onMatch>
  <onMismatch>DENY</onMismatch>
</filter> 

encoder

encoder子节点主要用来定义写入日志的格式。

<encoder>
  <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>

file和rollingPolicy

filerollingPolicy主要用来处理写入日志文件的appender。其中file用以确定文件路径。而rollingPolicy主要用来定义写入的频率。

一个简单的示例:

<appender name="FILEOUT"
    class="ch.qos.logback.core.rolling.RollingFileAppender">
    <append>true</append>
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>${logging.level}</level>
    </filter>
    <file>
        ${logging.path}/logs/logs.log
    </file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <FileNamePattern>${logging.path}/logs/logs.log.%d{yyyy-MM-dd}</FileNamePattern>
        <MaxHistory>30</MaxHistory>
    </rollingPolicy>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        <charset>UTF-8</charset>
    </encoder>
</appender>

一些实际案例

不写入Console的日志

以下的示例会在程序运行时写入文件日志,但不会打印在Console上。

<configuration>
    <appender name="FILEOUT"
    class="ch.qos.logback.core.rolling.RollingFileAppender">
        <append>true</append>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>${logging.level}</level>
        </filter>
        <file>
            ${logging.path}/logs/log.log
        </file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${logging.path}/logs/log.log.%d{yyyy-MM-dd}</FileNamePattern>
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    
    <root level="info">
        <appender-ref ref="FILEOUT"/>
    </root>
</configuration>

在Console显示日志,但将错误写入文件

以下的示例会在程序运行时打印在Console,但将错误信息写入文件日志。

<configuration>
    <appender name="STDOUT"
        class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <Pattern>%d{HH:mm:ss.SSS} %-5level %logger{80} - %msg%n</Pattern>
        </encoder>
    </appender>
    <appender name="FILEOUT"
    class="ch.qos.logback.core.rolling.RollingFileAppender">
        <append>true</append>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
          <level>ERROR</level>
        </filter>
        <file>
          ${logging.path}/logs/log.log
        </file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
          <FileNamePattern>${logging.path}/logs/log.log.%d{yyyy-MM-dd}</FileNamePattern>
          <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    
    <root level="${logging.level}">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

使用log4j2

在POM文件中引入Dependency:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
	<exclusions>
		<exclusion>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-logging</artifactId>
		</exclusion>
	</exclusions>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

具体使用,呃,等有时间再写吧。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值