这是我参与「掘金日新计划 · 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 不是最好的选择)
创建自定义 注解
@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实现日志记录(当然在日志文件中也可以很方便的查看和定位问题,因为后台管理中有一个日志模块,所以就实现了一下)
实践是检验真理的唯一准则,感兴趣的可以去试试呀!明天见咯 😃😃😃😃