@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Log { // 自定义操作日志记录注解 public String title() ; // 模块名称 public OperatorType operatorType() default OperatorType.MANAGE; // 操作人类别 public int businessType() ; // 业务类型(0其它 1新增 2修改 3删除) public boolean isSaveRequestData() default true; // 是否保存请求的参数 public boolean isSaveResponseData() default true; // 是否保存响应的参数 }
1.自定义@Log注解
这个注解的作用是通过在方法上添加@Log注解,自动记录操作日志。通过设置不同的属性值,可以定制日志的内容,包括操作模块、操作人类别、业务类型以及是否保存请求和响应参数等信息。
2.OperatorType
public enum OperatorType { // 操作人类别 OTHER, // 其他 MANAGE, // 后台用户 MOBILE // 手机端用户 }
3.@EnableLogAspect
定义一个注解,用于启用日志切面功能。
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Import(value = LogAspect.class) // 通过Import注解导入日志切面类到Spring容器中 public @interface EnableLogAspect { }
在启动类中加入注解
@RefreshScope
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients("com.huiye")
@EnableMongoRepositories("com.huiye")
@ComponentScan({"com.huiye"})
@MapperScan("com.huiye.core.mapper")
@EnableAspectJAutoProxy
@EnableScheduling
public class ManagerApplication {
public static void main(String[] args) {
SpringApplication.run(ManagerApplication.class , args);
}
}
4.定义切面类
@Aspect @Component @Slf4j public class LogAspect { // 环绕通知切面类定义 @Autowired private AsyncOperLogService asyncOperLogService ; @Around(value = "@annotation(sysLog)") public Object doAroundAdvice(ProceedingJoinPoint joinPoint , Log sysLog) { // 构建前置参数 SysOperLog sysOperLog = new SysOperLog() ; LogUtil.beforeHandleLog(sysLog , joinPoint , sysOperLog) ; Object proceed = null; try { proceed = joinPoint.proceed(); // 执行业务方法 LogUtil.afterHandleLog(sysLog , proceed , sysOperLog , 0 , null) ; // 构建响应结果参数 } catch (Throwable e) { // 代码执行进入到catch中, // 业务方法执行产生异常 e.printStackTrace(); // 打印异常信息 LogUtil.afterHandleLog(sysLog , proceed , sysOperLog , 1 , e.getMessage()) ; throw new RuntimeException(); } // 保存日志数据 asyncOperLogService.save(sysOperLog); // 返回执行结果 return proceed ; } }
5.工具类
public class LogUtil {
//操作执行之后调用
public static void afterHandleLog(Log sysLog, Object proceed,
SysOperLog sysOperLog, int status ,
String errorMsg) {
if(sysLog.isSaveResponseData()) {
sysOperLog.setJsonResult(JSON.toJSONString(proceed));
}
sysOperLog.setStatus(status);
sysOperLog.setErrorMsg(errorMsg);
}
//操作执行之前调用
public static void beforeHandleLog(Log sysLog,
ProceedingJoinPoint joinPoint,
SysOperLog sysOperLog) {
// 设置操作模块名称
sysOperLog.setTitle(sysLog.title());
sysOperLog.setOperatorType(sysLog.operatorType().name());
// 获取目标方法信息
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature() ;
Method method = methodSignature.getMethod();
sysOperLog.setMethod(method.getDeclaringClass().getName());
// 获取请求相关参数
ServletRequestAttributes requestAttributes = (ServletRequestAttributes)
RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
sysOperLog.setRequestMethod(request.getMethod());
sysOperLog.setOperUrl(request.getRequestURI());
sysOperLog.setOperIp(request.getRemoteAddr());
// 设置请求参数
if(sysLog.isSaveRequestData()) {
String requestMethod = sysOperLog.getRequestMethod();
if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
String params = Arrays.toString(joinPoint.getArgs());
sysOperLog.setOperParam(params);
}
}
sysOperLog.setOperName(AuthContextUtil.get().getUserName());
}
}
afterHandleLog:
在操作执行后记录日志,根据sysLog属性决定是否保存操作结果,同时设置sysOperLog的状态码和错误信息。
beforeHandleLog:
在操作执行前记录日志,获取并设置操作模块、类型、方法信息、请求参数等,并根据sysLog属性决定是否保存请求数据,最后设置操作人名称。
6.定义切面类中用到的接口和实现类
public interface AsyncOperLogService extends IService<SysOperLog> { // 保存日志数据 }
@Service
@RequiredArgsConstructor
public class AsyncOperLogServiceImpl extends ServiceImpl<SysOperLogMapper, SysOperLog> implements AsyncOperLogService {
private SysOperLogMapper sysOperLogMapper;
}
@Mapper public interface SysOperLogMapper extends BaseMapper<SysOperLog> { }
7.SysOperLog
定义一个与日志数据库表相对应的实体类
@Data
@Schema(description = "SysOperLog")
public class SysOperLog extends BaseEntity {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@Schema(description = "模块标题")
private String title;
@Schema(description = "方法名称")
private String method;
@Schema(description = "请求方式")
private String requestMethod;
private Integer businessType ; // 业务类型(0其它 1新增 2修改 3删除)
@Schema(description = "操作类别(0其它 1后台用户 2手机端用户)")
private String operatorType;
@Schema(description = "操作人员")
private String operName;
@Schema(description = "请求URL")
private String operUrl;
@Schema(description = "主机地址")
private String operIp;
@Schema(description = "请求参数")
private String operParam;
@Schema(description = "返回参数")
private String jsonResult;
@Schema(description = "操作状态(0正常 1异常)")
private Integer status;
@Schema(description = "错误消息")
private String errorMsg;
}
8.表结构
CREATE TABLE `sys_oper_log` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志主键',
`title` varchar(50) DEFAULT '' COMMENT '模块标题',
`business_type` varchar(20) DEFAULT '0' COMMENT '业务类型(0其它 1新增 2修改 3删除)',
`method` varchar(100) DEFAULT '' COMMENT '方法名称',
`request_method` varchar(10) DEFAULT '' COMMENT '请求方式',
`operator_type` varchar(20) DEFAULT '0' COMMENT '操作类别(0其它 1后台用户 2手机端用户)',
`oper_name` varchar(50) DEFAULT '' COMMENT '操作人员',
`dept_name` varchar(50) DEFAULT '' COMMENT '部门名称',
`oper_url` varchar(255) DEFAULT '' COMMENT '请求URL',
`oper_ip` varchar(128) DEFAULT '' COMMENT '主机地址',
`oper_param` varchar(2000) DEFAULT '' COMMENT '请求参数',
`json_result` varchar(2000) DEFAULT '' COMMENT '返回参数',
`status` int DEFAULT '0' COMMENT '操作状态(0正常 1异常)',
`error_msg` varchar(2000) DEFAULT '' COMMENT '错误消息',
`oper_time` datetime DEFAULT NULL COMMENT '操作时间',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
`is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '删除标记(0:不可用 1:可用)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=67 DEFAULT CHARSET=utf8mb3 COMMENT='操作日志记录';