操作日志的内容记录及比对

emmm,还是好久没更新博客了。老样子,懒,哈哈。

ok,回归正题,最近项目需要一个操作日志的内容比对,也就是哪些人在哪个业务的操作了哪些内容,操作前和操作后的内容比对及其他信息做为记录供人查阅。

最先想到的肯定是spring aop 拦截特定方法+自定义注解获取内容,操作前后的内容用json记录入库,比对内容可能就是jsonObject来进行比对。

因项目的特殊性和业务实体里可能会有循环引用等因素,在寻找了一些资料后决定

自定义注解 + MethodInterceptor + fastjson + jsondiffpatch + 消息队列

  • 自定义注解用来注明一些内容信息及要做记录的方法
  • 实现MethodInterceptor的invoke做方法前后及异常信息的拦截,request通过RequestContextListener的上下文来获取一些请求信息
  • fastjson做实体序列化和反序列化(因项目反序列化特殊性fastjson需要1.2.28+版本)
  • jsondiffpatch做内容比对 (jsondiffpatch的online demo
  • 消息队列做解析及入库解耦

先建立个自定义注解MyLog

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface MyLog {

    // 模块名
    String module() default "";

    // 操作内容
    String operate() default "";

    // 备注
    String remark() default "";

    //所属业务model class baseModel为业务基类
    Class<? extends BaseModel> clazz() default BaseModel.class;

}

实现methodInterceptor【也可以自己使用spring aop去设置拦截 请自行查询相关资料】


import com.alibaba.fastjson.JSON;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 *
 */
@Component
public class MyInterceptor implements MethodInterceptor {


    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {

        Method method = methodInvocation.getMethod();
//        if(method.isAnnotationPresent(MyLog.class)){
//            //未检测到注解则放行
//            return methodInvocation.proceed();
//        }

        MyLog myLog = method.getAnnotation(MyLog.class);
        if(myLog != null){
            //模块
            String module = myLog.module();
            //操作
            String operation = myLog.operation();
            //备注信息
            String remark = myLog.remark();
            //拦截的类
            Class<? extends BaseModel> clazz =  myLog.clazz();
        }

        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        if (request != null){
            //获取请求信息
            Map<String,String[]> parameterMap = request.getParameterMap();
            //请求头
            Map<String,String> headerMap = getHeader(request);

            //因项目的拦截方法请求request中会带id
            String id = request.getParameter("id");
            if(StringUtils.isNotBlank(id)){
//                BaseModel model = baseService.findById(id,clazz);
                //转json
//                 String json = JSON.toJSONString(model);
            }
        }

        //异常信息
        StringBuilder exceptionMessage = new StringBuilder();
        //方法返回值
        Object returnObj = null;
        //异常
        Exception exception = null;
        try {
            returnObj = methodInvocation.proceed();
        }catch (Exception e){
            exception = e;
            exceptionMessage.append(e.getClass().getName() + " : " + e.getMessage() + "\n");
            StackTraceElement[] trace = e.getStackTrace();
            for (StackTraceElement s : trace) {
                exceptionMessage.append("\tat " + s + "\r\n");
            }
        }

        //记录日志实体
        //log.setxxx(xxx);

        //发送消息队列
        //sendMessageService.send(logObject);


        if(exception != null){
            throw exception;
        }

        return returnObj;
    }


    /**
     * 获取header参数内容
     *
     * @return
     */
    private Map<String, String> getHeader(HttpServletRequest request) {
        Map<String, String> map = new HashMap<>(0);
        Enumeration headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            String value = request.getHeader(key);
            map.put(key, value);
        }
        return map;
    }

}

配置spring的自动代理

 <!-- 通知器或切面 -->
    <bean id="operationLogAroundMethodPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice">
            <ref bean="operationLogAroundAdvice"/>
        </property>
        <property name="patterns">
            <list>
               <!-- 拦截全部方法 -->  
                <value>.*</value>
            </list>
        </property>
    </bean>

    <!-- 自动代理 -->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="beanNames">
            <list>
               <!-- 哪些需要代理  -->
                <value>*Service</value>
            </list>
        </property>
        <property name="interceptorNames">
            <list>
                <value>operationLogAroundMethodPointcutAdvisor</value>
            </list>
        </property>
    </bean>


使用方式就是在你的service接口上加入Mylog注解进行信息配置啦~

    @MyLog(module = "模块名", operate = "新增操作",clazz = XXXXX.class)
    String add(BaseModel model) throws Exception;

业务逻辑就是大概这样:

拦截要记录的接口,获取必要信息,从数据库获取操作前的数据转json存入库,同时记录该业务id和业务类,查看日志的时候从库中读取新的操作后的对象,转成操作后的json  2个json进行比对即可。


至于jsondiffpatch怎么使用官方比我讲的明白

https://www.npmjs.com/package/jsondiffpatch


参考:
https://github.com/alibaba/fastjson/wiki/PropertyNamingStrategy_cn
https://github.com/alibaba/fastjson/wiki/%E5%BE%AA%E7%8E%AF%E5%BC%95%E7%94%A8
https://github.com/alibaba/fastjson/wiki/incompatible_change_list
https://github.com/alibaba/fastjson/wiki/enable_autotype

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章