日志框架与全局日志管理


日志框架与全局日志管理

1.⽇志框架的体系结构

刚刚接触到⽇志的同学可能会被各种⽇志框架吓到,包括各种⽇志框架之间的 jar 总是发⽣冲突,另很多 ⼩伙伴头疼不已。本章将学习各种 Java ⽇志框架发展过程,以及他们之间的关系,以及如何选型。

⼀、五花⼋⻔的⽇志⼯具包

1.1 ⽇志框架

  • JDK java.util.logging 包:java.util.logging 是 jdk1.4 发布的 java ⽇志包, 可以说是应⽤⽐较久远的⽇志⼯具包
  • log4j: apache 的⼀个开源项⽬,提供了强有⼒的 java ⽇志⽀持,甚⾄他也提供了其他语⾔包括 C、C++、.Net、PL/SQL 的接⼝,从⽽实现多语⾔并存的分布式环境⽇志打印。⽬前已经停⽌更 新,所以不推荐使⽤。
  • Logback:由 log4j 创始⼈设计的另⼀个开源⽇志组件,作为 Spring Boot 默认的⽇志框架,应⽤⽐较 ⼴泛。
  • log4j2 :Apache Log4j2 是对 Log4j 的升级,它⽐其前身 Log4j1.x 提供了重⼤改进,并提供了 Logback 中可⽤的许多改进,同时修复了 Logback 架构中的⼀些问题。它基于 LMAX 公司开发 Disruptor(⼀个开源的⽆锁并发框架),改善了 Log4j 和 Logback 在架构设计⽅⾯的缺陷,具有超⾼ 的吞吐量和低延迟,性能⽐ Log4j1.x 和 Logback 更好。

1.2 ⽇志⻔⾯

  • commons-logging: Apache commons 类库中的⼀员,他作为⼀个⽇志⻔⾯,能够⾃动选择使⽤ log4j 还是 JDK logging,但是他不依赖 Log4j,JDK Logging 的 API。如果项⽬的 classpath 中包含了 log4j 的类库,就会使⽤ log4j,否则就使⽤ JDK Logging。
  • SLF4J: 可以说是⽬前应⽤最为⼴泛的⽇志⻔⾯了,它提供了⼀个⽇志抽象层,允许你在后台使⽤ 任意⼀个⽇志类库。如:log4j、log4j2、logback。

1.3 ⽇志⻔⾯存在的意义

为什么不直接使⽤⽇志框架,⽽是搞出⼀个⽇志⻔⾯?

⽇志⻔⾯(SLF4J)主要是为了给 Java ⽇志访问提供⼀套标准、规范的 API 框架,其主要意义在于提供接 ⼝,具体的实现可以交由其他⽇志框架来实现,例如 log4j 和 logback 等。 对于⼀般的 Java 项⽬⽽⾔,⽇ 志框架会选择 slf4j-api 作为⻔⾯,配上具体的实现框架(log4j、log4j2、logback 等),中间使⽤桥接器 完成桥接。前⾯介绍的⼏种⽇志框架,每⼀种⽇志框架都有⾃⼰单独的 API,要使⽤对应的框架就要使⽤其对应的 API,这就⼤⼤的增加应⽤程序代码对于⽇志框架的耦合性要求。有了 SLF4J 这个⻔⾯之后,程序员永远 都是⾯向 SLF4J 编程,可以实现简单快速地替换底层的⽇志框架⽽不会导致业务代码需要做相应的修改。

在使⽤ SLF4J 进⾏⽇志记录时,通常都需要在每个需要记录⽇志的类中定义 Logger 变量,如下所示:

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

@RestController
public class LogTestController {
    private static final Logger logger = LoggerFactory.getLogger(LogTestController.class);

    @GetMapping("/test")
    public void test(){
        logger.trace("Trace ⽇志...");
        logger.debug("Debug ⽇志...");
        logger.info("Info ⽇志...");
        logger.warn("Warn ⽇志...");
        logger.error("Error ⽇志...");
   }
}

这显然属于重复性劳动,降低了开发效率,如果你在项⽬中引⼊了 Lombok,那么可以使⽤它提供的 @Slf4j 注解来⾃动⽣成上⾯那个变量,默认的变量名是 log,如果我们想采⽤惯⽤的 LOGGER 变量名, 那么可以在⼯程的 main/java ⽬录中增加 lombok.config ⽂件,并在⽂件中增加 lombok.log.fieldName=LOGGER 的配置项即可。

