SpringBoot中日志切面实现小结

 更新时间:2024年11月18日 10:06:33   作者:这个名字应该没人用吧  
本文介绍了SpringBoot中日志切面实现小结,通过定义一个自定义注解和创建一个日志切面类,为方法添加日志记录功能,感兴趣的可以了解一下

在应用开发中,日志记录对于监控、调试和追踪用户行为至关重要。Spring Boot 虽然内置了强大的日志框架,但在某些情况下,我们可能需要更细粒度的日志管理。Spring AOP 提供了一种灵活的方式来实现方法级别的日志记录,而无需侵入业务代码。本文将介绍如何通过 Spring AOP 切面来实现这一功能。

什么是Spring AOP?

Spring AOP 是一个面向切面的编程(AOP)框架,它允许开发者将横切关注点(如日志记录、事务管理等)与业务逻辑分离。通过使用 Spring AOP,我们可以在不修改业务代码的情况下,为应用程序添加日志记录功能。

Spring AOP 详细可以看这篇文章  Spring AOP入门

日志切面的设计

在我们的示例中,我们定义了一个 SysLog 注解,用于标记需要记录日志的方法。接着,我们创建了一个日志切面 LogAspect,它会拦截所有带有 SysLog 注解的方法,并记录日志信息。

SysLog 注解

首先,我们定义了一个 SysLog 注解,它可以用来标记需要记录日志的方法:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
    String value() default "";
}

这个注解非常简单,它只有一个 value 属性,用于存储日志的描述信息。

日志切面 LogAspect

接下来,我们创建了 LogAspect 切面类,它会拦截所有带有 SysLog 注解的方法:

@Aspect
@Component
public class LogAspect {
    // 省略其他成员变量和方法

    @Around("@annotation(sysLog)")
    public Object logAround(ProceedingJoinPoint joinPoint, SysLog sysLog) throws Throwable {
        // 日志记录逻辑
    }
}

在这个切面中,我们使用了 @Around 注解来定义一个环绕通知,它会在目标方法执行前后记录日志信息。

日志记录的实现

在 LogAspect 切面类中,我们实现了 logAround 方法,它会在目标方法执行前后记录日志信息:

@Around("@annotation(sysLog)")
public Object logAround(ProceedingJoinPoint joinPoint, SysLog sysLog) throws Throwable {
    Log log = new Log();
    log.setTimestamp(new Date());
    log.setDescription(sysLog.value());
    log.setMethodName(joinPoint.getSignature().getName());
    log.setParameters(Arrays.toString(joinPoint.getArgs()));

    try {
        Object result = joinPoint.proceed();
        log.setLevel("INFO");
        log.setMessage("方法 " + joinPoint.getSignature().getName() + " 执行成功。");
        return result;
    } catch (Exception e) {
        log.setLevel("ERROR");
        log.setMessage("方法 " + joinPoint.getSignature().getName() + " 执行过程中发生异常。");
        log.setException(e.toString());
        throw e;
    } finally {
        logService.save(log);
    }
}

在这个环绕通知中,我们首先创建了一个 Log 对象,并设置了日志的基本信息,如时间戳、描述、方法名和参数。然后,我们执行目标方法,并根据执行结果记录日志信息。如果目标方法执行过程中发生异常,我们会记录异常信息。

测试

完整代码

Maven依赖

在 pom.xml 文件中添加依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

SQL脚本

创建日志表的 SQL 脚本:

DROP TABLE IF EXISTS `logs`;
CREATE TABLE `logs`  (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '描述',
  `level` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '日志级别',
  `timestamp` datetime NOT NULL COMMENT '时间戳',
  `message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '日志消息',
  `class_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '类名',
  `method_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '方法名',
  `parameters` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '参数',
  `user_identifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户标识',
  `exception` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '异常信息',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '日志表' ROW_FORMAT = DYNAMIC;

日志实体类 Log

我们定义了一个 Log 实体类,用于存储日志信息:

/**
 * 日志实体类
 *
 * @author 王闻薪
 */
@Data
@TableName("logs")
public class Log implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主键ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 描述
     */
    @TableField("description")
    private String description;

    /**
     * 日志级别
     */
    @TableField("level")
    private String level;

    /**
     * 时间戳
     */
    @TableField("timestamp")
    private Date timestamp;

    /**
     * 日志消息
     */
    @TableField("message")
    private String message;

    /**
     * 类名
     */
    @TableField("class_name")
    private String className;

    /**
     * 方法名
     */
    @TableField("method_name")
    private String methodName;


    /**
     * 参数
     */
    @TableField("parameters")
    private String parameters;

    /**
     * 用户标识
     */
    @TableField("user_identifier")
    private String userIdentifier;

    /**
     * 异常信息
     */
    @TableField("exception")
    private String exception;

}

这个实体类映射到数据库中的 logs 表,用于存储日志的详细信息。

切面代码

日志切面类 LogAspect

package org.example.demo.aspectj;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.example.demo.annotation.SysLog;
import org.example.demo.entity.Log;
import org.example.demo.service.ILogService;
import org.example.demo.utils.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;

/**
 * 日志切面类
 * 用于拦截标注有@SysLog注解的方法,并记录日志信息。
 *
 * @author 王闻薪
 */
@Slf4j
@Aspect // 标记为切面类
@Component // 将此切面类交给Spring管理
public class LogAspect {

    @Autowired
    private ILogService logService;

    @Autowired
    private TokenService tokenService;

