在上一章内容中——应用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@AllArgsConstructorpublic class SysOperLog implements Serializable { private static final long serialVersionUID = 1L; /** 日志主键 */ private Long id; /** 操作模块 */ private String title; /** 业务类型(0其它 1新增 2批改 3删除) */ 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)@Documentedpublic @interface Log { /** * 模块 */ String title() default ""; /** * 性能 */ BusinessType businessType() default BusinessType.OTHER;}
应用aop对自定义的注解进行解决
@Aspect@Component@Slf4jpublic class LogAspect { @Autowired private AsyncLogService asyncLogService; // 配置织入点 @Pointcut("@annotation(com.javatrip.aop.annotation.Log)") public void logPointCut() {} /** * 解决完申请后执行 * * @param joinPoint 切点 */ @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult") public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) { handleLog(joinPoint, null, jsonResult); } /** * 拦挡异样操作 * * @param joinPoint 切点 * @param e 异样 */ @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); } } /** * 获取注解中对办法的形容信息 用于Controller层注解 * * @param log 日志 * @param operLog 操作日志 * @throws Exception */ 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中进行存库操作。
@Servicepublic 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/s...
点关注、不迷路
如果感觉文章不错,欢送关注、点赞、珍藏,你们的反对是我创作的能源,感激大家。
如果文章写的有问题,请不要悭吝,欢送留言指出,我会及时核查批改。
如果你还想更加深刻的理解我,能够微信搜寻「Java旅途」进行关注。回复「1024」即可取得学习视频及精美电子书。每天7:30准时推送技术文章,让你的下班路不在孤单,而且每月还有送书流动,助你晋升硬实力!