⼆、⽇志框架选型

  • Spring Boot 默认的⽇志记录框架使⽤的是 Logback
  • 其中 Log4j 可以认为是⼀个过时的函数库,已经停⽌更新,不推荐使⽤,相⽐之下,性能和功能也是最差的。
  • logback 虽然是 Spring Boot 默认的,但性能上还是不及 Log4j2,因此,在现阶段,⽇志记录⾸选 Log4j2。

SLF4J + Log4j2 是我们推荐的⽇志记录选型。

性能测试结果

参考:[log4j2 官网](Log4j – Log4j 2 Lock-free Asynchronous Loggers for Low-Latency Logging (apache.org))

三、⽇志级别

细说各⽇志框架整合配置前,我们先来⼤致了解下,最常⻅的⽇志的⼏个级别:ERROR, WARN, INFO, DEBUG 和 TRACE。像其他的,⽐如 ALL、OFF 和 FATAL 之类的开发过程中应该基本上是不会涉及的。所 以以下从低到⾼⼀次介绍下常⻅的⽇志级别。

  1. TRACE:追踪。⼀般上对核⼼系统进⾏性能调试或者跟踪问题时有⽤,此级别很低,⼀般上是不开 启的,开启后⽇志会很快就打满磁盘的。
  2. DEBUG:调试。这个⼤家应该不陌⽣了。开发过程中主要是打印记录⼀些运⾏信息之类的。
  3. INFO:信息。这个是最常⻅的了,⼤部分默认就是这个级别的⽇志。⼀般上记录了⼀些交互信息,⼀ 些请求参数等等。可⽅便定位问题,或者还原现场环境的时候使⽤。此⽇志相对来说是⽐较重要的。
  4. WARN:警告。这个⼀般上是记录潜在的可能会引发错误的信息。⽐如启动时,某某配置⽂件不存在 或者某个参数未设置之类的。
  5. ERROR:错误。这个也是⽐较常⻅的,⼀般上是在捕获异常时输出,虽然发⽣了错误,但不影响系统 的正常运⾏。但可能会导致系统出错或是宕机等。

⽇志级别从⼩到⼤为 trace<debug<info<warn<error<fatal,由于通常⽇志框架默认⽇志级别设置为 INFO,因此 trace 和 debug 级别的⽇志都看不到。

2022-04-17 13:59:16.566 INFO c.z.b.l.controller.LogTestController  :
Info ⽇志...
2020-04-17 13:59:16.566 WARN c.z.b.l.controller.LogTestController  :
Warn ⽇志...
2020-04-17 13:59:16.566 ERROR c.z.b.l.controller.LogTestController :
Error ⽇志...

四、常⻅术语概念解析

  1. appender:主要控制⽇志输出到哪⾥,⽐如:⽂件、数据库、控制台打印等。
  2. logger: ⽤来设置某⼀个包或者具体某⼀个类的⽇志打印级别、以及指定 appender。
  3. root:也是⼀个 logger,是⼀个特殊的⽗ logger。所有的⼦ logger 最终都会将输出流交给 root,除⾮ 在⼦ logger 中配置了 additivity=”false”。
  4. rollingPolicy:所有⽇志都放在⼀个⽂件是不好的,所以可以指定滚动策略,按照⼀定周期或⽂件⼤ ⼩切割存放⽇志⽂件。
  5. RolloverStrategy:⽇志清理策略。通常是指⽇志保留的时间。
  6. 异步⽇志:单独开⼀个线程做⽇志的写操作,达到不阻塞主线程的⽬的。

  • 同步⽇志,主线程要等到⽇志写磁盘完成之后,才能继续向下执⾏。
  • 异步⽇志,主线程写⽇志只是将⽇志消息放⼊⼀个队列,之后就继续向下执⾏,这个过程是在内存层 ⾯完成的。之后由专⻔的线程从队列中获取⽇志数据写⼊磁盘,所以不阻塞主线程。主线程(核⼼业 务代码)执⾏效率很⾼。

2.logback ⽇志框架配置

logback 既可以通过 application 配置⽂件进⾏⽇志的配置,⼜可以通过 logback-spring.xml 进⾏⽇志的配 置。通常情况下,使⽤全局配置⽂件 application.yml 或 properties 进⾏配置就⾜够了,如果您的⽇志输出 需求特别复杂⽽且需求⽐较个性化,可以考虑使⽤ logback-spring.xml 的配置⽅式。

⼀、application 配置⽂件实现⽇志配置

我们可以在 applicaition.properties(yml) ⽂件中进⾏⽇志的配置

