zoukankan      html  css  js  c++  java
  • 统一接口设计及日志管理

    对系统中的关键操作进行记录至关重要,尤其是在对某些重要业务或数据信息进行溯源时

    日志的记录越详细越好,但出于性能及业务等因素考虑,侧重点会各有不同

    最基本的记录至少要包括如下信息:

    1.所操作的接口

    2.操作人

    3.操作时间及设备信息

    4.进行了何种操作

    5.操作是否成功

    日志记录方式无非就两种

    1.高度代码耦合:在业务逻辑中直接调用日志记录接口

    2.采用AOP方式:AOP方式能和业务逻辑解耦

    第1种方式基本被淘汰,介绍第2种方式

    采用AOP方式记录日志,则要保证接口格式一致性,这样才能方便获取接口返回的相关信息

    接口返回应该包括几个方面:

    1.业务数据信息

    2.执行状态

    3.若失败还要返回错误码

    4.若失败还要返回错误信息

    同时为了方便统一日志记录,还应该在每个接口中返回具体的日志信息,不过不用展示出来

    所以,基本格式应该如下:

    1.成功时:

    {
        "content": {
            "sessionId": "10009",
            "userId": 10,
            "userName": "肖昌伟",
            "lastOperateTime": "2017-08-01 15:47:42",
            "token": "2d7bb2f683704cdc8baa7ccd8e993c33",
            "ip": "192.168.0.1"
        },
        "status": "OK",
        "errorCode": null,
        "errorMsg": null
    }

    2.失败时:

    {
        "errorCode": "000002",
        "errorMsg": "登录名[userName]不能为空, 当前值为[null]",
        "status": "ERROR"
    }

    在业务处理后要将具体的操作详情保存到返回接口里面(不用展示出来,只为了方便在aop中获取)

    日志记录的其它相关信息,比如访问的是那个模块的哪个接口等,可以通过自定义注解方式来实现

    package personal.changw.xiao.web.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    /**
     * @since 2017年07月31日 上午11:02:29
     * @author 肖昌伟 changw.xiao@qq.com
     * @description 日志记录标签
     * 可以使用在方法或者类上,可以根据需要决定谁的优先级更高
     */
    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RUNTIME)
    public @interface LogRecord {
    
        String system() default "";
    
        String module() default "";
    
        String menuLv1() default "";
    
        String menuLv2() default "";
    
    }

    使用方式如下:

    /**
         * 分页查找
         * @param keyWords        关键字
         * @param pageSize        每页条数 
         * @param pageNumber    页码
         * @param isPaging        是否分页(默认分页),为false时返回的条数信息请忽略
         * @return
         */
        @RequestMapping("/user/listByPage")
        @LogRecord(system="xxxx系统",module="基础信息管理",menuLv1="用户管理",menuLv2="用户列表查询")
        public Result listUserByPgae(@HibernateValidate UserQueryParam param) {
            //Page<UserInfo> page = new Page<UserInfo>(param.getIsPaging(), param.getPageNumber(), param.getPageSize());
            Page<UserInfo> page = new Page<UserInfo>(param);
            userService.listUserByPgae(page, param);
            return success(page,"查询用户列表,参数为:"+JSON.toJSONString(page));
        }

    AOP执行逻辑如下

    package personal.changw.xiao.web.aop;
    
    import java.lang.reflect.Method;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import personal.changw.xiao.web.annotation.LogRecord;
    import personal.changw.xiao.web.constant.Constants;
    import personal.changw.xiao.web.service.LogRecordService;
    import personal.changw.xiao.web.utils.IpUtil;
    import personal.changw.xiao.web.vo.common.OperaterLogVo;
    import personal.changw.xiao.web.vo.common.Result;
    import personal.changw.xiao.web.vo.common.UserSession;
    
    /**
     * @since 2017年07月31日 上午11:02:29
     * @author 肖昌伟 changw.xiao@qq.com
     * @description 日志记录AOP
     */
    public class LogRecordAOP {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(LogRecordAOP.class);
        
        @Autowired
        private LogRecordService logRecordService;
    
        @Autowired
        private HttpServletRequest request;
    
        public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
            //先查找类上的注解,没有再找方法上的注解
            LogRecord logRecord = joinPoint.getTarget().getClass().getAnnotation(LogRecord.class);
            if(logRecord == null){
                Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
                logRecord = method.getAnnotation(LogRecord.class);
            }
            if (logRecord != null) {
                Result result = (Result) joinPoint.proceed();
                UserSession userSession = (UserSession) request.getAttribute(Constants.HTTP_ATTR_SESSION_KEY);
                if (userSession != null) {
                    OperaterLogVo operatorLog = new OperaterLogVo();
                    if(result != null) {
                        operatorLog.setRemarks(result.getStatus());
                        operatorLog.setLogContent(result.getLogContent());
                    } else {
                        //系统中导出功能等返回结果为非Result的接口
                        //默认都为成功
                        operatorLog.setRemarks("OK");
                    }
                    operatorLog.setSystemName(logRecord.system());
                    operatorLog.setModuleName(logRecord.module());
                    operatorLog.setMenuLv1(logRecord.menuLv1());
                    operatorLog.setMenuLv2(logRecord.menuLv2());
        
                    String requestURI = request.getRequestURI().substring(request.getContextPath().length());
                    operatorLog.setUrl(request.getProtocol().split("/")[0] + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + requestURI);
                
                    operatorLog.setUserCode(String.valueOf(userSession.getUserId()));
                    operatorLog.setUserName(userSession.getUserName());
                    operatorLog.setIp(IpUtil.getRemoteRealIP(request));
                    try {
                        logRecordService.insertOperatorLog(operatorLog);
                    } catch (Exception e) {
                        LOGGER.error("日志记录出错:"+ e.getMessage());
                    }
                }
                return result;
            } else {
                return joinPoint.proceed();
            }
        }
    
    }

    通过上面的操作,配置好切面后就可进行日志记录了

    日志记录量是很大的,所以只记录关键地方并按期归档,最好是存在如elasticsearch、mongodb中,

    如果存在数据库中,分表是不错的选择

  • 相关阅读:
    Android java.lang.UnsupportedClassVersionError: com/android/dx/command/Main : Unsupported major.minor ver
    Android EditText光标位置(定位到最后)
    Android EditText获取光标位置并插入字符删除字符
    Android 仿微信小视频录制
    Android仿微信小视频录制功能
    Android消息机制之实现两个不同线程之间相互传递数据相互调用
    Android Data Binding 技术
    Android中解析XML
    Android 怎样把光标放在EditText中文本的末尾处?
    Hadoop HBase概念学习系列之RowKey设计(二十九)
  • 原文地址:https://www.cnblogs.com/xiaochangwei/p/7153556.html
Copyright © 2011-2022 走看看