在springboot中使用AOP进行全局日志记录

目录
  • 前言
    • 1、 spring AOP 是什么?
    • 2、spring AOP 能做什么?
    • 3、spring AOP 我能用 AOP 解决什么问题?
  • 一、引入依赖,增加自定义注解
    • 1、引入 maven 依赖
    • 2、增加自定义注解 OperationLog
  • 二、为自定义注解编写切面实现
  • 三、使用自定义日志注解

前言

此前项目上需要对用户的操作进行日志记录,以便后续追踪问题,所以就学习了使用 spring AOP 来进行日志记录。

1、 spring AOP 是什么?

spring 的两大核心就是 IOC 和 AOP,AOP 是 spring 框架面向切面的编程思想,AOP是横切性的技术,将涉及多业务流程的通用功能抽取并单独封装,形成独立的切面,再将这些切面横向切入到业务流程指定的位置中。

2、spring AOP 能做什么?

AOP 主要是从切面入手,将日志记录、安全控制、事务处理、全局异常处理等全局功能从我们的业务逻辑代码中划分出来。

3、spring AOP 我能用 AOP 解决什么问题?

正好我这边需要做一个全局的日志,如果每个方法都去写日志记录,这样代码量非常大,而且代码繁琐冗余,全局记录的话,只需要在方法上增加一个注解就可以实现日志记录。

好了,不多bb,直接上代码实现

一、引入依赖,增加自定义注解

1、引入 maven 依赖

        <!--利用 AOP 做操作日志记录-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2、增加自定义注解 OperationLog

@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解执行阶段
@Documented
public @interface OperationLog {
    String operDesc() default "";  // 操作说明
}

二、为自定义注解编写切面实现

@Aspect //标识一个切面类
@Component
public class OperationLogAspect {
    private static final Logger logger = LoggerFactory.getLogger(OperationLogAspect.class);
    @Autowired
    private OperatelogsService OperatelogsService;
    @Autowired
    private SysUserService userService;
    /**
     * 设置操作日志切入点 记录操作日志 在注解的位置切入代码
     */
    @Pointcut("@annotation(com.wxw.annotation.OperationLog)")
    public void operLogPoinCut() {
    }
    /**
     * 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
     *
     * @param joinPoint 切入点
     * @param keys      返回结果
     */
    @AfterReturning(value = "operLogPoinCut()", returning = "keys")
    public void saveOperLog(JoinPoint joinPoint, Object keys) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);
        Operatelogs operlog = new Operatelogs();
        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            // 获取操作
            OperationLog opLog = method.getAnnotation(OperationLog.class);
            if (opLog != null) {
                String operDesc = opLog.operDesc();
                operlog.setfDescript(operDesc); // 操作描述
            }
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            // 获取请求的方法名
            String methodName = method.getName();
            methodName = className + "." + methodName;
            operlog.setfRequestMethod(methodName); // 请求方法
            // 请求的参数
            Object[] arguments = joinPoint.getArgs();
            //判断参数数组是否为空
            Stream<?> stream = ArrayUtils.isEmpty(arguments) ? Stream.empty() :  Arrays.asList(arguments).stream();
            //过滤 joinPoint.getArgs()返回的数组中携带有Request或者Response对象
            List<Object> logArgs = stream
                    .filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)))
                    .collect(Collectors.toList());
            // 先将参数所在的list 转换成json 数组
            JSONArray jsonArray = JSONArray.parseArray(JSON.toJSONString(logArgs));
            //再转 json 字符串
            String params = jsonArray.toJSONString();
            operlog.setfRequestData(params); // 请求参数
            operlog.setfResponseData(JSON.toJSONString(keys)); // 返回结果
            SysUser user = getUser();
            operlog.setfOptby(user != null ? user.getfUserid() : 0); // 请求用户ID
            operlog.setfOptname(user != null ? user.getfUsercode() : "admin"); // 请求用户名称
            operlog.setfOpton(new Date()); // 创建时间
            operlog.setfIp(getIp()); // 请求IP
            operlog.setfRequestUrl(request.getRequestURI()); // 请求URI
            OperatelogsService.save(operlog);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 异常返回通知,用于拦截异常日志信息 连接点抛出异常后执行
     *
     * @param joinPoint 切入点
     * @param e         异常信息
     */
    @AfterThrowing(pointcut = "operLogPoinCut()", throwing = "e")
    public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);
        Operatelogs operlog = new Operatelogs();
        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            // 获取请求的方法名
            String methodName = method.getName();
            methodName = className + "." + methodName;
            // 请求的参数
            Object[] arguments = joinPoint.getArgs();
             //判断参数数组是否为空
            Stream<?> stream = ArrayUtils.isEmpty(arguments) ? Stream.empty() :  Arrays.asList(arguments).stream();
            //过滤 joinPoint.getArgs()返回的数组中携带有Request或者Response对象
            List<Object> logArgs = stream
                    .filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)))
                    .collect(Collectors.toList());
            // 先将参数所在的list 转换成json 数组
            JSONArray jsonArray = JSONArray.parseArray(JSON.toJSONString(logArgs));
            //再转 json 字符串
            String params = jsonArray.toJSONString();
            operlog.setfRequestData(params); // 请求参数
            operlog.setfRequestMethod(methodName); // 请求方法名
            operlog.setfExceptionName(e.getClass().getName()); // 异常名称
            operlog.setfResponseData(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace())); // 异常信息
            SysUser user = getUser();
            operlog.setfOptby(user != null ? user.getfUserid() : 0); // 请求用户ID
            operlog.setfOptname(user != null ? user.getfUsercode() : "admin"); // 请求用户名称
            operlog.setfOpton(new Date()); // 创建时间
            operlog.setfRequestUrl(request.getRequestURI()); // 请求URI
            operlog.setfIp(getIp()); // 请求IP
            operatelogsService.save(operlog);
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }
    /**
     * 获取用户信息,由于我这边 token 是使用 jwt 生成,所以可以从中获取用户id,再根据id获取用户数据
     *
     * @return
     */
    public SysUser getUser() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        SysUser sysUser = null;
        String token = request.getHeader("token");// 从 http 请求头中取出 token
        if(token != null){
            // 获取 token 中的 user id
            String userId = JWT.decode(token).getAudience().get(0);
            sysUser = userService.getOne(new QueryWrapper<SysUser>().lambda().eq(SysUser::getfUserid,
                    userId));
        }
        return sysUser;
    }
    /**
     * 获取ip
     *
     * @return
     */
    public String getIp() {
        //获得本机的ip和名称
        InetAddress addr = null;
        try {
            addr = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            throw new CustomException(500,e.getMessage());
        }
        String ip = addr.getHostAddress();
//      String hostname = addr.getHostName();
        return ip;
    }
    /**
     * 转换异常信息为字符串
     *
     * @param exceptionName    异常名称
     * @param exceptionMessage 异常信息
     * @param elements         堆栈信息
     */
    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuffer strbuff = new StringBuffer();
        for (StackTraceElement stet : elements) {
            strbuff.append(stet + "\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
        return message;
    }
}

