将锁有得外部访问都记录在日志文件里面,设计这个功能是为了(为什么):
1. 在不引入Promentheus进行接口监控时,基于日志文件就可以实现整个项目得监控。
2. 当出现问题时,可以基于此进行流量重放。
效果如下(预期):
来看一下请求日志记录得实现。
技术方案(选型)
如果单纯的将这个记录接口的请求信息,当作一个普通的需求来设计,我们可以怎么来实现呢?
基于过滤器Filter,来拦截web请求,记录请求相关信息。
基于AOP来实现方法拦截,借助@Around来实现请求方法执行前后增强,记录请求相关的信息
方案的选择?
Filter过滤器方案
关于过滤器的知识点,可以参考之前文章。若使用过滤器,则主要就是拦截web请求,具体的实现流程如下:
在过滤器的doFilter方法中,划分为三块:
- doBefore:表示将请求转发到Controller执行之前
- 记录开始执行时间
- 记录请求相关信息
- doFilter: 即将请求转发到Controller去执行
- doAfter: Controller方法执行完
- 记录结束时间,计算执行耗时
- 日志输出
使用这种方式的优缺点比较突出,优点是适用性强,实现简单,缺点是只能记录Controller的请求相关信息,如果我们想统计某个Service方法、Mapper方方法,那么这种方法不太合适
AOP切面方案
若使用AOP来实现,则关键点在于我需要拦截那些方法,即定义切点
基本策略与前面差不多,不过有几个关键点
定义切点:可以是直接拦截包路径方式,也可以是配合自定义注解,拦截某些特定注解的方式
- 使用Around环绕方式
- 使用AOP来实现的优缺点也比较明显
- 优点:
- 灵活性高,可以拦截任何共有方法
- 缺点:
- 需要自定义切点,通常不太容易一次编写,所有项目适用。
实现实例(Filter方案)
实现类
包路径:com/github/paicoding/forum/web/hook/filter/ReqRecordFilter.java
package com.github.paicoding.forum.web.hook.filter;
import com.github.paicoding.forum.api.model.context.ReqInfoContext;
import com.github.paicoding.forum.core.util.CrossUtil;
import com.github.paicoding.forum.core.util.IpUtil;
import com.github.paicoding.forum.service.statistics.service.StatisticsSettingService;
import com.github.paicoding.forum.web.global.GlobalInitService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
/**
* 1. 请求参数日志输出过滤器
* 2. 判断用户是否登录
*
* @date 2022/7/6
*/
@Slf4j
@WebFilter(urlPatterns = "/*", filterName = "reqRecordFilter", asyncSupported = true)
public class ReqRecordFilter implements Filter {
private static Logger REQ_LOG = LoggerFactory.getLogger("req");
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
long start = System.currentTimeMillis();
HttpServletRequest request = null;
try {
//构建请求上下文
request = this.initReqInfo((HttpServletRequest) servletRequest);
CrossUtil.buildCors(re