logging:
 level:
   root: info
   top.syhan.boot.log.controller: debug
 file:
   path: /Users/apple/Desktop/logs
   name:  /Users/apple/Desktop/logs/boot.log
   max-size: 10MB
   max-history: 10
 pattern:
   console: '%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread])
%highlight(%-5level) %boldMagenta(%logger{10}) - %cyan(%msg%n)'
   file: '%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger :
%msg%n'
  • logging.level.root=info 指定整个系统的默认⽇志级别是 info,⽇志级别统⼀化。
  • logging.level.com.zimug.boot.launch.controller=debug,指定某个特定的 package 的⽇志级别是 debug,⽇志级别个性化。优先级⻆度,个性配置⼤于统⼀配置。
  • logging.file.path 将⽇志输出到指定⽬录,如果不指定 logging.file.name,⽇志⽂件的默认名称是 spring.log。配置了 logging.file.name 之后,logging.file.path 配置失效。
  • ⽆论何种设置,Spring Boot 都会⾃动按天分割⽇志⽂件,也就是说每天都会⾃动⽣成⼀个新的 log ⽂ 件,⽽之前的会⾃动打成 GZ 压缩包。# ⽇志⽂件⼤⼩
  • 可以设置 logging.file.max-size=10MB 分割的每个⽇志的⽂件最⼤容量,超过这个 size 之后⽇志继续分隔。
  • 可以设置保留的⽇志时间 logging.file.max-history=10,以天为单位。
  • logging.pattern.file 输出到⽂件中的⽇志的格式。
  • logging.pattern.console 控制台输出⽇志的格式,为了在控制台调试时候显示效果更清晰,为⽇志增 加了颜⾊。red、green 等等。

⽇志格式占位符

配合这张图,看⼀下占位符和 logging.pattern.console 格式配置之间的关系

  • %d{HH:mm:ss.SSS}:⽇志输出时间(red)
  • %thread:输出⽇志的进程名字,这在 Web 应⽤以及异步任务处理中很有⽤ (green)
  • %-5level:⽇志级别,并且使⽤ 5 个字符靠左对⻬ (highlight ⾼亮蓝⾊)
  • %logger:⽇志输出类的名字 (boldMagenta 粗体洋红⾊)
  • %msg:⽇志消息 (cyan 蓝绿⾊)
  • %n:平台的换⾏符

⼆、使⽤ logback-spring.xml 实现⽇志配置

2.1 需求

⼀般情况下,使⽤全局配置⽂件 application.yml 或 properties 进⾏配置就⾜够了,如果你的⽇志输出需求 特别复杂,可以考虑使⽤ logback-spring.xml 的配置⽅式。

spring boot ⽤⾃带的 logback 打印⽇志,多环境打印:

  1. ⽣产环境输出到控制台和⽂件,⼀天⼀个⽂件,保留 30 天。
  2. 开发环境输出到控制台和打印 sql(mybatis)输出,⽣产环境不打印这个信息。
  3. 测试环境只输出到控制台。不输出到⽂件。

打印 Mybatis SQL,只需要把使⽤到 Mybatis 的 package 的⽇志级别调整为 DEBUG,就可以将 SQL 打印出来。

前提:项⽬已经⽀持 application.yml 的 profile 多环境配置

2.2 需求实现

因为 logback 是 spring boot 的默认⽇志框架,所以不需要引⼊ maven 依赖,直接上 logback-spring.xml 放 在 resources 下⾯。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <!--引⼊默认的⼀些设置-->
  <include
resource="org/springframework/boot/logging/logback/defaults.xml"/>
  <!--web信息-->
  <logger name="org.springframework.web" level="info"/>

  <!--写⼊⽇志到控制台的appender,⽤默认的,但是要去掉charset,否则windows下tomcat
下乱码-->
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>${CONSOLE_LOG_PATTERN}</pattern>
    </encoder>
  </appender>

  <!--定义⽇志⽂件的存储地址 勿在 LogBack 的配置中使⽤相对路径-->
  <property name="LOG_PATH" value="/Users/syhan/Desktop/logs"/>
  <!--写⼊⽇志到⽂件的appender-->
  <appender name="FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
    <rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!--⽇志⽂件输出的⽂件名,每天⼀个⽂件-->
      <FileNamePattern>${LOG_PATH}.%d{yyyy-MM-dd}.log</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>
    </encoder>
    <!--⽇志⽂件最⼤的⼤⼩-->
    <triggeringPolicy
