注解之 日志记录

230 阅读3分钟

log.png

这是我参与「掘金日新计划 · 8 月更文挑战」的第29天,点击查看活动详情

前言

日志记录在每一个系统中是不可缺少,日志记录可以很好的帮我们排查定位问题。开发环境中可以使用debug 等方式封号的定位问题,但是发布生产环境后可以查看日志文件。除了这种还有另一种记录方式,本文就是使用 aop 和自定义注解实现日志的记录。当然这些数据记录在像 elasticsearch中是一个不错的方案,本文演示使用存储到还是传统的数据库中。

首先我们添加一下日志表的结构

CREATE TABLE `sys_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `ip` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '操作IP',
  `log_type` int(8) DEFAULT NULL COMMENT '1是登录日志2是操作日志',
  `where_type` int(8) DEFAULT NULL COMMENT '1是boss登录 2是租户端登录',
  `run_type` int(3) DEFAULT NULL COMMENT '操作类型 1 操作记录2异常记录',
  `user_name` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '操作人',
  `description` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '操作描述',
  `action_method` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '请求方法',
  `action_url` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL,
  `params` text COLLATE utf8mb4_bin COMMENT '请求参数',
  `browser` varchar(500) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '浏览器',
  `class_path` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '类路径',
  `request_method` varchar(10) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '请求方法',
  `start_time` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '开始时间',
  `finish_time` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '完成时间',
  `consuming_time` bigint(11) DEFAULT NULL COMMENT '消耗时间',
  `ex_desc` varchar(1000) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '异常详情信息',
  `ex_detail` text COLLATE utf8mb4_bin COMMENT '异常描述',
  `tenant_datakey` int(11) DEFAULT NULL COMMENT '租户标识',
  `location` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '操作地点',
  `os` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '操作系统',
  `status` int(8) DEFAULT '1' COMMENT '1成功2是失败',
  `return_data` text COLLATE utf8mb4_bin COMMENT '返回数据',
  `update_date` datetime DEFAULT NULL,
  `create_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_type` (`run_type`) USING BTREE COMMENT '日志类型'
) ENGINE=InnoDB AUTO_INCREMENT=3966 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='系统日志';

当然设计这个的初衷还是在管理后台有一个日志模块(当然直接放到mysql 不是最好的选择) image.png

创建自定义 注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SysOperaLog {
    String value() default "";
}

Aop的代码当然这里具体的insert的方法这里 就不贴了。

@Aspect
@Component
public class LogAspect {
@Pointcut("@annotation(com.xk.bugvip.base.aop.log.SysOperaLog)")
public void sysLogAspect(){
}
/**
 * 拦截控制器得操作日志
 * @param joinpoint
 * @throws Throwable
 */
@Before(value = "sysLogAspect()")
public void recordLog(JoinPoint joinpoint ) throws Throwable{
    SysLog sysLog  =new SysLog();
    //将当前实体保存到threadLoacl
    sysLogThreadLocal.set(sysLog);
    //开始时间
    long beginTime = Instant.now().toEpochMilli();
    HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
    sysLog.setActionUrl(URLUtil.getPath(request.getRequestURI()));
    sysLog.setStartTime(LocalDateTime.now().toString());
    //todo 设置操作人
    // sysLog.setUserName();
    //设置ip
    String ip = ServletUtil.getClientIP(request);
    sysLog.setIp(ip);
    //设置地址
    IpData ipData = IpGetAdders.doPostOrGet();
    sysLog.setLocation(ipData.getCname());
    sysLog.setRequestMethod(request.getMethod());
    //设置浏览器
        String header = request.getHeader("User-Agent");
    sysLog.setBrowser(UserAgentUtil.parse(header).getBrowser().toString());
    //设置操作系统
    sysLog.setOs(UserAgentUtil.parse(header).getOs().toString());
    //访问目标方法的参数  可动态改变参数值
    Object[] args=joinpoint.getArgs();
    //获取执行的方法名
    sysLog.setActionMethod(joinpoint.getSignature().getName());
    //类名
    sysLog.setClassPath(joinpoint.getTarget().getClass().getName());
    sysLog.setActionMethod(joinpoint.getSignature().getName());
    sysLog.setParams(Arrays.toString(args));
    String controllerMethodDescription = LogUtil.getControllerMethodDescription(joinpoint);
    sysLog.setDescription(controllerMethodDescription);
    long endTime = Instant.now().toEpochMilli();
    sysLog.setConsumingTime(endTime-beginTime);
}

/**
 * 返回通知
 * @param ret
 */
@AfterReturning(returning = "ret",pointcut = "sysLogAspect()")
public void doAfterReturning(Object ret) {
    SysLog sysLog = sysLogThreadLocal.get();
    //处理完请求 返回内容
    //处理添加日志
    sysLog.setRunType(LogConstant.LOG_INFO);
    sysLog.setLogType(LogConstant.OPERATION_LOG);
    sysLog.setFinishTime(LocalDateTime.now().toString());
    sysLog.setStatus(1);
    //sysLog.setReturnData(ret.toString());
    //发布事件
    applicationContext.publishEvent(new SysLogEvent(sysLog));
    //移除当前log实体
    sysLogThreadLocal.remove();

}
/**
 * 异常通知
 * @param e
 */
@AfterThrowing(pointcut = "sysLogAspect()", throwing = "e")
public void doAfterThrowable(Throwable e){
    SysLog sysLog = sysLogThreadLocal.get();
    //异常
    sysLog.setRunType(LogConstant.LOG_THORW);
    sysLog.setLogType(LogConstant.OPERATION_LOG);
    //异常对象
    sysLog.setExDetail(LogUtil.getStackTrace(e));
    //异常信息
    sysLog.setExDesc(e.getMessage());
    sysLog.setStatus(2);
    sysLog.setReturnData(e.toString());
    //发布事件
    applicationContext.publishEvent(new SysLogEvent(sysLog));
    //移除当前log实体
    sysLogThreadLocal.remove();
    //当然这里可以写其他的 处理 比如发送邮件等等....
}
}

方法请求中使用 注解 @SysOperaLog

@PostMapping("/getlist")
@SysOperaLog("获取日志列表")
public R getlist(@RequestBody ListVo parm){
    return  iLogService.getList(parm);
}

到这我们这个注解+Aop实现日志记录(当然在日志文件中也可以很方便的查看和定位问题,因为后台管理中有一个日志模块,所以就实现了一下)

实践是检验真理的唯一准则,感兴趣的可以去试试呀!明天见咯 😃😃😃😃