目录
步骤:
- 引入AOP依赖
- 创建操作日志表和实体类
- 创建自定义注解(用于记录哪些方法需要记录日志)
- 创建切面,监听自定义的注解
一.Maven依赖
Swagger也是用来记录日志的,可加可不加
<!-- AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
二.创建日志记录表和实体类
create_time和update_time字段在BaseEntity里面,且为自动填充
package com.dhp.ets.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
/**
* @Description
* @Author dhp
* @create 2025-05-12 10:29
*/
@Data
@TableName(value = "sys_action_log")
public class SysActionLogBean extends BaseEntity{
@TableId
private String id;
/*
操作人
*/
private String userId ;
/*
操作人姓名
*/
private String userName ;
/*
操作类型
*/
private String actionType ;
/*
操作对象
*/
private String actionClass ;
/*
操作方法
*/
private String actionMethod ;
/*
操作参数
*/
private String actionParams ;
/*
操作时间
*/
private Date actionTime ;
/*
操作IP
*/
private String actionIp ;
}
三.创建记录日志自定义注解
该注解的value值用于记录操作类型是什么,比如新增,修改,删除.....
/**
* 用于操作日志记录
*/
@Target(ElementType.METHOD) //该注解用于方法上
@Retention(RetentionPolicy.RUNTIME) //运行期间仍然有效,可以在运行时通过反射获取注解的属性和值
public @interface SysLog {
/**
* 是否允许为空
* @return
*/
String value() default "";
}
四.创建切面类
controller类用Swagger的@Api注解记录该控制层的所有方法是针对什么的,在方法上用@SysLog自定义注解用于记录该方法具体做什么的,该切面类用于监听用了@SysLog注解的方法,获取该方法和类的信息
该切面类没有记录人员信息,具体根据实际项目来写入
package com.dhp.utils.syslog;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.dhp.ets.entity.SysActionLogBean;
import com.dhp.ets.service.SysActionLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.regex.Pattern;
/**
* 系统日志:切面处理类
*/
@Aspect
@Component
public class SysLogAspect {
private static Logger logger = LoggerFactory.getLogger(SysLogAspect.class);
@Autowired
private SysActionLogService sysActionLogService;
//定义切点 @Pointcut
//在注解的位置切入代码
@Pointcut("@annotation(com.dhp.utils.syslog.SysLog)")
public void logPoinCut() {
logger.info("记录操作日志");
}
//切面 配置通知
//@AfterReturning("logPoinCut()")
@Before("logPoinCut()")
public void saveSysLog(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//保存日志
SysActionLogBean sysActionLog = new SysActionLogBean();
//从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//获取切入点所在的方法
Method method = signature.getMethod();
//获取操作
SysLog sysLog = method.getAnnotation(SysLog.class);
if (sysLog != null) {
String value = sysLog.value();
sysActionLog.setActionType(value);//保存获取的操作
}
//获取请求的类名
String className = joinPoint.getTarget().getClass().getName();
String classChineseName = "";
//获取类的注解
Annotation[] annotations = joinPoint.getTarget().getClass().getAnnotations();
for(Annotation annotation :annotations){
if(annotation.annotationType().getName().indexOf("io.swagger.annotations.Api")>=0){
//提取中文字符串
classChineseName = getAnnotationDesc(annotation.toString());
}
}
//获取请求的方法名
String methodName = method.getName();
//处理请求的类名,只保留最后一段 并过滤掉操作日志本身
String classSimpleName ;
String[] classPaths = className.split("\\.");
if(classPaths.length>0){
classSimpleName = classPaths[classPaths.length-1];
}else{
classSimpleName = className;
}
//请求的参数
Object[] args = joinPoint.getArgs();
//将参数所在的数组转换成json
String params = JSON.toJSONString(args);
//处理新增和修改参数,过滤掉不重要的字段
if (methodName.equals("save")) {
JSONArray jsonArray = JSONArray.parseArray(params);
JSONObject jsonObject = JSONObject.parseObject(jsonArray.get(0).toString());
jsonObject.remove("createTime");
jsonObject.remove("updateTime");
if (jsonObject.containsKey("id")) {
methodName = "update";
sysActionLog.setActionType("修改");
}
params = jsonObject.toJSONString();
}
sysActionLog.setActionClass(classSimpleName);
if(classChineseName!=null&&classChineseName.length()>0){
sysActionLog.setActionClass(classChineseName);
}
sysActionLog.setActionMethod(methodName);
sysActionLog.setActionParams(params);
logger.info("操作参数:{}",params);
sysActionLog.setActionTime(new Date());
//获取用户名
// if(ShiroUtils.getUser()!=null) {
// sysActionLog.setUserId(ShiroUtils.getUser().getId());
// sysActionLog.setUserName(ShiroUtils.getUser().getUserName());
// }
sysActionLog.setActionIp(getIpAddress(request));
//调用service保存SysLog实体类到数据库
sysActionLogService.saveOrUpdate(sysActionLog);
}
public final String getIpAddress(HttpServletRequest request) {
String ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
//根据网卡取本机配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
logger.error("获取IP异常", e );
}
if(inet!=null){
ipAddress = inet.getHostAddress();
}
}
}
if (ipAddress != null && ipAddress.length() > 15 && ipAddress.indexOf(',') >= 0) { //"***.***.***.***".length() = 15
ipAddress = ipAddress.substring(0,ipAddress.indexOf(','));
}
return ipAddress;
}
//提取中文字符串
public String getAnnotationDesc(String desc){
String pattern = "[\\u4E00-\\u9FA5]+";
StringBuilder sb = new StringBuilder();
String[] splitStr = desc.split("");
for(String str:splitStr) {
if(Pattern.matches(pattern, str)){
sb = sb.append(str) ;
}
}
return sb.toString();
}
}
五.测试
5.1controller类
@Api注解记录该类的方法是人员管理的方法,@SysLog用于记录保存操作
/**
* @Author dhp
* @create 2025-02-24 15:07
*/
@Api(description = "人员管理")
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@ApiOperation(value = "保存用户")
@PostMapping("save")
@SysLog("保存")
public WebResult save(@RequestBody User user){
return userService.saveUsers(user);
}
}
5.2 postman测试数据
人员表:
操作日志记录表: