栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java > SpringBoot

spring-boot-route(十七)使用aop记录操作日志

SpringBoot 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

spring-boot-route(十七)使用aop记录操作日志

在上一章内容中——[使用logback管理日志],我们详细讲述了如何将日志生成文件进行存储。但是在实际开发中,使用文件存储日志用来快速查询问题并不是最方便的,一个优秀系统除了日志文件还需要将操作日志进行持久化,来监控平台的操作记录。今天我们一起来学习一下如何通过apo来记录日志。

为了让记录日志更加灵活,我们将使用自定义的注解来实现重要操作的日志记录功能。

一 日志记录表

日志记录表主要包含几个字段,业务模块,操作类型,接口地址,处理状态,错误信息以及操作时间。数据库设计如下:

CREATE TABLE `sys_oper_log` (
   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '日志主键',
   `title` varchar(50) CHARACTER SET utf8 DEFAULT '' COMMENT '模块标题',
   `business_type` int(2) DEFAULT '0' COMMENT '业务类型(0其它 1新增 2修改 3删除)',
   `method` varchar(255) CHARACTER SET utf8 DEFAULT '' COMMENT '方法名称',
   `status` int(1) DEFAULT '0' COMMENT '操作状态(0正常 1异常)',
   `error_msg` varchar(2000) CHARACTER SET utf8 DEFAULT '' COMMENT '错误消息',
   `oper_time` datetime DEFAULT NULL COMMENT '操作时间',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB CHARSET=utf8mb4 CHECKSUM=1 COMMENT='操作日志记录'

对应的实体类如下:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class SysOperLog implements Serializable {
    private static final long serialVersionUID = 1L;

    
    private Long id;

    
    private String title;

    
    private Integer businessType;

    
    private String method;

    
    private String errorMsg;

    private Integer status;

    
    private Date operTime;
}
二 自定义注解及处理

自定义注解包含两个属性,一个是业务模块title,另一个是操作类型businessType。

@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface Log {
    
    String title() default "";

    
    BusinessType businessType() default BusinessType.OTHER;
}

使用aop对自定义的注解进行处理

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Autowired
    private AsyncLogService asyncLogService;

    // 配置织入点
    @Pointcut("@annotation(com.javatrip.aop.annotation.Log)")
    public void logPointCut() {}

    
    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
 handleLog(joinPoint, null, jsonResult);
    }

    
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
 handleLog(joinPoint, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) {
 try {
     // 获得注解
     Log controllerLog = getAnnotationLog(joinPoint);
     if (controllerLog == null) {
  return;
     }

     SysOperLog operLog = new SysOperLog();
     operLog.setStatus(0);
     if (e != null) {
  operLog.setStatus(1);
  operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
     }
     // 设置方法名称
     String className = joinPoint.getTarget().getClass().getName();
     String methodName = joinPoint.getSignature().getName();
     operLog.setMethod(className + "." + methodName + "()");
     // 处理设置注解上的参数
     getControllerMethodDescription(joinPoint, controllerLog, operLog);
     // 保存数据库
     asyncLogService.saveSysLog(operLog);
 } catch (Exception exp) {
     log.error("==前置通知异常==");
     log.error("日志异常信息 {}", exp);
 }
    }

    
    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog) {
 // 设置action动作
 operLog.setBusinessType(log.businessType().ordinal());
 // 设置标题
 operLog.setTitle(log.title());
    }

    
    private Log getAnnotationLog(JoinPoint joinPoint) {
 Signature signature = joinPoint.getSignature();
 MethodSignature methodSignature = (MethodSignature) signature;
 Method method = methodSignature.getMethod();
 if (method != null) {
     return method.getAnnotation(Log.class);
 }
 return null;
    }
}

操作类型的枚举类:

public enum BusinessType {
    
    OTHER,

    
    INSERT,

    
    UPDATE,

    
    DELETE,
}

使用异步方法将操作日志存库,为了方便我直接使用jdbcTemplate在service中进行存库操作。

@Service
public class AsyncLogService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    
    @Async
    public void saveSysLog(SysOperLog log) {

 String sql = "INSERT INTO sys_oper_log(title,business_type,method,STATUS,error_msg,oper_time) VALUES(?,?,?,?,?,?)";
 jdbcTemplate.update(sql,new Object[]{log.getTitle(),log.getBusinessType(),log.getMethod(),log.getStatus(),log.getErrorMsg(),new Date()});
    }
}
三 编写接口测试

将自定义注解写在业务方法上,测试效果

@RestController
@RequestMapping("person")
public class PersonController {
    @GetMapping("/{name}")
    @Log(title = "system",businessType = BusinessType.OTHER)
    public Person getPerson(@PathVariable("name") String name, @RequestParam int age){
 return new Person(name,age);
    }

    @PostMapping("add")
    @Log(title = "system",businessType = BusinessType.INSERT)
    public int addPerson(@RequestBody Person person){
 if(StringUtils.isEmpty(person)){
     return -1;
 }
 return 1;
    }

    @PutMapping("update")
    @Log(title = "system",businessType = BusinessType.UPDATE)
    public int updatePerson(@RequestBody Person person){
 if(StringUtils.isEmpty(person)){
     return -1;
 }
 return 1;
    }

    @DeleteMapping("/{name}")
    @Log(title = "system",businessType = BusinessType.DELETE)
    public int deletePerson(@PathVariable(name = "name") String name){
 if(StringUtils.isEmpty(name)){
     return -1;
 }
 return 1;
    }
}

当然,还可以在数据库中将请求参数和响应结果也进行存储,这样就能看出具体接口的操作记录了。

此是spring-boot-route系列的第十七篇文章,这个系列的文章都比较简单,主要目的就是为了帮助初次接触Spring Boot 的同学有一个系统的认识。本文已收录至我的github,欢迎各位小伙伴star!

github:https://github.com/binzh303/spring-boot-route

点关注、不迷路

如果觉得文章不错,欢迎关注点赞收藏,你们的支持是我创作的动力,感谢大家。

如果文章写的有问题,请不要吝啬,欢迎留言指出,我会及时核查修改。

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号