我是靠谱客的博主 英俊奇迹,最近开发中收集的这篇文章主要介绍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项目,异常处理所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复