记录用户业务请求日志

news/2025/9/24 10:27:54/文章来源:https://www.cnblogs.com/kohler21/p/19108707

在用户的一般使用的时候,对于很多操作类型的接口,为了后面便于追查问题,需要记录用户的请求日志。

用户的请求日志目前主流的存储方式有:

  1. 日志文件
  2. 数据库
  3. MongoDB
  4. ElasticSearch

在商城的项目中暂时存放在MySQL中了。

增加注解

增加专门的注解标识哪些是需要记录用户日志的。

注解就叫BizLog注解:

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

其中的value参数,用来接收用户自定义的日志描述。

后面只要有接口打上了这个注解,就会自动将用户的业务请求日志记录到数据库中。

增加业务日志表

为了方便后续追溯用户的请求行为,将用户的业务请求日志记录到数据库的某一张表中。

这样以后就可以通过这张表查询数据了。

CREATE TABLE `biz_log` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',`method_name` varchar(30) NOT NULL COMMENT '方法名称',`description` varchar(30) NOT NULL COMMENT '描述',`request_ip` varchar(15) NOT NULL COMMENT '请求ip',`browser` varchar(200)  NULL COMMENT '浏览器',`url` varchar(100) NOT NULL COMMENT '请求地址',`param` varchar(300)  NULL COMMENT '请求参数',`time` int NOT NULL COMMENT '耗时,毫秒级',`exception` varchar(300)  NULL COMMENT '异常',`status` tinyint(1) NOT NULL DEFAULT 1 COMMENT '状态 1:成功 0:失败',`create_user_id` bigint NOT NULL COMMENT '创建人ID',`create_user_name` varchar(30) NOT NULL COMMENT '创建人名称',`create_time` datetime(3) DEFAULT NULL COMMENT '创建日期',`update_user_id` bigint DEFAULT NULL COMMENT '修改人ID',`update_user_name` varchar(30)  DEFAULT NULL COMMENT '修改人名称',`update_time` datetime(3) DEFAULT NULL COMMENT '修改时间',`is_del` tinyint(1) DEFAULT '0' COMMENT '是否删除 1:已删除 0:未删除',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='业务日志表';

增加业务日志拦截器

