When you work hard to a certain extent, luck will come across you by chance

One, foreword

The appearance of the log is to record the operation behavior of the operator in the system, which can effectively save the operation of the user, so that there are traces to follow. At the same time, the good log specification can quickly and effectively locate the problem.

Second, the overview

2.1 Implementation Roadmap

Using Spring’s annotation-based AOP to achieve the logging function, AOP also conforms to the open and closed principle, the modification of code is forbidden, the extension of code is allowed. The current log version records several important functions, such as operator information, operation method, operation running time, request parameters and return parameters, and can be modified or supplemented if required.

  • Preparation, design the log table, which fields are needed.
  • Custom annotations, which attributes need to be added to the annotations to identify the type of operation
  • Generally, a pointcut is required in the pointcut class, and the operation Around the pointcut can be @Before the pointcut or @After, but this project uses the form @around in the middle, which can record the operation time of users. Surround notifications can do everything pre, post, and final.
  • Log in: After writing the notification, finally insert the log information.
2.2 AOP implementation process

Third, database design

Operation log table:

field type describe
Oper_id bigint The primary key id
Oper_model Varchar(64) Operation module
Oper_desc Varchar(64) Operation description
Oper_requ_param Varchar(1024) Request parameters
Oper_resp_param Varchar(1024) Returns the parameter
Oper_user_id Integer The operator id
Oper_user_name Varchar(255) Operator name
Oper_uri Varchar(255) The request uri
Oper_ip Varchar(64) Request IP
Oper_create_time datetime Operating time
Oper_finish_time bigint Request duration: ms

Four, code implementation

4.1 Custom annotations
@target (ElementType.METHOD) // The Target location where annotations are placed,METHOD is annotable at the METHOD level @retention (retentionPolicy.runtime) // What phase annotations are executed, @documented Public @Interface OperationLog {String operModul() default ""; String operDesc() default ""; }}Copy the code
4.2 cut class
@Aspect  //声明这是一个切面类
@Component  //此类交由Spring容器管理
public class OperLogAspect {

    @Autowired
    private OperLogService operLogService;

    //设置操作日志切入点,记录操作日志, 在注解的位置切入代码
    @Pointcut("@annotation(com.giant.cloud.component.aop.OperationLog)")
    public void operLogPointCut(){
    }

