Skip to content

acuity-log-starter 实现了几个功能:

功能

  1. MDC 参数
  2. @WebLog 记录操作日志
  3. defaults.xml、 defaults-dev.xml、 defaults-prod.xml、 全局的 logback 日志基础模版

MDC 参数

logback 内置的日志字段还是比较少,如果我们需要打印有关业务的更多的内容,包括自定义的一些数据,需要借助 logback MDC 机制,MDC 为“Mapped Diagnostic Context”(映射诊断上下文),即将一些运行时的上下文数据通过 logback 打印出来;此时我们需要借助 org.sl4j.MDC 类。

MDC 类基本原理其实非常简单,其内部持有一个 InheritableThreadLocal 实例,用于保存 context 数据,MDC 提供了 put/get/clear 等几个核心接口,用于操作 ThreadLocal 中的数据;ThreadLocal 中的 K-V,可以在 logback.xml 中声明,最终将会打印在日志中。

可以看到本系统中打印的日志参数格式为:

java
[${spring.application.name}:${server.port}:%X{tenant}:%X{userid}] %d{yyyy-MM-dd HH:mm:ss.SSS}[%5p] ${PID} [%X{trace}] [%t:%r] [%logger{50}.%M:%L] %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}
[${spring.application.name}:${server.port}:%X{tenant}:%X{userid}] %d{yyyy-MM-dd HH:mm:ss.SSS}[%5p] ${PID} [%X{trace}] [%t:%r] [%logger{50}.%M:%L] %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}

其中 %X{tenant}%X{userid}%X{trace} 三个参数就是通过 MDC 传递给 logback 的, 他们分别表示 租户编码、用户 id、请求 ID

实现步骤:

  1. 项目启动时,会加载 spring.factories 文件中的 AcuityMdcAdapterInitializer 类, 该类对 AcuityMdcAdapter 进行初始化(执行其 static 代码库)
  2. 在 HeaderThreadLocalInterceptor 拦截器中对每个请求调用 MDC.put 方法,对上面的三个参数进行设值
java
MDC.put(ContextConstants.LOG_TRACE_ID, StrUtil.isEmpty(traceId) ? StrUtil.EMPTY : traceId);
MDC.put(ContextConstants.JWT_KEY_TENANT, getHeader(request, ContextConstants.JWT_KEY_TENANT));
MDC.put(ContextConstants.JWT_KEY_USER_ID, getHeader(request, ContextConstants.JWT_KEY_USER_ID));
MDC.put(ContextConstants.LOG_TRACE_ID, StrUtil.isEmpty(traceId) ? StrUtil.EMPTY : traceId);
MDC.put(ContextConstants.JWT_KEY_TENANT, getHeader(request, ContextConstants.JWT_KEY_TENANT));
MDC.put(ContextConstants.JWT_KEY_USER_ID, getHeader(request, ContextConstants.JWT_KEY_USER_ID));

@WebLog 记录操作日志

  1. 配置文件介绍
yaml
acuity:
  log:
    enabled: false # false=禁用操作日志 true=开启
    type: DB # 日志存储位置   DB=数据库  LOGGER=日志文件
acuity:
  log:
    enabled: false # false=禁用操作日志 true=开启
    type: DB # 日志存储位置   DB=数据库  LOGGER=日志文件
  1. 注解属性介绍详看@WebLog 中的注释! 其中 value 属性是支持 SpEL 表达式的. (不懂 SpEL 的百度一下)
  2. 注解使用举例