三、使用自定义日志注解

将自定义注解加在方法上,请求方法之后,就可以将方法的日志记录到自己的数据库里面了。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Springboot接口项目如何使用AOP记录日志

    一. 背景 一直想给项目构建一个统一的日志收集系统,先迈出第一步,构建一个日志收集类,用AOP实现无侵入日志收集 二. 环境 1.此随笔内容基于spring boot项目 2.数据库为mysql 5.7.9版本 3.jdk 版本为1.8 三. 说明 此版采用数据库存储,之后考虑使用elasticsearch等工具存储 四. 内容 1.构建日志采集实体类:BaseLogMessage public class BaseLogMessage { private String serverIP; pr

  • spring-boot使用AOP统一处理日志

    AOP我想大家都很清楚,有时候我们需要处理一些请求日志,或者对某些方法进行一些监控,如果出现例外情况应该进行怎么样的处理,现在,我们从spring-boot中引入AOP. [开发环境:jdk版本号为1.8,spring boot的版本号为1.4.1]{style="background-color:#FF0000"} 首先,我们先引入jar包, POM文件添加如下内容: <!--引用AOP--> <dependency> <groupId>org.s

  • 详解Spring Boot中使用AOP统一处理Web请求日志

    在spring boot中,简单几步,使用spring AOP实现一个拦截器: 1.引入依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframewo

  • 详解基于SpringBoot使用AOP技术实现操作日志管理

    操作日志对于程序员或管理员而言,可以快速定位到系统中相关的操作,而对于操作日志的管理的实现不能对正常业务实现进行影响,否则即不满足单一原则,也会导致后续代码维护困难,因此我们考虑使用AOP切面技术来实现对日志管理的实现. 文章大致内容: 1.基本概念 2.基本应用 3.日志管理实战 对这几部分理解了,会对AOP的应用应该很轻松. 一.基本概念 项目 描述 Aspect(切面) 跨越多个类的关注点的模块化,切面是通知和切点的结合.通知和切点共同定义了切面的全部内容--它是什么,在何时和何处完成其功

  • 在springboot中使用AOP进行全局日志记录

    目录 前言 1. spring AOP 是什么? 2.spring AOP 能做什么? 3.spring AOP 我能用 AOP 解决什么问题? 一.引入依赖,增加自定义注解 1.引入 maven 依赖 2.增加自定义注解 OperationLog 二.为自定义注解编写切面实现 三.使用自定义日志注解 前言 此前项目上需要对用户的操作进行日志记录,以便后续追踪问题,所以就学习了使用 spring AOP 来进行日志记录. 1. spring AOP 是什么? spring 的两大核心就是 IOC

  • SpringBoot中使用AOP打印接口日志的方法

    前言 AOP 是 Aspect Oriented Program (面向切面)的编程的缩写.他是和面向对象编程相对的一个概念.在面向对象的编程中,我们倾向于采用封装.继承.多态等概念,将一个个的功能在对象中来实现.但是,我们在实际情况中也发现,会有另外一种需求就是一类功能在很多对象的很多方法中都有需要.例如有一些对数据库访问的方法有事务管理的需求,有很多方法中要求打印日志.按照面向对象的方式,那么这些相同的功能要在很多地方来实现或者在很多地方来调用.这就非常繁琐并且和这些和业务不相关的需求耦合太

  • SpringBoot中通过AOP整合日志文件的实现

    目录 1.导入相关的依赖 2.log4j2 日志文件 3.dao层的接口以及实现类 4.Service层业务实现类 5.Controller层接口控制类 6.编写业务类增强类,加入一个日志文件记录 7.运行测试,查看结果 1.导入相关的依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter&

  • java通过AOP实现全局日志打印详解

    目录 几个常用的切点注解,这次使用了@Before和@Around 切Controller打印请求的接口.参数.返回值以及耗时情况. 切Service打印日志,URL,请求方式,IP,类名,方法 总结 几个常用的切点注解,这次使用了@Before和@Around 1.@Before 前置增强(目标方法执行之前,执行注解标注的内容) 2.@AfterReturning 后置增强(目标方法正常执行完毕后,执行) 3.@Around 环绕增强(目标方法执行前后,分别执行一些代码) 4.@AfterTh

  • SpringBoot中利用AOP和拦截器实现自定义注解

    目录 前言 Spring实现自定义注解 1.引入相关依赖 2.相关类 Java实现自定义注解 通过Cglib实现 通过JDk动态代理实现 Cglib和JDK动态代理的区别 写在最后 前言 最近遇到了这样一个工作场景,需要写一批dubbo接口,再将dubbo接口注册到网关中,但是当dubbo接口异常的时候会给前端返回非常不友好的异常.所以就想要对异常进行统一捕获处理,但是对于这种service接口使用@ExceptionHandler注解进行异常捕获也是捕获不到的,应为他不是Controller的

  • SpringBoot中使用Redis作为全局锁示例过程

    目录 一.模拟没有锁情况下的资源竞争 二.使用redis加锁 微服务的项目中,一个服务我们启动多份,在不同的进程中.这些服务是无状态的,而由数据存储容器(mysql/redis/es)进行状态数据的持久化.这就会导致资源竞争,出现多线程的问题. 一.模拟没有锁情况下的资源竞争 public class CommonConsumerService { //库存个数 static int goodsCount = 900; //卖出个数 static int saleCount = 0; publi

  • springboot实现基于aop的切面日志

    本文实例为大家分享了springboot实现基于aop的切面日志的具体代码,供大家参考,具体内容如下 通过aop的切面方式实现日志 通切面拦截所有指定包下的所有方法 @Aspect @Component @EnableAspectJAutoProxy public class LogAspect1{     Logger logger = LoggerFactory.getLogger(LogAspect.class);      /**  * 拦截切点  */     @Pointcut("e

  • springMVC自定义注解,用AOP来实现日志记录的方法

    需求背景 最近的一个项目,在项目基本完工的阶段,客户提出要将所有业务操作的日志记录到数据库中,并且要提取一些业务的关键信息(比如交易单号)体现在日志中. 为了保证工期,在查阅了资料以后,决定用AOP+自定义注解的方式来完成这个需求. 准备工作 自定义注解需要依赖的jar包有 aspectjrt-XXX.jar ,aspectjweaver-XXX.jar,XXX代表版本号. 自定义注解 在项目下单独建立了一个log包,来存放日志相关的内容 **.common.log.annotation //自

  • PHP中错误与异常的日志记录用法分析

    本文分析了PHP中错误与异常的日志记录用法.分享给大家供大家参考,具体如下: 提到 Nginx + PHP 服务的错误日志,我们通常能想到的有 Nginx 的 access 日志.error 日志以及 PHP 的 error 日志.虽然看起来是个很简单的问题,但里面其实又牵扯到应用配置以及日志记录位置的问题,如果是在 ubuntu 等系统下使用 apt-get 的方式来安装,其自有一套较为合理的的配置文件可用.再者运行的应用程序中的配置也会影响到日志记录的方式及内容. 错误与异常的区别 关于错误

  • 运用Spring Aop+注解实现日志记录

    目录 1. 介绍 2. 实践 2.1 定义注解 2.2 切面类 2.3 编写测试方法 2.4 运行结果 3. 总结 4. 参考文章 1. 介绍 我们都知道Spring框架的两大特性分别是 IOC (控制反转)和 AOP (面向切面),这个是每一个Spring学习视频里面一开始都会提到的.在日常项目中,我们也会经常使用IOC控制反转,但是却感觉AOP很少会运用到.其实AOP大有用处,甚至可以让你偷偷懒. 举一个例子,假如现在要让你记录每一个请求的请求IP,请求的方法,请求路径,请求的参数,返回参数

随机推荐