Spring Boot-AOP日志管理

AOP

Spring的两大核心之一的AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是对传统 OOP(Object-Oriented Programming,面向对象编程)的补充。是一种通过动态代理实现程序功能扩展和统一维护的一种技术。
本篇介绍通过使用AOP,在进行接口调用时根据其访问的控制层方法打印对应的请求日志,例如:调用接口,客户端浏览器型号,电脑系统型号, 方法执行时间,请求参数等等。

AOP的相关术语

  1. 横切关注点
    从每个方法中抽取出来的同一类非核心业务。
  2. 切面(Aspect)
    封装横切关注点信息的类,每个关注点体现为一个通知方法。
  3. 通知(Advice)
    切面必须要完成的各个具体工作
  4. 目标(Target)
    被通知的对象
  5. 代理(Proxy)
    向目标对象应用通知之后创建的代理对象
  6. 连接点(Joinpoint)
    横切关注点在程序代码中的具体位置
  7. 切入点(pointcut)
    定位连接点的方式

导入依赖

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Aop组件-->
        <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>
        <!--lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

AOP的实现

AspectJ是实现了AOP思想的最流行的AOP框架,本文将使用AspectJ注解的方式实现AOP。

  • 将目标对象和切面类标识为IOC的组件,并在切面类上通过@Aspect注解将其标识为一个切面组件
  • 在切面类中,将抽取的非核心业务代码(横切关注点)封装为一个通知方法,通过切入点表达式作用于抽取横切关注点的位置,即连接点

上代码

自定义切面类

package com.wk.aop;

import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Arrays;

/**
 * @description: 日志切面 打印请求日志
 * @author: wk
 * @create: 2022-03-17 20:14
 */
@Slf4j   // lombok中日志注解
@Aspect  // 表明是一个切面类
@Component
public class LogAspect {

    /**
     * 进入方法时间戳
     */
    private Long startTime;
    /**
     * 方法结束时间戳(计时)
     */
    private Long endTime;

    public LogAspect() {
    }

    /**
     * 定义切入点表达式
     * 访问修饰符 返回值 包名.包名.包名...类名.方法名(参数列表)
     * 权限修饰符可以使用默认 第一个*表示返回值类型  ..表示当前包以及其子包下 第二个*表示任意方法 (..)表示任意参数列表
     */
    private final String POINTCUT = "execution(* com.wk.controller..*(..))";

    /**
     * 前置通知,方法之前执行
     * @param joinPoint
     */
    @Before(POINTCUT)
    public void doBefore(JoinPoint joinPoint) {
        // 获取当前的HttpServletRequest对象
        ServletRequestAttributes attributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 获取请求头中的User-Agent
        UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
        // 打印请求的内容
        startTime = System.currentTimeMillis();
        log.info("请求开始时间:{}", LocalDateTime.now());
        log.info("请求Url : {}", request.getRequestURL().toString());
        log.info("请求方式 : {}", request.getMethod());
        log.info("请求ip : {}", request.getRemoteAddr());
        log.info("请求内容类型 : {}", request.getContentType());
        log.info("请求参数 : {}", Arrays.toString(joinPoint.getArgs()));
        // 系统信息
        log.info("浏览器 : {}", userAgent.getBrowser().toString());
        log.info("浏览器版本 : {}", userAgent.getBrowserVersion());
        log.info("操作系统: {}", userAgent.getOperatingSystem().toString());
    }

    /**
     * 后置通知
     * @param joinPoint
     */
    @After(POINTCUT)
    public void doAfter(JoinPoint joinPoint) {
        System.out.println("Logger-->后置通知,方法名:"+joinPoint.getSignature().getName()+",方法执行完毕");
    }

    /**
     * 返回通知 正常结束时进入此方法
     *
     * @param ret
     */
    @AfterReturning(returning = "ret", pointcut = POINTCUT)
    public void doAfterReturning(Object ret) {
        endTime = System.currentTimeMillis();
        log.info("请求结束时间 : {}", LocalDateTime.now());
        log.info("请求耗时 : {}", (endTime - startTime));
        // 处理完请求,返回内容
        log.info("请求返回 : {}", ret);
    }

    /**
     * 异常通知: 1. 在目标方法非正常结束,发生异常或者抛出异常时执行
     *
     * @param throwable
     */
    @AfterThrowing(pointcut = POINTCUT, throwing = "throwable")
    public void doAfterThrowing(Throwable throwable) {
        // 保存异常日志记录
        log.error("发生异常时间 : {}", LocalDateTime.now());
        log.error("抛出异常 : {}", throwable.getMessage());
    }
}

