zoukankan      html  css  js  c++  java
  • 工作中面临日志问题以及自己现有日志的备份

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

        具体是这样:

         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  希望各位大神能够入住,多多提出好的想法。也希望学弟学妹们,能够在此得到好的方向.

    成功不可复制,失败或可避免;成功都一样,失败各不同;或许失败值得你警惕;
  • 相关阅读:
    php mysql基本操作
    php之curl的使用
    linux下操作php和apache
    ThinkPHP_目录结构和初始化
    ubuntu下计划任务cron的使用
    linux下操作mysql
    php操作csv文件
    HTML5基础一:常用布局标签
    liunx下shell脚本的创建和使用
    一个完整的ajax登陆实例
  • 原文地址:https://www.cnblogs.com/mlliud/p/5275654.html
Copyright © 2011-2022 走看看