class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <MaxFileSize>10MB</MaxFileSize>
    </triggeringPolicy>
  </appender>

  <!--异步写⽇志到⽂件-->
  <appender name="asyncFileAppender"
class="ch.qos.logback.classic.AsyncAppender">
    <discardingThreshold>0</discardingThreshold>
    <queueSize>500</queueSize>
    <appender-ref ref="FILE"/>
</appender>

  <!--⽣产环境:打印控制台和输出到⽂件-->
  <springProfile name="prod">
    <root level="info">
      <appender-ref ref="CONSOLE"/>
      <appender-ref ref="asyncFileAppender"/>
    </root>
  </springProfile>

  <!--开发环境:打印控制台-->
  <springProfile name="dev">
    <!-- 打印sql -->
    <logger name="top.syhan.boot.log" level="DEBUG"/>
    <root level="DEBUG">
      <appender-ref ref="CONSOLE"/>
    </root>
  </springProfile>

  <!--测试环境:打印控制台-->
  <springProfile name="test">
    <root level="info">
      <appender-ref ref="CONSOLE"/>
    </root>
  </springProfile>
</configuration>

异步⽇志配置:

  • 异步⽇志 queueSize 默认值 256,异步⽇志队列的容量。
  • discardingThreshold:当异步⽇志队列的剩余容量⼩于这个阈值,会丢弃 TRACE, DEBUG or INFO 级别的⽇志。如果不希望丢弃⽇志(即全量保存),那可以设置为 0。但是当队列占满后,⾮阻塞的 异步⽇志会变成阻塞的同步⽇志。所以在⾼并发低延迟要求的系统⾥⾯针对不重要的⽇志可以设置 discardingThreshold 丢弃策略,值⼤于 0。

2.3 测试

上⾯配置完成之后,可以使⽤如下代码测试⼀下,是否满⾜了 2.1 节中提出的需求。

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

@RestController
public class LogTestController {
    private static final Logger logger = LoggerFactory.getLogger(LogTestController.class);

    @GetMapping("/testlog")
    public void test(){
        logger.trace("Trace ⽇志...");
        logger.debug("Debug ⽇志...");
        logger.info("Info ⽇志...");
        logger.warn("Warn ⽇志...");
        logger.error("Error ⽇志...");
   }
}

3.log4j2 ⽇志框架配置

⼀、引⼊ maven 依赖

Spring Boot 默认使⽤ LogBack,但是我们没有看到显示依赖的 jar 包,其实是因为所在的 jar 包 springboot-starter-logging 都是作为 spring-boot-starter-web 或者 spring-boot-starter 依赖的⼀部分。 如果这⾥要使⽤ Log4j2,需要从 spring-boot-starter-web 中去掉 spring-boot-starter-logging 依赖, 同时显示声明使⽤ Log4j2 的依赖 jar 包,具体如下:

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

⼆、添加配置⽂件 log4j2-spring.xml

在 resources ⽬录下新建⼀个 log4j2-spring.xml ⽂件,放在 src/main/resources ⽬录下即可被 Spring Boot 应⽤识别。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <properties>
    <!--⽇志输出位置-->
    <property name="LOG_HOME">/Users/apple/Desktop/logs</property>
  </properties>

  <Appenders>
    <!-- 将⽇志输出到控制台-->
    <Console name="CONSOLE" target="SYSTEM_OUT">
      <!--设置⽇志格式及颜⾊-->
      <PatternLayout pattern="[%style{%d}{bright,green}][%highlight{%p}][%style{%t}{bright,blue}][%style{%C}{bright,yellow}]:%msg%n%style{%throwable}{red}" disableAnsi="false" noConsoleNoAnsi="false"/>
    </Console>

    <!-- 将⽇志输出到⽂件-->
    <RollingFile name="FILE-APPENDER"
                 fileName="${LOG_HOME}/log4j2-demo.log"
                 filePattern="${LOG_HOME}/log4j2-demo-%d{yyyy-MM-dd}-%i.log">
      <!--设置⽇志格式-->
      <PatternLayout>
        <pattern>[%d][%p][%t][%C] %m%n</pattern>
      </PatternLayout>
      <Policies>
        <!-- 设置⽇志⽂件切分参数 -->
        <SizeBasedTriggeringPolicy size="100 MB"/>
        <TimeBasedTriggeringPolicy/>
      </Policies>
      <!--设置最⼤存档数-->
      <DefaultRolloverStrategy max="20"/>
    </RollingFile>
  </Appenders>

  <Loggers>
    <!-- 根⽇志设置 -->
    <Root level="debug">
      <AppenderRef ref="CONSOLE" level="debug"/>
      <AppenderRef ref="FILE-APPENDER" level="info"/>
    </Root>

    <!--spring⽇志-->
    <Logger name="org.springframework" level="info"/>
    <!-- mybatis⽇志 -->
    <Logger name="com.mybatis" level="warn"/>
  </Loggers>
