🔊 API Response Error Log Update

This commit is contained in:
hyojin kim 2026-01-09 13:39:18 +09:00
부모 4e79794750
커밋 1ab78e881f
13개의 변경된 파일277개의 추가작업 그리고 39개의 파일을 삭제

파일 보기

@ -2,16 +2,14 @@ package com.snp.batch.jobs.compliance.batch.reader;
import com.snp.batch.common.batch.reader.BaseApiReader;
import com.snp.batch.jobs.compliance.batch.dto.ComplianceDto;
import com.snp.batch.jobs.shipdetail.batch.dto.ShipDetailComparisonData;
import com.snp.batch.service.BatchDateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpStatusCode;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
@ -115,6 +113,23 @@ public class ComplianceDataRangeReader extends BaseApiReader<ComplianceDto> {
.queryParam("toDate", params.get("toDate"))
.build())
.retrieve()
// 1. 에러 상태 코드 감지 (4xx, 5xx)
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class) // 에러 바디를 문자열로 읽음
.flatMap(errorBody -> {
// 2. 로그에 상태 코드와 에러 메세지 출력
log.error("[{}] API 호출 오류 발생!", getReaderName());
log.error("[{}] ERROR CODE: {}, REASON: {}",
getReaderName(),
clientResponse.statusCode(),
errorBody);
// 3. 상위로 예외 던지기 (배치 중단을 원할 경우)
return Mono.error(new RuntimeException(
String.format("API 호출 실패 (%s): %s", clientResponse.statusCode(), errorBody)
));
})
)
.bodyToMono(new ParameterizedTypeReference<List<ComplianceDto>>() {})
.block();
}

파일 보기

@ -4,8 +4,10 @@ import com.snp.batch.common.batch.reader.BaseApiReader;
import com.snp.batch.jobs.event.batch.dto.*;
import com.snp.batch.service.BatchDateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatusCode;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.time.LocalDateTime;
import java.util.*;
@ -210,6 +212,22 @@ public class EventDataReader extends BaseApiReader<EventDetailDto> {
.queryParam("toDay", params.get("toDay"))
.build())
.retrieve()
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class) // 에러 바디를 문자열로 읽음
.flatMap(errorBody -> {
// 2. 로그에 상태 코드와 에러 메세지 출력
log.error("[{}] API 호출 오류 발생!", getReaderName());
log.error("[{}] ERROR CODE: {}, REASON: {}",
getReaderName(),
clientResponse.statusCode(),
errorBody);
// 3. 상위로 예외 던지기 (배치 중단을 원할 경우)
return Mono.error(new RuntimeException(
String.format("API 호출 실패 (%s): %s", clientResponse.statusCode(), errorBody)
));
})
)
.bodyToMono(EventResponse.class)
.block();
}
@ -222,6 +240,22 @@ public class EventDataReader extends BaseApiReader<EventDetailDto> {
.queryParam("eventID", eventId)
.build())
.retrieve()
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class) // 에러 바디를 문자열로 읽음
.flatMap(errorBody -> {
// 2. 로그에 상태 코드와 에러 메세지 출력
log.error("[{}] API 호출 오류 발생!", getReaderName());
log.error("[{}] ERROR CODE: {}, REASON: {}",
getReaderName(),
clientResponse.statusCode(),
errorBody);
// 3. 상위로 예외 던지기 (배치 중단을 원할 경우)
return Mono.error(new RuntimeException(
String.format("API 호출 실패 (%s): %s", clientResponse.statusCode(), errorBody)
));
})
)
.bodyToMono(EventDetailResponse.class)
.block();
}

파일 보기

@ -6,6 +6,8 @@ import com.snp.batch.service.BatchDateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.http.HttpStatusCode;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Map;
@ -102,6 +104,23 @@ public class AnchorageCallsRangeReader extends BaseApiReader<AnchorageCallsDto>
.queryParam("stopDate", params.get("toDate"))
.build())
.retrieve()
// 1. 에러 상태 코드 감지 (4xx, 5xx)
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class) // 에러 바디를 문자열로 읽음
.flatMap(errorBody -> {
// 2. 로그에 상태 코드와 에러 메세지 출력
log.error("[{}] API 호출 오류 발생!", getReaderName());
log.error("[{}] ERROR CODE: {}, REASON: {}",
getReaderName(),
clientResponse.statusCode(),
errorBody);
// 3. 상위로 예외 던지기 (배치 중단을 원할 경우)
return Mono.error(new RuntimeException(
String.format("API 호출 실패 (%s): %s", clientResponse.statusCode(), errorBody)
));
})
)
.bodyToFlux(AnchorageCallsDto.class)
.collectList()
.block();

