目录
1.使用注解
/**
* 切面日志
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AuditLog {
/**
* 日志的名称:例如:操作劵
* @return
*/
String name();
/**
* Ognl表达式
* 名称下面的标识:比如用户id,方便搜索
* 使用方式:方法(String id,String name,UserDO userDo);
* 获取id:idOgnl=id,获取name:idOgnl=name
* 获取userDo的属性值 idOgnl=userDo.id
* @return
*/
String idOgnl() default "";
/**
* 类型:新增,修改
* @return
*/
String type() default "未知";
}
2.AOP实现
package com.dehua.aop;
import com.alibaba.fastjson.JSON;
import com.dehua.entity.AuditLogDO;
import com.dehua.util.CommonJoinPointUtil;
import lombok.extern.slf4j.Slf4j;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Map;
@Slf4j
@Aspect
@Order
@Component
public class AuditLogAop {
// @Resource
// private AuditLogService auditLogService;
/**
* 日志切入点
*/
@Pointcut("@annotation(com.dehua.aop.AuditLog)")
private void getLogPointCut() {
}
/**
* 操作成功返回结果记录日志
*
* @param joinPoint
* @param result
*/
@AfterReturning(pointcut = "getLogPointCut()", returning = "result")
public void doAfterReturning(JoinPoint joinPoint, Object result) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
AuditLog commonLog = method.getAnnotation(AuditLog.class);
String auditName = commonLog.name();
String idOgnl = commonLog.idOgnl();
String auditType = commonLog.type();
// HttpServletRequest request = HttpServletUtil.getRequest();
String requestURI = "test/user/add";//request.getRequestURI();
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Map<String, Object> paramMap = CommonJoinPointUtil.getMethodArgsAsMap(joinPoint);
String resultJson = JSON.toJSONString(result);
String traceId = "traceId";//TraceIdUtils.getTraceId();
AuditLogDO auditLoDO = new AuditLogDO();
auditLoDO.setAuditName(auditName);
auditLoDO.setAuditNo(getAuditNo(idOgnl, paramMap));
auditLoDO.setAuditType(auditType);
auditLoDO.setReqUrl(requestURI);
auditLoDO.setClassName(className);
auditLoDO.setMethodName(methodName);
auditLoDO.setParamJson(JSON.toJSONString(paramMap));
auditLoDO.setResultJson(resultJson);
auditLoDO.setTraceId(traceId);
auditLoDO.setCreateDate(LocalDateTime.now());
auditLoDO.setCreateUser("");
log.info("---auditLoDO:{}---",JSON.toJSONString(auditLoDO));
// auditLogService.save(auditLoDO); 这里直接写库,可以优化成mq异步,日志量大可以按照时间分表
}
private String getAuditNo(String idOgnl, Map<String, Object> paramMap) {
OgnlContext context = new OgnlContext();
context.setRoot(paramMap);
Object root = context.getRoot();
try {
return String.valueOf(Ognl.getValue(idOgnl, context, root));
} catch (OgnlException e) {
log.error("Ognl key:{} error:", idOgnl, e);
}
return idOgnl;
}
}
3.控制层
可以在接口方法上使用,也可以在需要记录日志的方法上使用,
package com.dehua.controller;
import com.dehua.aop.AuditLog;
import com.dehua.entity.UserDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("test/user/")
public class UserController {
@AuditLog(name = "操作用户", idOgnl = "userDO.name", type = "add")
@PostMapping("add")
public String add(UserDO userDO) {
log.info("添加用户");
return "ok";
}
@AuditLog(name = "操作用户", idOgnl = "userDO.id", type = "update")
@PostMapping("update")
public String update(UserDO userDO) {
log.info("修改用户");
return "ok";
}
}
4.实体类
package com.dehua.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class AuditLogDO {
private Long id;
private String auditName;
private String auditNo;
private String auditType;
private String reqUrl;
private String className;
private String methodName;
private String paramJson;
private String resultJson;
private String traceId;
private LocalDateTime createDate;
private String createUser;
}
package com.dehua.entity;
import lombok.Data;
@Data
public class UserDO {
private Long id;
private String name;
private Integer age;
}
5.工具类
package com.dehua.util;
import com.alibaba.fastjson.JSON;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
public class CommonJoinPointUtil {
private CommonJoinPointUtil() {
}
/**
* 获取切面的参数JSON
*
* @param joinPoint
* @return
*/
public static String getMethodArgsAsJson(JoinPoint joinPoint) {
Map<String, Object> map = getMethodArgsAsMap(joinPoint);
return JSON.toJSONString(map);
}
/**
* 获取切面的参数Map
* @param joinPoint
* @return
*/
public static Map<String, Object> getMethodArgsAsMap(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
// 参数名数组
String[] parameterNames = ((MethodSignature) signature).getParameterNames();
// 构造参数组集合
Map<String, Object> map = new HashMap<>();
Object[] args = joinPoint.getArgs();
for (int i = 0; i < parameterNames.length; i++) {
if (isUsefulParam(args[i])) {
// 将参数转换为 JSON
// map.put(parameterNames[i], objectMapper.writeValueAsString(args[i]));
map.put(parameterNames[i], args[i]);
}
}
return map;
}
/**
* 判断是否需要拼接的参数,过滤掉HttpServletRequest,MultipartFile,HttpServletResponse等类型参数
*
* @param arg
* @return
*/
private static boolean isUsefulParam(Object arg) {
return !(arg instanceof MultipartFile) &&
!(arg instanceof HttpServletRequest) &&
!(arg instanceof HttpServletResponse);
}
}
6.依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.6.11</version>
</dependency>
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.0.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.6.11</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>