敛饺乖 发表于 2025-9-24 11:03:49

记录用户业务请求日志

在用户的一般使用的时候,对于很多操作类型的接口,为了后面便于追查问题,需要记录用户的请求日志。
用户的请求日志目前主流的存储方式有:

[*]日志文件
[*]数据库
[*]MongoDB
[*]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 {

    @Autowired
    private 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).append(": ").append(argValues);
            }
      }
      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 {//可以不继承BaseEntity

    private 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 {
    @Autowired
    private BizLogMapper bizLogMapper;
    @Override
    public void save(BizLogEntity bizLogEntity) {
      FillUserUtil.fillCreateUserInfo(bizLogEntity);
      bizLogMapper.insert(bizLogEntity);
    }
}测试效果


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

坡琨 发表于 2025-10-14 07:11:18

谢谢楼主提供!

钨哄魁 发表于 2025-10-20 12:48:00

过来提前占个楼

博咱 发表于 2025-11-24 08:00:54

这个有用。

轩辕娅童 发表于 前天 11:23

分享、互助 让互联网精神温暖你我
页: [1]
查看完整版本: 记录用户业务请求日志