在咱们平常的开发过程当中,咱们能够经过接口日志去查看这个接口的一些详细信息。好比客户端的IP,客户端的类型,响应的时间,请求的类型,请求的接口方法等等,咱们能够对这些数据进行统计分析,提取出咱们想要的信息。java
这里,咱们使用的是Spring的两大杀器之AOP,经过在Controller层定义切点,而后对请求对象进行分析获取接口信息,同时开启一个ThreadLocal来记录响应时间。spring
@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>
复制代码
在以前的统一返回值和异常处理中咱们已经定义过这个类,这里是对其进行完善。这里我再把代码再写一下:api
@Aspect
@Order(5)
@Component
@Slf4j
public class ResponseAop 复制代码
直接在这里定义基本类型会有同步问题,因此咱们定义一个ThreadLocal对象来记录消耗的时间。浏览器
ThreadLocal<Long> startTime = new ThreadLocal<>();
复制代码
这里须要注意的是切点的写法,必定要正确才能保证AOP生效!这里附上一些简单的写法,后续会单独开一章讲解execution表达式的书写。spring-boot
execution(public * *(..))
execution(* set*(..))
execution(* com.xyz.service.Service.*(..))
execution(* com.xyz.service.*.*(..))
xecution(* com.xyz.service..*.*(..))
/** * 切点 */
@Pointcut("execution(public * indi.viyoung.viboot.*.controller..*(..))")
public void httpResponse() {
}
复制代码
@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(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毫秒
复制代码
能够看出,咱们已经获取到咱们想要的信息~测试
在后面的应用实战中,咱们会将这些信息保存到数据库中,而且使用一些数据分析工具进行分析。spa
您的推荐是对我最大的帮助!操作系统