背景:最近在梳理线上服务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);
}
}