[Java] SpringBoot uses AOP for log parsing and printing + system exception global processing configuration


Preface

In order to facilitate the deployment of the project on the server, when bugs and certain special requirements arise, and the logs cannot be seen or exceptions cannot be processed quickly and timely, the role of AOP log interception and printing will be reflected. At the same time, adding a tool to generate author logs to the server will make it easier to handle problems;


提示:以下是本篇文章正文内容,下面案例可供参考

1. Import Lombok

code show as below

            <!--Lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.24</version>
            </dependency>

2. Create a log printing model

code show as below:

import lombok.Data;

/**
 * @author 孤巷.
 * @description 日志打印model
 * @date 2021/3/13 11:44
 */
@Data
public class SystemRequestLogModel {
    
    
  private String ip;
  private String url;
  private String httpMethod;
  private String classMethod;
  private Object requestParams;
  private Object result;
  private Long timeCost;
}

3. Create log aspect tool class

code show as below:

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @author 孤巷.
 * @description 方法级日志切面
 * @date 2021/3/22 11:36
 */
@Slf4j
@Component
public class RequestUtil {
    
    

    @Autowired(required = false)
    protected HttpServletRequest request; // 自动注入request

    /**
     * reqContext对象中的key: 转换好的json对象
     */
    private static final String REQ_CONTEXT_KEY_PARAMJSON = "REQ_CONTEXT_KEY_PARAMJSON";

    /**
     * 获取客户端ip地址 *
     */
    public String getClientIp() {
    
    
        String ipAddress = null;
        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();
        }

        // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
        if (ipAddress != null && ipAddress.length() > 15) {
    
    
            if (ipAddress.indexOf(",") > 0) {
    
    
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        }
        if ("0:0:0:0:0:0:0:1".equals(ipAddress)) {
    
    
            ipAddress = "127.0.0.1";
        }
        return ipAddress;
    }

    public String getRequestParams(JoinPoint proceedingJoinPoint) {
    
    
        // 参数名
        String[] paramNames =
                ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterNames();
        // 参数值
        Object[] paramValues = proceedingJoinPoint.getArgs();
        return buildRequestParam(paramNames, paramValues);
    }

    private String buildRequestParam(String[] paramNames, Object[] paramValues) {
    
    
        Map<String, Object> requestParams = new HashMap<>();
        for (int i = 0; i < paramNames.length; i++) {
    
    
            Object value = paramValues[i];
            if (value instanceof HttpServletRequest || value instanceof HttpServletResponse) {
    
    
                continue;
            }
            // 如果是文件对象
            if (value instanceof MultipartFile) {
    
    
                MultipartFile file = (MultipartFile) value;
                value = file.getOriginalFilename(); // 获取文件名
            }
            requestParams.put(paramNames[i], value);
        }

        JSONObject json = new JSONObject(requestParams);
        return json.toJSONString();
    }
}


4. Some constant classes that need to be used

code show as below:

/**
 * @author 孤巷.
 * @description 常量
 * @date 2021/3/13 11:44
 */
public class Constant {
    
    
    public static final String NULL_POINTER_EXCEPTION = "空指针异常";
    public static final String PARSE_EXCEPTION = "格式解析异常";
    public static final String ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION = "超出索引异常";
    public static final String SQL_EXCEPTION = "SQL异常";
    public static final String INTERNAL_SERVER_ERROR = "内部服务器异常";
    public static final String NETWORK_BUSY = "网络繁忙,请稍后重试";
    public static final String SQL_EXCEPTION_MESSAGE = "数据库执行异常:";
    public static final String DATE_FORMAT_ERROR = "时间格式异常";
    public static final String DATABASE_CONNECT_OUT_TIME = "数据库连接超时";
}

5. Create interface request aspect

Here comes the point! code show as below:

import cn.hutool.core.io.IORuntimeException;
import cn.hutool.crypto.CryptoException;
import cn.hutool.http.HttpException;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.whxx.common.constant.Constant;
import com.whxx.common.exception.IllegalTokenException;
import com.whxx.common.exception.NetworkBusyException;
import com.whxx.common.exception.PayException;
import com.whxx.common.exception.SysConfigException;
import com.whxx.common.model.SystemRequestLogModel;
import com.whxx.common.util.RequestUtil;
import com.whxx.common.util.ResultUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.sql.SQLException;
import java.text.ParseException;
import java.time.format.DateTimeParseException;

