Guns第九节日志系统
日志机构有哪些功能,第一种记录异常日志,捕获程序里面出现的异常,然后异常记录下来。
第二个是业务日志,修改分为修改前,修改后。
怎么样为某个业务加上业务日志的功能。
它是通过来标记哪些业务 加上业务日子。
这个标记必须放在controller层,下面一个例子介绍一下它的用法,
第一种,添加和删除,
咱们看一**解的源代码,我们可以看到三个参数,注解的作用
,
,
,
为什么要添加唯一的标识呢,因为你需要用这样的一个标识,来记录你是添加了哪一个管理员,你添加了谁。唯一的标识是一个字段,并且这个字段是请求bean里面的一个字段
,这个是请求bean的封装,可以用account
(添加和删除的时候),添加和删除的时候不能用
作为唯一标识的key,因为id是数据库自动生成的。当你记录业务日志的时候,你需要用一个key来标记业务的唯一性。比如说添加管理员,你添加了谁,就需要用account或者phone来标记管理员的唯一性。如果一个key表示不了,就可以用两个key来标记唯一性。
,
,
字典功能用于查找key的中文名称和字段的中文名称。
什么是字典。大家设想一下,比如说我们记录日志的时候,现在就以添加管理员为例,你需要知道,
字典都是放到这个字典常量里面,这里名称就是对应一个类的名称,这里先介绍一下字典的作用,比如说我们添加用户,你添加用户的时候,添加的时候,假如说我们有个业务就是需要记录添加了哪些字段,这些字段的值分别是什么?你现在往数据库记录的时候,比如说账户是admin,那你现在要记录到数据库里面,当你把这些数据
传输给后台的时候,后台接受到的都是一个个的英文字段
,然而这个日志记录功能不一定是给开发 人员看的,开发人员可能看到,比如说插入了一条日志,日志是这样的
,你可能当他点击提交的时候可以获取到这样的字符串,这样的字符串对于开发人员来说很简单,但是对于一个使用这个项目的人来说,这串字符串不知道干什么用的。虽然说这个记录日志插进去了,这个日志是有了,但是他不知道这个日志的详情
。因为他不知道这些英文字段和这个1,2是干什么的。你传过来的时候就是这样一串字符串。你不可能直接插进去。直接记录到数据库里面。也许知道开发人员才能看懂,而用户看不懂,所以我们需要对它进行转化,需要转化哪些呢?我们需要把这些字段的英文名都转化成中文名。所以说出现了一个字典的概念。这个就是字典,
,所以说大家在记录日志的时候会有非常多的字典。我们现在以用户字典为例
,当你传过来一个这样的字符串的时候
,我在记录日志的时候不可能把他记录下来,我必须先做一个转化,所以说字典的作用就是转化。第一是转化它的一些字段名称,第二是转化它的一些值,比如说一是什么,二是什么,我这个数据库里面一是男,二是女。这个只有我自己知道,如果给业务人员用的话,所以要做一个转化。所以最终要达到一个效果可能是这样的,我新增了一个账号是admin,
,所以借用一个字典我们期望的值是这样的。下面介绍一下字典的实现.字典本质上市维护了两个hashmap
,这个hashmap都是string,string类型的,第一个string就是key,第二个string就是它的值。我们看一下具体的实现,就是我们可以把userId映射成账号。
,当我们插入这条日志的时候,循环到这个字段
,我们找一下有没有account,
,通过本质上维护的这个map,它的中文名称是账号
的意思,我就可以插入这个日志的时候,把这个字段名
转换成账号,同理其他的一些字段你都可以转化成中文名称。其实这个字典很简单
,两个map,大家可能会问道第二个map是什么?第二个map就是像刚才说的这种值
,把这种值转化成中文名称
,比如说它这里面放的sex.key是1,那么sex.value是男,做一个这样的转化,第二个是做值得转化,第一个是做字段名称的英文名称,转化到中文名称里面,第二个就是转化成值,第二个字典就是转化成值得字典,这个map里面维护的不是中文名称和英文名称,通过这个sex,比如它是1或者2,你不能通过这样一个简单的字典
就拿到对应的中文名称。我们需要一个方法才能拿到中文名称,比如说它传的时候可能是1,可能是2,所以第二个map里面维护的是key还有方法的名称,这个是key,这个是方法名称,这个方法是哪个类的方法呢?他是ConstantFactory类的方法。
,我们都可以复制过来。
,大家可以看到它传进去了一个值,返回了一个中文名称。
,所以你写的这个些方法必须在ConstantFactory里面找到。它是这样一个转化过程。它是通过方法
把对应的值改成中文名称的
。
这个是注解的三个参数的含义,大家在写业务需要记录日志的时候,需要准备什么呢?需要些两个地方,第一个地方:我们以添加管理员为例,因为日志有两种情况,一种是涉及到修改的,一种是不涉及到修改的。不涉及到修改的就是现在这种情况。不涉及到修改的我们一会再说一下,因为涉及到修改的需要记录一下修改前是什么样,修改后是什么样。我们先说这种简单的不涉及到修改的。怎么记录日志。我们需要写一个@BusinessLog这样一个注解
,然后准备一个字典
。
涉及到修改的,如何日志记录,首先注解和字典都是要加的。修改的时候需要记录一下它修改之前的信息,举个例子,比如说用户管理,修改之前,它是这些数据,,修改之后就先不用管,就是说你在修改一个东西的时候,你需要记录修改之前的信息。你才可以把日志记录成什么样呢?
,我就修改这三个数据,我点击提交,大家看日志记录里面记录成什么样了,
,账号是admin,我们需要这样的效果,就必须记录一下旧值是什么,新值是什么?当你在点修改按钮的时候。你才可以把这些日志记录出来。所以说如何保存修改之前的数据。需要你手动加一个东西。就是在你详情页面的时候,比如说你点详情的时候,会弹出这样一个详情页面
。就在弹出详情的地方加一个记录日志。
就是在这个加一个
,
,这个类就是要记录你当前要修改哪一条数据。把这个数据的对象保存起来。
,这个scope_session的意思就是这个spring容器中的bean,它的数据范围是session,当你登陆这个系统的时候,你有一个session,它的scope就是这个session,session大家知道,就是对于不同用户session期间这个bean生效。
因为跳转到编辑页面的时候,你的这些数据都获取出来了,你这样把获取到的数据保存起来,然后我点提交的时候,再从缓存中拿出来。
点击修改的时候的这些字段必须和点击修改的时候的dto一致。什么意思呢,就是刚才看到的那些字段,必须跟点击修改的时候的dto是一支的
,这个guns管理系统的日志就不会记录这个字段。
spring AOP就是做了一个切面。具体就是通过LogAop这个类实现了这个记录。
,大家看这里有一个Aspect,所以这就是一个切面,
,springbean,spring容器。然后它拦截了businesslog
,当你在方法上写
的时候,它就会走这个切面
。所以说它拦截的是这个annotation,然后它执行了一个环绕切面
,就是在执行业务的前面,执行业务的后面,你可以在一个方法里面写上这些业务
,先执行一个业务,执行业务之后才记录日志
。比如这里会报异常
,当你添加用户,修改用户的时候报异常了,这一段是不会走的
,就不记录日志了。你
执行成功之后,它才会记录日志。然后具体看一下日志记录的过程。日志记录的过程是这样的。
它执行成功之后,它再执行
。大家看一下handle方法的内容。
第一步,获取拦截的方法名,这一系列的方法就是获取到拦截的对象,然后获取到拦截的方法名称,然后获取到方法。
,
获取它的类名。获取到它的参数
,它的参数就是这些参数
。
其实这上面的一系列数据
为下面的日志做准备,获取一些相关的信息,这个就是获取annotation
,下面从annotation里面分别获取到这三个值
。
。获取这三个值之后,他们每一个都对应了一个字典的实体类
,
,
如果不涉及到修改直接记录日志,,
。
如果涉及到修改的时候,它是如何记录的呢
首先判断value里面是否有这样的文字,依据这个文字去判断,是否是编辑,是否是修改数据。因为涉及到修改,需要对比变化,就是跟我们这个效果一样,如果涉及到变化,需要对比之前是什么样,之后是什么样。然后重点看一下涉及到修改的过程。
,这个就是保存修改之前的对象,刚才又介绍到,然后这里是取出保存的这个对象
,获取请求的参数
,通过一个工具类,httpkit获取到所有的请求参数,这个map
里面就是参数的名称还有参数的值。然后这个msg返回值
就是
,然后我们看一下这个里面是如何比对如何记录的。首先传递了字典的classname
,key(业务的唯一标识)object1就是数据变化之前的那个对象,比如说修改用户为例,修改之前就是这个样
,Object2就是刚请求修改的,我刚点提交的时候这些参数和对应的值。
点进去看一下,
,比较这修改之前的数据,
,
获取到了具体的字典类,他是通过一个string类型的字典名称,然后直接获取到了这个字典类,然后看一下它的实现,它的实现其实很简单,
,就是通过Class.forname()这样一个反射调用,把包名称和类名
拼接起来。这样就行成了一个类名。这个类的名称就是class.forname就可以获取到类的类对象,获取到之后直接newInstance,相当于new了一个map,就是相当于记录日志写的这个map
,然后返回,这就相当于用一个工厂方法
,
通过名称实际地获取到了一个map类,
解析多key(用复合字段来标识唯一性),当然也可以解析一个key,多个key我们可以解析,最后我们解析成这样
,
解析的过程是这样的
首先传了map的实例对象,然后传了key,传了请求参数
,
如果它=-1,就是key里面没有逗号的话,解析一个key就行了,
,通过这个
拿到key的中文名称。
之前说过通过他拿取key的中文名称,取出第一个map的值,然后通过第一个map的key取出之就是map的中文名称。
请求参数获取到key的值,比如说请求的是账号account,通过请求参数找到account的值,就拿到了这个admin,然后这个admin需不需要包装呢?因为这个admin它是账号,它没有办法包装,如果它是性别的话,它需要进行一个包装,把1获取2包装成一个男或者女,下面这一步就是判断需不需要把值进行包装。
从这里得到
,
,大家可以看到他是第二个,第二个是获取字段的翻译
,他是对应的一个方法名,
这一步就是写这个值是不是需要翻译,如果这个字段
需要翻译的话,在这个map里面有这个根据字段对应的方法名。
首先第一个参数value,就是把sex的值传过来,把方法名传过来,它就可以通过这个工厂类得到这个值
,把性别中的1包装成男,把性别中的2包装成女,它肯定返回的就是一个中文名称。大家看一下包装的过程
,首先因为我们知道methodName全部都是在constantFactory里面,所以它先获取ConstantFactory对象,
,然后呢还是通过反射的方式拿到具体的方法
,
拿到的就是一个结果,其实这两句话就相当于me.getSexName(field),但是java不能传一个变量,所以必须通过反射的方式写出来,通过反射的方式获取到这个method,然后这个参数
就是这个method的类型,method.invoke(me,field),调用me.()这个方法
,然后传一个参数,传一个参数就是这个参数
,结果就是被包装之后的值,然后下面还有一个方法当我捕获到异常之后
,这种情况就是当出现异常的时候需要再进行转化,比如说参数类型是
的时候,需要再进行一次调用,如果再一次调用还有问题的话,就说明这个过程中确实有一个业务异常,如果这个解析成功的话,
,说明这个异常是错误的异常,需要通过这样的特殊的转化,才可以把这个值
解析出。这个Field在调用
的时候,Integer类型的参数在执行的时候会有问题。所以说这里对Integer单独做了一个处理
。然后获取到被包装的值。
接着往下讲,
,
这一步执行之后得到的是第一行
,然后我们还需要进行下面的比对和拼接。
,
获取到对应两个类的类型,然后
获取到fields字段,然后遍历这些字段,
获取到那些get方法,举个例子比如说userController里面我们保存一个对象user,比如说遍历这个字段的时候,遍历到account,那么这句话的意思就是
readmethod就是获取它的get方法,获取到这个字段的get方法
,获取到它的get方法之后呢,invoke()
调用这个对象
的get方法获取当前循环的这个字段的值
,
获取修改之后对应字段的值,
请求前请求后的值都有了,这里都判断了一下是否都等于null,如果都等于null的话,继续不记录这个字段,
如果这里是时间类型的话,需要进行一个格式化,并且又判断了一下,
如果是Integer类型的话,转化成Integer,
如果请求前参数和请求后参数不相等话才开始记录它们的变化.
如果相等的话就直接跳过这个字段了,跳过这个字段下一步就是遍历下一个字段,String就是我们的返回值,String现在拼接到了它
,在拼接过程中这个separetor就是分割符,我们的分割符就是用三个分号分割,拼装了一个分割符,然后下面
,dict.map又获取到了这个字典,获取到它的field.getName(),这一句就是获取到了它的中文名称,大家可以看到get
是第一个dictory,他获取到了这个字段的中文名称,所以我们在记录日志的时候我们看到的是
中文名称,其实它们都是从字典里面拿出来的。
我们刚刚讲的是涉及到修改的,涉及到修改的需要比对修改之前修改之后,然后保存,然后输出这个信息,然后这个是不涉及到修改的,我们就直接记录这个key就行了。
这个是获取请求参数,
这个是通过工厂方法和字典的类名,获取字典的实例。
获取到信息的文字。
下一步具体就是记录日志了,把上面收集到的所有信息保存到数据库里面,保存到数据库呢,它用了一个异步的方式,我们可以看到保存的时候都是用的LogManager,
,当你想记录日志的时候,你只需要向这个类传递一个task,就可以记录日志了,
,记录日志的时候不会占用业务的时间,这是这样一个作用,异步保存日志的作用,
是核心的方法,然后这个task呢又是通过
创建的
,都是通过mybatis的一个mapper,insert这个东西,
,然后创建这个bean,也是用了这个工厂
,set进去这个
new出来的log对象里面一系列的set方法,创建好这个bean之后,insert这个bean就可以了,这是这个类的作用
,它是创建任务,他是通过
创建了任务,然后把这个任务扔给logManager执行,
这个整个logaop的过程,就是日志记录的核心类。然后涉及到日志记录相关的类都有哪些呢?Log包里面,
,还有这些字典
,还有这个
,还有一个注解