资讯专栏INFORMATION COLUMN

Spring Boot 2.x(十一):AOP实战--打印接口日志

Youngdze / 2886人阅读

摘要:接口日志有啥用在我们日常的开发过程中,我们可以通过接口日志去查看这个接口的一些详细信息。在切入点返回内容之后切入内容可以用来对处理返回值做一些加工处理。

接口日志有啥用

在我们日常的开发过程中,我们可以通过接口日志去查看这个接口的一些详细信息。比如客户端的IP,客户端的类型,响应的时间,请求的类型,请求的接口方法等等,我们可以对这些数据进行统计分析,提取出我们想要的信息。

怎么拿到接口日志

这里,我们使用的是Spring的两大杀器之AOP,通过在Controller层定义切点,然后对请求对象进行分析获取接口信息,同时开启一个ThreadLocal来记录响应时间。

关于AOP的注解

@Aspect:将一个类定义为切面类。

@Pointcut:定义一个切入点。

@Before:在切入点开始处切入内容。

@After:在切入点结尾处切入内容。

@AfterReturning:在切入点返回内容之后切入内容(可以用来对处理返回值做一些加工处理。

@Around:在切入点前后切入内容,并自己控制何时执行切入点自身的内容

@AfterThrowing:用来处理当切入内容部分抛出异常之后的处理逻辑。

@Order:在切入点前的操作,按order的值由小到大执行;在切入点后的操作,按order的值由大到小执行。

实战应用 一:引入依赖

首先,我们需要新增引入aop的依赖,以及用于分析客户端信息的UserAgentUtils包,还有用于@Slf4j打印日志的Lombok的包:

        
            org.springframework.boot
            spring-boot-starter-aop
        
        
            eu.bitwalker
            UserAgentUtils
            1.20
        
二:定义一个ResponseAop切面类

在之前的统一返回值和异常处理中我们已经定义过这个类,这里是对其进行完善。这里我再把代码再写一下:

@Aspect
@Order(5)
@Component
@Slf4j
public class ResponseAop
三:定义一个ThreadLocal变量

直接在这里定义基本类型会有同步问题,所以我们定义一个ThreadLocal对象来记录消耗的时间。

ThreadLocal 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毫秒

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

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

公众号

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/75858.html

相关文章

  • SpringBoot 实战 (十一) | 整合数据缓存 Cache

    摘要:前言如题,今天介绍的数据缓存。说明确实做了数据缓存,第二次的测试结果是从数据缓存中获取的,并没有直接查数据库。为为的数据做了缓存插入数据返回的结果数据库中的结果访问结果如下图。后语以上为数据缓存的教程。 微信公众号:一个优秀的废人如有问题或建议,请后台留言,我会尽力解决你的问题。 前言 如题,今天介绍 SpringBoot 的数据缓存。做过开发的都知道程序的瓶颈在于数据库,我们也知道内...

    idealcn 评论0 收藏0
  • SpringBoot 2.X Kotlin系列之AOP统一打印日志

    showImg(https://segmentfault.com/img/remote/1460000018819338?w=1024&h=500); 在开发项目中,我们经常会需要打印日志,这样方便开发人员了解接口调用情况及定位错误问题,很多时候对于Controller或者是Service的入参和出参需要打印日志,但是我们又不想重复的在每个方法里去使用logger打印,这个时候希望有一个管理者统一...

    Nino 评论0 收藏0
  • [开源作品] skadmin 管理系统

    摘要:简介项目基于的前后端分离的管理系统,项目采用分模块开发方式,权限控制采用,基于角色的访问控制,支持数据字典数据权限管理前端菜单支持动态路由,另外还有其他的功能模块日志管理代码生成器系统监控云存储管理系统工具等等。 简介 项目基于 Spring Boot 2.1.0 、 Spring Data JPA、 Spring Security、Redis、Vue的前后端分离的管理系统,项目采用分...

    codergarden 评论0 收藏0

发表评论

0条评论

Youngdze

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<