我是靠谱客的博主 英俊奇迹,最近开发中收集的这篇文章主要介绍SpringBoot项目异常分类&全局日志打印1. 在gateway项目内 网关过滤器内部异常处理2. 普通SpringBoot项目,异常处理,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

  • 1. 在gateway项目内 网关过滤器内部异常处理
    • 1. 1 配置类
    • 1.2 自定义异常处理器
    • 1.3 异常处理
  • 2. 普通SpringBoot项目,异常处理
    • 2.1 异常分类
    • 2.2 切面
    • 2.3 上下文
    • 2.4 异常捕捉
    • 2.5 如果是入参校验上面的

1. 在gateway项目内 网关过滤器内部异常处理

1. 1 配置类

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;
import java.util.Collections;
import java.util.List;

/**
 * 网关内部(比如 过滤器) 发生异常 配置
 *
 * @author wangqinglong01
 */
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnClass(WebFluxConfigurer.class)
@AutoConfigureBefore(WebFluxAutoConfiguration.class)
@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
public class ExceptionAutoConfig {

    private ServerProperties serverProperties;

    private ApplicationContext applicationContext;

    private ResourceProperties resourceProperties;

    private List<ViewResolver> viewResolvers;

    private ServerCodecConfigurer serverCodecConfigurer;

    public ExceptionAutoConfig(ServerProperties serverProperties,
                               ResourceProperties resourceProperties,
                               ObjectProvider<List<ViewResolver>> viewResolversProvider,
                               ServerCodecConfigurer serverCodecConfigurer,
                               ApplicationContext applicationContext) {
        this.serverProperties = serverProperties;
        this.applicationContext = applicationContext;
        this.resourceProperties = resourceProperties;
        this.viewResolvers = viewResolversProvider
                .getIfAvailable(() -> Collections.emptyList());
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    /**
     * 网关 底层 是 webflux 的响应式编程
     * org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration
     * 会调用如下
     * org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler#renderErrorResponse方法
     *所以
     * 要比 ErrorWebFluxAutoConfiguration 小,表示其优先调用
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
        // 注入自己的 CustomExceptionHandler 处理类
        DefaultErrorWebExceptionHandler exceptionHandler = new CustomExceptionHandler(
                errorAttributes, this.resourceProperties,
                this.serverProperties.getError(), this.applicationContext);
        exceptionHandler.setViewResolvers(this.viewResolvers);
        exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
        return exceptionHandler;
    }
}

1.2 自定义异常处理器

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.*;
import reactor.core.publisher.Mono;

import java.util.Map;

/**
 * 自定义网关web层异常处理器
 *
 * @author wangqinglong01
 */
@Slf4j
@SuppressWarnings(ComConstant.ALL)
public class CustomExceptionHandler extends DefaultErrorWebExceptionHandler {

    @Autowired
    private GateWayException gateWayException;


    public CustomExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
                                  ErrorProperties errorProperties, ApplicationContext applicationContext) {
        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
    }


    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

    /**
     * 重写 异常方法 塞入自己的 GateWayException
     */
    @Override
    protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
        boolean includeStackTrace = isIncludeStackTrace(request, MediaType.ALL);
        Map<String, Object> error = getErrorAttributes(request, includeStackTrace);
        Throwable throwable = getError(request);
        return ServerResponse.status(super.getHttpStatus(error))
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .body(BodyInserters.fromObject(gateWayException.handle(throwable)));

    }
}

1.3 异常处理

import io.netty.channel.ConnectTimeoutException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * 网关内部异常
 *
 * @author wangqinglong01
 */
@Slf4j
@Component
public class GateWayException {


    @ExceptionHandler(value = {Exception.class})
    public CustomResult handle(Throwable ex) {

        if (ex instanceof ConnectTimeoutException) {
            return timeoutHandle((ConnectTimeoutException) ex);
        }
        return CustomResult.R(ResultCodeEnum.ERROR_GATEWAY, null, ErrorUtil.getErrorStackTraceMsg(ex));
    }


    /**
     * 网关超时 异常
     */
    @ExceptionHandler(value = {ConnectTimeoutException.class})
    @ResponseStatus(HttpStatus.GATEWAY_TIMEOUT)
    public CustomResult timeoutHandle(ConnectTimeoutException ex) {
        log.error("connect timeout exception:{}", ex.getMessage());
        return CustomResult.R(ResultCodeEnum.ERROR_GATEWAY, "网关超时异常",ErrorUtil.getErrorStackTraceMsg(ex));
    }
}

2. 普通SpringBoot项目,异常处理

2.1 异常分类

分为前端异常和后端异常

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