파일 보기

@ -6,6 +6,8 @@ import com.snp.batch.service.BatchDateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.http.HttpStatusCode;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Map;
@ -101,6 +103,23 @@ public class BerthCallsRangeReader extends BaseApiReader<BerthCallsDto> {
.queryParam("stopDate", params.get("toDate"))
.build())
.retrieve()
// 1. 에러 상태 코드 감지 (4xx, 5xx)
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class) // 에러 바디를 문자열로 읽음
.flatMap(errorBody -> {
// 2. 로그에 상태 코드와 에러 메세지 출력
log.error("[{}] API 호출 오류 발생!", getReaderName());
log.error("[{}] ERROR CODE: {}, REASON: {}",
getReaderName(),
clientResponse.statusCode(),
errorBody);
// 3. 상위로 예외 던지기 (배치 중단을 원할 경우)
return Mono.error(new RuntimeException(
String.format("API 호출 실패 (%s): %s", clientResponse.statusCode(), errorBody)
));
})
)
.bodyToFlux(BerthCallsDto.class)
.collectList()
.block();

파일 보기

@ -6,6 +6,8 @@ import com.snp.batch.service.BatchDateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.http.HttpStatusCode;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Map;
@ -100,6 +102,23 @@ public class CurrentlyAtRangeReader extends BaseApiReader<CurrentlyAtDto> {
.queryParam("stopDate", params.get("toDate"))
.build())
.retrieve()
// 1. 에러 상태 코드 감지 (4xx, 5xx)
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class) // 에러 바디를 문자열로 읽음
.flatMap(errorBody -> {
// 2. 로그에 상태 코드와 에러 메세지 출력
log.error("[{}] API 호출 오류 발생!", getReaderName());
log.error("[{}] ERROR CODE: {}, REASON: {}",
getReaderName(),
clientResponse.statusCode(),
errorBody);
// 3. 상위로 예외 던지기 (배치 중단을 원할 경우)
return Mono.error(new RuntimeException(
String.format("API 호출 실패 (%s): %s", clientResponse.statusCode(), errorBody)
));
})
)
.bodyToFlux(CurrentlyAtDto.class)
.collectList()
.block();

파일 보기

@ -6,6 +6,8 @@ import com.snp.batch.service.BatchDateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.http.HttpStatusCode;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Map;
@ -98,6 +100,23 @@ public class DestinationRangeReader extends BaseApiReader<DestinationDto> {
.queryParam("stopDate", params.get("toDate"))
.build())
.retrieve()
// 1. 에러 상태 코드 감지 (4xx, 5xx)
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class) // 에러 바디를 문자열로 읽음
.flatMap(errorBody -> {
// 2. 로그에 상태 코드와 에러 메세지 출력
log.error("[{}] API 호출 오류 발생!", getReaderName());
log.error("[{}] ERROR CODE: {}, REASON: {}",
getReaderName(),
clientResponse.statusCode(),
errorBody);
// 3. 상위로 예외 던지기 (배치 중단을 원할 경우)
return Mono.error(new RuntimeException(
String.format("API 호출 실패 (%s): %s", clientResponse.statusCode(), errorBody)
));
})
)
.bodyToFlux(DestinationDto.class)
.collectList()
.block();

파일 보기

@ -6,6 +6,8 @@ import com.snp.batch.service.BatchDateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.http.HttpStatusCode;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Map;
@ -100,6 +102,23 @@ public class PortCallsRangeReader extends BaseApiReader<PortCallsDto> {
.queryParam("stopDate", params.get("toDate"))
.build())
.retrieve()
// 1. 에러 상태 코드 감지 (4xx, 5xx)
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class) // 에러 바디를 문자열로 읽음
.flatMap(errorBody -> {
// 2. 로그에 상태 코드와 에러 메세지 출력
log.error("[{}] API 호출 오류 발생!", getReaderName());
log.error("[{}] ERROR CODE: {}, REASON: {}",
getReaderName(),
clientResponse.statusCode(),
errorBody);
// 3. 상위로 예외 던지기 (배치 중단을 원할 경우)
return Mono.error(new RuntimeException(
String.format("API 호출 실패 (%s): %s", clientResponse.statusCode(), errorBody)
));
})
)
.bodyToFlux(PortCallsDto.class)
.collectList()
.block();

파일 보기