    /**
     * 环绕通知
     * 拦截带有@SysLog注解的方法,并在方法执行前后进行日志记录。
     *
     * @param joinPoint 连接点对象,包含方法的调用信息。
     * @param sysLog 注解对象,包含日志描述信息。
     * @return 方法执行结果。
     * @throws Throwable 可能抛出的异常。
     */
    @Around("@annotation(sysLog)")
    public Object logAround(ProceedingJoinPoint joinPoint, SysLog sysLog) throws Throwable {
        // 创建日志对象
        Log log = new Log();
        log.setLevel("INFO");
        log.setTimestamp(new Date());
        log.setUserIdentifier(getUserId().toString());
        log.setDescription(sysLog.value());
        // 记录方法调用前的日志
        logBefore(joinPoint, log);

        try {
            // 继续执行目标方法
            Object result = joinPoint.proceed();

            // 记录方法调用后的日志
            logAfter(joinPoint, result, log);
            log.setMessage("方法 " + joinPoint.getSignature().getName() + " 执行成功。");

            return result;
        } catch (Exception e) {
            // 记录异常日志
            logException(joinPoint, e, log);
            log.setLevel("ERROR");
            log.setMessage("方法 " + joinPoint.getSignature().getName() + " 执行过程中发生异常。");
            throw e; // 重新抛出异常
        } finally {
            // 保存日志到数据库
            logService.save(log);
        }
    }

    /**
     * 记录方法调用前的日志信息。
     *
     * @param joinPoint 连接点对象,包含方法的调用信息。
     * @param log 日志对象,用于存储日志信息。
     */
    private void logBefore(ProceedingJoinPoint joinPoint, Log log) {
        log.setClassName(joinPoint.getSignature().getDeclaringTypeName());
        log.setMethodName(joinPoint.getSignature().getName());
        log.setParameters(Arrays.toString(joinPoint.getArgs()));
        log.setTimestamp(new Date()); // 设置日志时间
    }

    /**
     * 记录方法调用后的日志信息。
     *
     * @param joinPoint 连接点对象,包含方法的调用信息。
     * @param result 方法执行结果。
     * @param log 日志对象,用于存储日志信息。
     */
    private void logAfter(ProceedingJoinPoint joinPoint, Object result, Log log) {
        log.setMessage(log.getMessage() + result);
    }

    /**
     * 记录方法执行过程中的异常信息。
     *
     * @param joinPoint 连接点对象,包含方法的调用信息。
     * @param e 捕获的异常对象。
     * @param log 日志对象,用于存储日志信息。
     */
    private void logException(ProceedingJoinPoint joinPoint, Exception e, Log log) {
        log.setException(e.toString());
        log.setMessage(log.getMessage() + " 异常信息:" + e.getMessage());
    }

    /**
     * 获取当前用户ID。
     *
     * @return 用户ID。
     */
    private Long getUserId() {
        return tokenService.getUserId();
    }
}

结论

通过使用 Spring AOP 和注解,我们可以灵活地为 Spring Boot 应用程序添加日志记录功能,而无需修改业务逻辑代码。这不仅提高了代码的可维护性,还使得日志记录变得更加方便和强大。

到此这篇关于SpringBoot中日志切面实现小结的文章就介绍到这了,更多相关SpringBoot 日志切面内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java读取Properties文件几种方法总结

    Java读取Properties文件几种方法总结

    这篇文章主要介绍了 Java读取Properties文件几种方法总结的相关资料,需要的朋友可以参考下
    2017-04-04
  • Spring AOP的底层实现方式-代理模式

    Spring AOP的底层实现方式-代理模式

    这篇文章主要介绍了Spring AOP的底层实现方式-代理模式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Java通过动态代理实现一个简单的拦截器操作

    Java通过动态代理实现一个简单的拦截器操作

    这篇文章主要介绍了Java通过动态代理实现一个简单的拦截器操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • SpringBoot2零基础到精通之配置文件与web开发

    SpringBoot2零基础到精通之配置文件与web开发

    SpringBoot是一种整合Spring技术栈的方式(或者说是框架),同时也是简化Spring的一种快速开发的脚手架,本篇让我们一起学习配置文件以及web相关的开发
    2022-03-03
  • java中jdbcTemplate的queryForList(坑)

    java中jdbcTemplate的queryForList(坑)

    本文主要介绍了java中jdbcTemplate的queryForList,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • Kafka中Producer和Consumer的作用详解

    Kafka中Producer和Consumer的作用详解

    这篇文章主要介绍了Kafka中Producer和Consumer的作用详解,Kafka是一个分布式的流处理平台,它的核心是消息系统,Producer是Kafka中用来将消息发送到Broker的组件之一,它将消息发布到主题,并且负责按照指定的分区策略将消息分配到对应的分区中,需要的朋友可以参考下
    2023-12-12
  • spring容器启动实现初始化某个方法(init)

    spring容器启动实现初始化某个方法(init)

    这篇文章主要介绍了spring容器启动实现初始化某个方法(init),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Mybatis主配置文件的properties标签详解

    Mybatis主配置文件的properties标签详解

    这篇文章主要介绍了Mybatis主配置文件的properties标签,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • Java中Arraylist的最大长度

    Java中Arraylist的最大长度

    这篇文章主要介绍了Java中Arraylist的最大长度,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • Java持久化框架Hibernate与Mybatis优劣及选择详解

    Java持久化框架Hibernate与Mybatis优劣及选择详解

    这篇文章主要介绍了Java持久化框架Hibernate与Mybatis优劣及选择详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05

最新评论