文章目录
- 一、记录日志的意义
- 二、日志数据表结构
- 三、记录日志思想
- 四、切面类环境搭建
- 五、保存日志数据
一、记录日志的意义
后台管理系统记录操作日志的意义非常重要,主要体现在以下几个方面:
- 安全性:操作日志可以记录管理员操作行为,以此来监控和防止管理员滥用权限或进行其他不当操作。如果后台管理系统没有记录操作日志,那么一旦出现不当操作,就无法对其进行追踪和定位,造成不可估量的安全风险。
- 追溯性:操作日志可以帮助管理员及时发现问题,并可以通过日志进行快速定位和处理。例如某个用户投诉自己的订单异常,管理员可以直接通过查询该订单的操作日志,找到问题所在并进行修改或解决。
因此,后台管理系统记录操作日志,对于维护系统的安全稳定性、保障客户数据的完整性和隐私性、提高系统及时响应和处理能力等方面具有重要意义,是保障企业正常运营和客户满意度的重要手段。
二、日志数据表结构
根据项目的需要,日志表结构设计如下:
CREATE TABLE `sys_oper_log` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志主键',
`title` varchar(50) DEFAULT '' COMMENT '模块标题',
`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 '操作人员',
`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 '错误消息',
`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=80 DEFAULT CHARSET=utf8mb3 COMMENT='操作日志记录'
三、记录日志思想
AOP记录日志的主要优点:
-
低侵入性:AOP记录日志不需要修改原有的业务逻辑代码,只需要新增一个切面即可。
-
统一管理:通过AOP记录日志可以将各个模块中需要记录日志的部分进行统一管理,降低了代码重复度,提高了代码可维护性和可扩展性。
-
提升效率:通过引入AOP记录日志,可以避免手动编写日志记录代码,减少了开发人员的工作量,提升了开发效率。
-
安全性:通过AOP记录日志,可以收集系统的操作日志,帮助管理员及时发现问题并进行调整,从而提高系统的安全性。
AOP记录日志的整体思想:
1、基于自定义注解来确定切入点(优势:可以通过自定义注解携带一些变化的参数,比如模块名称、操作类别)
2、基于环绕通知来完成日志记录
四、切面类环境搭建
1、创建日志模块并引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
2、自定义MyLog注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
/**
* 模块名称
*/
public String title();
/*
* 操作人类别
*/
public OperatorType operatorType() default OperatorType.MANAGE;
/*
* 业务类型(0其它 1新增 2修改 3删除)
*/
public int businessType();
/*
* 是否保存请求的参数
*/
public boolean isSaveRequestData() default true;
/*
* 是否保存响应的参数
*/
public boolean isSaveResponseData() default true;
}
3、自定义注解中用到了枚举类OperatorType,定义如下:
public enum OperatorType {
/*
* 其他
*/
OTHER,
/*
* 后台用户
*/
MANAGE,
/*
* 手机端用户
*/
MOBILE
}
4、定义一个切面类,并且在该切面类中提供一个环绕通知方法,代码如下所示:
@Aspect
@Component
@Slf4j
public class LogAspect {
@Autowired
private AsyncOperLogService asyncOperLogService;
@Around(value = "@annotation(sysLog)")
public Object doAroundAdvice(ProceedingJoinPoint joinPoint, MyLog sysLog) {
// 构建前置参数
SysOperLog sysOperLog = new SysOperLog();
LogUtil.beforeHandleLog(sysLog, joinPoint, sysOperLog);
Object proceed = null;
try {
proceed = joinPoint.proceed();
// 执行业务方法
LogUtil.afterHandlLog(sysLog, proceed, sysOperLog, 0, null);
// 构建响应结果参数
} catch (Throwable e) {
// 业务方法执行产生异常
e.printStackTrace(); // 打印异常信息
LogUtil.afterHandlLog(sysLog, proceed, sysOperLog, 1, e.getMessage());
throw new RuntimeException();
}
// 保存日志数据
asyncOperLogService.saveSysOperLog(sysOperLog);
// 返回执行结果
return proceed;
}
}
5、定义注解EnableLogAspect
想让LogAspect这个切面类在其他的业务服务中进行使用,那么就需要该切面类纳入到Spring容器中。Spring Boot默认会扫描和启动类所在包相同包中的bean以及子包中的bean。而LogAspect切面类不满足扫描条件,因此无法直接在业务服务中进行使用。那么此时可以通过自定义注解进行实现,代码如下所示:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(value = LogAspect.class)
public @interface EnableLogAspect {
/*
* 通过Import注解导入日志切面类到Spring容器中
*/
}
五、保存日志数据
切面类环境搭建好后,我们在后台系统中开始使用。
首先在后台系统模块中引入日志模块的依赖坐标:
然后编写AsyncOperLogService的实现类,用来保存日志数据到数据库
@Service
public class AsyncOperLogServiceImpl implements AsyncOperLogService {
@Autowired
private SysOperLogMapper sysOperLogMapper;
@Override
public void saveSysOperLog(SysOperLog sysOperLog) {
sysOperLogMapper.insert(sysOperLog);
}
}
最后编写SQL语句:
@Mapper
public interface SysOperLogMapper {
@Insert("insert into sys_oper_log values " +
"(#{id}, #{title}, #{method}, #{requestMethod}, #{operatorType}, #{operName},\n" +
"#{operUrl}, #{operIp}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, now(), now(), 0)")
void insert(SysOperLog sysOperLog);
}