Skip to content

09.panda log

Bing edited this page Jul 27, 2018 · 1 revision

简介

日志模块是基于dorado来实现的。用到了AOP技术,极大降低了代码的侵入性。提供两种配置方式:代理类方式和注解方式。可以用日志模块实现三元审计的功能。默认情况下,日志模块会记录项目中所有通过UpdateAction提交的数据变更情况信息。我们可以通过属性配置和接口来关闭和自定义日志的记录规则和记录范围。

背景

对于一些特别敏感的信息,业务希望系统能够记录下信息的每次变更,比如:某某在什么时间什么页面什么模块把什么字段的值从什么值修改到什么值。如果把这类逻辑写在业务代码里面,那将会是一场噩梦,更可怕的是,有些业务方法是以jar包的形式提供的,没办法直接在jar里编写日志相关逻辑代码。panda的日志模块就是为了解决这些繁琐的问题,默认自带这些功能,不需要做任何配置。它会有一套默认的规则,当然,这套规则也是可以自定义的。

用法

基于注解方式

提供了两个日志注解@Log和@NotLog

@Log

  • 加了@Log的方法,表示支持日志记录
  • @Log属性定义日志记录规则
  • @Log支持添加到类和方法上
  • 类和方法同时加了@Log,方法记录规则优先级高于类

@NotLog

  • 与@Log相反,表示方法不支持日志记录
  • 只支持在方法添加@NotLog
  • 在@Log加在类上的情况下,配合使用@NotLog。@Log加在类上表示类的所有公共方法都支持日志记录,除了加@NotLog注解的方法以外

示例

  1. 保存方法记录日志,记录数据的增删改
@Transactional
@DataResolver
@Log
public void save(List<User> users) {
  JpaUtil.save(user);
}
记录规则:
  • 日志模块会通过AOP来拦截save方法,在save执行完后,遍历参数users,根据User对象的脏状态,来记录日志
  • 脏状态为修改,日志记录的操作就为“修改”操作,并记录下操作人和操作时间,以及修改的属性和该属性值的变化轨迹
  • 脏状态为新增,日志记录的操作就为“新增”操作,并记录新增的属性内容
  • 脏状态为删除,日志记录的操作就为“删除”操作
  • 无状态,则不记录
扩展:如果save参数不是集合类型,而是一个User类型,日志模块就会直接取User对象参数进行日志记录,不会遍历
  1. 查询方法记录日志
@Transactional(readOnly = true)
@DataProvider
@Log
public void load(Page<User> page) {
  .....
}
记录规则:日志模块会通过AOP来拦截load方法,在load执行完后,遍历参数page的entities属性,根据User对象,来记录日志,日志记录的操作就为“查看”操作,并记录下操作人和操作时间
扩展:如果load不是分页查询,通过返回值来返回查询结果,日志模块会自动选择返回值来记录日志
  1. 指定日志记录来自那个模块和日志的分类(分类在三元审计中很有用,可以通过分类指定日志是安全日志、审计日志和用户日志)
@Transactional
@DataResolver
@Log(module = "xxx模块", category = "xxx分类")
public void save(List<User> users) {
  JpaUtil.save(user);
}
  1. @Log属性支持dorado的EL表达式
@Transactional
@DataResolver
@Log(module = "${entity.id}xxx模块", category = "${entity.name}xxx分类")
public void save(List<User> users) {
  JpaUtil.save(user);
}

注释:项目中的所有dorado的EL表达式变量都可以,日志模块还额外添加了一些变量,如:JOIN_POINT(切点)、LOG_DEFINITION(日志配置信息)、TARGET(日志记录的上下文目标对象,可以是任何类型)、entityState(日志记录的最小单位对象的dorado实体类状态)、returnValue(方法的返回值),entity(日志记录的最小单位对象)和args(方法的参数,数组类型,取法:arg[0]代表第一个参数)。后面四个主要给开发人员用的,其他的主要是内部使用。在第一个例子中,users参数就是TARGET,日志模块会遍历TARGET,entity就是遍历的当前项User,第二例子中,page.getEnties()为TARGET。 5. 指定上下文目标对象TARGET为某个参数