    @Around(value = "operLogPointCut()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Exception {
        // 1.方法执行前的处理,相当于前置通知
        // 获取方法签名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取方法
        Method method = methodSignature.getMethod();
        // 从切面织入点处通过反射机制获取织入点处的方法
        Map<String,Object> joinPointInfo=getJoinPointInfoMap(joinPoint);
        //获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        // 创建一个日志对象(准备记录日志)
        SubOperLog operLog=new SubOperLog();

        Long startTime=System.currentTimeMillis();
        operLog.setOperCreateTime(DateUtil.date(startTime));//操作时间
        Object result = null;

        //让代理方法执行
        try {
            result = joinPoint.proceed();
            // 获取方法上面的注解
            OperationLog operationLog = method.getAnnotation(OperationLog.class);
            if (operationLog != null) {
                String operModul = operationLog.operModul();
                String operDesc = operationLog.operDesc();
                operLog.setOperModel(operModul);                // 操作模块
                operLog.setOperDesc(operDesc);                  // 操作描述
            }
            // 请求的参数
            Map<String, String> rtnMap = converMap(request.getParameterMap());
            operLog.setOperRespParam(JSON.toJSONString(result));  //返回参数
            operLog.setOperUri(request.getRequestURI());          //请求uri
            operLog.setOperIp(getIpAddr(request));                //获取ip
            //完善请求信息
            Long returnTime=System.currentTimeMillis();
            operLog.setOperFinishTime(returnTime-startTime);   //耗时时间

            if(request.getRequestURI().equalsIgnoreCase("/login")){
                if(StrUtil.isNotEmpty(joinPointInfo.get("paramMap").toString())){
                    operLog.setOperRequParam(joinPointInfo.get("paramMap").toString());//请求参数
                    String operRequParam = operLog.getOperRequParam();
                    String resp=operLog.getOperRespParam();

                    if(StrUtil.isNotEmpty(resp)){
                        ObjectMapper objectMapper = new ObjectMapper();
                        ResponseData  responseData=objectMapper.readValue(resp,ResponseData.class);
                        SubUserVO subUserVO=objectMapper.convertValue(responseData.getData(),SubUserVO.class);
                        operLog.setOperUserName(subUserVO.getName());
                        operLog.setSysId(subUserVO.getSysId());
                        operLog.setOperUserId(subUserVO.getId());
                        operLogService.insertLog(operLog);
                    }
                }
            }else{
                if(StrUtil.isNotEmpty(joinPointInfo.get("paramMap").toString())){
                    operLog.setOperRequParam(joinPointInfo.get("paramMap").toString());//请求参数
                    String operRequParam = operLog.getOperRequParam();
                    if(StrUtil.isNotEmpty(operRequParam)){
                        ObjectMapper objectMapper = new ObjectMapper();
                        RequestData requestData = objectMapper.readValue(operRequParam, RequestData.class);
                        SubUserVO user = objectMapper.convertValue(requestData.getUser(), SubUserVO.class);
                        operLog.setOperUserId(user.getId());
                        operLog.setSysId(user.getSysId());
                        operLog.setOperUserName(user.getName());
                        operLogService.insertLog(operLog);
                    }
                }
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return result;
    }
/**
     * 转换request 请求参数
     *
     * @param paramMap request获取的参数数组
     */
    private Map<String, String> converMap(Map<String, String[]> paramMap) {
        Map<String, String> rtnMap = new HashMap<String, String>();
        for (String key : paramMap.keySet()) {
            rtnMap.put(key, paramMap.get(key)[0]);
        }
        return rtnMap;
    }

    /**
     * 获取IP地址的方法
     * @param request   传一个request对象下来
     * @return
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals("127.0.0.1")) {
                    // 根据网卡取本机配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    ipAddress = inet.getHostAddress();
                }
            }
            // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
                // = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress="";
        }
        // ipAddress = this.getRequest().getRemoteAddr();

        return ipAddress;
    }


    /**
     * 获得切入点方法信息
     * @Author xiao lei
     * @Date  2020/12/18 11:22
     * @param  * @param joinPoint
     * @return java.util.Map<java.lang.String,java.lang.Object>
     **/
    private static Map<String, Object> getJoinPointInfoMap(JoinPoint joinPoint) {
        Map<String,Object> joinPointInfo=new HashMap<>();
        String classPath=joinPoint.getTarget().getClass().getName();
        String methodName=joinPoint.getSignature().getName();
        joinPointInfo.put("classPath",classPath);
        Class<?> clazz=null;
        CtMethod ctMethod=null;
        LocalVariableAttribute attr=null;
        int length=0;
        int pos = 0;

        try {
            clazz = Class.forName(classPath);
            String clazzName=clazz.getName();
            ClassPool pool=ClassPool.getDefault();
            ClassClassPath classClassPath=new ClassClassPath(clazz);
            pool.insertClassPath(classClassPath);
            CtClass ctClass=pool.get(clazzName);
            ctMethod=ctClass.getDeclaredMethod(methodName);
            MethodInfo methodInfo=ctMethod.getMethodInfo();
            CodeAttribute codeAttribute=methodInfo.getCodeAttribute();
            attr=(LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
            if(attr==null){
                return joinPointInfo;
            }
            length=ctMethod.getParameterTypes().length;
            pos= Modifier.isStatic(ctMethod.getModifiers())?0:1;

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NotFoundException e) {
            e.printStackTrace();
        }
        Map<String,Object> paramMap=new HashMap<>();
        Object[] paramsArgsValues=joinPoint.getArgs();
        String[] paramsArgsNames=new String[length];
        for (int i=0;i<length;i++){
            paramsArgsNames[i]=attr.variableName(i+pos);
            String paramsArgsName=attr.variableName(i+pos);
            if(paramsArgsName.equalsIgnoreCase("request")||
                    paramsArgsName.equalsIgnoreCase("response")||
                    paramsArgsName.equalsIgnoreCase("session")
            ){
                break;
            }
            Object paramsArgsValue = paramsArgsValues[i];
            paramMap.put(paramsArgsName,paramsArgsValue);
            joinPointInfo.put("paramMap",JSON.toJSONString(paramsArgsValue));
        }
        return joinPointInfo;
    }
}

Copy the code
4.3 the results

Custom annotation information on the service layer to be displayed. The final display result is as follows: