Spring Boot 2.x(十一):AOP实战--打印接口日志
接口日志有啥用
在我们日常的开发过程中,我们可以通过接口日志去查看这个接口的一些详细信息。比如客户端的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包和所有子包里的任意类的任意方法的执行:
execution(* 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毫秒
可以看出,我们已经获取到我们想要的信息~
在后面的应用实战中,我们会将这些信息保存到数据库中,并且使用一些数据分析工具进行分析。
公众号
您的推荐是对我最大的帮助!
Spring Boot 2.x(十一):AOP实战--打印接口日志的更多相关文章
- Spring boot 配置tomcat后 控制台不打印SQL日志
在pom.xml中配置tomcat启动处加上: <dependency> <groupId>org.springframework.boot</groupId> & ...
- Spring Boot 自定义注解,AOP 切面统一打印出入参请求日志
其实,小哈在之前就出过一篇关于如何使用 AOP 切面统一打印请求日志的文章,那为什么还要再出一篇呢?没东西写了? 哈哈,当然不是!原因是当时的实现方案还是存在缺陷的,原因如下: 不够灵活,由于是以所有 ...
- spring boot 和shiro的代码实战demo
spring boot和shiro的代码实战 首先说明一下,这里不是基础教程,需要有一定的shiro知识,随便百度一下,都能找到很多的博客叫你基础,所以这里我只给出代码. 官方文档:http://sh ...
- Spring Boot 2.X(十一):全局异常处理
前言 在 Java Web 系统开发中,不管是 Controller 层.Service 层还是 Dao 层,都有可能抛出异常.如果在每个方法中加上各种 try catch 的异常处理代码,那样会使代 ...
- Spring Boot干货系列:(七)默认日志框架配置
Spring Boot干货系列:(七)默认日志框架配置 原创 2017-04-05 嘟嘟MD 嘟爷java超神学堂 前言 今天来介绍下Spring Boot如何配置日志logback,我刚学习的时候, ...
- (转)Spring Boot干货系列:(七)默认日志logback配置解析
转:http://tengj.top/2017/04/05/springboot7/ 前言 今天来介绍下Spring Boot如何配置日志logback,我刚学习的时候,是带着下面几个问题来查资料的, ...
- Spring Boot从入门到精通(八)日志管理实现和配置信息分析
Spring Boot对日志的处理,与平时我们处理日志的方式完全一致,它为Java Util Logging.Log4J2和Logback提供了默认配置.对于每种日志都预先配置使用控制台输出和可选的文 ...
- Spring Boot入门(四):开发Web Api接口常用注解总结
本系列博客记录自己学习Spring Boot的历程,如帮助到你,不胜荣幸,如有错误,欢迎指正! 在程序员的日常工作中,Web开发应该是占比很重的一部分,至少我工作以来,开发的系统基本都是Web端访问的 ...
- Java | Spring Boot Swagger2 集成REST ful API 生成接口文档
Spring Boot Swagger2 集成REST ful API 生成接口文档 原文 简介 由于Spring Boot 的特性,用来开发 REST ful 变得非常容易,并且结合 Swagg ...
随机推荐
- BZOJ_1085_[SCOI2005]骑士精神_IDDFS
BZOJ_1085_[SCOI2005]骑士精神_DFS Description 在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位.在任何时候一个骑士都能按照骑 士的走法(它可 ...
- JVM内存异常与常用内存参数设置总结
Java Web程序由于引入大量第三方java类库,在启动时经常会遇到内存溢出(Memory Overflow)或者内存泄漏(Memory leak)问题,导致程序启动失败. 一.OOM异常分类: O ...
- RocketMQ源码 — 十一、 RocketMQ事务消息
分布式事务是一个复杂的问题,rmq实现了事务的最终一致性,rmq保证本地事务成功消息一定会发送成功并被成功消费,如果本地事务失败了,消息不会被发送. rmq事务消息的实现过程为: producer发送 ...
- Windows上安装配置SSH教程(4)——WinSCP+OpenSSH 使用公钥自动登陆
-------------------- 知识点汇总:http://www.cnblogs.com/feipeng8848/p/8559803.html -------------------- 重要 ...
- 自学Python,新手上路,好资源免费分享
Python 可以用来做什么? 在我看来,基本上可以不负责任地认为,Python 可以做任何事情.无论是从入门级选手到专业级选手都在做的爬虫,还是Web 程序开发.桌面程序开发还是科学计算.图像处理, ...
- 基于滴答清单 Web 开发的 PC 客户端
基于滴答清单 Web 开发的 PC 客户端 关于「滴答清单」 滴答清单是一款不可多得的 GTD 效率工具,它有着清晰明了的界面设计.恰到好处的功能设置.稳定的同步服务,如果你还缺少一款简洁而有效的 G ...
- openJDK知识整理及概念
上周同事去听了阿里openJDK的讲座,收集整理了一下.随着Oracle 撒手,Java 8 官方支持时间持续到 2020 年 12 月:对商业用户(Commercial Users),2019 年 ...
- C#语言介绍
C#(读作“See Sharp”)是一种简单易用的新式编程语言,不仅面向对象,还类型安全. C# 源于 C 语言系列,C.C++.Java 和 JavaScript 程序员很快就可以上手使用. C# ...
- EFCore中 join on的不同
当 多条件 left join on 时 LEFT OUTER JOIN on new { u.UserId, ue.ExamId } equals new { sac.UserId, sac.Exa ...
- Mybatis-Plus入门示例
1.内容: Mybatis-Plus只是在Mybatis的基础上,实现了功能增强,让开发更加简洁高效. Mybatis-Plus并没有修改Mybatis的任何特性. 2.入门示例: 2.1 需求:使用 ...