工作中面临日志问题以及自己现有日志的备份

    问题的产生,必有其理由。说白点也就是客户需要,没办法的事。不过也到给我们添了不少麻烦。本人也希望大牛们能给在下提提更多的思路,在下在此谢过。

    具体是这样:

     1.要记录操作人员,操作时间,操作相应模块

         2.要记录操作的原始数据(ps:列级别)和变更后数据

    面临问题:

         1.各个方法相对独立,没有公共接口

         2.形参顺序不一,类型不一以及方法名称不一

 针对以上,要是解决其实也很简单,主要不怕麻烦。写个接口,每个模块去调用,这种简单而且有效的方法。不过模块太多而且繁琐这要累死个人。

   目前就我自己发现以及朋友提醒再加上资料等信息,大致将其分为2类,共四种方法:

   第一类(每个相关业务中需要手工添加执行功能部分):

        1.写日志操作类,在相关模块中添加此接口调用。这种方法比较简单,如果一开始就有这部分代码,那就更好解决了。此种方法自不必多说,跟正常接口一样。

        2.利用消息中间件,2和1实现类似,也需要每次都调用jms,不过论性能2比1好点

        3.利用log4j日志记录,相关请看:http://blog.csdn.net/ziruobing/article/details/3919501,logback也有相关

    第二类(使用Aop,拦截器等):

   1.Aop,该实现思路@BussAnnotation注解可以标示相关业务信息(ps:新增,删除,修改等操作,以及所属模块等信息,aop中形参ProceedingJoinPoint可以获取参数对象

      @Component("userManager")

 

   public class UserManagerApplogicImpl implements UserManagerApplogic {
    @BussAnnotation(moduleName="人员管理",option="添加用户")
    public void addUser(String name) {
      System.out.println("add a User!Name is "+name);
    }
   }

 

  @Aspect
  @Component
  public class LogInterceptor {
    @Pointcut("execution(public * com.mlliud..*.addUser(..))")
    public void aApplogic() {}
    @Around(value = "aApplogic() && @annotation(annotation) &&args(object,..) ", argNames = "annotation,object")
    public Object interceptorApplogic(ProceedingJoinPoint pj,BussAnnotation annotation, Object object) throws Throwable {
      System.out.println("moduleName:"+annotation.moduleName());
      System.out.println("option:"+annotation.option());
      pj.proceed();
      return object;
    }
  } 

      2.拦截器,这个简单日志还好,其他目测算了吧。个人愚笨只有想到根据访问链接记录一些日志。

      看来以上问题,针对我们项目做分析。得出一下结论:

           1.第一类在项目初期考虑或者有相关需求还好,涉及每个模块相关代码都要改,可以手工加入。要是项目结构复杂或者代码量较多,我觉得很坑.....

           2.第二类aop中@BussAnnotation注解虽然可以标示些关键信息,但是我们毕竟要把整个vo的相关变更信息记录。由于当时参数没有特殊规范,以及类型没有规划,造成ProceedingJoinPoint获取参数没有规律可寻,例如:addUser(User u,int a),updateUser(User u,String x) 这种我获取第一个参数,往数据库中存就可以。其他你懂的....

           3.拦截器....

    维护时间:2016-03-15

    经过短暂思考最后选择了,最终选择了aop。具体查看代码:

  1.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface BussAnnotation {
    String moduleName();
    int option(); /* 0:新增 1:修改 2:删除 */
    String repository();
}
View Code

     2.具体的aop

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import cn.mobilizer.channel.comm.mybatis.MyBatisDao;
import cn.mobilizer.channel.interceptor.annotation.BussAnnotation;

@Aspect
@Component
public class LogInterceptor implements ApplicationContextAware {/*ApplicationContextAware  主要是为了可以从spring容器中获取dao层对象*/
    private final static String PACKAGELAB = "cn.base";

    private static ApplicationContext context;

    private Object oldVo;
    private Object newVo;

    @SuppressWarnings("static-access")
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.context = applicationContext;
    }

    @Pointcut("execution(public * cn.base.service.ClientUserService.update(..))")
    public void recordLog() {
    }

    @Before(value = "recordLog() && @annotation(annotation) &&args(object,..) ", argNames = "annotation,object")
    public void recordLogBefore(JoinPoint point, BussAnnotation annotation, Object object) {
        int option = annotation.option();
        String repository = annotation.repository();
        Object[] args = point.getArgs();
        if (option>0) {
            setVo(args, "1", repository);
        } else {
            setVo(args, oldVo, repository);
        }
    }

    @After(value = "recordLog() && @annotation(annotation) &&args(object,..) ", argNames = "annotation,object")
    public void recordLogAfter(JoinPoint point, BussAnnotation annotation, Object object) {
        String moduleName = annotation.moduleName();
        Map<String, String> result = optionContent(oldVo, newVo);
        System.out.println(result);
    }
    /**
     * 通过反射比较对象参数值是否相等,并将其拼成json类型字符串
     */
    private Map<String, String> optionContent(Object olds, Object news) {
        Map<String, String> result = new HashMap<String, String>();
        StringBuffer oldStr = new StringBuffer("{");
        StringBuffer newStr = new StringBuffer("{");
        if (olds == null && news == null) {
            return null;
        }
        // 得到对象的类
        Class oldClazz = olds.getClass();
        Class newClazz = news.getClass();
        // 获取对象属性
        Field[] fields = oldClazz.getDeclaredFields();
        if (fields != null && fields.length > 0) {
            try {
                Method oldMethod, newMethod;
                Object oldValue, newValue;
                String fieldName;
                for (Field field : fields) {
                    fieldName = field.getName();
                    if ("serialVersionUID".equals(fieldName)) {
                        continue;
                    }
                    // 利用源对象的get方法和目标对象的set方法,来给目标对象赋值
                    oldMethod = oldClazz.getMethod("get" + uppercaseFirst(fieldName));
                    newMethod = newClazz.getMethod("get" + uppercaseFirst(fieldName));
                    oldValue = oldMethod.invoke(olds);
                    newValue = newMethod.invoke(news);
                    if (oldValue != null) {
                        if (!oldValue.equals(newValue)) {
                            oldStr.append("\"").append(fieldName).append("\":").append(oldValue).append(",");
                            newStr.append("\"").append(fieldName).append("\":").append(newValue).append(",");
                        }
                    } else {
                        if (newValue != null) {
                            oldStr.append("\"").append(fieldName).append("\":").append(oldValue).append(",");
                            newStr.append("\"").append(fieldName).append("\":").append(newValue).append(",");
                        }
                    }
                }
                oldStr.append("}");
                newStr.append("}");
                result.put("old", oldStr.toString());
                result.put("new", newStr.toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return result;
    }
    private String uppercaseFirst(String name) {
        String firstLab = name.substring(0, 1);
        return name.replaceFirst(firstLab, firstLab.toUpperCase());
    }

    private String getFieldValue(Object obj, String fieldName) {
        // 得到对象的类
        Class clazz = obj.getClass();
        String result = null;
        try {
            Method method = clazz.getMethod("get" + uppercaseFirst(fieldName));
            result = String.valueOf(method.invoke(obj));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    private void setVo(Object[] args, Object oldVo, String repository) {
        if (args != null) {
            try {
                String clazzInfo;
                /*这里是获取我们自己封装的vo,obj.getClass().getName()该会获取包+类(ps:cn.base.po.ClientUser),我是根据包判断是否是我们自己的*/
                for (Object obj : args) {
                    clazzInfo = obj.getClass().getName();
                    if (clazzInfo.indexOf(PACKAGELAB) != -1) {
                        newVo = obj;
                        break;
                    }
                }
                if (!"1".equals(oldVo)) {
                    /*MyBatisDao为dao层公共集成方法*/
                    MyBatisDao dao = (MyBatisDao) context.getBean(repository)/*从spring容器中获取bean,根据名称获取*/;
                    Map<String, Object> paramMap = new HashMap<String, Object>();
                    paramMap.put("clientUserId", getFieldValue(newVo, "clientUserId"));
                    this.oldVo = dao.load("selectByPrimaryKey", paramMap);/*执行获取数据方法*/
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
View Code

     3.MyBatisDao中代码

    /* 日志使用 */
    @SuppressWarnings({"unchecked"})
    public <T> T load(String key, Object params) {
        if (params != null) {
            return (T) getSqlSession().selectOne(createStatementName(key), params);
        } else {
            return null;
        }
    }
View Code

     4.具体方法上添加以下代码

     @BussAnnotation(moduleName = "人员管理", option = 1, repository = "clientUserDao")
      public int update(ClientUser clientUser, String oldRoleNames, Integer oldParentId)

    以上总结,不管是添加,修改封装的实体vo,都是变更后的,可以根据其vo的id从数据库中 查询。所以用前置通知获取拦截方法没有执行前的数据库信息。删除想法是根据int option = annotation.option();获取类型判断是否是删除,如果是删除获取固定第几个形参,在进行操作。

     维护时间:2016-03-16

     遇到问题以及变更:

     1.该BussAnnotation新增int keyLocat(); /* 关键参数存放位置 新增和修改是vo位置 删除是id位置 */

     使用:@BussAnnotation(moduleName = "人员管理", option = 1, repository = "clientUserDao", keyLocat = 1)

     2.LogInterceptor类中@After用@AfterReturning替换。具体原因:@After不管拦截方法是否正确执行都会执行(报错),@AfterReturning只有aop拦截方法正确运行才会执行

     3.@AfterReturning 该方法中报错不影响拦截的方法,不用担心该报错会被事务影响

     4.问题  本来我们使用的是多数据源,但是经过测试发现@AfterReturning的方法中切换数据源无效,最终选择了表迁移

     5.@Pointcut("execution(public * cn.base.*.service.*.*(..))")

      public void recordLog() {
     }

 

     @Before(value = "recordLog() && @annotation(annotation) &&args(object,..) ", argNames = "annotation,object")
     public void recordLogBefore(JoinPoint point, BussAnnotation annotation, Object object) {

     这种我发现    @BussAnnotation(moduleName = "人员管理", option = 1, repository = "clientUserDao")才会执行,也变相可以说只有写才会被拦截,你定义全部方法也没问题

 

     写此篇文章希望各位大神能给咱提下想法,寻找更好方法....

        也希望在此宣传下群:189770377  希望各位大神能够入住,多多提出好的想法。也希望学弟学妹们,能够在此得到好的方向.

 

posted @ 2016-03-14 14:37  ╰︶赖床专业户こ  阅读(616)  评论(0编辑  收藏  举报