梳理服务500错误总结

313 阅读1分钟

背景:最近在梳理线上服务500错误的时候,遇到一类错误很奇怪, 网关(SpringCloudGateWay)的access_log里可以看到500错误。但是对应的下游服务没有任何日志。

分析过程:因为这个是偶现的,想着打印对应的错误日志以便更好的解决问题,但是网关服务的默认实现就是返回500,具体什么原因导致的错误并不确定。通过查阅资料发现可以通过自定义异常来实现。

代码实现

@Component
public class CustomErrorAttributes extends DefaultErrorAttributes {
    private Logger logger = LoggerFactory.getLogger(CustomErrorAttributes.class);

    @Override
    public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
        Throwable throwable = this.getError(request);
        MergedAnnotation<ResponseStatus> responseStatusAnnotation = MergedAnnotations
                .from(throwable.getClass(), MergedAnnotations.SearchStrategy.TYPE_HIERARCHY).get(ResponseStatus.class);
        HttpStatus errorStatus = determineHttpStatus(throwable, responseStatusAnnotation);
        Map<String, Object> map = new HashMap<>();
        map.put("error", determineErrorCode(throwable));
        map.put("message", errorStatus.value());
        return map;
    }

    private HttpStatus determineHttpStatus(Throwable error, MergedAnnotation<ResponseStatus> responseStatusAnnotation) {
        if (error instanceof ResponseStatusException) {
            return ((ResponseStatusException) error).getStatus();
        }
        return responseStatusAnnotation.getValue("code", HttpStatus.class).orElse(HttpStatus.BAD_REQUEST);
    }

    int determineErrorCode(Throwable throwable) {
        if (throwable instanceof ConnectException) {
            logger.error("ConnectException,50001 error.reason is:{}",throwable.getMessage());
            return 50001;
        }
        logger.error("10045 error.reason is:{}",throwable.getMessage());
        return 10045;
    }
}

单元测试:对应的UT代码(基于mockito来实现的)

public class CustomErrorAttributesTest {

    CustomErrorAttributes customErrorAttributes;
    @Mock
    ServerRequest request;
    @Mock
    ErrorAttributeOptions errorAttributeOptions;
    @Mock
    Throwable throwable;
    @Mock
    ResponseStatusException responseStatusException;
    @Mock
    ConnectException connectException;

    @BeforeMethod
    public void before(){
        MockitoAnnotations.initMocks(this);
        mockStatic(ErrorAttributeOptions.class);
    }

    @Test
    public void test(){
        customErrorAttributes = spy(CustomErrorAttributes.class);
        doReturn(throwable).when((DefaultErrorAttributes)customErrorAttributes).getError(request);
        Map<String, Object> errorAttributes = customErrorAttributes.getErrorAttributes(request,errorAttributeOptions);
        AssertJUnit.assertEquals(errorAttributes.get("error"),10045);
        AssertJUnit.assertEquals(errorAttributes.get("message"),400);
        doReturn(responseStatusException).when((DefaultErrorAttributes)customErrorAttributes).getError(request);
        when(responseStatusException.getStatus()).thenReturn(HttpStatus.BAD_REQUEST);
        errorAttributes = customErrorAttributes.getErrorAttributes(request,errorAttributeOptions);
        AssertJUnit.assertEquals(errorAttributes.get("error"),10045);
        AssertJUnit.assertEquals(errorAttributes.get("message"),400);
        doReturn(connectException).when((DefaultErrorAttributes)customErrorAttributes).getError(request);
        errorAttributes = customErrorAttributes.getErrorAttributes(request, errorAttributeOptions);
        AssertJUnit.assertEquals(errorAttributes.get("error"),50001);
        AssertJUnit.assertEquals(errorAttributes.get("message"),400);

    }
}

参考:edgar615.github.io/spring-clou…