java
// 禁用操作日志
@WebLog(enabled = false)
//  response = false不记录方法的响应参数
@WebLog(value = "'分页列表查询:第' + #params?.current + '页, 显示' + #params?.size + '行'", response = false)
// requestByError = false 表示, 方法报错时, 也不记录请求参数
@WebLog(value = "'保存订单:订单编码' + #data?.code + ', 订单Id:' + #data?.name", request = false, requestByError = false)
// 禁用操作日志
@WebLog(enabled = false)
//  response = false不记录方法的响应参数
@WebLog(value = "'分页列表查询:第' + #params?.current + '页, 显示' + #params?.size + '行'", response = false)
// requestByError = false 表示, 方法报错时, 也不记录请求参数
@WebLog(value = "'保存订单:订单编码' + #data?.code + ', 订单Id:' + #data?.name", request = false, requestByError = false)
  1. 原理: 通过 AOP 拦截标记了@WebLog 的方法, 拦截后, SysLogAspect 中的方法用于获取被拦截方法的入参和返回值. 然后发布一个 SysLogEvent 事件 (为了让操作日志尽可能少的影响方法调用时长,采用事件方式来异步处理, 大家也可以改成消息队列之类的), SysLogListener 监听器接收到事件后, 调用consumer.accept 方法, 让对操作日志进行存储.

defaults.xml 全局的 logback 日志基础模版

这个 logback 的配置文件将整个项目中最常用的日志配置抽取到这里.

  1. 通过 springProperty 标签设置的参数, 可以在项目的 application.yml 配置文件中, 设置 source 属性对其 defaultValue 进行覆盖
yaml
log.path 属性来源于 yml配置中的 logging.file.path 属性, 若yml中没有配置 logging.file.path, 则取默认值/data/projects/logs ,在logback配置文件的任意地方, 都能使用 ${log.path}读取这个参数.
<springProperty scope="context" name="log.path" source="logging.file.path" defaultValue="/data/projects/logs"/>

yml:
logging:
  file:
    path: /root/logs  # 在yml中设置这个参数后, 日志的生成路径就会到 /root/logs
log.path 属性来源于 yml配置中的 logging.file.path 属性, 若yml中没有配置 logging.file.path, 则取默认值/data/projects/logs ,在logback配置文件的任意地方, 都能使用 ${log.path}读取这个参数.
<springProperty scope="context" name="log.path" source="logging.file.path" defaultValue="/data/projects/logs"/>

yml:
logging:
  file:
    path: /root/logs  # 在yml中设置这个参数后, 日志的生成路径就会到 /root/logs
  1. 文件中有类似 ASYNC_CONTROLLER_APPENDER CONTROLLER_APPENDER 之类的 Appender, 区别在于
  • ASYNC_ 开头的 Appender 采用了异步输出日志, 而没有 ASYNC_ 开头的 Appender 采用了实时输出日志.
  • 异步输出 性能明显好于 实时输出,
  • 异步输出有一些参数无法记录, 所以在生产环境打印的日志文件中, 有很多 ?.
txt
# 2个问号 表示那个方法, 那行 , 但由于使用了异步输出, 所以输出为 ?
[acuity-authority-server:8760:0000:2] 2020-12-15 17:53:12.191[ INFO] 22531 [50b3bd53c2f344e790834b587a7f1891] [task-13961:934466928] [top.acuity.commons.database.mybatis.WriteInterceptor.?:?] mapper id=top.acuity.box.authority.dao.common.OptLogExtMapper.insert, userId=0
# 2个问号 表示那个方法, 那行 , 但由于使用了异步输出, 所以输出为 ?
[acuity-authority-server:8760:0000:2] 2020-12-15 17:53:12.191[ INFO] 22531 [50b3bd53c2f344e790834b587a7f1891] [task-13961:934466928] [top.acuity.commons.database.mybatis.WriteInterceptor.?:?] mapper id=top.acuity.box.authority.dao.common.OptLogExtMapper.insert, userId=0
  1. defaults.xml 中配置了如下信息
  • springProperty: spring 环境配置
  • appender: 定义了 Controller、Service、Dao、第三方 jar、全局异常类、root 等 appender
  1. defaults-dev.xml: 实时配置文件,用于配置项目在开发环境的全局日志输出规则。性能低,实时性高。

  2. defaults-prod.xml : 异步配置文件,用于配置项目在生产环境的全局日志输出规则。 性能高,实时性低。

  3. 项目运行中的业务日志,请在各自项目的 resources 目录下配置 logback-spring.xml(生产) 或 logback-spring-dev.xml (开发)

欢迎使用天源云Saas快速开发系统