</configuration>
  • 两个 Appender,⼀个叫做 CONSOLE ⽤于输出⽇志到控制台,⼀个叫做 FILE-APPENDER 输出⽇志到⽂件

  • PatternLayout ⽤于指定输出⽇志的格式,[%d] [%p] [%t] [%C] %m %n 这些占位符将结合下⽂测试 结果为⼤家介绍

  • Policies ⽤于指定⽂件切分参数

    • TimeBasedTriggeringPolicy 默认的 size 是 1,结合 filePattern 定义%d{yyyy-MM-dd},则每天 ⽣成⼀个⽂件(最⼩的时间切分粒度是⼩时)
    • <SizeBasedTriggeringPolicy size="100 MB"/>当⽂件⼤⼩到 100MB 的时候,切分⼀个新的⽇ 志⽂件
  • <DefaultRolloverStrategy max="20"/>表示⽂件最⼤的存档数量,多余的将被删除

三、⾃定义配置⽂件

但是我们通常会有这样⼀个需求,就是不同的环境使⽤不同的配置,⽐如:我们需要三个 log4j2 xml ⽂件:

  • log4j2-dev.xml 开发环境⽇志配置
  • log4j2-prod.xml ⽣产环境⽇志配置
  • log4j2-test.xml 测试环境⽇志配置

但是 Spring Boot 并不知道 log4j2-.xml 这些配置⽂件是⼲什么的,所以需要通过在 application.yml ⽂件中显示声明才⾏。

举例:在 application-dev.yml ⾥⾯使⽤ log4j2-dev.xml 配置⽂件

logging:
  config: classpath:log4j2-dev.xml

以此类推,在 application-prod.yml ⾥⾯使⽤ log4j2-prod.xml 配置⽂件,在 application-test.yml ⾥⾯使 ⽤ log4j2-test.xml 配置⽂件。

说⼀下占位符

<PatternLayout pattern="[%style{%d}{bright,green}][%highlight{%p}][%style{%t}{bright,blue}][%style{%C}{bright,yellow}]:%msg%n%style{%throwable}{red}" disableAnsi="false" noConsoleNoAnsi="false"/>
  • %d : date 时间
  • %p : ⽇志级别
  • %t : thread 线程名称
  • %C: class 类⽂件名称
  • %msg:⽇志信息
  • %n 换⾏
  • %style{%throwable}{red} 加样式,异常信息红⾊显示

4.拦截器实现统⼀访问⽇志

⼀、需求

我们本节要实现的需求

  • 针对当前系统的每⼀次接⼝访问,要记录是什么⼈访问的(⽤户名)、什么时间访问的、访问耗时多 ⻓时间、使⽤什么 HTTP method ⽅法访问的、访问结果如何等。可以称为审计⽇志。
  • 将访问记录审计⽇志,输出到⼀个单独的⽇志⽂件 access.log

⼆、定义访问⽇志内容记录实体类

package top.syhan.boot.domain;
import top.syhan.boot.util.FormatUtils;
import lombok.Data;
import java.util.Date;
/**
* @description: 访问⽇志内容记录实体类
* @author: syhan
* @date: 2022-04-04
**/
@Data
public class AccessLog {
    /**
     * 访问者⽤户名
     */
    private String username;

    /**
     * 请求路径
     */
    private String uri;

    /**
     * 请求消耗时⻓
     */
    private Integer duration;

    /**
     * http ⽅法:GET、POST等
     */
    private String httpMethod;

    /**
     * http 请求响应状态码
     */
    private Integer httpStatus;

    /**
     * 访问者ip
     */
    private String ip;

    /**
     * 此条记录的创建时间
     */
    private Date createTime;

    @Override
    public String toString() {
      return "{" + "username=" + this.username + "," + "uri=" + this.uri + "," + "duration=" + this.duration + "," + "httpMethod=" + this.httpMethod + "," + "httpStatus=" + this.httpStatus + "," + "ip=" + this.ip + "," + "createTime=" + FormatUtils.forTime(this.createTime) + "}";
   }
}

三、⾃定义⽇志拦截器

