使用SpringBootAOP记录操作日志、异常日志
本文最后更新于:2025年6月25日 晚上
数据库创建两张表:exception_log、operation_log
exception_log.sql
CREATE TABLE `exception_log` (
`exc_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '主键Id',
`exc_req_param` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '异常请求参数',
`exc_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '异常名称',
`exc_message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '异常信息',
`exc_user_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '操作员Id',
`exc_user_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '操作员名称',
`exc_method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '异常方法',
`exc_uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '异常请求地址',
`exc_ip` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '异常IP',
`exc_version` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '版本号',
`exc_create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`exc_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
operation_log.sql
CREATE TABLE `operation_log` (
`oper_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '主键ID',
`oper_modul` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '功能模块',
`oper_type` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '操作类型',
`oper_desc` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '操作描述',
`oper_req_param` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '请求参数',
`oper_resp_param` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '响应参数',
`oper_user_id` varbinary(64) NULL DEFAULT NULL COMMENT '操作员Id',
`oper_user_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '操作员名称',
`oper_method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '操作方法',
`oper_uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '操作地址',
`oper_ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '操作IP',
`oper_create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`oper_version` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '版本号',
PRIMARY KEY (`oper_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
添加Maven依赖
<!-- SpringAop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
创建操作日志注解类:OperLog.java
import java.lang.annotation.*;
/**
* 操作日志自定义注解
*/
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented
public @interface OperLog {
String operModul() default ""; // 操作模块
String operType() default ""; // 操作类型
String operDesc() default ""; // 操作描述
}
创建切面类记录操作日志:OperLogAspect.java
import com.alibaba.fastjson.JSON;
import com.hh.core.log.entity.ExceptionLog;
import com.hh.core.log.entity.OperationLog;
import com.hh.core.log.service.IExceptionLogService;
import com.hh.core.log.service.IOperationLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @author Smile
* @version 1.0
* @description: 切面处理类,操作日志异常日志记录处理
* @date 2022/3/30 10:54
*/
@Aspect
@Component
@Slf4j
public class OperLogAspect {
@Autowired
private IOperationLogService logService;
@Autowired
private IExceptionLogService exceptionLogService;
private Method method;
private String requestMethod;
private String requestURI;
private String paramterJson;
/**
* 操作版本号
* 项目启动时从命令行传入,例如:java -jar xxx.war --version=201902
*/
@Value("${version}")
private String operVersion;
@Pointcut("@annotation(com.hh.core.log.OperLog)") // 设置操作日志切入点 记录操作日志 在注解的位置切入代码
public void operLogPointCut() {
}
@Pointcut("execution (* com.hh.core.controller..*.*(..))")
public void operExceptionPointCut() {
}
/**
* 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
* @param joinPoint 切入点
* @param keys 返回结果
*/
@AfterReturning(value = "operLogPointCut()", returning = "keys")
public void saveOperLog(JoinPoint joinPoint, Object keys) {
getRequestInfo(joinPoint, keys, null);
OperLog annotation = method.getAnnotation(OperLog.class);
String operModul = annotation.operModul();
String operType = annotation.operType();
String operDesc = annotation.operDesc();
OperationLog operationLog = new OperationLog();
operationLog.setOperId(UUID.randomUUID().toString());
operationLog.setOperModul(operModul);
operationLog.setOperType(operType);
operationLog.setOperDesc(operDesc);
operationLog.setOperUri(requestURI);
operationLog.setOperMethod(requestMethod);
operationLog.setOperReqParam(paramterJson);
operationLog.setOperRespParam(keys.toString());
operationLog.setOperCreateTime(LocalDateTime.now());
operationLog.setOperVersion(operVersion);
logService.save(operationLog);
}
/**
* 异常返回通知,用于拦截异常日志信息 连接点抛出异常后执行
* @param joinPoint 切入点
* @param e 异常信息
*/
@AfterThrowing(value = "operExceptionPointCut()", throwing = "e")
public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
getRequestInfo(joinPoint, null, e);
ExceptionLog exceptionLog = new ExceptionLog();
exceptionLog.setExcId(UUID.randomUUID().toString());
exceptionLog.setExcUri(requestURI);
exceptionLog.setExcReqParam(paramterJson);
exceptionLog.setExcMethod(requestMethod);
exceptionLog.setExcName(e.getClass().getName());
exceptionLog.setExcMessage(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace()));
exceptionLog.setExcVersion(operVersion);
exceptionLog.setExcCreateTime(LocalDateTime.now());
exceptionLogService.save(exceptionLog);
}
private Map<String, String> coverMap(Map<String, String[]> parameterMap) {
Map rtnMap = new HashMap<String, String>();
for (String key : parameterMap.keySet()) {
rtnMap.put(key, parameterMap.get(key)[0]);
}
return rtnMap;
}
/**
* 转换异常信息为字符串
*
* @param exceptionName 异常名称
* @param exceptionMessage 异常信息
* @param elements 堆栈信息
*/
public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
StringBuffer strbuff = new StringBuffer();
for (StackTraceElement stet : elements) {
strbuff.append(stet + "\n");
}
String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
return message;
}
/*
* 获取一些请求和访问的信息,如请求的Uri,参数,请求的方法等
*/
private void getRequestInfo(JoinPoint joinPoint, Object keys, Throwable e) {
// 获取RequestAttributes,从而去获取HttpServletRequest
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest req = (HttpServletRequest) attributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
// 从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
method = signature.getMethod();
requestMethod = joinPoint.getTarget().getClass().getName() + "." + method.getName();
requestURI = req.getRequestURI();
Map<String, String> map = coverMap(req.getParameterMap());
paramterJson = JSON.toJSONString(map);
}
}
在请求的controller方法上添加注解:@OperLog(operModul=”xxx”,operType=”xxx”,operDesc=”xxxx”)
使用SpringBootAOP记录操作日志、异常日志
https://codeofhh.cn/2022/03/31/使用SpringBootAOP记录操作日志、异常日志/