SpringBoot——记录日志
1.自定义@Log注解
这个注解的作用是通过在方法上添加@Log注解,自动记录操作日志。通过设置不同的属性值,可以定制日志的内容,包括操作模块、操作人类别、业务类型以及是否保存请求和响应参数等信息。
1 @Target({ElementType.METHOD}) 2 @Retention(RetentionPolicy.RUNTIME) 3 public @interface Log { // 自定义操作日志记录注解 4 5 public String title() ; // 模块名称 6 public OperatorType operatorType() default OperatorType.MANAGE; // 操作人类别 7 public int businessType() ; // 业务类型(0其它 1新增 2修改 3删除) 8 public boolean isSaveRequestData() default true; // 是否保存请求的参数 9 public boolean isSaveResponseData() default true; // 是否保存响应的参数 10 11 }
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 { }
在启动类中加入注解
@SpringBootApplication @EnableConfigurationProperties(value = {UserAuthProperties.class, MinioProperties.class}) @EnableScheduling @EnableLogAspect 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 ; } }
值得注意的是一定要抛出 RuntimeException()异常,否则会出现事务失效的情况。或者使用@Order注解(值越小优先级越高 )降低自定义切面类的优先级,让事务切面类的优先级要高于自定义切面类。
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='操作日志记录';