通过⾃定义拦截器的⽅式,记录审计⽇志。

  • 拦截器的 preHandle ⽅法,可以⽤于拦截请求处理开始。⽤于记录请求开始时间等信息保存到 Http Request,⽤于后续计算请求时⻓。
  • 拦截器的 postHandle ⽅法,可以⽤于拦截请求处理完成。可以从 Request 对象获取开始时间,计算本次请求总的处理时⻓等信息。
package top.syhan.boot.interceptor;
import top.syhan.boot.domain.AccessLog;
import top.syhan.boot.util.AddressIpUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
/**
* @description: ⾃定义⽇志拦截器
* @author: syhan
* @date: 2022-04-04
**/
@Component
@Slf4j
public class AccessLogInterceptor implements HandlerInterceptor {
    /**
     * 进⼊SpringMVC的Controller之前开始记录⽇志实体
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
        //创建⽇志实体
        AccessLog accessLog = new AccessLog();
        // 设置IP地址
        accessLog.setIp(request.getRemoteAddr());
        //accessLog.setIp(AddressIpUtils.getIpAddress(request));
        //设置请求⽅法,GET,POST...
        accessLog.setHttpMethod(request.getMethod());
        //设置请求路径(端点)
        accessLog.setUri(request.getRequestURI());
        //设置请求开始时间
        request.setAttribute("sendTime", System.currentTimeMillis());
        //设置请求实体到request内,⽅便afterCompletion⽅法调⽤
        request.setAttribute("accessLog", accessLog);
        return true;
   }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
      //获取本次请求⽇志实体
      AccessLog accessLog = (AccessLog) request.getAttribute("accessLog");
      //获取请求错误码,根据需求存⼊数据库,这⾥不保存
      int status = response.getStatus();
      accessLog.setHttpStatus(status);
      // 设置访问者
      accessLog.setUsername(request.getParameter("username"));
      //当前时间
      long currentTime = System.currentTimeMillis();
      //请求开始时间
      long sendTime = Long.parseLong(request.getAttribute("sendTime").toString());
      //设置请求时间差
      accessLog.setDuration(Integer.valueOf((currentTime - sendTime) + ""));
      accessLog.setCreateTime(new Date());
      //打印⽇志
      log.info(String.valueOf(accessLog));
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) throws Exception {
      log.info("afterCompletion... ");
    }
}

LoggerFactory.getLogger(“ACCESS-LOG”)获取⼀个⽇志配置中的 Logger 的名字,⽤于打印⽇志输 出,持久化到⽇志⽂件⾥。

四、拦截器注册

@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
    //设置排除路径,spring boot 2.*,注意排除掉静态资源的路径,不然静态资源⽆法访问
    private final String[] excludePath = {"/static"};

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AccessLogInterceptor()).addPathPatterns("/**").excludePathPatterns(exclud
ePath);
   }
}

五、 “ACCESS-LOG”的⽇志 Logger 定义

配置参考以 Log4J2 配置为例

  • LoggerFactory.getLogger(“ACCESS-LOG”) 代码去配置⽂件⾥⾯找⼀个 name 为 ACCESS-LOG 的 Logger 配置。
  • 该 Logger 是⼀个 AsyncLogger,指向的输出⽬标是 ACCESS-APPENDER。
  • ACCESS-APPENDER 是⼀个⽇志⽂件输出配置,⽇志⽂件是 access-log.log。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <properties>
        <!--⽇志输出位置-->
        <property name="LOG_HOME">/Users/apple/Desktop</property>
    </properties>
    <Appenders>
        <!-- 将⽇志输出到⽂件-->
        <RollingFile name="ACCESS-APPENDER" fileName="${LOG_HOME}/access.log" filePattern="${LOG_HOME}/access-%d{yyyy-MM-dd}-%i.log">
            <!--设置⽇志格式-->
            <PatternLayout>
                <pattern>[%d][%p][%t][%C] %m%n</pattern>
            </PatternLayout>
            <Policies>
                <!-- 设置⽇志⽂件切分参数 -->
                <SizeBasedTriggeringPolicy size="100MB"/>
                <TimeBasedTriggeringPolicy/>
            </Policies>
            <!--设置最⼤存档数-->
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>
    </Appenders>
    <Loggers>
        <AsyncLogger name="ACCESS-LOG" level="debug" additivity="false">
            <AppenderRef ref="ACCESS-APPENDER" level="info"/>
        </AsyncLogger>
    </Loggers>
</configuration>

文章作者: Syhan
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Syhan !
评论
  目录