通过 AOP 和自定义注解实现请求日志收集功能
概述
今天给大家介绍一下:如何通过 AOP 和自定义注解实现全局请求日志收集功能。
一般线上程序都会遇到一个问题,如何排查线上 bug,因为有时候这种 bug 是突发性,测试很难复现的出来。所以记录出错那一刻的请求信息,就异常的关键了。
那请求信息,我们该如何记录下来呢,总不能通过 log 日志一个个记录下来吧,这样工作量大,而且很难扩展。不用着急,今天就教给大家一招,轻轻松松实现日志收集功能。
接下来我们就来看看,我是如何通过:AOP 和自定义注解来实现请求日志统一收集功能。
核心流程
流程图如下所示:
我们先在接口上面添加自定义注解,这样每次请求就都会走 AOP 的处理中心。在处理中心中,我们可以将请求信息存储到数据库中,方便后期排查问题。
自定义注解
我们先来看看自定义注解是如何实现的,代码如下所示:
/**
自定义注解
@author linzhiqiang
@date 2019/4/26
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestLog {
/**
请求模块名称
@return
*/
public String module() default "";
/**
接口详情描述
@return
*/
public String operationDesc() default "";
}
这边我们定义了两个字段,一个是接口的模块名称,另外一个就是接口具体的功能描述。
接口注释
这两个参数需要在接口处传入,现在我们就来看看接口是如何添加注解和传值的,代码如下所示:
/**
request 测试专用
@return
*/
@RequestLog(module = "requestTest", operationDesc = "request 测试专用")
@RequestMapping(value = "requestTest", method = RequestMethod.POST)
public String requestTest(@RequestBody ArticleSubjectDto articleSubjectDto) {
String result = null;
try {
System.out.println("我的是方法");
result = "请求成功";
}catch (Exception e){
logger.error("requestTest 查询失败", e);
return JsonUtils.toJson(ResponseUtils.failInServer(result));
}
return JsonUtils.toJson(ResponseUtils.success(result));
}
我们可以看到,在接口上面添加我们自定义的注解,然后写上对应的参数值就可以了。
AOP 处理中心
最后我们来看看最核心的 AOP 处理中心是如何实现的,代码如下所示:
/**
切面 AOP
@author linzhiqiang
*/
@Aspect
@Component
public class SystemLogAspect {
private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);
private static final String UNKNOWN = "unknown";
@Autowired
private LogRepository logRepository;
/**
1,表示在哪个类的哪个方法进行切入。配置有切入点表达式。
2,对有 @SystemLog 标记的方法,记录其执行参数及返回结果。
*/
@Pointcut("execution(* com.minimal..controller...(..))&&@annotation(com.minimal.common.sdk.log.RequestLog)")
public void controllerAspect() {
}
/**
配置 controller 环绕通知,使用在方法 aspect()上注册的切入点
*/
@Around("controllerAspect()")
public Object aroundMethod(ProceedingJoinPoint point) throws Throwable {
if (logger.isDebugEnabled()) {
logger.info(">>>>>>>>>>>>>>>进入日志切面<<<<<<<<<<<<<<<<");
}
// 获取接口的路径地址
String methodTarget = point.getTarget().getClass().getName() + "." + point.getSignature().getName() + "()";
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
OperateLogPO operLogPO = new OperateLogPO();
operLogPO.setId(UUID.randomUUID().toString());
operLogPO.setMethod(methodTarget);
operLogPO.setCreateTime(new Date());
operLogPO.setIp(getClientIpAddr(request));
operLogPO.setBrownerNo(getBrownerNo(request));
operLogPO.setOsNo(getOsNo(request));
// 获取接口的请求参数
operLogPO.setParams(JsonUtils.toJson(point.getArgs()));
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
RequestLog log = method.getAnnotation(RequestLog.class);
String desc = log.operationDesc();
String module = log.module();
operLogPO.setModule(module);
operLogPO.setOperationDesc("模块:" + module + ",操作行为:" + desc);
logger.info("前置通知>>>>>>>>>>>>>>>操作模块:" + module + ",操作方法:" + methodTarget + ",操作行为:" + desc + "<<<<<<<<<<<<<<<<");
Object result = null;
try {
result = point.proceed();
// 设置请求结果
operLogPO.setResult(JsonUtils.toJson(result));
// 返回通知(操作成功:1,操作失败:2)
operLogPO.setStatus("1");
} catch (Throwable e) {
operLogPO.setStatus("2");
// 异常通知
throw new RuntimeException(e);
} finally {
// 后置通知
logger.info("后置通知>>>>>>>>>>>>>>>操作模块:" + module + ",操作方法:" + methodTarget + ",操作行为:" + desc + ",操作结果:" + operLogPO.getStatus() + "!(操作成功:1,操作失败:2)<<<<<<<<<<<<<<<<");
logRepository.insert(operLogPO);
}
return result;
}
/**
功能:获取 IP 地址
@param request
@return
*/
public static String getClientIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
评论