/**
 * 请求客户端异常
 *
 * @author wangqinglong01
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
public abstract class AbstractRequestException extends RuntimeException {
    private static final long serialVersionUID = -8567398013218726753L;


    /**
     * 异常码
     */
    private int code;

    /**
     * 异常描述
     */
    private String description;


    protected AbstractRequestException(String message, int code, String description) {
        super(message);
        this.code = code;
        this.description = description;
    }

    protected AbstractRequestException(ResultEnum resultEnum) {
        super(resultEnum.getDetail());
        this.code = resultEnum.getCode();
        this.description = resultEnum.getDetail();
    }
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

/**
 * 服务端异常
 *
 * @author wql
 * @date 2022/10/20 11:24
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
public abstract class AbstractServerException extends RuntimeException {
    private static final long serialVersionUID = 6916461514992309856L;

    /**
     * 异常码
     */
    private int code;

    /**
     * 异常描述
     */
    private String description;


    public AbstractServerException(String message, int code, String description) {
        super(message + description);
        this.code = code;
        this.description = description;
    }

    public AbstractServerException(ResultEnum resultEnum) {
        super(resultEnum.getDetail());
        this.code = resultEnum.getCode();
        this.description = resultEnum.getDetail();
    }
}
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 客户端参数异常
 *
 * @author wql
 * @date 2022/10/20 11:38
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class RequestException extends AbstractRequestException {
    private static final long serialVersionUID = 5139819692587196984L;

    /**
     * 异常码
     */
    private int code;

    /**
     * 异常描述
     */
    private String description;


    public RequestException(String message, int code, String description) {
        super(message, code, description);
    }

    public RequestException(ResultEnum resultEnum) {
        super(resultEnum);
    }

    public RequestException() {
        super(ResultEnum.FAILURE_CLIENT);
    }
}

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 服务端操作异常
 *
 * @author wql
 * @date 2022/10/20 11:38
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class ServerException extends AbstractServerException {
    private static final long serialVersionUID = -665147796030183682L;

    /**
     * 异常码
     */
    private int code;

    /**
     * 异常描述
     */
    private String description;


    public ServerException(String message, int code, String description) {
        super(message, code, description);
    }

    public ServerException(ResultEnum resultEnum) {
        super(resultEnum);
    }

    public ServerException() {
        super(ResultEnum.FAILURE_SERVER);
    }
}

2.2 切面

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
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.core.annotation.Order;
import org.springframework.stereotype.Component;
import static org.slf4j.MDC.clear;

/**
 * 出入参切面
 *
 * @author wangqinglong01
 */
@Slf4j
@Aspect
@Component
@Order(1)
public class LogAspect {


    @Pointcut("execution(public * com.xxx.xxx.controller..*.*(..)) &&" +
            "!execution(* com.xxx.xxx.controller.RosterExcelController.*(..))")
    public void pointcut() {}


    /**
     * 环绕通知
     *
     * @param joinPoint 切入点
     * @return object
     */
    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        initContext(joinPoint);
        ContextLogBO contextLogBO = ContextLogUtil.get();
        StopWatch sw = StopWatch.createStarted();
        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Exception e) {
            contextLogBO.setSuccess(Boolean.FALSE);
            contextLogBO.setCode(ResultEnum.FAILURE.getCode());
            contextLogBO.setErrMsg(ErrorUtil.getErrorStackTraceMsg(e));
            throw e;
        } finally {
            sw.stop();
            contextLogBO.setCost(sw.getTime());
            contextLogBO.setRequestResult(result);
            mdc(contextLogBO);
            clear();
        }
        return result;
    }
}

2.3 上下文

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 全局日志工具类
 *
 * @author wql
 * @date 2022/9/27 13:09
 */
@Slf4j
@SuppressWarnings("ALL")
public class ContextLogUtil {


    private static final ThreadLocal<ContextLogBO> CONTEXT = TransmittableThreadLocal.withInitial(ContextLogBO::new);

    public static ContextLogBO get() {
        return CONTEXT.get();
    }

    public static void put(ContextLogBO contextLogBO) {
        CONTEXT.set(contextLogBO);
    }

    public static void clear() {
        CONTEXT.remove();
    }


    /**
     * 根据请求初始化上下文对象
     */
    public static void initContext(ProceedingJoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        ContextLogBO contextLogBO = new ContextLogBO();
        contextLogBO.setOutLists(Lists.newArrayList());
        contextLogBO.setSuccess(Boolean.TRUE);
        contextLogBO.setCode(ResultEnum.SUCCESS.getCode());
        contextLogBO.setMethodType(request.getMethod());
        contextLogBO.setUrl(request.getRequestURL().toString());
        contextLogBO.setIp(IpUtil.getIpAddr(request));
        contextLogBO.setTraceId(request.getHeader(X_TRACE_ID));
        contextLogBO.setSpanId(request.getHeader(X_SPAN_ID));
        contextLogBO.setRequestId(UuidUtil.getUuid());
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String method = signature.getDeclaringTypeName() + StrUtil.DOT + signature.getName();
        contextLogBO.setMethod(method);
        contextLogBO.setRequestParams(getFieldsName(joinPoint));
        put(contextLogBO);
    }