测试结果

直接调用controller下接口

2022-03-18 20:41:40.513  INFO 23180 --- [nio-8888-exec-4] com.wk.aop.LogAspect                     : 请求开始时间:2022-03-18T20:41:40.513
2022-03-18 20:41:40.513  INFO 23180 --- [nio-8888-exec-4] com.wk.aop.LogAspect                     : 请求Url : http://localhost:8888/user/pageList
2022-03-18 20:41:40.513  INFO 23180 --- [nio-8888-exec-4] com.wk.aop.LogAspect                     : 请求方式 : GET
2022-03-18 20:41:40.514  INFO 23180 --- [nio-8888-exec-4] com.wk.aop.LogAspect                     : 请求ip : 0:0:0:0:0:0:0:1
2022-03-18 20:41:40.514  INFO 23180 --- [nio-8888-exec-4] com.wk.aop.LogAspect                     : 请求内容类型 : null
2022-03-18 20:41:40.514  INFO 23180 --- [nio-8888-exec-4] com.wk.aop.LogAspect                     : 请求参数 : [3, 5]
2022-03-18 20:41:40.514  INFO 23180 --- [nio-8888-exec-4] com.wk.aop.LogAspect                     : 浏览器 : CHROME9
2022-03-18 20:41:40.514  INFO 23180 --- [nio-8888-exec-4] com.wk.aop.LogAspect                     : 浏览器版本 : 92.0.4515.131
2022-03-18 20:41:40.514  INFO 23180 --- [nio-8888-exec-4] com.wk.aop.LogAspect                     : 操作系统: WINDOWS_10
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3dfa974f] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@423460067 wrapping com.mysql.cj.jdbc.ConnectionImpl@478c29b8] will not be managed by Spring
==>  Preparing: select * from sys_user limit ?,?
==> Parameters: 10(Integer), 5(Integer)
<==    Columns: id, user_name, nick_name, pass_word, phone_number, email, address, create_time, create_user, update_time, update_user
<==        Row: 1504379036472725511, 武杰, 万杰, mollit, 90, o.knynlcdi@qq.com, 天津重庆市商都县, 2022-03-17 19:02:51, proident ipsum consectetur, 2022-03-17 19:02:51, in occaecat ullamco
<==        Row: 1504379036472725512, 易刚, 程军, commodo ut et, 20, e.lrfko@qq.com, 四川省南通市其它区, 2022-03-17 19:02:55, ad, 2022-03-17 19:02:55, ad consequat dolore
<==        Row: 1504379036472725513, 罗秀兰, 叶杰, sunt ut incididunt, 76, c.qrzvq@qq.com, 云南省新余市涪陵区, 2022-03-17 19:02:59, consectetur magna dolor, 2022-03-17 19:02:59, consequat
<==      Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3dfa974f]
2022-03-18 20:41:40.520  INFO 23180 --- [nio-8888-exec-4] com.wk.aop.LogAspect                     : 请求结束时间 : 2022-03-18T20:41:40.520
2022-03-18 20:41:40.520  INFO 23180 --- [nio-8888-exec-4] com.wk.aop.LogAspect                     : 请求耗时 : 7
2022-03-18 20:41:40.520  INFO 23180 --- [nio-8888-exec-4] com.wk.aop.LogAspect                     : 请求返回 : BaseResult(code=20000, msg=处理成功, resultData=[User(id=1504379036472725511, userName=武杰, nickName=万杰, passWord=mollit, address=天津重庆市商都县, phoneNumber=90, email=o.knynlcdi@qq.com, createUser=proident ipsum consectetur, updateUser=in occaecat ullamco, createTime=Thu Mar 17 19:02:51 CST 2022, updateTime=Thu Mar 17 19:02:51 CST 2022), User(id=1504379036472725512, userName=易刚, nickName=程军, passWord=commodo ut et, address=四川省南通市其它区, phoneNumber=20, email=e.lrfko@qq.com, createUser=ad, updateUser=ad consequat dolore, createTime=Thu Mar 17 19:02:55 CST 2022, updateTime=Thu Mar 17 19:02:55 CST 2022), User(id=1504379036472725513, userName=罗秀兰, nickName=叶杰, passWord=sunt ut incididunt, address=云南省新余市涪陵区, phoneNumber=76, email=c.qrzvq@qq.com, createUser=consectetur magna dolor, updateUser=consequat, createTime=Thu Mar 17 19:02:59 CST 2022, updateTime=Thu Mar 17 19:02:59 CST 2022)])
Logger-->后置通知,方法名:pageList,方法执行完毕
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值