利用 AOP 记录接口日志

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 利用 AOP 记录接口日志

常见的小需求

       在我们的后端项目中有很多要调用第三方接口的地方,而调用接口就免不了会因为传递给接口的参数有问题报错,或者对接口的返回值处理不全导致报错或后续的流程有问题。

       对于调试接口通常的做法就是把入参用接口工具向接口地址提交,然后把获取到的返回值和项目中的返回值进行比对。这样来看,在代码中调用接口的入参和调用接口后的返回值对于排错来说就非常重要了。那这样的话,我们可以在每个调用接口地址的前后使用输出日志的方式来记录,就可以得到调用接口的入参和接口的返回值,从而有利于我们以后的调试了。

       我们可以使用 SLF4J 或者 LogBack 等日志框架,在调用接口时来输出一下入参和返回值,大致方法是在调用接口前调用 logger.info 输出入参,然后调用接口后再次调用 logger.info 输出返回值。这样的方式虽然没有问题,但是在每个接口调用前后都要加这样的日志输出代码显得过于麻烦,且不优雅。


简单的解决方法

       在 Spring 框架中为我们提供了 AOP,即面向切面编程。AOP 通过动态代理来管理切面环境,通过反射可以使我们在非侵入的方式下为我们增加前置、后置等方法用来贯穿整个代码层面,从而让我们更加关注业务本身的开发。在 Spring 中的事务就是通过 AOP 来进行管理的,我们这里通过 AOP 完成一个接口调用时打印入参和返回值的功能。

       AOP 有一些名词需要理解,但是不理解这些名词好像又不影响我们实际 AOP 的使用。这些名词包括,切面、通知、引入、切点、连接点和织入。这里我们不讨论这些名词,直接上代码来进行演示。


代码演示

       首先引入依赖,依赖如下:

<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.7</version></dependency>

       然后,我们来定义一个切面,所谓的切面不过是一个被 @Aspect 注解修饰的类,在类中可以定义一些通知,通知通常包含(前置通知、后置通知、返回通知、异常通知等) ,这里我们定义了前置通知和返回通知。通知是针对切点进行的,切点是用于针对的具体的方法,我们切面类如下:

@Aspect@Component@Slf4jpublicclassTestAspect {
@Pointcut("execution(public * io.coderup2u.xxx.util.yyy.*.*(..))")
publicvoidcontrollerMethod() {}
@Before("controllerMethod()")
publicvoidbefore(JoinPointjoinPoint) {
    }
@AfterReturning(value="controllerMethod()", returning="methodResult")
publicvoidafterReturning(JoinPointjoinPoint, ObjectmethodResult) {
    }
}

       在上面的代码中,使用 @Before 和 @AfterReturning 定义了两个通知,分别是前置通知和返回通知使用 @Pointcut 定义了一个切点,通过 execution 的正则表达式来确定一个连接点(所谓的连接点就是我们实际的业务类),这里 execution 正则表达式的意思是,当执行 io.coderup2u.xxx.util.yyy 下的所有方法时,都会执行切面中的前置通知和返回通知。前置方法是在业务方法执行前被执行,返回通知是在业务方法执行后且没有异常时执行。

       在 before 和 afterReturning 方法中都有一个 JoinPoint 类型的参数,通过该参数可以得到被执行具体方法的名称以及参数,afterReturning 方法的 methodResult 参数可以得到方法执行后的返回值。具体代码如下:

@Pointcut("execution(public * io.coderup2u.xxx.util.yyy.*.*(..))")
publicvoidcontrollerMethod() {}
@Before("controllerMethod()")
publicvoidbefore(JoinPointjoinPoint)
{
MethodSignaturems= (MethodSignature) joinPoint.getSignature();
Methodmethod=ms.getMethod();
longtid=Thread.currentThread().getId();
log.info("===> TID:{} => 准备调用 {} 方法", tid, method.getName());
if (joinPoint.getArgs().length==0) {
return ;
    }
log.info("===> TID:{} => 它的参数如下:", tid);
for (inti=0; i<joinPoint.getArgs().length; i++) {
Objectarg=joinPoint.getArgs()[i];
log.info("===> TID:{} => 第 {} 个参数是:{}", tid, i+1, arg.toString());
    }
}
@AfterReturning(value="controllerMethod()", returning="methodResult")
publicvoidafterReturning(JoinPointjoinPoint, ObjectmethodResult)
{
MethodSignaturems= (MethodSignature) joinPoint.getSignature();
Methodmethod=ms.getMethod();
longtid=Thread.currentThread().getId();
log.info("<=== TID:{} => 方法: {} 的返回值为: {}", tid, method.getName(), methodResult.toString());
}

      最后调用一个接口,来看下 AOP 记录的日志,日志如下:

===>TID:40=>准备调用getAccessToken方法<===TID:40=>方法: getAccessToken的返回值为: {"code":0,"data":{"accessToken":"xxxxxxxxxxxx","expiresIn":7200},"message":"成功"}

       可以看到上面的输出,帮我们输出了线程的 ID,也输出了调用的方法名和方法的返回结果。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
3月前
|
Java API 数据安全/隐私保护
(工作经验)优雅实现接口权限校验控制:基于自定义注解、AOP与@ConditionalOnProperty配置开关的通用解决方案
(工作经验)优雅实现接口权限校验控制:基于自定义注解、AOP与@ConditionalOnProperty配置开关的通用解决方案
108 1
|
5月前
|
SQL 监控 Java
在IDEA 、springboot中使用切面aop实现日志信息的记录到数据库
这篇文章介绍了如何在IDEA和Spring Boot中使用AOP技术实现日志信息的记录到数据库的详细步骤和代码示例。
在IDEA 、springboot中使用切面aop实现日志信息的记录到数据库
|
13天前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
56 8
|
2月前
|
JSON Java 数据库
SpringBoot项目使用AOP及自定义注解保存操作日志
SpringBoot项目使用AOP及自定义注解保存操作日志
61 1
|
4月前
|
Java
日志框架log4j打印异常堆栈信息携带traceId,方便接口异常排查
日常项目运行日志,异常栈打印是不带traceId,导致排查问题查找异常栈很麻烦。
|
5月前
|
Java 应用服务中间件 HSF
Java应用结构规范问题之AllLoggers接口获取异常日志的Logger实例的问题如何解决
Java应用结构规范问题之AllLoggers接口获取异常日志的Logger实例的问题如何解决
|
5月前
|
存储 监控 Java
|
5月前
分享一种接口的日志格式
分享一种接口的日志格式
66 13
|
4月前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
AOP(面向切面编程)能够帮助我们在不修改现有代码的前提下,为应用程序添加新的功能或行为。Micronaut框架中的AOP模块通过动态代理机制实现了这一目标。AOP将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,提高模块化程度。在Micronaut中,带有特定注解的类会在启动时生成代理对象,在运行时拦截方法调用并执行额外逻辑。例如,可以通过创建切面类并在目标类上添加注解来记录方法调用信息,从而在不侵入原有代码的情况下增强应用功能,提高代码的可维护性和可扩展性。
96 1
|
2月前
|
安全 Java 编译器
什么是AOP面向切面编程?怎么简单理解?
本文介绍了面向切面编程(AOP)的基本概念和原理,解释了如何通过分离横切关注点(如日志、事务管理等)来增强代码的模块化和可维护性。AOP的核心概念包括切面、连接点、切入点、通知和织入。文章还提供了一个使用Spring AOP的简单示例,展示了如何定义和应用切面。
294 1
什么是AOP面向切面编程?怎么简单理解?