Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/src/main/resources/config/ai-setting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ q-asker:
chat-timeout-ms: 360000
chunk:
max-count-variants: 5
multiple:
chunk-mode: 1 # 1: 단일 호출, 2: 멀티턴 순차 (A→B→C 중복방지), 3: 패턴별 병렬 (A/B/C)
3 changes: 3 additions & 0 deletions app/src/main/resources/config/resilience.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ resilience4j:
permitted-number-of-calls-in-half-open-state: 2
automatic-transition-from-open-to-half-open-enabled: true
event-consumer-buffer-size: 10
slow-call-duration-threshold:
seconds: 60
slow-call-rate-threshold: 100
record-exceptions:
- com.icc.qasker.ai.exception.GeminiInfraException
metrics:
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ plugins {
group = "com.icc.qasker"
// 프로젝트 버전 (Docker 이미지 태그, 배포 아티팩트 버전에 사용)
// 예: jib으로 빌드하면 Docker 이미지에 "1.7.0" 태그가 붙음
version = "3.0.1"
version = "3.0.2"

// Git hooks 경로를 .githooks/로 자동 설정
// 예: ./gradlew build 실행 시 자동으로 git config core.hooksPath .githooks 적용
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class NoOpConvertService implements ConvertService {

@Override
public Path convertToPdf(Path inputFile) {
log.warn("문서 변환 서비스가 비활성화 상태입니다. LibreOffice가 설치되지 않은 환경입니다.");
log.warn("[문서 변환 비활성화] LibreOffice 미설치 환경에서 변환 요청 수신");
throw new UnsupportedOperationException("문서 변환이 비활성화된 환경입니다.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public long decode(String hashId) {
throw new CustomException(ExceptionMessage.PROBLEM_SET_NOT_FOUND);
}
if (decoded.length > 1) {
log.error("중복된 ID가 발견되었습니다: {}", hashId);
log.error("[해시 디코딩 이상] 중복된 ID 발견 hashId={}", hashId);
}
return decoded[decoded.length - 1];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ private void sendSlack(String webhookUrl, String username, String icon, String t
.retrieve()
.toBodilessEntity();
} catch (Exception e) {
log.warn("Slack 알림 실패: {}", e.toString());
log.warn("[Slack 알림 실패] 웹훅 전송 실패", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ public ResponseEntity<CustomErrorResponse> handleCustomException(
log.warn(
"[{}] {}", customException.getContext(), customException.getMessage(), customException);
} else {
log.warn(customException.getMessage(), customException);
log.warn("[클라이언트 오류] 요청 처리 중 예외 발생", customException);
}
} else {
if (customException.getContext() != null) {
log.error(
"[{}] {}", customException.getContext(), customException.getMessage(), customException);
} else {
log.error(customException.getMessage(), customException);
log.error("[서버 오류] 처리 중 예외 발생", customException);
}
}

Expand All @@ -47,7 +47,7 @@ public ResponseEntity<CustomErrorResponse> handleCustomException(
@ExceptionHandler(CallNotPermittedException.class)
public ResponseEntity<CustomErrorResponse> handleCustomException(
CallNotPermittedException exception) {
log.error("Circuit Breaker Activated", exception);
log.error("[서킷 브레이커] AI 서버 차단 상태", exception);

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new CustomErrorResponse(ExceptionMessage.AI_SERVER_COMMUNICATION_ERROR.getMessage()));
Expand All @@ -56,7 +56,7 @@ public ResponseEntity<CustomErrorResponse> handleCustomException(
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ResponseEntity<CustomErrorResponse> handleMaxUploadSizeExceededException(
MaxUploadSizeExceededException e) {
log.warn("파일 업로드 크기 초과: {}", e.getMessage());
log.warn("[파일 업로드 제한] 업로드 크기 초과", e);
return ResponseEntity.status(ExceptionMessage.FILE_SIZE_EXCEEDED.getHttpStatus())
.body(new CustomErrorResponse(ExceptionMessage.FILE_SIZE_EXCEEDED.getMessage()));
}
Expand All @@ -66,14 +66,14 @@ public void handleSseException() {}

@ExceptionHandler(AsyncRequestTimeoutException.class)
public ResponseEntity<CustomErrorResponse> handleAsyncRequestTimeoutException() {
log.warn("SSE 비동기 요청 타임아웃 발생");
log.warn("[SSE 타임아웃] 비동기 요청 타임아웃 발생");
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
.body(new CustomErrorResponse(ExceptionMessage.AI_SERVER_COMMUNICATION_ERROR.getMessage()));
}

@ExceptionHandler(Exception.class)
public ResponseEntity<CustomErrorResponse> handleCustomException(Exception exception) {
log.error("Unexpected Error Occurred", exception);
log.error("[예상치 못한 오류] 처리되지 않은 예외 발생", exception);

return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new CustomErrorResponse(ExceptionMessage.DEFAULT_ERROR.getMessage()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ public int generateQuiz(GenerationRequestToAI request) {
if (!(e.getCause() instanceof java.util.concurrent.TimeoutException)) {
throw new GeminiInfraException("Gemini 블로킹 컨텍스트 오류", e);
}
log.warn("BLANK 스트리밍 타임아웃 (6분 초과): 생성된 문항 {}개 유지", delivered.get());
log.warn("[BLANK 스트리밍 타임아웃] 6분 초과, 생성된 문항 유지 deliveredCount={}", delivered.get());
metricsRecorder.recordStreamingTimeout("BLANK");
metricsRecorder.recordRequestDuration(
1,
Expand All @@ -188,7 +188,7 @@ public int generateQuiz(GenerationRequestToAI request) {
throw e;
} catch (Exception e) {
if (delivered.get() > 0) {
log.warn("BLANK 스트리밍 중 오류 발생이나 {}개 문항은 전달됨. 부분 성공 처리.", delivered.get(), e);
log.warn("[BLANK 부분 성공] 스트리밍 중 오류 발생이나 문항 전달됨 deliveredCount={}", delivered.get(), e);
metricsRecorder.recordStreamingTimeout("BLANK");
metricsRecorder.recordRequestDuration(
1,
Expand Down
Loading
Loading