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='操作日志记录';

 

posted @ 2024-05-30 18:53  AND20211206  阅读(1575)  评论(0)    收藏  举报