Separation and unified processing of Java logs and exceptions

    How to elegantly decouple business logic from logging and exception handling. The following is my personal understanding and processing method. Friends who have better ideas are welcome to leave a message to discuss.

    A common method to separate business logic and non-business logic code is AOP. Without further ado, just look at the code.

    Let's talk about log processing first. First, define an annotation class:

/**
 * Author : Nathan.yang
 * Date   : 2017/12/6
 * Email  : [email protected]
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface Log {
}
/**
 * Author : Nathan.Yang
 * Date   : 2017/12/6
 * Email  : [email protected]
 */
@Aspect
@Order(1)
@Component
public class WebLogAspect {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    ThreadLocal<Long> startTime = new ThreadLocal<Long>();

    //Define all functions under the package of the pointcut
    @Pointcut("@annotation(cn.com.nathanyang.nathan_tool.log.Log)")
    public void webLog(){}

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        startTime.set(System.currentTimeMillis());

        // Receive the request, record the content of the request
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // record the content of the request
        logger.info("AOP URL : " + request.getRequestURL().toString());
        logger.info("AOP HTTP_METHOD : " + request.getMethod());
        logger.info("AOP IP : " + request.getRemoteAddr());
        logger.info("AOP CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("AOP ARGS : " + Arrays.toString(joinPoint.getArgs()));

    }

    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        // After processing the request, return the content
        logger.info("AOP RESPONSE : " + JSON.toJSONString(ret));
        logger.info("AOP SPEND TIME : {}ms", (System.currentTimeMillis() - startTime.get()));
    }

    The doBefore method will be called before the called target method runs. The doBefore method can print out some information related to the target method, such as: request path, method name, parameters, etc.

    The doAfterReturning method will be called after the target method has finished running. This method can print out the running result of the target method and count the time-consuming of this method.

    The processing of the log is almost complete.

    Next is the unified handling of exceptions. Similarly, we can also use AOP to catch method exceptions. But before that, we need to deal with the exception class and exception encoding.

    The first is the Result class, which is the specification class returned by the interface, including three important parts: code (the code of the success or failure of the operation) msg ​​(the accompanying description of the operation result) ext (the return information that the operation may carry).

/**

 * Author : Nathan.yang
 *
 * Date   : 2018/1/23
 * Email  : [email protected]
 */
public class Result<T> implements Serializable {
    private String code;

    private String msg;

    private T ext;

    With Result, we can design ResultCode. Define commonly used variables in ResultCode.

/**

 * Author : Nathan.yang
 *
 * Date   : 2018/1/23
 * Email  : [email protected]
 */
public class ResultCode {
    private String code;
    private String msg;

    public final static String SUCCESSCODE = "0";
    public final static String FAILCODE = "-1";
    private final static ResultCode SUCCESS = new ResultCode(SUCCESSCODE,"SUCCESS");
    private final static ResultCode FAIL = new ResultCode(FAILCODE,"FAIL");

    With ResultCode, the next step is to define our exception encoding information according to ResultCode. Considering that different modules are developed separately, ResultCode is encapsulated for each module, and its own exception code prefix is ​​defined. This way there will be no coding duplication issues.

/**
 * Author : Nathan.Yang
 * Date   : 2018/2/24
 * Email  : [email protected]
 */
public class SysCode extends ResultCode {
    private static String pre = "00";

    public SysCode(String code, String msg) {
        super(pre + code, msg);
    }
}
/**
 * Author : Nathan.Yang
 * Date   : 2018/2/24
 * Email  : [email protected]
 */
public class SysCodeMap {
    public static final SysCode SYS_ERROR = new SysCode("001","系统异常");
}

Encapsulation of exception classes

/**
 * Author : nathan.yang
 * Date   : 2017/11/15.
 * Email  : [email protected]
 */
public class BaseException extends RuntimeException {
    private ResultCode resultCode;

    public BaseException(ResultCode resultCode) {
        super(resultCode.getMsg());
        this.resultCode = resultCode;
    }

    public ResultCode getResultCode() {
        return resultCode;
    }

    public void setResultCode(ResultCode resultCode) {
        this.resultCode = resultCode;
    }
}

Well, the preparations are complete, and the next step is to capture and handle exceptions.

@Around("webLog()")
    public Object doException(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        try{
            return proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
        }catch (Exception e){
            Result result;
            if (e instanceof BaseException){
                ResultCode resultCode = ((BaseException)e).getResultCode();
                result = Result.fail(resultCode);
                logger.warn("Custom exception: {}",resultCode.getMsg());
            }else if (e instanceof HttpRequestMethodNotSupportedException){
                result = Result.fail(ResultCode.fail(e.getMessage()));
                logger.warn("HttpRequestMethodNotSupportedException:{}",e.getMessage());
            }else if (e instanceof MethodArgumentNotValidException){
                List<ObjectError> list = ((MethodArgumentNotValidException)e).getBindingResult().getAllErrors();
                StringBuffer buffer = new StringBuffer();
                list.forEach((error) -> {
                    if(error instanceof FieldError) {
                        buffer.append(((FieldError)error).getField()).append(":");
                    }

                    buffer.append(error.getDefaultMessage()).append(",");
                });
                result = Result.fail(ResultCode.fail(buffer.toString()));
                logger.warn("Parameter exception:{}",e.getMessage());
            }else if (e instanceof HttpMessageNotReadableException){
                result = Result.fail(ResultCode.fail("please check your request body!"));
                logger.warn("Parameter exception: {}",e.getMessage());
            }else if (e instanceof MethodArgumentTypeMismatchException){
                result = Result.fail(ResultCode.fail("please check your parameter type!"));
                logger.warn("Parameter exception: {}",e.getMessage());
            }else{
                result = Result.fail(ResultCode.fail());
                logger.warn("System exception: {}",e.getMessage());
            }
            return result;
        }

    }

    Use Around to catch the exception, if it is a custom exception, get the ResultCode from the exception to construct the Result and return it. If it is not a custom exception, construct the Result based on the exception information and return it.

    Next see how to use it.

/**
     * register
     * @param account
     * @param pw
     * @return
     */
    @PostMapping("/user/register")
    @ValidateGroup(fileds = {
            @ValidateField(index = 0,notNull = true),
            @ValidateField(index = 1,notNull = true)
    })
    @Log
    public Result register(String account,String pw){
        return Result.success(userService.register(account, pw));
    }
@Override
    public User register(String account, String pw) {
        //check duplicate
        User user = userRepository.findByAccount(account);
        if (null != user){
            throw new BaseException(PermissionCodeMap.USER_EXIST);
        }

    The above is the unified separation of Java log and exception handling.

    Original article, please note the source for reprinting, thank you!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325979503&siteId=291194637