【Spring Boot 快速入门】二十、Spring Boot 基于AOP注解实现日志记录功能

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【Spring Boot 快速入门】二十、Spring Boot 基于AOP注解实现日志记录功能

前言


  在很多后台管理系统中,有明确的权限和角色的管控,当然也少不了操作日志的记录。本文将基于Spring 的AOP特性开发一个日志记录功能。下面记录一下整个开发工程


快速开始


  使用Spring的AOP特性,首先了解AOP是什么,AOP在程序开发过程中是指面向切面编程,通过预编译和动态代理实现程序功能。AOP中主要有切点、切面、连接点、目标群、通知、织入方式等。通知类型常用的有前置通知、环绕通知、后置通知等,在日志记录的过程中一般使用环绕通知。具体的AOP的相关概念大家不熟悉的可以去查询一下。


版本信息


  本次Spring Boot 基于AOP注解实现日志记录功能,主要版本信息如下:


Spring Boot 2.3.0.RELEASE
aspectjweaver 1.9.6
maven 3

 主要引入的依赖是aspectjweaver,如果aspectjweaver 和Spring Boot 版本不一致,可能会报找不到切点等相关的异常,可以替换位相关版本即可解决。


<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>


基础信息


  实现日志记录功能,主要是将操作日志记录到数据库中或者搜索引擎中,方便查询。在日志中需要记录操作人、操作时间、请求的参数、请求的ip、请求的连接、操作类型等信息。本次示例的建表SQL如下:


CREATE TABLE `log_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `model` varchar(255) DEFAULT NULL COMMENT '模块',
  `log_type` tinyint(4) DEFAULT NULL COMMENT '类型0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=登录,9=清空数据,10查询',
  `url` varchar(255) DEFAULT NULL COMMENT '请求链接',
  `method` varchar(255) DEFAULT NULL COMMENT '请求方法',
  `class_name` varchar(255) DEFAULT NULL COMMENT '类名',
  `method_name` varchar(255) DEFAULT NULL COMMENT '方法名',
  `params` varchar(500) DEFAULT NULL COMMENT '请求参数',
  `ip` varchar(255) DEFAULT NULL COMMENT 'ip地址',
  `user_id` int(11) DEFAULT NULL COMMENT '操作人id',
  `user_name` varchar(255) DEFAULT NULL COMMENT '操作人',
  `sys_info` varchar(255) DEFAULT NULL COMMENT '系统信息',
  `create_user` varchar(200) CHARACTER SET utf8 DEFAULT NULL COMMENT '创建人',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_user` varchar(200) CHARACTER SET utf8 DEFAULT NULL COMMENT '更新人',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `data_state` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0 删除   1未删除',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;

 本次将操作类型分为11类包登录、退出、增删改查、导入导出、清空等相关日志操作类别。


@Getter
@AllArgsConstructor
public enum LogTypeEnum {
    /**
     * 0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=登录,9=清空数据,10查询
     * */
    OTHER(0,"其它"),
    ADD(1,"新增"),
    UPDATE(2,"修改"),
    DEL(3,"删除"),
    AUTH(4,"授权"),
    EXPORT(5,"导出"),
    IMPORT(6,"导入"),
    QUIT(7,"强退"),
    GENERATE_CODE(8,"登录"),
    CLEAR(9,"清空"),
    QUERY(10,"查询"),
    ;
    @EnumValue
    private int value;
    private String desc;
}


Log注解


  当数据初始化完成之后,就是编写一个自定义的Log注解。本次使用的注解主要是针对方法进行注解。具体如下:

  • @Target:注解的目标位置,主要可以有接口、类、枚举、字段、方法、构造函数、包等位置。可以根据需要进行配置。日志使用的本次基于方法注解。
  • @Retention:是指注解保留的位置,可以在源码中、类中、运行中。本次日志操作记录肯定是在运行中使用,所以选择RUNTIME。
  • @Documented:字面意思文档,也就是说明该注解将被包含在javadoc中。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    /**
     * 模块
     * */
    String model() default "";
    /**
     * 操作
     * */
    LogTypeEnum logType() default LogTypeEnum.OTHER;
}


LogAspect


  定义一个日志的LogAspect切面。在类中需要使用@Aspect和 @Component指明这是一个切面方法在运行中进行扫描包。需要注意的是这个方法中需要定义切面和通知类型。最后根据注解和请求参数中的信息,查询到操作日志需要的信息。由于本次无需登录直接接口请求,所以操作人和操作id在演示中使用了默认值。本次示例只提取了部分操作日志信息,在项目中需要加入的日志信息多,可以根据需求进行修改。


@Aspect
@Component
public class LogAspect {
    @Resource
    private LogInfoMapper logInfoMapper;
    /**
     * @ClassName logPointCut
     * @Description:切点信息
     * @Author JavaZhan @公众号:Java全栈架构师
     * @Version V1.0
     **/
    @Pointcut("@annotation(com.example.demo.log.Log)")
    public void logPointCut(){
    }
    /**
     * @ClassName aroundForLog
     * @Description:环绕通知
     * @Author JavaZhan @公众号:Java全栈架构师
     * @Version V1.0
     **/
    @Around("logPointCut()")
    public Object aroundForLog(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        Object result = point.proceed();
        saveSmsLog(point);
        return result;
    }
    /**
     * @ClassName saveLogInfo
     * @Description: 保存操作日志
     * @Author JavaZhan @公众号:Java全栈架构师
     * @Version V1.0
     **/
    private void saveLogInfo(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LogInfo logInfo = new LogInfo();
        logInfo.setClassName(joinPoint.getTarget().getClass().getName());
        logInfo.setMethodName(signature.getName());
        Log log = method.getAnnotation(Log.class);
        if(log != null){
            logInfo.setModel(log.model());
            logInfo.setLogType(log.logType().getValue());
        }
        Object[] args = joinPoint.getArgs();
        try{
            String params = JSONObject.toJSONString(args);
            logInfo.setParams(params);
        }catch (Exception e){
        }
        ServletRequestAttributes servletRequestAttributes =   (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        logInfo.setIp(IpUtils.getIpAddr(request));
        logInfo.setUrl(request.getServletPath());
        logInfo.setMethod(request.getMethod());
        logInfo.setUserId(123);
        logInfo.setUserName("admin");
        logInfo.setCreateTime(new Date());
        logInfo.setDataState(1);
        //保存操作日志
        logInfoMapper.insert(logInfo);
    }
}


测试日志


  本示例将基于常用的接口进行测试。在Controller方法中,我们调用自定义注解,根据方法的实际使用含义指定方法的model信息和操作类型信息即可。


@RequestMapping("user")
@Controller
public class UserController {
    @Resource
    private UserService userService;
    @RequestMapping("getAllUser")
    @ResponseBody
    @Log(model = "查询用户列表",logType = LogTypeEnum.QUERY)
    public List<User> getAllUser(){
        return userService.getAllUser();
    }
    @RequestMapping("getUserById")
    @ResponseBody
    @Log(model = "获取指定用户",logType = LogTypeEnum.QUERY)
    public User getUserById(Integer id ){
        return userService.getById(id);
    }
}


调用接口:http://127.0.0.1:8888/user/getAllUser 返回的数据信息如下。


image.png


查看日志表中,可以看到已经新增一条查询用户列表的日志信息。


image.png


下面访问其他接口:http://127.0.0.1:8888/user/getUserById?id=1 返回的数据信息如下。


image.png


查看日志表中,可以看到已经新增一条获取指定用户的日志信息。


image.png


结语


  好了,以上就是Spring Boot 基于AOP注解实现日志记录功能的示例



相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
19天前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
150 73
|
14天前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
45 21
|
6天前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
36 8
|
19天前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
2月前
|
XML 安全 Java
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
本文介绍了Java日志框架的基本概念和使用方法,重点讨论了SLF4J、Log4j、Logback和Log4j2之间的关系及其性能对比。SLF4J作为一个日志抽象层,允许开发者使用统一的日志接口,而Log4j、Logback和Log4j2则是具体的日志实现框架。Log4j2在性能上优于Logback,推荐在新项目中使用。文章还详细说明了如何在Spring Boot项目中配置Log4j2和Logback,以及如何使用Lombok简化日志记录。最后,提供了一些日志配置的最佳实践,包括滚动日志、统一日志格式和提高日志性能的方法。
607 31
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
|
1月前
|
监控 安全 Apache
什么是Apache日志?为什么Apache日志分析很重要?
Apache是全球广泛使用的Web服务器软件,支持超过30%的活跃网站。它通过接收和处理HTTP请求,与后端服务器通信,返回响应并记录日志,确保网页请求的快速准确处理。Apache日志分为访问日志和错误日志,对提升用户体验、保障安全及优化性能至关重要。EventLog Analyzer等工具可有效管理和分析这些日志,增强Web服务的安全性和可靠性。
|
3月前
|
XML JSON Java
Logback 与 log4j2 性能对比:谁才是日志框架的性能王者?
【10月更文挑战第5天】在Java开发中,日志框架是不可或缺的工具,它们帮助我们记录系统运行时的信息、警告和错误,对于开发人员来说至关重要。在众多日志框架中,Logback和log4j2以其卓越的性能和丰富的功能脱颖而出,成为开发者们的首选。本文将深入探讨Logback与log4j2在性能方面的对比,通过详细的分析和实例,帮助大家理解两者之间的性能差异,以便在实际项目中做出更明智的选择。
390 3
|
10天前
|
SQL 关系型数据库 MySQL
MySQL事务日志-Undo Log工作原理分析
事务的持久性是交由Redo Log来保证,原子性则是交由Undo Log来保证。如果事务中的SQL执行到一半出现错误,需要把前面已经执行过的SQL撤销以达到原子性的目的,这个过程也叫做"回滚",所以Undo Log也叫回滚日志。
MySQL事务日志-Undo Log工作原理分析
|
1月前
|
存储 监控 安全
什么是事件日志管理系统?事件日志管理系统有哪些用处?
事件日志管理系统是IT安全的重要工具,用于集中收集、分析和解释来自组织IT基础设施各组件的事件日志,如防火墙、路由器、交换机等,帮助提升网络安全、实现主动威胁检测和促进合规性。系统支持多种日志类型,包括Windows事件日志、Syslog日志和应用程序日志,通过实时监测、告警及可视化分析,为企业提供强大的安全保障。然而,实施过程中也面临数据量大、日志管理和分析复杂等挑战。EventLog Analyzer作为一款高效工具,不仅提供实时监测与告警、可视化分析和报告功能,还支持多种合规性报告,帮助企业克服挑战,提升网络安全水平。
|
3月前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1766 14
MySQL事务日志-Redo Log工作原理分析