基于自定义注解与 AOP 切面实现接口日志全面数据库存储

基于自定义注解与 AOP 切面实现接口日志全面数据库存储

一、引言

在当今复杂的软件系统开发与运维过程中,详细且精准地记录接口的各项信息对于系统性能监测、问题排查、安全审计以及业务分析都有着极为关键的意义。本文将深入讲解如何运用自定义注解与 AOP(面向切面编程)技术,全面地将接口的入参信息、出参信息以及接口执行用时记录到数据库中,从而构建一套完善的接口日志管理体系。

二、技术选型与环境搭建

本项目基于 Java 语言开发,采用 Spring Boot 框架搭建应用的基础架构,这有助于快速整合各类组件并实现便捷的开发部署。在数据库层面,选用 MySQL 作为数据存储的后端,以其稳定性和广泛的应用支持来保障日志数据的可靠存储。数据持久化操作借助 MyBatis 框架实现,它能够高效地处理 Java 对象与数据库表之间的映射关系。日志记录方面,依旧以 SLF4J 作为抽象层,结合 Logback 进行灵活的日志打印配置。

三、数据库设计优化

重新设计用于存储接口日志的数据库表,以更好地适应记录入参、出参和执行用时的需求:

CREATE TABLE interface_log (id INT AUTO_INCREMENT PRIMARY KEY,interface_path VARCHAR(255) NOT NULL,invocation_time TIMESTAMP NOT NULL,user_info VARCHAR(255),input_parameters VARCHAR(255),output_parameters VARCHAR(255),execution_time BIGINT,description VARCHAR(255)
);

新增的 execution_time 字段用于存储接口执行所耗费的时间,单位为毫秒,以便后续对接口性能进行分析评估。

四、自定义注解设计

定义 LoggableFullDB 自定义注解,用于明确需要进行全面日志记录到数据库的接口方法:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoggableFullDB {String value() default "";
}

五、AOP 切面实现升级

构建更为完善的 AOP 切面类 LoggingAspectFullDB,负责精确地拦截被 LoggableFullDB 注解标记的方法,并全面地记录接口相关信息到数据库:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.util.Arrays;
import java.util.Date;@Aspect
@Component
public class LoggingAspectFullDB {private static final Logger logger = LoggerFactory.getLogger(LoggingAspectFullDB.class);// 在方法执行前记录相关信息并准备插入数据库的数据@Before("@annotation(loggableFullDB)")public void beforeMethodExecution(JoinPoint joinPoint, LoggableFullDB loggableFullDB) {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getServletRequest();InterfaceLogDTO logDTO = new InterfaceLogDTO();// 记录接口路径地址logDTO.setInterfacePath(request.getRequestURI());// 记录调用时间logDTO.setInvocationTime(new Date());// 尝试获取用户信息(假设用户信息存储在请求头中的某个字段,可根据实际情况修改)logDTO.setUserInfo(request.getHeader("user"));// 记录方法参数logDTO.setInputParameters(Arrays.toString(joinPoint.getArgs()));logDTO.setDescription(loggableFullDB.value());// 记录方法开始执行时间,用于后续计算执行用时logDTO.setStartTime(System.currentTimeMillis());// 此处可将 logDTO 传递给数据库操作层进行插入操作的准备,例如调用 service 层方法logService.prepareLogInsert(logDTO);}// 在方法执行成功返回后记录出参信息并完成数据库插入操作@AfterReturning(pointcut = "@annotation(loggableFullDB)", returning = "result")public void afterMethodExecution(JoinPoint joinPoint, LoggableFullDB loggableFullDB, Object result) {InterfaceLogDTO logDTO = logService.getLogDTOForUpdate(); // 获取之前准备的 logDTO// 记录返回结果logDTO.setOutputParameters(result.toString());// 计算接口执行用时并存储long endTime = System.currentTimeMillis();logDTO.setExecutionTime(endTime - logDTO.getStartTime());// 执行数据库插入操作logService.insertLog(logDTO);}
}

其中 InterfaceLogDTO 数据传输对象也相应地更新:

public class InterfaceLogDTO {private String interfacePath;private Date invocationTime;private String userInfo;private String inputParameters;private String outputParameters;private Long executionTime;private Long startTime;private String description;// 省略 getter 和 setter 方法
}

六、数据库操作层实现优化

优化 LogService 接口及其实现类 LogServiceImpl,确保与新的日志记录需求和数据库表结构相适配:

