✨这里是第七人格的博客✨小七,欢迎您的到来~✨
🍅系列专栏:【架构思想】🍅
✈️本篇内容: 从0到1带你实现一个业务日志组件✈️
🍱本篇收录完整代码地址:gitee.com/diqirenge/b…
楔子
前段时间小七接到一个任务,要为某些后台操作添加业务操作日志,并且需要将日志存储在数据库中。看了看当前项目中的代码,很*很暴力,直接就在对应的地方写的一条插入语句就ok了,抛开技术不谈,这样的代码还是很优雅的!
但是小七想起了以前看过的美团的一篇技术博客——《如何优雅地记录操作日志?》 ,正好有2天时间完成这个需求,时间充足,还等什么?开干
分析需求
业务方需求很简单,总结起来就是 把什么人在什么时间对什么东西做了什么操作 给记录下来,当然再做好一点的话,可以将两次操作的不同记录下来。我们这一次就完成基础需求就好了。
确认方案
首先我们来思考一下可行的几种方案:
直接持久化,在需要保存日志的地方写sql就好了。
将保存操作抽象提取为工具类,在需要保存日志的地方,调用工具类。
使用AOP生成操作日志。
使用canal监听数据库变化,记录操作日志。
针对第一点,优势在于不用动脑子;针对第二点,优势在于比第一点动了一点脑子。这两种方案小七都是不推荐的,耦合性太高,拓展性太低。
针对第三点,也是很多公司采取的方案,但是大家一般都只是,简单的拼接了一下出入参,结合美团的博客——《如何优雅地记录操作日志?》 我们可以考虑使用SpEL来做动态模版。这种方案可行且优雅。
针对第四点,只能根据数据库的更改做日志记录,局限太大,不考虑。
综上所述,我们最后选择使用AOP来生成动态的操作日志。
基础架构
方案确定了,接下来,我们需要定义一下我们的基础架构。因为是根据AOP来玩的,所以我们需要2个东西,注解和切面。
分支名称
231013-52javaee.com-DemandAnalysis
仓库地址
gitee.com/diqirenge/b…
分支描述
根据需求分析,完成注解设计。
代码实现
注解一:业务日志注解 BizLog
根据需求分析,我们可以抽象出我们需要的日志属性
需求 | 翻译 |
---|---|
什么人 | 操作者 |
什么时间 | 操作时间(这个不需要传入,以系统计算为准) |
什么东西 | xx系统,xx模块,xx业务id |
什么操作 | 操作类型 |
以上属性按道理就可以完成我们的需求了,但是为了拓展性,我们可以再添加以下属性
拓展 | 翻译 |
---|---|
操作结果 | 1、成功模板 2、失败模版 |
记录条件 | 根据某些条件,判断是否需要记录日志。比如:证件类型为身份证的才记录 |
详情 | 拓展字段,存放其他不好定义的信息 |
具体代码如下:
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Repeatable(BizLogs.class)
public @interface BizLog {
/**
* 操作者,必填
*/
String operator();
/**
* 成功模板,必填
*/
String success();
/**
* 系统,默认取spring-application-name
*/
String system() default "";
/**
* 模块
*/
String module() default "";
/**
* 操作类型:比如增删改查
*/
String type() default "";
/**
* 关联的业务id
*/
String bizNo() default "";
/**
* 失败模板
*/
String fail() default "";
/**
* 拓展字段
* 记录更详细的其他信息
*/
String detail() default "";
/**
* 记录条件 默认 true
* true代表要记录,false代表不记录
*/
String condition() default "";
}
注解解析:
// 用于指示将被注解的元素包含在生成的Java文档中
@Documented
// 表明这个注解可以修饰(作用)在方法上
@Target(ElementType.METHOD)
// 对应Java代码的加载和运行顺序
// 范围大小:java源文件