@ -5,11 +5,10 @@ import com.snp.batch.jobs.movement.batch.dto.StsOperationDto;
import com.snp.batch.service.BatchDateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.http.HttpStatusCode;
import reactor.core.publisher.Mono;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
@ -103,6 +102,23 @@ public class StsOperationRangeReader extends BaseApiReader<StsOperationDto> {
.queryParam("stopDate", params.get("toDate"))
.build())
.retrieve()
// 1. 에러 상태 코드 감지 (4xx, 5xx)
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class) // 에러 바디를 문자열로 읽음
.flatMap(errorBody -> {
// 2. 로그에 상태 코드와 에러 메세지 출력
log.error("[{}] API 호출 오류 발생!", getReaderName());
log.error("[{}] ERROR CODE: {}, REASON: {}",
getReaderName(),
clientResponse.statusCode(),
errorBody);
// 3. 상위로 예외 던지기 (배치 중단을 원할 경우)
return Mono.error(new RuntimeException(
String.format("API 호출 실패 (%s): %s", clientResponse.statusCode(), errorBody)
));
})
)
.bodyToFlux(StsOperationDto.class)
.collectList()
.block();
@ -113,8 +129,6 @@ public class StsOperationRangeReader extends BaseApiReader<StsOperationDto> {
if (data == null) {
int totalBatches = (int) Math.ceil((double) allData.size() / batchSize);
log.info("[{}] 전체 {} 개 배치 처리 완료", getReaderName(), totalBatches);
/*log.info("[{}] 총 {} 개의 IMO 번호에 대한 API 호출 종료",
getReaderName(), allImoNumbers.size());*/
}
}

파일 보기

@ -6,6 +6,8 @@ import com.snp.batch.service.BatchDateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.http.HttpStatusCode;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Map;
@ -99,6 +101,23 @@ public class TerminalCallsRangeReader extends BaseApiReader<TerminalCallsDto> {
.queryParam("stopDate", params.get("toDate"))
.build())
.retrieve()
// 1. 에러 상태 코드 감지 (4xx, 5xx)
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class) // 에러 바디를 문자열로 읽음
.flatMap(errorBody -> {
// 2. 로그에 상태 코드와 에러 메세지 출력
log.error("[{}] API 호출 오류 발생!", getReaderName());
log.error("[{}] ERROR CODE: {}, REASON: {}",
getReaderName(),
clientResponse.statusCode(),
errorBody);
// 3. 상위로 예외 던지기 (배치 중단을 원할 경우)
return Mono.error(new RuntimeException(
String.format("API 호출 실패 (%s): %s", clientResponse.statusCode(), errorBody)
));
})
)
.bodyToFlux(TerminalCallsDto.class)
.collectList()
.block();

파일 보기

@ -6,6 +6,8 @@ import com.snp.batch.service.BatchDateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.http.HttpStatusCode;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Map;
@ -86,7 +88,6 @@ public class TransitsRangeReader extends BaseApiReader<TransitsDto> {
/**
* Query Parameter를 사용한 API 호출
* @param startDate,stopDate
* @return API 응답
*/
private List<TransitsDto> callApiWithBatch() {
@ -100,6 +101,23 @@ public class TransitsRangeReader extends BaseApiReader<TransitsDto> {
.queryParam("stopDate", params.get("toDate"))
.build())
.retrieve()
// 1. 에러 상태 코드 감지 (4xx, 5xx)
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class) // 에러 바디를 문자열로 읽음
.flatMap(errorBody -> {
// 2. 로그에 상태 코드와 에러 메세지 출력
log.error("[{}] API 호출 오류 발생!", getReaderName());
log.error("[{}] ERROR CODE: {}, REASON: {}",
getReaderName(),
clientResponse.statusCode(),
errorBody);
// 3. 상위로 예외 던지기 (배치 중단을 원할 경우)
return Mono.error(new RuntimeException(
String.format("API 호출 실패 (%s): %s", clientResponse.statusCode(), errorBody)
));
})
)
.bodyToFlux(TransitsDto.class)
.collectList()
.block();

파일 보기

@ -7,15 +7,14 @@ import com.snp.batch.jobs.pscInspection.batch.dto.PscInspectionDto;
import com.snp.batch.service.BatchDateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatusCode;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@ -117,6 +116,23 @@ public class PscApiReader extends BaseApiReader<PscInspectionDto> {
.queryParam("toDay", params.get("toDay"))
.build())
.retrieve()
// 1. 에러 상태 코드 감지 (4xx, 5xx)
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class) // 에러 바디를 문자열로 읽음
.flatMap(errorBody -> {
// 2. 로그에 상태 코드와 에러 메세지 출력
log.error("[{}] API 호출 오류 발생!", getReaderName());
log.error("[{}] ERROR CODE: {}, REASON: {}",
getReaderName(),
clientResponse.statusCode(),
errorBody);
// 3. 상위로 예외 던지기 (배치 중단을 원할 경우)
return Mono.error(new RuntimeException(
String.format("API 호출 실패 (%s): %s", clientResponse.statusCode(), errorBody)
));
})
)
.bodyToMono(String.class)
.block();

파일 보기

@ -1,17 +1,15 @@
package com.snp.batch.jobs.risk.batch.reader;
import com.snp.batch.common.batch.reader.BaseApiReader;
import com.snp.batch.jobs.compliance.batch.dto.ComplianceDto;
import com.snp.batch.jobs.risk.batch.dto.RiskDto;
import com.snp.batch.service.BatchDateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpStatusCode;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
@ -112,6 +110,23 @@ public class RiskDataRangeReader extends BaseApiReader<RiskDto> {
.queryParam("toDate", params.get("toDate"))
.build())
.retrieve()
// 1. 에러 상태 코드 감지 (4xx, 5xx)
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class) // 에러 바디를 문자열로 읽음
.flatMap(errorBody -> {
// 2. 로그에 상태 코드와 에러 메세지 출력
log.error("[{}] API 호출 오류 발생!", getReaderName());
log.error("[{}] ERROR CODE: {}, REASON: {}",
getReaderName(),
clientResponse.statusCode(),
errorBody);
// 3. 상위로 예외 던지기 (배치 중단을 원할 경우)
return Mono.error(new RuntimeException(
String.format("API 호출 실패 (%s): %s", clientResponse.statusCode(), errorBody)
));
})
)
.bodyToMono(new ParameterizedTypeReference<List<RiskDto>>() {})
.block();
}

파일 보기

@ -9,32 +9,12 @@ import com.snp.batch.service.BatchDateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.http.HttpStatusCode;
import reactor.core.publisher.Mono;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
/**
* 선박 상세 정보 Reader (v2.0 - Chunk 기반)
*
* 기능:
* 1. ship_data 테이블에서 IMO 번호 전체 조회 (최초 1회)
* 2. IMO 번호를 100개씩 분할하여 배치 단위로 처리
* 3. fetchNextBatch() 호출 시마다 100개씩 API 호출
* 4. Spring Batch가 100건씩 Process Write 수행
*
* Chunk 처리 흐름:
* - beforeFetch() IMO 전체 조회 (1회)
* - fetchNextBatch() 100개 IMO로 API 호출 (1,718회)
* - read() 1건씩 반환 (100번)
* - Processor/Writer 100건 처리
* - 반복... (1,718번의 Chunk)
*
* 기존 방식과의 차이:
* - 기존: 17만건 전체 메모리 로드 Process Write
* - 신규: 100건씩 로드 Process Write (Chunk 1,718회)
*/
@Slf4j
public class ShipDetailUpdateDataReader extends BaseApiReader<ShipDetailComparisonData> {
@ -247,6 +227,22 @@ public class ShipDetailUpdateDataReader extends BaseApiReader<ShipDetailComparis
return webClient.get()
.uri(url)
.retrieve()
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class) // 에러 바디를 문자열로 읽음
.flatMap(errorBody -> {
// 2. 로그에 상태 코드와 에러 메세지 출력
log.error("[{}] API 호출 오류 발생!", getReaderName());
log.error("[{}] ERROR CODE: {}, REASON: {}",
getReaderName(),
clientResponse.statusCode(),
errorBody);
// 3. 상위로 예외 던지기 (배치 중단을 원할 경우)
return Mono.error(new RuntimeException(
String.format("API 호출 실패 (%s): %s", clientResponse.statusCode(), errorBody)
));
})
)
.bodyToMono(ShipDetailApiResponse.class)
.block();
}
@ -275,6 +271,22 @@ public class ShipDetailUpdateDataReader extends BaseApiReader<ShipDetailComparis
.queryParam("toDay", params.get("toDay"))
.build())
.retrieve()
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class) // 에러 바디를 문자열로 읽음
.flatMap(errorBody -> {
// 2. 로그에 상태 코드와 에러 메세지 출력
log.error("[{}] API 호출 오류 발생!", getReaderName());
log.error("[{}] ERROR CODE: {}, REASON: {}",
getReaderName(),
clientResponse.statusCode(),
errorBody);
// 3. 상위로 예외 던지기 (배치 중단을 원할 경우)
return Mono.error(new RuntimeException(
String.format("API 호출 실패 (%s): %s", clientResponse.statusCode(), errorBody)
));
})
)
.bodyToMono(ShipUpdateApiResponse.class)
.block();
}