    private static Map<String, Object> getFieldsName(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] parameterNames = u.getParameterNames(method);
        if (parameterNames != null) {
            Map<String, Object> paramMap = new HashMap<>(parameterNames.length);
            for (int i = 0; i < parameterNames.length; i++) {
                paramMap.put(parameterNames[i], args[i]);
            }
            return paramMap;
        }
        return Collections.emptyMap();
    }


    public static void mdc(ContextLogBO contextLogBO) {
        Map<String, Object> map = Maps.newHashMap();
        try {
            map = JSONObject.parseObject(JSON.toJSONString(contextLogBO), Map.class);
        } catch (Exception e) {
            log.error(ErrorUtil.getErrorStackTraceMsg(e));
        } finally {
            map.forEach((k, v) -> {
                if (Objects.nonNull(v)) {
                    if (v instanceof Integer) {
                        MDC.put(k, String.valueOf(v));
                    } else if (v instanceof String) {
                        MDC.put(k, (String) v);
                    } else {
                        MDC.put(k, JSON.toJSONString(v));
                    }
                }
            });
        }
        log.info("Log:{}", JSON.toJSONString(contextLogBO, SerializerFeature.WriteMapNullValue));
        MDC.clear();
    }


    public static void handLog(OutRpcBO outRpcBO) {
        ContextLogBO bo = get();
        bo.getOutLists().add(outRpcBO);
    }
}

2.4 异常捕捉

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 基础异常
 *
 * @author zhangshuai01
 */
@Slf4j
@RestControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class AppGlobalExceptionHandler {


    @ExceptionHandler(value = {AbstractRequestException.class})
    public Object clientExceptionHandler(Exception e) {
        setLogParams(ResultEnum.FAILURE_CLIENT.getCode(), e);
        return ResponseBaseVo.fail(ResultEnum.FAILURE_CLIENT.getCode(), e.getMessage());
    }


    @ExceptionHandler({AbstractServerException.class})
    public Object serverExceptionHandler(Exception e) {
        setLogParams(ResultEnum.FAILURE_SERVER.getCode(), e);
        return ResponseBaseVo.fail(ResultEnum.FAILURE_SERVER.getCode(), e.getMessage());
    }


    @ExceptionHandler({Exception.class})
    public Object defaultExceptionHandler(Exception e) {
        setLogParams(ResultEnum.FAILURE.getCode(), e);
        return ResponseBaseVo.fail(ResultEnum.FAILURE.getCode(), e.getMessage());
    }


    public void setLogParams(Integer code, Exception e) {
        ContextLogBO contextLogBO = ContextLogUtil.get();
        contextLogBO.setSuccess(Boolean.FALSE);
        contextLogBO.setCode(code);
        contextLogBO.setErrMsg(null);
        log.error("出现异常时,以该日志为准:Log:【{}】.error=>【{}】", JSON.toJSONString(contextLogBO),
                ErrorUtil.getErrorStackTraceMsg(e));
    }
}

2.5 如果是入参校验上面的

在这里插入图片描述
也可以通过全局异常打印出来
在这里插入图片描述
效果
在这里插入图片描述
将下面这个放到 2.4里面即可

 @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object validationBodyException(MethodArgumentNotValidException exception) {
        BindingResult result = exception.getBindingResult();
        String message = "";
        if (result.hasErrors()) {
            List<ObjectError> errors = result.getAllErrors();
            if (errors.size() > 0) {
                FieldError fieldError = (FieldError) errors.get(0);
                message = fieldError.getDefaultMessage();
            }
        }
        setLogParams(ERROR.getCode(), ErrorUtil.getErrorStackTraceMsg(exception));
        return GenericResponse.error(RepStatusEnum.PARAM_ERROR.getCode(), message);
    }

最后

以上就是英俊奇迹为你收集整理的SpringBoot项目异常分类&全局日志打印1. 在gateway项目内 网关过滤器内部异常处理2. 普通SpringBoot项目,异常处理的全部内容,希望文章能够帮你解决SpringBoot项目异常分类&全局日志打印1. 在gateway项目内 网关过滤器内部异常处理2. 普通SpringBoot项目,异常处理所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(57)

评论列表共有 0 条评论

立即
投稿
返回
顶部