/**
 * @author 孤巷.
 * @description 接口请求切面
 * @date 2020/11/17 9:44
 */
@Aspect
@Component
@Slf4j
public class ControllerRequestAspect {
    
    

    @Resource
    private RequestUtil requestUtil;

    /**
     * 扫描所有Controller
     */
    @Pointcut("execution(public * com.whxx.exchange.controller.*.*(..))")
    public void request() {
    
    
    }

    @Around("request()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    
    
        long start = System.currentTimeMillis();
        ServletRequestAttributes attributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
    
    
            throw new Exception("ServletRequestAttributes is null");
        }
        HttpServletRequest request = attributes.getRequest();
        SystemRequestLogModel systemRequestLogModel = new SystemRequestLogModel();
        systemRequestLogModel.setIp(request.getRemoteAddr());
        systemRequestLogModel.setUrl(request.getRequestURL().toString());
        systemRequestLogModel.setHttpMethod(request.getMethod());
        if ("get".equalsIgnoreCase(request.getMethod())) {
    
    
            // 处理GET请求中文参数乱码
            String decodeParams = "";
            if (request.getQueryString() != null) {
    
    
                try {
    
    
                    decodeParams = URLDecoder.decode(request.getQueryString(), "utf-8"); // 将中文转码
                } catch (UnsupportedEncodingException e) {
    
    
                    e.printStackTrace();
                }
            }
            systemRequestLogModel.setRequestParams(decodeParams);
        } else {
    
    
            systemRequestLogModel.setRequestParams(
                    requestUtil.getRequestParams(proceedingJoinPoint));
        }
        systemRequestLogModel.setClassMethod(
                String.format(
                        "%s.%s",
                        proceedingJoinPoint.getSignature().getDeclaringTypeName(),
                        proceedingJoinPoint.getSignature().getName()));
        Object result;
        try {
    
    
            result = proceedingJoinPoint.proceed();
            systemRequestLogModel.setResult(result);
            systemRequestLogModel.setTimeCost(System.currentTimeMillis() - start);
            log.info("请求信息 == {}", systemRequestLogModel);
        } catch (Exception e) {
    
    
            result = exceptionGet(e, systemRequestLogModel);
        }
        return result;
    }

    private JSONObject exceptionGet(Exception e, SystemRequestLogModel systemRequestLogModel) {
    
    
        systemRequestLogModel.setResult(e);
        log.error(e.getMessage(), e);
        log.error("异常信息 == {}", systemRequestLogModel);
        if (e instanceof NetworkBusyException) {
    
     // 自定义异常:对外接口调用异常,失败/或对外接口自身报错
            return ResultUtil.error(Constant.NETWORK_BUSY);
        } else if (e instanceof PayException) {
    
     //自定义异常:支付异常
            return ResultUtil.fail(e.getMessage());
        } else if (e instanceof SysConfigException) {
    
     //自定义异常:系统配置异常
            return ResultUtil.fail(e.getMessage());
        } else if (e instanceof IllegalTokenException) {
    
    //自定义异常:非法token
            return ResultUtil.fail(e.getMessage());
        } else if (e instanceof NullPointerException) {
    
     // 空指针异常
            return ResultUtil.error(Constant.NULL_POINTER_EXCEPTION);
        } else if (e instanceof DuplicateKeyException) {
    
     // 唯一键冲突
            return ResultUtil.error(Constant.DUPLICATE_KEY);
        } else if (e instanceof ParseException) {
    
     // 时间格式解析异常
            return ResultUtil.error(Constant.PARSE_EXCEPTION);
        } else if (e instanceof ArrayIndexOutOfBoundsException) {
    
     // 超出索引异常
            return ResultUtil.error(Constant.ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION);
        } else if (e instanceof SQLException) {
    
     // sql异常
            return ResultUtil.error(Constant.SQL_EXCEPTION);
        } else if (e instanceof DateTimeParseException) {
    
     // 时间格式解析异常
            return ResultUtil.error(Constant.DATE_FORMAT_ERROR);
        } else if (e instanceof CannotGetJdbcConnectionException) {
    
     // 数据库连接超时
            return ResultUtil.error(Constant.DATABASE_CONNECT_OUT_TIME);
        }  else if (e instanceof CryptoException) {
    
     // 二维码无效
            return ResultUtil.fail(Constant.CRYPTO_ERROR);
        } else if (e instanceof IORuntimeException) {
    
      //请求被拒绝,服务器未开启
            return ResultUtil.fail(e.getMessage());
        } else if (e instanceof HttpException) {
    
     //hutool http请求超时
            return ResultUtil.fail(e.getMessage());
        } else if (e instanceof BadSqlGrammarException) {
    
    //数据库执行异常
            return ResultUtil.error(Constant.SQL_EXCEPTION_MESSAGE + ((BadSqlGrammarException) e).getSQLException().getMessage());
        } else if (e instanceof JSONException) {
    
     //JSON相关异常
            return ResultUtil.fail(e.getMessage());
        } else if (e instanceof RuntimeException) {
    
    
            return ResultUtil.error(e.getMessage());
        } else {
    
     // 其他未知异常
            return ResultUtil.error(Constant.INTERNAL_SERVER_ERROR);
        }
    }
}