package com.kailong.interceptor;import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.kailong.annotation.BizLog;
import com.kailong.entity.log.BizLogEntity;
import com.kailong.service.log.BizLogService;
import com.kailong.util.IpUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
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.lang.reflect.Method;@Aspect
@Component
public class BizLogAspect {@Autowiredprivate BizLogService bizLogService;@Pointcut("@annotation(com.kailong.annotation.BizLog)")public void pointcut() {}@Around("pointcut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();Object result = joinPoint.proceed();long time = System.currentTimeMillis() - startTime;BizLogEntity bizLogEntity = createBizLogEntity(joinPoint, httpServletRequest);bizLogEntity.setTime((int) time);bizLogEntity.setStatus(1);bizLogService.save(bizLogEntity);return result;}private String getParam(JoinPoint joinPoint) {StringBuilder params = new StringBuilder("{");Object[] argValues = joinPoint.getArgs();String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();if (argValues != null) {for (int i = 0; i < argValues.length; i++) {params.append(" ").append(argNames[i]).append(": ").append(argValues[i]);}}return params.append("}").toString();}private BizLogEntity createBizLogEntity(JoinPoint joinPoint, HttpServletRequest httpServletRequest) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();BizLog bizLog = method.getAnnotation(BizLog.class);String methodName = joinPoint.getTarget().getClass().getName() + "." + signature.getName();BizLogEntity bizLogEntity = new BizLogEntity();bizLogEntity.setDescription(bizLog.value());bizLogEntity.setMethodName(methodName);bizLogEntity.setStatus(1);bizLogEntity.setRequestIp(IpUtil.getIpAddr(httpServletRequest));bizLogEntity.setUrl(httpServletRequest.getRequestURI());bizLogEntity.setBrowser(getBrowserName(httpServletRequest));bizLogEntity.setParam(getParam(joinPoint));return bizLogEntity;}@AfterThrowing(pointcut = "pointcut()", throwing = "e")public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();BizLogEntity bizLogEntity = createBizLogEntity(joinPoint, httpServletRequest);bizLogEntity.setStatus(0);bizLogEntity.setException(e.getMessage());bizLogService.save(bizLogEntity);}private String getBrowserName(HttpServletRequest httpServletRequest) {String userAgentString = httpServletRequest.getHeader("User-Agent");UserAgent ua = UserAgentUtil.parse(userAgentString);return ua.getBrowser().toString();}
}

这个拦截器会记录用户业务请求的ip、地址、参数、浏览器和接口耗时都数据。

如果用户业务请求失败了,也会记录一条失败的数据。

BizLog相关类

(基础设置,实际项目需修改)

BizLogEntity

package com.kailong.entity.log;/*** created by kailong on 2025/9/23*/import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.kailong.entity.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;
import java.util.Date;/*** 业务日志实体类* 对应表:biz_log*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("biz_log") // 指定MyBatis-Plus对应的表名
@Schema(description = "业务日志实体类,用于记录系统业务操作日志") // Swagger3类级别描述
public class BizLogEntity extends BaseEntity implements Serializable {//可以不继承BaseEntityprivate static final long serialVersionUID = 1L;/*** 日志ID(主键,自增)*/@TableId(value = "id", type = IdType.AUTO) // MyBatis-Plus主键注解,指定自增策略@Schema(description = "日志ID", example = "1") // Swagger3字段描述+示例值private Long id;/*** 方法名称(记录调用的业务方法名)*/@Schema(description = "方法名称", example = "getUserInfo")private String methodName;/*** 操作描述(记录业务操作的简要说明)*/@Schema(description = "操作描述", example = "查询用户信息")private String description;/*** 请求IP(记录发起请求的客户端IP地址)*/@Schema(description = "请求IP", example = "192.168.1.100")private String requestIp;/*** 浏览器类型(记录发起请求的浏览器信息,如Chrome、Firefox)*/@Schema(description = "浏览器类型", example = "Chrome 120.0.0.0")private String browser;/*** 请求地址(记录请求的URL路径,如/api/user/info)*/@Schema(description = "请求地址", example = "/api/user/info")private String url;/*** 请求参数(记录请求的参数信息,如{"userId":1})*/@Schema(description = "请求参数", example = "{\"userId\":1}")private String param;/*** 耗时(记录业务方法执行的耗时,单位:毫秒)*/@Schema(description = "耗时(毫秒)", example = "50")private Integer time;/*** 异常信息(记录业务方法执行过程中抛出的异常信息,无异常则为空)*/@Schema(description = "异常信息", example = "java.lang.NullPointerException: 用户不存在")private String exception;/*** 状态(1:成功 0:失败,记录业务操作的执行结果)*/@Schema(description = "状态(1:成功 0:失败)", example = "1")private int status;/*** 创建人ID(记录创建该日志的用户ID)*/@Schema(description = "创建人ID", example = "1001")private Long createUserId;/*** 创建人名称(记录创建该日志的用户名)*/@Schema(description = "创建人名称", example = "admin")private String createUserName;/*** 创建时间(记录日志的创建时间,默认为当前时间)*/@Schema(description = "创建时间", example = "2025-09-23 17:13:18")private Date createTime;/*** 修改人ID(记录最后修改该日志的用户ID,无修改则为空)*/@Schema(description = "修改人ID", example = "1002")private Long updateUserId;/*** 修改人名称(记录最后修改该日志的用户名,无修改则为空)*/@Schema(description = "修改人名称", example = "operator")private String updateUserName;/*** 修改时间(记录最后修改该日志的时间,无修改则为空)*/@Schema(description = "修改时间", example = "2025-09-23 17:15:30")private Date updateTime;/*** 是否删除(1:已删除 0:未删除,逻辑删除标记)*/@Schema(description = "是否删除(1:已删除 0:未删除)", example = "0")private Integer isDel;
}

BizLogMapper(用的MyBatisplus)

@Mapper
public interface BizLogMapper extends BaseMapper<BizLogEntity> {
}

BizLogService

public interface BizLogService {public void save(BizLogEntity bizLogEntity);
}

BizLogServiceImpl

@Service
public class BizLogServiceImpl implements BizLogService {@Autowiredprivate BizLogMapper bizLogMapper;@Overridepublic void save(BizLogEntity bizLogEntity) {FillUserUtil.fillCreateUserInfo(bizLogEntity);bizLogMapper.insert(bizLogEntity);}
}

测试效果

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

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

相关文章

[C++:类的默认成员函数——Lesson7.const成员函数] - 指南

[C++:类的默认成员函数——Lesson7.const成员函数] - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consola…

55.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--新增功能--实现手机邮箱登录 - 实践

55.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--新增功能--实现手机邮箱登录 - 实践2025-09-24 10:26 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !im…

游戏网站的设计方案新农村建设管理网站

Problem - C - Codeforces 题目分析 <1>0&#xff1a;想被分割至左边&#xff1b; 1&#xff1a;想被分割至右边 <2>使得左右两侧均有一半及其以上的人满意&#xff08;我*******&#xff09; <3>答案若有多个&#xff0c;取最接近中间位置的答案 <4…

怎么选择网站建设干部信息管理系统

1 、请用 Python 手写实现插入排序。 解析&#xff1a; 插入排序&#xff08; Insertion Sort &#xff09;的工作原理是通过构建有序序列&#xff0c;对于未排序数据&#xff0c; 在已排序序列中从后向前扫描&#xff0c;找到相应位置并插入。 算法执行步骤&#xff1a; &…

详细介绍:Xilinx系列FPGA实现12G-SDI音视频编解码,支持4K60帧分辨率,提供2套工程源码和技术支持

详细介绍:Xilinx系列FPGA实现12G-SDI音视频编解码,支持4K60帧分辨率,提供2套工程源码和技术支持pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block …

CentOS6.8安装docker教程

在VMware新安装CentOS6.8系统CentOS6.8可在阿里镜像库下载: https://mirrors.aliyun.com/centos-vault/6.8/isos/x86_64/ 在新安装系统配置yum源2.1 进入目录 /etc/yum.repos.d2.2 对原配置备份mv *.repo ./bak2.3 下…

使用 VMware Workstation 安装 CentOS-7 虚拟机

使用 VMware Workstation 安装 CentOS-7 虚拟机1. 环境说明和软件准备 环境说明:宿主机操作系统:Window 10 宿主机 CPU 架构:x86_64 虚拟机软件:VMware Workstation Pro 15 虚拟机系统:CentOS-7.6软件下载:CentO…

K12教育 和 STEAM教育

K12教育定义:K12是“Kindergarten through twelfth grade”的缩写,指从幼儿园(Kindergarten,通常5-6岁)到十二年级(Grade 12,通常17-18岁)的教育阶段。它涵盖了学前教育、小学教育、初中教育和高中教育,是国际…

网站底部代码大全建设网站一定要电脑吗

binary 和 varbinary固定长度 (binary) 的或可变长度 (varbinary) 的 binary 数据类型。binary [ ( n ) ]固定长度的 n 个字节二进制数据。N 必须从 1 到 8,000。存储空间大小为 n4 字节。varbinary [ ( n ) ]n 个字节变长二进制数据。n 必须从 1 到 8,000。存储空间大小为实际…

uv Python安装镜像加速

uv Python安装镜像加速感谢南京大学开源镜像站!国内目前能找到的唯一镜像! Windows系统cmd设置环境变量命令如下: setx UV_PYTHON_INSTALL_MIRROR "https://mirror.nju.edu.cn/github-release/astral-sh/pytho…

AT_arc167_c [ARC167C] MST on Line++

首先遇到这种题先不要慌,先拆贡献。 考察一个权值为 \(a_i\) 的边会被 MST 包含多少次,因为我们确定了 \(p\),所以 \(a\) 的顺序就没有关系了,我们先将 \(a\) 排序,钦定某一种边权出现次数很难做,但是我们如果钦…

CentOS操作系统

CentOS操作系统CentOS操作系统更新时间:2025-07-31 09:59:20产品详情我的收藏 本文详细介绍CentOS所处的生命周期阶段,以及可以采取哪些应对方案来应对CentOS停止维护后的风险。CentOS生命周期概述 CentOS Linux 是一…

龙虎榜——20250912 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Lombok无法使用get set方法

问题描述:使用lombok@Data注解后,无法调用实体类的getter setter方法。 解决方案:安装lombok插件(记得安装后应用)

网站怎么做直播小制作简单手工

基本思想&#xff1a;需要使用linux系统调用alliedvisio工业相机完成业务&#xff0c;这里只做驱动相机调用&#xff0c;具体不涉及业务开发 Alvium 相机选型 - Allied Vision 一、先用软件调用一下用于机器视觉和嵌入式视觉的Vimba X 软件开发包 - Allied Vision VimbaX_Set…

网站与网页移动商城官网 积分兑换

论文笔记整理&#xff1a;谭亦鸣&#xff0c;东南大学博士生。来源&#xff1a;WWW 2020链接&#xff1a;https://dl.acm.org/doi/pdf/10.1145/3366423.3380114概述这篇论文关注的任务是&#xff1a;基于给定文本的“多跳问题生成”&#xff08;多关系问题&#xff09;。作者提…

redis的哈希扩容

Redis 哈希的扩容过程是其高效性的关键所在,它采用了一种非常巧妙的渐进式 rehash 策略来避免一次性扩容带来的服务停顿。 步骤 1:准备工作 当满足扩容条件时,为 ht[1] 分配空间。新的大小根据上述规则计算。 将字典…

vite tailwindcss配置

1. 安装tailwindcss依赖yarn add tailwindcss @tailwindcss/vite2.新建css文件引入tailwindcss//assets/styles/tailwind.css@import tailwindcss;3.main.js引入css文件import ./assets/styles/tailwind.css4.配置vite…

window系统下使用二进制包安装MySQL数据库

window系统使用二进制包安装MySQL数据库以下仅为本人工作、学习过程中所接触到的内容,不足之处欢迎指出。 安装说明 1、安装数据库的window系统为win7专业版64位2、MySQL版本为mysql-5.7.17-winx64 下载解压 下载地址…

在Vona ORM中实现多数据库/多数据源

在Vona ORM中实现多数据库/多数据源非常直观、简便。下面以 Model User/Order 为例,通过查询用户的订单列表,来演示多数据库/多数据源的使用方法在Vona ORM中实现多数据库/多数据源非常直观、简便。下面以 Model Use…