728x90
DemoFeignErrorDecoder
public class DemoFeignErrorDecoder implements ErrorDecoder {
private final ErrorDecoder errorDecoder = new Default();
@Override
public Exception decode(String methodKey, Response response) {
HttpStatus httpStatus = HttpStatus.resolve(response.status());
if (httpStatus == HttpStatus.NOT_FOUND) {
System.out.println("[DemoFeignErrorDecoder] Http Status = " + httpStatus);
throw new RuntimeException(String.format("[RuntimeException] Http Status is %s", httpStatus));
}
return errorDecoder.decode(methodKey, response);
}
}
DemoFeignConfig
@Configuration
@RequiredArgsConstructor
public class DemoFeignConfig {
@Bean
public DemoFeignInterceptor feignInterceptor() {
return DemoFeignInterceptor.of();
}
@Bean
public DemoFeignErrorDecoder demoFeignErrorDecoder() {
return new DemoFeignErrorDecoder();
}
}
DemoFeignClient
@FeignClient(
name = "demo-client",
url = "${feign.url.prefix}",
configuration = DemoFeignConfig.class
)
public interface DemoFeignClient {
@GetMapping("/get") // -> http://localhost:8080/target_server/get get 으로 요청이 감
ResponseEntity<BaseResponseInfo> callGet(@RequestHeader("CustomHeaderName") String customHeader,
@RequestParam("name") String name,
@RequestParam("age") Long age);
@PostMapping("/post") // -> http://localhost:8080/target_server/post post 으로 요청이 감
ResponseEntity<BaseResponseInfo> callPost(@RequestHeader("CustomHeaderName") String customHeader,
@RequestBody BaseRequestInfo baseRequestInfo);
@GetMapping("/error") // -> http://localhost:8080/target_server/error get 으로 요청이 감
ResponseEntity<BaseResponseInfo> callErrorDecoder();
}
위와 같이 코드 작성 후 외부 API 와의 연동 시 예외가 발생하면 ErrorDecoder에서 받게 됩니다.
이제 예외 상황 마다 대응하는 코드를 작성하면 됩니다.
이제 로그를 찍어보겠습니다.
feign에서 제공하는 Logger 클래스가 있습니다. 이 Logger 클래스를 상속받은 뒤 구현체를 구현해 주면 됩니다.
FeignCustomLogger
@Slf4j
@RequiredArgsConstructor
public class FeignCustomLogger extends Logger {
private static final int DEFAULT_SLOW_API_TIME = 3_000;
private static final String SLOW_API_NOTICE = "Slow API";
@Override
protected void log(String configKey, String format, Object... args) {
//로그를 남길때 어떤 형식으로 남길지 정해준다.
System.out.println(String.format(methodTag(configKey) + format, args));
// log.info(String.format(methodTag(configKey) + format, args));
}
@Override
protected void logRequest(String configKey, Level logLevel, Request request) {
System.out.println("[logRequest] : " + request);
// super.logRequest(configKey, logLevel, request);
// log.info(String.valueOf(request));
}
/*
이 메서드 하나로 위의 메서드를 컨트롤 할 수 있다.
request, response 둘다 컨트롤 가능
*/
@Override
protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, long elapsedTime) throws IOException {
String protocolVersion = resolveProtocolVersion(response.protocolVersion());
String reason =
response.reason() != null && logLevel.compareTo(Level.NONE) > 0 ? " " + response.reason()
: "";
int status = response.status();
log(configKey, "<--- %s %s%s (%sms)", protocolVersion, status, reason, elapsedTime);
if (logLevel.ordinal() >= Level.HEADERS.ordinal()) {
for (String field : response.headers().keySet()) {
if (shouldLogResponseHeader(field)) {
for (String value : valuesOrEmpty(response.headers(), field)) {
log(configKey, "%s: %s", field, value);
}
}
}
int bodyLength = 0;
if (response.body() != null && !(status == 204 || status == 205)) {
// HTTP 204 No Content "...response MUST NOT include a message-body"
// HTTP 205 Reset Content "...response MUST NOT include an entity"
if (logLevel.ordinal() >= Level.FULL.ordinal()) {
log(configKey, ""); // CRLF
}
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
bodyLength = bodyData.length;
if (logLevel.ordinal() >= Level.FULL.ordinal() && bodyLength > 0) {
log(configKey, "%s", decodeOrDefault(bodyData, UTF_8, "Binary data"));
}
if (elapsedTime > DEFAULT_SLOW_API_TIME) {
log(configKey, "[%s] elapsedTime : %s", SLOW_API_NOTICE, elapsedTime);
}
log(configKey, "<--- END HTTP (%s-byte body)", bodyLength);
return response.toBuilder().body(bodyData).build();
} else {
log(configKey, "<--- END HTTP (%s-byte body)", bodyLength);
}
}
return response;
}
}
log 메서드는 로그를 어떤 형식으로 남길지에 대해 정의 하는 메서드입니다.
logRequest는 요청할때의 로그를 남기게 됩니다.
logAndRebufferResponse는 요청과 응답에 대해 모두 로그를 남기게 됩니다.
저는 기본 시간 3초를 DEFAULT_SLOW_API_TIME 으로 정의해두고 만약 외부 API와의 통신 시 이 시간보다 길어지게 되면 Slow API라는 로그를 남기게 되어 어떤 API 호출 시 내가 예상한 시간보다 오래 걸리는지 알 수 있게 설정해 두었습니다.
이제 이 로그가 찍히게 되면 알림이라던지 후처리 로직을 통해 현재 문제가 있음을 알아갈 수 있는 방법을 생각해 보면 될 것 같습니다.
728x90
'Spring Boot > Feign Client' 카테고리의 다른 글
Feign Client - Interceptor (0) | 2023.01.15 |
---|---|
Feign Client 기본 구성 및 흐름 (0) | 2023.01.14 |
댓글