Finally, you only need to change the controller path below to the actual project path.

@Pointcut(“execution(public * com.whxx.exchange.controller..(…))”)

6. System exception global configuration

code show as below:

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.whxx.common.util.ResultUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.UnexpectedTypeException;
import java.util.List;
import java.util.Objects;

/**
 * @author 孤巷.
 * @description 异常全局处理配置
 * @date 2020/11/17 9:44
 */
@RestControllerAdvice
@Slf4j
public class ExceptionControllerAdvice {
    
    

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public JSONObject methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
    
    
        BindingResult result = e.getBindingResult();
        List<ObjectError> errorList = result.getAllErrors();
        String defaultMessage = errorList.get(0).getDefaultMessage();
        log.error(String.format("参数校验异常1 == {}%s", result.getAllErrors()));
        return ResultUtil.failIllegalParameter(
                "参数【"
                        + Objects.requireNonNull(e.getFieldError()).getField()
                        + "】校验异常:"
                        + defaultMessage);
    }

    /**
     * 参数校验注解错误导致的异常
     */
    @ExceptionHandler(UnexpectedTypeException.class)
    public JSONObject unexpectedTypeException(UnexpectedTypeException e) {
    
    
        return ResultUtil.error(e.getMessage());
    }

    @ExceptionHandler(BindException.class)
    public JSONObject bindExceptionHandler(BindException e) {
    
    
        BindingResult result = e.getBindingResult();
        List<ObjectError> errorList = result.getAllErrors();
        String defaultMessage = errorList.get(0).getDefaultMessage();
        log.error(String.format("参数校验异常2 == {}%s", result.getAllErrors()));
        return ResultUtil.failIllegalParameter(
                "参数【"
                        + Objects.requireNonNull(e.getFieldError()).getField()
                        + "】异常:"
                        + defaultMessage);
    }

    @ExceptionHandler(InvalidFormatException.class)
    public JSONObject handHttpConverterException(InvalidFormatException e) {
    
    
        StringBuilder errors = new StringBuilder();
        List<JsonMappingException.Reference> path = e.getPath();
        for (JsonMappingException.Reference reference : path) {
    
    
            errors.append("参数【")
                    .append(reference.getFieldName())
                    .append("】输入不合法,需要的是 ")
                    .append(e.getTargetType().getName())
                    .append(" 类型,")
                    .append("提交的值是:")
                    .append(e.getValue().toString());
        }
        log.error("参数校验异常3 == {}", errors);
        return ResultUtil.failIllegalParameter(errors.toString());
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    public JSONObject handleMissingServletRequestParameterException(
            MissingServletRequestParameterException e) {
    
    
        StringBuilder errors = new StringBuilder();
        errors.append("参数【").append(e.getParameterName()).append("】为必传字段:").append(e.getMessage());
        log.error("参数校验异常4 == {}", errors);
        return ResultUtil.failIllegalParameter(errors.toString());
    }

    @ExceptionHandler(HttpMessageConversionException.class)
    public JSONObject handleConversionException(HttpMessageConversionException e) {
    
    
        StringBuilder errors = new StringBuilder();
        errors.append("参数校验异常5:").append(e.getMessage());
        log.error("参数校验异常 == {}", errors);
        return ResultUtil.failIllegalParameter(errors.toString());
    }
}

Summarize

The above is what I will talk about today. This article only briefly introduces the use of AOP. AOP can not only achieve the effect of log printing, but also implement many useful methods. We will talk about it in the future, so please continue to pay attention. Thanks!

Guess you like

Origin blog.csdn.net/qq_42666609/article/details/130200851