第三方系统调用的时候需要记录相关请求日志,包括请求参数、请求路径、返回值等信息, 方便后续接口调用次数统计以及异常问题排查,当前主流方式有一下三种:
- Spring AOP
- Spring Interceptor
- Filter
本文主要介绍前两种实现方式。
线程缓存
为了记录完整请求时间、以及方便更新请求结果,可以使用ThreadLocal缓存当前日志ID以及请求开始时间。
Spring AOP
在如下场景使用AOP更加方便:
- HTTP异步调用
- MQ消息调用
AOP简介
@Before
:前置通知,在方法执行之前执行@After
:后置通知,在方法完成后调用通知,无论方法是否执行成功@AfterRunning
:返回通知,在方法返回结果之后执行@AfterThrowing
:异常通知,在方法抛出异常之后@Around
:环绕通知,围绕着方法执行;只有这个注解可以使用ProceedingJoinPoint
参数
关键实现
- 定义 HttpReqLogAspect
- 添加注解:@Component、@Aspect、@Order(1)
- 定义切面:@Pointcut(“execution(* y2023.m10.d08.reqlog.mvc.controller..*(..))”)
- 请求开始时记录请求参数:@Before(“webLog()”)
- 获取httpRequest对象:((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest()
- 获取url参数:request.getParameterMap()
- 获取body参数:joinPoint.getArgs() 比对 url 参数后多出来的参数就是body参数
- 缓存当前日志ID:LogParamUtil.setLogId(douyinLog.getId());
- 异常时记录异常结果:@AfterThrowing
- 请求结束时记录返回结果:@AfterReturning
具体代码参考:HttpReqLogAspect.java
Spring interceptor
使用interceptor可以借助SpringMVC相关工具类,比较方便的获取返回结果、请求Body参数, 在如下场景使用interceptor更加方便:
- HTTP同步调用
关键实现
- 定义 LogInterceptor:extends WebRequestHandlerInterceptorAdapter implements ResponseBodyAdvice, RequestBodyAdvice
- webmvc5以下版本,需要继承 HandlerInterceptorAdapter
- webmvc6以上版本,需要继承 WebRequestHandlerInterceptorAdapter
- 实现两个接口:ResponseBodyAdvice, RequestBodyAdvice,借助入口方便获取需要记录的信息
- HandlerInterceptor
- preHandle
- 获取url参数:request.getParameterMap()
- 缓存当前日志ID:LogParamUtil.setLogId(douyinLog.getId());
- afterCompletion:请求结束,属于 LogInterceptor 最后一个环节
- preHandle
- RequestBodyAdvice:这里处理请求参数
- supports:只有包含@RequestBody注解的Controller才会进入 supports 方法;只有返回ture才会进入后续body解析
- beforeBodyRead:解析body参数
- afterBodyRead:解析body完成后的方法,有需要记录请求参数body的操作,放在这里执行
- handleEmptyBody:是否特殊处理body为null的参数
- ResponseBodyAdvice:这里处理返回结果
- supports:都会进入supports方法,只要 supports为true 都会生效
- beforeBodyWrite:结果返回之前的方法入口,有需要记录返回结果的操作,放在这里执行
具体代码参考:LogInterceptor.java
持久化保存
日志文件容易丢失信息,所以考虑采用数据库存储。
表结构设计
create table request_log
(
id bigint PRIMARY KEY auto_increment DEFAULT 1,
uri varchar(256) not null,
method varchar(16) not null,
req_param text not null,
resp_param text null,
async_result text null,
was_success tinyint not null,
create_time datetime default CURRENT_TIMESTAMP not null,
update_time datetime default CURRENT_TIMESTAMP not null
)
comment '接口调用日志' collate = utf8mb4_general_ci;
代码参考:
参考资料
开始写作吧
![image-alter](/image/post/2023/10/08/01/xxx.png)
文档信息
- 本文作者:Bob.Zhu
- 本文链接:https://adolphor.github.io/2023/10/08/01-request-log-by-spring/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)