@Log(context = "arg(0)")
public void xxx(List<User> users) {
 ...
}
  1. 指定上下文目标对象TARGET为返回值
@Log(context = ContextProvider.RETURN_VALUE)
public User xxx() {
 ....
 return user;
}
扩展:自定义上下文目标对象获取规则,可以实现ContextProvider接口来扩展
  1. 指定dataPath(默认的上下文提供者不支持) 假设Dept有属性users
@Log(context = "arg(0)", datapath="users") //表示dept.users为TARGET
public void xxx(Dept dept) {
 ....
}
  1. UpdateAction提交过来的数据是棵脏树 假设Dept有属性users
@Transactional
@DataResolver
@Log //不仅记录部门的更改情况,还会记录部门下的子记录的更改情况(递归执行)
public void save(List<Dept> depts) {
 ....
}

基于代理类方式

  1. 定义一个或多个代理类,实现LogProxy接口
  2. 定义一个或多个代理类需要代理的范围,继承AbstractLogProxyAspect抽象类

定义代理类

假设有一个类如下

@Service
public class UserServiceImpl implements UserService {
  public void save(List<User> users) {...};
  public List<User> load(){...};
  public boolean isExist() {...};
}

不想在UserServiceImpl类中加@Log注解,可以在代理类里面代替UserServiceImpl加注解,规则是一样的,如下:

@Component
@Log
public class UserServiceLogProxy implements LogProxy {
  public void save() {};
  public void load(){};
  @NotLog
  public void isExist() {};
  @Override
  boolean support(Object obj) {
    return obj instanceof UserService;
  }
}

定义代理范围

@Aspect
@Component
public class LogProxyAspect extends AbstractLogProxyAspect {
  @Override
  @Pointcut("@annotation(com.bstek.dorado.annotation.DataResolver)")
  protected void logPointcut() {}
}

注释:@Pointcut的用法,可以去查询AOP规范定义

属性配置

#配置@Log属性disabled的默认值
panda.log.defaultDisabledAttr=false
#配置@Log属性logger的默认值
panda.log.defaultLoggerAttr=defaultLogger
#配置@Log属性category的默认值
panda.log.defaultCategoryAttr=
#配置@Log属性desc的默认值
panda.log.defaultDescAttr=log.modifyInfo(entity)
#配置@Log属性module的默认值
panda.log.defaultModuleAttr=
#配置@Log属性operation的默认值
panda.log.defaultOperationAttr=entityState%=%='NONE'?'查看'%:entityState%=%='MODIFIED'?'修改'%:entityState%=%='NEW'?'新增'%:entityState%=%='DELETED'?'删除'%:entityState%=%='MOVED'?'排序'%:'其他'
#配置@Log属性operationUser的默认值
panda.log.defaultOperationUserAttr=loginUsername
#配置@Log属性operationUserNickname的默认值
panda.log.defaultOperationUserNicknameAttr=loginUser.nickname
#配置@Log属性title的默认值
panda.log.defaultTitleAttr=empty entity.name? entity.id : entity.name
#配置@Log属性var的默认值
panda.log.defaultVarAttr=entity
#配置@Log属性context的默认值
panda.log.defaultContextAttr=SMART
#禁用日志模块
panda.log.disabled=false
#禁用默认的日志记录规则
panda.log.defaultLog=true
#默认属性记录格式(第一个占位符是属性,第二个是老的值,第三个是新的值,例如:年龄:20 -> 30)
panda.log.defaultFormat=%s %: %s -> %s%n
#日期属性记录格式,其他特殊类型可以通过属性panda.log.formatXXX=....,其中XXX为类型的名称
panda.log.formatDate=%s %: %tF %2$tT -> %tF %3$tT%n