因为我们是使用注解的方式记录日志,所以我们首先要定义一个注解。
1.定义注解
/** * 标记需要做业务日志的方法 * * @author gao.mq */ @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface BussinessLog { /** * 业务的名称,例如:"修改用户" */ String value() default ""; /** * 被修改的实体的唯一标识,例如:用户实体的唯一标识为"id" */ String key() default "id"; /** * 字典(用于查找key的中文名称和字段的中文名称) */ String dict() default "SystemDict"; }
2.定义aop进行拦截
/** * 日志记录 * * @author gao.mq */ @Aspect @Component public class LogAop { private Logger log = LoggerFactory.getLogger(this.getClass()); @Pointcut(value = "@annotation(com.common.annotion.log.BussinessLog)") public void cutService() { } @Around("cutService()") public Object recordSysLog(ProceedingJoinPoint point) throws Throwable { //先执行业务 Object result = point.proceed(); try { handle(point); } catch (Exception e) { log.error("日志记录出错!", e); } return result; } private void handle(ProceedingJoinPoint point) throws Exception { //获取拦截的方法名 Signature sig = point.getSignature(); MethodSignature msig = null; if (!(sig instanceof MethodSignature)) { throw new IllegalArgumentException("该注解只能用于方法"); } msig = (MethodSignature) sig; Object target = point.getTarget(); Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes()); String methodName = currentMethod.getName(); //如果当前用户未登录,不做日志 ShiroUser user = ShiroKit.getUser(); if (null == user) { return; } //获取拦截方法的参数 String className = point.getTarget().getClass().getName(); Object[] params = point.getArgs(); //获取操作名称 BussinessLog annotation = currentMethod.getAnnotation(BussinessLog.class); String bussinessName = annotation.value(); String key = annotation.key(); String dictClass = annotation.dict(); StringBuilder sb = new StringBuilder(); for (Object param : params) { sb.append(param); sb.append(" & "); } //如果涉及到修改,比对变化 String msg; if (bussinessName.indexOf("修改") != -1 || bussinessName.indexOf("编辑") != -1) { Object obj1 = LogObjectHolder.me().get(); Map<String, String> obj2 = HttpKit.getRequestParameters(); msg = Contrast.contrastObj(dictClass, key, obj1, obj2); } else { Map<String, String> parameters = HttpKit.getRequestParameters(); AbstractDictMap dictMap = DictMapFactory.createDictMap(dictClass); msg = Contrast.parseMutiKey(dictMap,key,parameters); } LogManager.me().executeLog(LogTaskFactory.bussinessLog(user.getId(), bussinessName, className, methodName, msg)); } }
上面的代码有身份认证个人Id等方面的要求,可以根据自己的需求进行增删。都要注释
3. 上面最后一行向数据库执行插入日志记录执行的信息。看下LogManager
/** * 日志管理器 * 开启线程进行异步操作 * @author gao.mq */ public class LogManager { //日志记录操作延时 private final int OPERATE_DELAY_TIME = 10; //异步操作记录日志的线程池 private ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10); private LogManager() { } public static LogManager logManager = new LogManager(); public static LogManager me() { return logManager; } public void executeLog(TimerTask task) { executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); } }
4.executeLog()方法的参数是TimerTask,我们操作的时候传入的是LogTaskFactory.bussinessLog(user.getId(), bussinessName, className, methodName, msg)
/** * 日志操作任务创建工厂 * * @author gao.mq */ public class LogTaskFactory { private static Logger logger = LoggerFactory.getLogger(LogManager.class); private static OperationLogMapper operationLogMapper = Db.getMapper(OperationLogMapper.class); public static TimerTask bussinessLog(final Integer userId, final String bussinessName, final String clazzName, final String methodName, final String msg) { return new TimerTask() { @Override public void run() { OperationLog operationLog = LogFactory.createOperationLog( LogType.BUSSINESS, userId, bussinessName, clazzName, methodName, msg, LogSucceed.SUCCESS); try { //这里进行向日志表插入数据 operationLogMapper.insert(operationLog); } catch (Exception e) { logger.error("创建业务日志异常!", e); } } }; } public static TimerTask exceptionLog(final Integer userId, final Exception exception) { return new TimerTask() { @Override public void run() { String msg = ToolUtil.getExceptionMsg(exception); OperationLog operationLog = LogFactory.createOperationLog( LogType.EXCEPTION, userId, "", null, null, msg, LogSucceed.FAIL); try { //这里进行向日志表插入数据 operationLogMapper.insert(operationLog); } catch (Exception e) { logger.error("创建异常日志异常!", e); } } }; } }
在记录数据变更的时候,对比变化临时保存修改的变量。不一定需要记录,这里一并贴出来。
/** * 被修改的对象临时存放的地方 * @author gao.mq */ @Component @Scope(scopeName = WebApplicationContext.SCOPE_SESSION) public class LogObjectHolder implements Serializable{ private Object object = null; public void set(Object obj) { this.object = obj; } public Object get() { return object; } public static LogObjectHolder me(){ LogObjectHolder bean = SpringContextHolder.getBean(LogObjectHolder.class); return bean; } }
记录和比较对象属性的变化
/** * 字典映射抽象类 * @author gao.mq */ public abstract class AbstractDictMap { protected HashMap<String, String> dictory = new HashMap<>(); protected HashMap<String, String> fieldWarpperDictory = new HashMap<>(); public AbstractDictMap(){ put("id","主键id"); init(); initBeWrapped(); } /** * 初始化字段英文名称和中文名称对应的字典 */ public abstract void init(); public String get(String key) { return this.dictory.get(key); } public void put(String key, String value) { this.dictory.put(key, value); } public String getFieldWarpperMethodName(String key){ return this.fieldWarpperDictory.get(key); } public void putFieldWrapperMethodName(String key,String methodName){ this.fieldWarpperDictory.put(key,methodName); } }
使用:
@RequestMapping("/add") @ResponseBody @BussinessLog(value = "新增订单",key="contractNumber",dict = Dict.OrderMap) public Tip addOrder(Order order){ return null; }