zoukankan      html  css  js  c++  java
  • 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毫秒
    

    可以看出,我们已经获取到我们想要的信息~

    在后面的应用实战中,我们会将这些信息保存到数据库中,并且使用一些数据分析工具进行分析。

    公众号

    您的推荐是对我最大的帮助!

  • 相关阅读:
    Angular Universal 学习笔记
    SAP Spartacus 如何获得当前渲染页面的 CMS 元数据
    Angular 服务器端渲染的学习笔记(二)
    Angular 服务器端渲染的学习笔记(一)
    第三方外部 Saas提供商如何跟使用 SAP 系统的客户进行对接接口集成
    如何从 SAP Spartacus Product Detail 页面,找到其 Angular 实现 Component 的位置
    具备自动刷新功能的 SAP ABAP ALV 报表
    C++学习目录
    c--条件编译
    c--文件读写--二进制
  • 原文地址:https://www.cnblogs.com/viyoung/p/10416230.html
Copyright © 2011-2022 走看看