public interface LogService {void prepareLogInsert(InterfaceLogDTO logDTO);InterfaceLogDTO getLogDTOForUpdate();void insertLog(InterfaceLogDTO logDTO);
}
import org.springframework.stereotype.Service;@Service
public class LogServiceImpl implements LogService {private ThreadLocal<InterfaceLogDTO> logDTOLocal = new ThreadLocal<>();@Overridepublic void prepareLogInsert(InterfaceLogDTO logDTO) {logDTOLocal.set(logDTO);}@Overridepublic InterfaceLogDTO getLogDTOForUpdate() {return logDTOLocal.get();}@Overridepublic void insertLog(InterfaceLogDTO logDTO) {// 使用 MyBatis 或其他数据库操作工具将 logDTO 中的数据插入到数据库表中// 这里省略具体的数据库插入代码,例如:sqlSession.insert("insertInterfaceLog", logDTO);logDTOLocal.remove();}
}

七、在接口中应用自定义注解

在具体的接口方法上应用 LoggableFullDB 注解,如下示例:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class OrderController {@LoggableFullDB("获取订单详情接口")@GetMapping("/order/details")public Order getOrderDetails(String orderId) {// 模拟获取订单详情的业务逻辑Order order = new Order();order.setId(orderId);order.setCustomerName("John Doe");order.setTotalAmount(100.0);return order;}
}

八、日志打印配置

application.propertiesapplication.yml 文件中配置 Logback 进行日志打印输出:

# Logback 配置
logging:level:root: INFOpattern:console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"

九、总结

通过精心设计的自定义注解、强大的 AOP 切面技术以及完善的数据库操作层构建,我们成功实现了接口日志的全面数据库存储方案。该方案能够精确地记录接口的入参信息、出参信息以及执行用时,并将这些关键数据可靠地存储到数据库中。这不仅为系统的运维监控提供了丰富且准确的数据依据,也为后续的业务分析和性能优化奠定了坚实的基础。在实际项目应用中,可依据具体业务场景和需求进一步对数据库表结构、日志记录策略以及数据处理流程进行深度优化与扩展,以实现更为高效和智能的接口日志管理系统。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/64899.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch相关知识@1

目录标题 Lucene1. **什么是 Lucene?**2. **Lucene 在 Elasticsearch 中的作用**3. **Lucene 的核心功能**(1) **倒排索引**(2) **分词**(3) **查询解析**(4) **相关性评分** 4. **为什么 Elasticsearch 使用 Lucene?**5. **Lucene 和 Elasticsearch 的区别**6. **总结** 分片…

Cesium材质——Material

简介&#xff1a; Cesium.Material对象的目的&#xff0c;就是生成一段名称为czm_getMaterial的函数&#xff08;示例代码如下&#xff09;&#xff0c; 这个czm_getMaterial函数&#xff0c;是shader代码&#xff0c;会被放到片元着色器中使用。 czm_material czm_getMater…

UE5 渲染管线 学习笔记

兰伯特 SSS为散射的意思 带Bias的可以根据距离自动切换mip的卷积值 而带Level的值mipmaps的定值 #define A8_SAMPLE_MASK .a 这样应该就很好理解了 这个只采样a通道 带Level的参考上面的 朝左上和右下进行模糊 带Bias参考上面

1114 Family Property (25)

This time, you are supposed to help us collect the data for family-owned property. Given each persons family members, and the estate&#xff08;房产&#xff09;info under his/her own name, we need to know the size of each family, and the average area and n…

canvas绘制仪表盘刻度盘

canvas画布可以实现在网页上绘制图形的方法&#xff0c;比如图表、图片处理、动画、游戏等。今天我们在vue模板下用canvas实现仪表盘的绘制。 对canvas不熟悉的同学可以先了解下canvas的API文档&#xff1a;canvas API中文网 - Canvas API中文文档首页地图 一、创建模板&#…

Docker 环境配置

官网 https://www.docker.com (opens new window)- Mac、Windows、Linux。 Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中&#xff0c;然后发布到任何流行的 Mac、Linux或Windows操作系统的机器上&#xff0c;也可以实…

Spring Boot 中实现自定义注解记录接口日志功能

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的全栈工程师 欢迎分享 / 收藏 / 赞 / 在看…

【超详细实操内容】django的身份验证系统之限制用户访问的三种方式

目录 1、使用request.user.is_authenticated属性 2、装饰器login_required 3、LoginRequiredMixin类 通常情况下,网站都会对用户限制访问,例如,未登录的用户不可访问用户中心页面。Django框架中使用request.user.isauthenticated属性、装饰器loginrequired和LoginRequire…

scss配置全局变量报错[sass] Can‘t find stylesheet to import.

路径没有错误&#xff0c;使用别名即可 后又提示Deprecation Warning: Sass import rules are deprecated and will be removed in Dart Sass 3.0.0. 将import改为use 使用时在$前添加全局变量所在文件&#xff0c;即variable.

【Java 基础】-- TreeMap 和HashMap 的内存开销情况

在相同数据量的情况下&#xff0c;Java TreeMap 和 HashMap 的内存开销有所不同&#xff0c;这是由于它们底层数据结构的差异导致的。以下是两者内存开销的分析与比较&#xff1a; 1. 底层数据结构差异 1.1 HashMap HashMap 是基于哈希表实现的。每个键值对存储在一个桶中&am…

微信小程序实现二维码海报保存分享功能

首先在写这个二维码分享海报的时候试过很多方法&#xff0c;比如&#xff1a;canvas中的这个createCanvasContext创建上下文的方法&#xff0c;去网上一搜就是一大堆&#xff0c;但其实这个方法已经被废弃了。Canvas 实例&#xff0c;可通过 SelectorQuery 获取。这是绘制背景图…

基于Qlearning强化学习的机器人路线规划matlab仿真

目录 1.算法仿真效果 2.算法涉及理论知识概要 3.MATLAB核心程序 4.完整算法代码文件获得 1.算法仿真效果 matlab2022a仿真结果如下&#xff08;完整代码运行后无水印&#xff09;&#xff1a; 训练过程 测试结果 仿真操作步骤可参考程序配套的操作视频。 2.算法涉及理论…

9 RCC使用HSE、HSI配置时钟

一、时钟树 RCC&#xff1a;reset clock control,复位和时钟控制器。HSE是外部的高速时钟信号&#xff0c;可以由有源晶振或者无源晶振提供。如果使用HSE或者HSE经过PLL倍频之后的时钟作为系统时钟SYSCLK&#xff0c;当HSE故障时候&#xff0c;不仅HSE会被关闭&#xff0c;PLL…

认识数据结构之——排序

一、 插入排序&#xff1a; 直接插入排序(以排升序为例)&#xff1a; 排序思想&#xff1a; 单趟&#xff1a;记录某个位置的值&#xff0c;一个一个和前面的值比较&#xff0c;碰到更大的就往后覆盖&#xff0c;碰到更小的或者相等的就结束&#xff0c;最后将记录的值插入到…

uniapp 微信小程序 功能入口

单行单独展示 效果图 html <view class"shopchoose flex jsb ac" click"routerTo(要跳转的页面)"><view class"flex ac"><image src"/static/dyd.png" mode"aspectFit" class"shopchooseimg"&g…

【集合】Java 8 - Stream API 17种常用操作与案例详解

文章目录 Java8 Stream API 17种常用操作与案例详解1. collect()&#xff1a;将流中的元素收集到集合中2. filter()&#xff1a;根据条件过滤流中的元素3. map()&#xff1a;元素映射为另一个值4. forEach()&#xff1a;对流中的元素执行操作5. flatMap()&#xff1a;将流中的元…

加入SFTP 用户------(小白篇 2)

以下是为 SFTP 添加一个新用户&#xff08;例如 newuser&#xff09;的完整步骤&#xff1a; 1.服务器上加入SFTP------(小白篇 1) 2.加入SFTP 用户------(小白篇 2) 3.代码加入SFTP JAVA —&#xff08;小白篇3&#xff09; 4.代码加入SFTP Python —&#xff08;小白篇4…

苍穹外卖-day05redis 缓存的学习

苍穹外卖-day05 课程内容 Redis入门Redis数据类型Redis常用命令在Java中操作Redis店铺营业状态设置 学习目标 了解Redis的作用和安装过程 掌握Redis常用的数据类型 掌握Redis常用命令的使用 能够使用Spring Data Redis相关API操作Redis 能够开发店铺营业状态功能代码 功能实…

Linux之系统管理

一、相关命令 筛选 grep&#xff0c;可以用来进行筛选&#xff0c;例如对目录筛选课写成 # 过滤出带serv的 ls /usr/sbin | grep serv2. 对服务的操作 2.1 centos6版本 service 服务名 start|stop|restart|status # start&#xff1a;开启 # stop&#xff1a;停止 # restart…

什么?Flutter 可能会被 SwiftUI/ArkUI 化?全新的 Flutter Roadmap

在刚刚过去的 FlutterInProduction 活动里&#xff0c;Flutter 官方除了介绍「历史进程」和「用户案例」之外&#xff0c;也着重提及了未来相关的 roadmap &#xff0c;其中就有 3.27 里的 Swift Package Manager 、 Widget 实时预览 和 Dart 与 native 平台原生语言直接互操作…