写点什么

通过 AOP 和自定义注解实现请求日志收集功能

  • 2021 年 11 月 12 日
  • 本文字数:2366 字

    阅读完需:约 8 分钟

概述

今天给大家介绍一下:如何通过 AOP 和自定义注解实现全局请求日志收集功能。


一般线上程序都会遇到一个问题,如何排查线上 bug,因为有时候这种 bug 是突发性,测试很难复现的出来。所以记录出错那一刻的请求信息,就异常的关键了。


那请求信息,我们该如何记录下来呢,总不能通过 log 日志一个个记录下来吧,这样工作量大,而且很难扩展。不用着急,今天就教给大家一招,轻轻松松实现日志收集功能。


接下来我们就来看看,我是如何通过:AOP 和自定义注解来实现请求日志统一收集功能。

核心流程

流程图如下所示:



我们先在接口上面添加自定义注解,这样每次请求就都会走 AOP 的处理中心。在处理中心中,我们可以将请求信息存储到数据库中,方便后期排查问题。

自定义注解

我们先来看看自定义注解是如何实现的,代码如下所示:


/**


  • 自定义注解

  • @author linzhiqiang


【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


  • @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;


}

评论

发布
暂无评论
通过AOP和自定义注解实现请求日志收集功能