接口日志有啥用
在我们日常的开发过程中, 我们可以通过接口日志去查看这个接口的一些详细信息. 比如客户端的 IP, 客户端的类型, 响应的时间, 请求的类型, 请求的接口方法等等, 我们可以对这些数据进行统计分析, 提取出我们想要的信息.
怎么拿到接口日志
这里, 我们使用的是 Spring 的两大杀器之 AOP, 通过在 Controller 层定义切点, 然后对请求对象进行分析获取接口信息, 同时开启一个 ThreadLocal 来记录响应时间.
关于 AOP 的注解
@Aspect: 将一个类定义为切面类.
@Pointcut: 定义一个切入点.
@Before: 在切入点开始处切入内容.
@After: 在切入点结尾处切入内容.
@AfterReturning: 在切入点返回内容之后切入内容 (可以用来对处理返回值做一些加工处理.
@Around: 在切入点前后切入内容, 并自己控制何时执行切入点自身的内容
@AfterThrowing: 用来处理当切入内容部分抛出异常之后的处理逻辑.
@Order: 在切入点前的操作, 按 order 的值由小到大执行; 在切入点后的操作, 按 order 的值由大到小执行.
实战应用
一: 引入依赖
首先, 我们需要新增引入 aop 的依赖, 以及用于分析客户端信息的 UserAgentUtils 包, 还有用于 @Slf4j 打印日志的 Lombok 的包:
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-aop</artifactId>
- </dependency>
- <dependency>
- <groupId>eu.bitwalker</groupId>
- <artifactId>UserAgentUtils</artifactId>
- <version>1.20</version>
- </dependency>
二: 定义一个 ResponseAop 切面类
在之前的统一返回值和异常处理中我们已经定义过这个类, 这里是对其进行完善. 这里我再把代码再写一下:
- @Aspect
- @Order(5)
- @Component
- @Slf4j
- public class ResponseAop
三: 定义一个 ThreadLocal 变量
直接在这里定义基本类型会有同步问题, 所以我们定义一个 ThreadLocal 对象来记录消耗的时间.
ThreadLocal<Long> startTime = new ThreadLocal<>();
四: 定义切点
这里需要注意的是切点的写法, 一定要正确才能保证 AOP 生效! 这里附上一些简单的写法, 后续会单独开一章讲解 execution 表达式的书写.
任意公共方法:
execution(public * *(..))
任何一个以 "set" 开始的方法的执行:
execution(* set*(..))
Service 接口的任意方法的执行:
execution(* com.xyz.service.Service.*(..))
定义在 service 包里的任意方法的执行:
execution(* com.xyz.service.*.*(..))
定义在 service 包和所有子包里的任意类的任意方法的执行:
- e
- xecution(* com.xyz.service..*.*(..))
- /**
- * 切点
- */
- @Pointcut("execution(public * indi.viyoung.viboot.*.controller..*(..))")
- public void httpResponse() {
- }
五: 在 @Before 中获取请求信息
- @Before("httpResponse()")
- public void doBefore(JoinPoint joinPoint){
- // 开始计时
- startTime.set(System.currentTimeMillis());
- ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
- HttpServletRequest request = attributes.getRequest();
- // 打印请求的内容
- UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));// 获取请求头中的 User-Agent
- log.info("接口路径:{}" , request.getRequestURL().toString());
- log.info("浏览器:{}", userAgent.getBrowser().toString());
- log.info("浏览器版本:{}",userAgent.getBrowserVersion());
- log.info("操作系统: {}", userAgent.getOperatingSystem().toString());
- log.info("IP : {}" , request.getRemoteAddr());
- log.info("请求类型:{}", request.getMethod());
- log.info("类方法 :" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
- log.info("请求参数 : {}" + Arrays.toString(joinPoint.getArgs()));
- }
六: 在 @AfterReturning 中获取方法的返回值和执行时间
- @AfterReturning(returning = "ret" , pointcut = "httpResponse()")
- public void doAfterReturning(Object ret){
- // 处理完请求后, 返回内容
- log.info("方法返回值:{}" , ret);
- log.info("方法执行时间:{} 毫秒", (System.currentTimeMillis() - startTime.get()));
- }
七: 测试结果
下面, 我们对一个接口进行访问:
2019-02-21 21:03:31.358 INFO 11788 --- [nio-8090-exec-5] indi.viyoung.viboot.aop.ResponseAop : 接口路径: http://localhost:8090/users
2019-02-21 21:03:31.359 INFO 11788 --- [nio-8090-exec-5] indi.viyoung.viboot.aop.ResponseAop : 浏览器: Chrome
2019-02-21 21:03:31.359 INFO 11788 --- [nio-8090-exec-5] indi.viyoung.viboot.aop.ResponseAop : 浏览器版本: 72.0.3626.109
2019-02-21 21:03:31.360 INFO 11788 --- [nio-8090-exec-5] indi.viyoung.viboot.aop.ResponseAop : 操作系统: MAC_OS_X
2019-02-21 21:03:31.360 INFO 11788 --- [nio-8090-exec-5] indi.viyoung.viboot.aop.ResponseAop : IP : 0:0:0:0:0:0:0:1
2019-02-21 21:03:31.360 INFO 11788 --- [nio-8090-exec-5] indi.viyoung.viboot.aop.ResponseAop : 请求类型: GET
2019-02-21 21:03:31.360 INFO 11788 --- [nio-8090-exec-5] indi.viyoung.viboot.aop.ResponseAop : 类方法 : indi.viyoung.viboot.apilog.controller.UserController.findAll
2019-02-21 21:03:31.360 INFO 11788 --- [nio-8090-exec-5] indi.viyoung.viboot.aop.ResponseAop : 请求参数 : {} []
...
2019-02-21 21:03:31.393 INFO 11788 --- [nio-8090-exec-5] indi.viyoung.viboot.aop.ResponseAop : 方法返回值: ReturnVO{code='2000', message='操作成功', data=[User(id=10000001, password=123456, userName=vi-young), User(id=10000002, password=123456, userName=vi-young), User(id=10000003, password=123123, userName=lxt), User(id=10000004, password=123456, userName=yangwei)]}
2019-02-21 21:03:31.393 INFO 11788 --- [nio-8090-exec-5] indi.viyoung.viboot.aop.ResponseAop : 方法执行时间: 36 毫秒
可以看出, 我们已经获取到我们想要的信息~
在后面的应用实战中, 我们会将这些信息保存到数据库中, 并且使用一些数据分析工具进行分析.
活动预告
来源: https://www.cnblogs.com/viyoung/p/10416230.html