From f7e6e71c7d66ac5d6bd86fec5c09485332db4eaa Mon Sep 17 00:00:00 2001 From: JiWoo Date: Sun, 19 May 2024 22:53:27 +0900 Subject: [PATCH] [Refactor] Log refactor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: Log level 변경 * refactor: 환경 별 Log Setting 변경 완료 * refactor: 로그파일 저장 기간을 10일로 단축 * refactor(FirebaseNotificationService): message 변환 로직을 메서드로 추출 * test(AdminAcceptanceTest): 관리자의 공지 전송 인수테스트 작성 --- scripts/start.sh | 5 +- .../handler/CommonExceptionHandler.java | 16 ++-- .../exception/FirebaseExceptionHandler.java | 2 +- .../adapter/out/firebase/FirebaseAdapter.java | 4 + .../service/FirebaseNotificationService.java | 82 ++++++++++++------- .../DepartmentNoticeScraperTemplate.java | 12 +-- .../KuisHomepageNoticeScraperTemplate.java | 36 +++++--- .../scrap/KuisNoticeScraperTemplate.java | 2 +- .../worker/scrap/client/ProxyJsoupClient.java | 8 +- .../notice/LatestPageNoticeApiClient.java | 12 +-- .../notice/DepartmentNoticeUpdater.java | 2 +- .../notice/KuisHomepageNoticeUpdater.java | 4 +- .../update/notice/KuisNoticeUpdater.java | 4 +- .../update/notice/NoticeUpdateSupport.java | 16 ++-- src/main/resources/application-dev.yml | 9 +- src/main/resources/application-local.yml | 11 ++- src/main/resources/application-prod.yml | 12 +-- src/main/resources/logback-spring.xml | 65 +++++++++++++++ .../acceptance/AdminAcceptanceTest.java | 37 ++++++++- .../event/MessageAdminEventListenerTest.java | 7 -- .../support/IntegrationTestSupport.java | 4 - .../notice/DepartmentNoticeUpdaterTest.java | 2 +- .../notice/KuisHomepageNoticeUpdaterTest.java | 3 - .../update/notice/KuisNoticeUpdaterTest.java | 2 +- 24 files changed, 239 insertions(+), 118 deletions(-) create mode 100644 src/main/resources/logback-spring.xml diff --git a/scripts/start.sh b/scripts/start.sh index d3ea87cf..3bb72abd 100644 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -2,9 +2,6 @@ PROJECT_ROOT="/home/ec2-user/app" JAR_FILE="$PROJECT_ROOT/spring-webapp.jar" - -APP_LOG="$PROJECT_ROOT/application.log" -ERROR_LOG="$PROJECT_ROOT/error.log" DEPLOY_LOG="$PROJECT_ROOT/deploy.log" TIME_NOW=$(date +%c) @@ -18,7 +15,7 @@ cp $PROJECT_ROOT/build/libs/*.jar $JAR_FILE # jar 파일 실행 chmod 755 $JAR_FILE echo "$TIME_NOW > $JAR_FILE 파일 실행" >> $DEPLOY_LOG -nohup java -jar -Dspring.profiles.active=prod -Xms256m -Xmx256m $JAR_FILE > $APP_LOG 2> $ERROR_LOG & +nohup java -jar -Dspring.profiles.active=prod -Xms256m -Xmx256m $JAR_FILE & CURRENT_PID=$(pgrep -f $JAR_FILE) echo "$TIME_NOW > 실행된 프로세스 아이디 $CURRENT_PID 입니다." >> $DEPLOY_LOG diff --git a/src/main/java/com/kustacks/kuring/common/exception/handler/CommonExceptionHandler.java b/src/main/java/com/kustacks/kuring/common/exception/handler/CommonExceptionHandler.java index a611a31c..f2b47c29 100644 --- a/src/main/java/com/kustacks/kuring/common/exception/handler/CommonExceptionHandler.java +++ b/src/main/java/com/kustacks/kuring/common/exception/handler/CommonExceptionHandler.java @@ -22,55 +22,55 @@ public class CommonExceptionHandler { @ExceptionHandler public ResponseEntity MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException exception) { - log.error("[MethodArgumentNotValidException] {}", exception.getMessage()); + log.warn("[MethodArgumentNotValidException] {}", exception.getMessage()); return ResponseEntity.status(ErrorCode.API_MISSING_PARAM.getHttpStatus()) .body(new ErrorResponse(ErrorCode.API_MISSING_PARAM)); } @ExceptionHandler public ResponseEntity NotFoundExceptionHandler(NotFoundException exception) { - log.error("[NotFoundException] {}", exception.getMessage()); + log.warn("[NotFoundException] {}", exception.getMessage()); return ResponseEntity.status(exception.getErrorCode().getHttpStatus()) .body(new ErrorResponse(exception.getErrorCode())); } @ExceptionHandler(AdminException.class) public ResponseEntity AdminExceptionHandler(AdminException exception) { - log.error("[APIException] {}", exception.getErrorCode().getMessage(), exception); + log.warn("[APIException] {}", exception.getErrorCode().getMessage(), exception); return ResponseEntity.status(exception.getErrorCode().getHttpStatus()) .body(new ErrorResponse(exception.getErrorCode())); } @ExceptionHandler public ResponseEntity MissingServletRequestParameterExceptionHandler(MissingServletRequestParameterException exception) { - log.error("[MissingServletRequestParameterException] {}", exception.getMessage()); + log.warn("[MissingServletRequestParameterException] {}", exception.getMessage()); return ResponseEntity.status(ErrorCode.API_MISSING_PARAM.getHttpStatus()) .body(new ErrorResponse(ErrorCode.API_MISSING_PARAM)); } @ExceptionHandler public ResponseEntity ConstraintViolationExceptionHandler(ConstraintViolationException exception) { - log.error("[ConstraintViolationException] {}", exception.getMessage()); + log.warn("[ConstraintViolationException] {}", exception.getMessage()); return ResponseEntity.status(ErrorCode.API_INVALID_PARAM.getHttpStatus()) .body(new ErrorResponse(ErrorCode.API_INVALID_PARAM)); } @ExceptionHandler public ResponseEntity HttpMediaTypeNotAcceptableExceptionHandler(HttpMediaTypeNotAcceptableException exception) { - log.error("[HttpMediaTypeNotAcceptableException] {}", exception.getMessage()); + log.warn("[HttpMediaTypeNotAcceptableException] {}", exception.getMessage()); return ResponseEntity.status(ErrorCode.API_NOT_ACCEPTABLE.getHttpStatus()) .body(new ErrorResponse(ErrorCode.API_NOT_ACCEPTABLE)); } @ExceptionHandler public ResponseEntity FirebaseSubscribeExceptionHandler(FirebaseSubscribeException exception) { - log.error("[FirebaseSubscribeException] {}", exception.getMessage()); + log.warn("[FirebaseSubscribeException] {}", exception.getMessage()); return ResponseEntity.status(ErrorCode.API_FB_SERVER_ERROR.getHttpStatus()) .body(new ErrorResponse(ErrorCode.API_FB_SERVER_ERROR)); } @ExceptionHandler public void InternalLogicExceptionHandler(InternalLogicException e) { - log.error("[InternalLogicException] {}", e.getErrorCode().getMessage(), e); + log.warn("[InternalLogicException] {}", e.getErrorCode().getMessage(), e); } } diff --git a/src/main/java/com/kustacks/kuring/message/adapter/out/exception/FirebaseExceptionHandler.java b/src/main/java/com/kustacks/kuring/message/adapter/out/exception/FirebaseExceptionHandler.java index 3b93c933..4503dda9 100644 --- a/src/main/java/com/kustacks/kuring/message/adapter/out/exception/FirebaseExceptionHandler.java +++ b/src/main/java/com/kustacks/kuring/message/adapter/out/exception/FirebaseExceptionHandler.java @@ -13,7 +13,7 @@ public class FirebaseExceptionHandler { @ExceptionHandler public ResponseEntity firebaseCommonExceptionHandler(FirebaseBusinessException exception) { - log.error("[FirebaseBusinessException] {}", exception.getMessage()); + log.warn("[FirebaseBusinessException] {}", exception.getMessage()); return ResponseEntity.status(exception.getErrorCode().getHttpStatus()) .body(new ErrorResponse(exception.getErrorCode())); diff --git a/src/main/java/com/kustacks/kuring/message/adapter/out/firebase/FirebaseAdapter.java b/src/main/java/com/kustacks/kuring/message/adapter/out/firebase/FirebaseAdapter.java index cc387db4..b1977341 100644 --- a/src/main/java/com/kustacks/kuring/message/adapter/out/firebase/FirebaseAdapter.java +++ b/src/main/java/com/kustacks/kuring/message/adapter/out/firebase/FirebaseAdapter.java @@ -13,11 +13,13 @@ import com.kustacks.kuring.message.application.service.exception.FirebaseSubscribeException; import com.kustacks.kuring.message.application.service.exception.FirebaseUnSubscribeException; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; import java.util.List; +@Slf4j @Component @Profile("prod | dev") @RequiredArgsConstructor @@ -44,6 +46,7 @@ public void subscribeToTopic( TopicManagementResponse response = firebaseMessaging.subscribeToTopic(tokens, topic); if (response.getFailureCount() > 0) { + log.warn("[{}] 구독 실패", tokens.get(0)); throw new FirebaseSubscribeException(); } } catch (FirebaseMessagingException | FirebaseSubscribeException exception) { @@ -60,6 +63,7 @@ public void unsubscribeFromTopic( TopicManagementResponse response = firebaseMessaging.unsubscribeFromTopic(tokens, topic); if (response.getFailureCount() > 0) { + log.warn("[{}] 구독 취소 실패", tokens.get(0)); throw new FirebaseUnSubscribeException(); } } catch (FirebaseMessagingException | FirebaseUnSubscribeException exception) { diff --git a/src/main/java/com/kustacks/kuring/message/application/service/FirebaseNotificationService.java b/src/main/java/com/kustacks/kuring/message/application/service/FirebaseNotificationService.java index df6cb18a..7dbdecd7 100644 --- a/src/main/java/com/kustacks/kuring/message/application/service/FirebaseNotificationService.java +++ b/src/main/java/com/kustacks/kuring/message/application/service/FirebaseNotificationService.java @@ -52,32 +52,23 @@ public void sendTestNotificationByAdmin(AdminTestNotificationCommand command) { @Override public void sendNotificationByAdmin(AdminNotificationCommand command) { try { - Message newMessage = Message.builder() - .setNotification(Notification - .builder() - .setTitle(command.title()) - .setBody(command.body()) - .build()) - .putAllData(objectMapper.convertValue(command, Map.class)) - .setTopic(serverProperties.ifDevThenAddSuffix(ALL_DEVICE_SUBSCRIBED_TOPIC)) - .build(); - + Message newMessage = createMessageFromCommand(command); firebaseMessagingPort.send(newMessage); } catch (FirebaseMessagingException exception) { throw new FirebaseMessageSendException(); } } - public void sendNotificationList(List noticeList) { + public void sendNotifications(List noticeList) { List notificationDtoList = createNotification(noticeList); + if (notificationDtoList.isEmpty()) { + log.info("새로운 공지가 없습니다."); + return; + } try { - this.sendNoticeMessageList(notificationDtoList); - log.info("FCM에 {}개의 공지 메세지를 전송했습니다.", notificationDtoList.size()); - log.info("전송된 공지 목록은 다음과 같습니다."); - for (Notice notice : noticeList) { - log.info("아이디 = {}, 날짜 = {}, 카테고리 = {}, 제목 = {}", notice.getArticleId(), notice.getPostedDate(), notice.getCategoryName(), notice.getSubject()); - } + loggingNoticeSendInfo(notificationDtoList); + this.sendNoticeMessages(notificationDtoList); } catch (FirebaseMessageSendException e) { log.error("새로운 공지의 FCM 전송에 실패했습니다."); throw new InternalLogicException(ErrorCode.FB_FAIL_SEND, e); @@ -87,6 +78,21 @@ public void sendNotificationList(List noticeList) { } } + private void loggingNoticeSendInfo(List notificationDtoList) { + log.info("FCM에 {}카테고리에 {}개의 공지 메세지를 전송.", + notificationDtoList.get(0).getCategory(), notificationDtoList.size()); + + log.info("전송된 공지 목록은 다음과 같습니다."); + for (NoticeMessageDto noticeMessageDto : notificationDtoList) { + log.info("아이디 = {}, 날짜 = {}, 카테고리 = {}, 제목 = {}", + noticeMessageDto.getArticleId(), + noticeMessageDto.getPostedDate(), + noticeMessageDto.getCategory(), + noticeMessageDto.getSubject() + ); + } + } + /** * Firebase message에는 두 가지 paylaad가 존재한다. * 1. notification @@ -104,31 +110,49 @@ private void sendNotification(NoticeMessageDto messageDto) throws FirebaseMessag sendBaseNotification(messageDto, serverProperties::ifDevThenAddSuffix); } - private void sendBaseNotification(NoticeMessageDto messageDto, UnaryOperator suffixUtil) throws FirebaseMessageSendException { + private void sendBaseNotification( + NoticeMessageDto messageDto, + UnaryOperator suffixUtil + ) throws FirebaseMessageSendException { try { - Message newMessage = Message.builder() - .setNotification(Notification - .builder() - .setTitle(buildTitle(messageDto.getCategoryKorName())) - .setBody(messageDto.getSubject()) - .build()) - .putAllData(objectMapper.convertValue(messageDto, Map.class)) - .setTopic(suffixUtil.apply(messageDto.getCategory())) - .build(); - + Message newMessage = createMessageFromDto(messageDto, suffixUtil); firebaseMessagingPort.send(newMessage); } catch (FirebaseMessagingException exception) { throw new FirebaseMessageSendException(); } } + private Message createMessageFromCommand(AdminNotificationCommand command) { + return Message.builder() + .setNotification(Notification + .builder() + .setTitle(command.title()) + .setBody(command.body()) + .build()) + .putAllData(objectMapper.convertValue(command, Map.class)) + .setTopic(serverProperties.ifDevThenAddSuffix(ALL_DEVICE_SUBSCRIBED_TOPIC)) + .build(); + } + + private Message createMessageFromDto(NoticeMessageDto messageDto, UnaryOperator suffixUtil) { + return Message.builder() + .setNotification(Notification + .builder() + .setTitle(buildTitle(messageDto.getCategoryKorName())) + .setBody(messageDto.getSubject()) + .build()) + .putAllData(objectMapper.convertValue(messageDto, Map.class)) + .setTopic(suffixUtil.apply(messageDto.getCategory())) + .build(); + } + private List createNotification(List willBeNotiNotices) { return willBeNotiNotices.stream() .map(NoticeMessageDto::from) .toList(); } - private void sendNoticeMessageList(List messageDtoList) throws FirebaseMessageSendException { + private void sendNoticeMessages(List messageDtoList) throws FirebaseMessageSendException { messageDtoList.forEach(this::sendNotification); } diff --git a/src/main/java/com/kustacks/kuring/worker/scrap/DepartmentNoticeScraperTemplate.java b/src/main/java/com/kustacks/kuring/worker/scrap/DepartmentNoticeScraperTemplate.java index 76283f67..865c640a 100644 --- a/src/main/java/com/kustacks/kuring/worker/scrap/DepartmentNoticeScraperTemplate.java +++ b/src/main/java/com/kustacks/kuring/worker/scrap/DepartmentNoticeScraperTemplate.java @@ -22,9 +22,9 @@ public class DepartmentNoticeScraperTemplate { public List scrap(DeptInfo deptInfo, Function> decisionMaker) throws InternalLogicException { List requestResults = requestWithDeptInfo(deptInfo, decisionMaker); - log.info("[{}] HTML 파싱 시작", deptInfo.getDeptName()); + log.debug("[{}] HTML 파싱 시작", deptInfo.getDeptName()); List noticeDtoList = htmlParsingFromScrapingResult(deptInfo, requestResults); - log.info("[{}] HTML 파싱 완료", deptInfo.getDeptName()); + log.debug("[{}] HTML 파싱 완료", deptInfo.getDeptName()); for (ComplexNoticeFormatDto complexNoticeFormatDto : noticeDtoList) { if (complexNoticeFormatDto.getNormalNoticeSize() == 0) { @@ -38,12 +38,12 @@ public List scrap(DeptInfo deptInfo, Function requestWithDeptInfo(DeptInfo deptInfo, Function> decisionMaker) { long startTime = System.currentTimeMillis(); - log.info("[{}] HTML 요청", deptInfo.getDeptName()); + log.debug("[{}] HTML 요청", deptInfo.getDeptName()); List reqResults = decisionMaker.apply(deptInfo); - log.info("[{}] HTML 수신", deptInfo.getDeptName()); + log.debug("[{}] HTML 수신", deptInfo.getDeptName()); long endTime = System.currentTimeMillis(); - log.info("[{}] 파싱에 소요된 초 = {}", deptInfo.getDeptName(), (endTime - startTime) / 1000.0); + log.debug("[{}] 파싱에 소요된 초 = {}", deptInfo.getDeptName(), (endTime - startTime) / 1000.0); return reqResults; } @@ -57,7 +57,7 @@ private List htmlParsingFromScrapingResult(DeptInfo dept RowsDto rowsDto = deptInfo.parse(document); List importantNoticeFormatDtos = rowsDto.buildImportantRowList(viewUrl); List normalNoticeFormatDtos = rowsDto.buildNormalRowList(viewUrl); - log.info("[{}] 공지 개수 = {}", deptInfo.getDeptName(), importantNoticeFormatDtos.size() + normalNoticeFormatDtos.size()); + log.debug("[{}] 공지 개수 = {}", deptInfo.getDeptName(), importantNoticeFormatDtos.size() + normalNoticeFormatDtos.size()); noticeDtoList.add(new ComplexNoticeFormatDto(importantNoticeFormatDtos, normalNoticeFormatDtos)); } diff --git a/src/main/java/com/kustacks/kuring/worker/scrap/KuisHomepageNoticeScraperTemplate.java b/src/main/java/com/kustacks/kuring/worker/scrap/KuisHomepageNoticeScraperTemplate.java index 234abd69..a9caa151 100644 --- a/src/main/java/com/kustacks/kuring/worker/scrap/KuisHomepageNoticeScraperTemplate.java +++ b/src/main/java/com/kustacks/kuring/worker/scrap/KuisHomepageNoticeScraperTemplate.java @@ -19,38 +19,52 @@ @Component public class KuisHomepageNoticeScraperTemplate { - public List scrap(KuisHomepageNoticeInfo kuisNoticeInfo, Function> decisionMaker) throws InternalLogicException { + public List scrap( + KuisHomepageNoticeInfo kuisNoticeInfo, + Function> decisionMaker + ) throws InternalLogicException { List requestResults = requestWithDeptInfo(kuisNoticeInfo, decisionMaker); - log.info("[{}] HTML 파싱 시작", kuisNoticeInfo.getCategoryName()); + log.debug("[{}] HTML 파싱 시작", kuisNoticeInfo.getCategoryName()); List noticeDtoList = htmlParsingFromScrapingResult(kuisNoticeInfo, requestResults); - log.info("[{}] HTML 파싱 완료", kuisNoticeInfo.getCategoryName()); + log.debug("[{}] HTML 파싱 완료", kuisNoticeInfo.getCategoryName()); + validateScrapedNoticeCountIsNotZero(noticeDtoList); + + return noticeDtoList; + } + + private void validateScrapedNoticeCountIsNotZero(List noticeDtoList) { for (ComplexNoticeFormatDto complexNoticeFormatDto : noticeDtoList) { if (complexNoticeFormatDto.getNormalNoticeSize() == 0) { throw new InternalLogicException(ErrorCode.NOTICE_SCRAPER_CANNOT_SCRAP); } } - - return noticeDtoList; } - private List requestWithDeptInfo(KuisHomepageNoticeInfo kuisNoticeInfo, Function> decisionMaker) { + private List requestWithDeptInfo( + KuisHomepageNoticeInfo kuisNoticeInfo, + Function> decisionMaker + ) { long startTime = System.currentTimeMillis(); - log.info("[{}] HTML 요청", kuisNoticeInfo.getCategoryName()); + log.debug("[{}] HTML 요청", kuisNoticeInfo.getCategoryName()); List reqResults = decisionMaker.apply(kuisNoticeInfo); - log.info("[{}] HTML 수신", kuisNoticeInfo.getCategoryName()); + log.debug("[{}] HTML 수신", kuisNoticeInfo.getCategoryName()); long endTime = System.currentTimeMillis(); - log.info("[{}] 파싱에 소요된 초 = {}", kuisNoticeInfo.getCategoryName(), (endTime - startTime) / 1000.0); + log.debug("[{}] 파싱에 소요된 초 = {}", kuisNoticeInfo.getCategoryName(), (endTime - startTime) / 1000.0); return reqResults; } - private List htmlParsingFromScrapingResult(KuisHomepageNoticeInfo kuisNoticeInfo, List requestResults) { + private List htmlParsingFromScrapingResult( + KuisHomepageNoticeInfo kuisNoticeInfo, + List requestResults + ) { List noticeDtoList = new LinkedList<>(); + for (ScrapingResultDto reqResult : requestResults) { Document document = reqResult.getDocument(); String viewUrl = reqResult.getViewUrl(); @@ -58,7 +72,7 @@ private List htmlParsingFromScrapingResult(KuisHomepageN RowsDto rowsDto = kuisNoticeInfo.parse(document); List importantNoticeFormatDtos = rowsDto.buildImportantRowList(viewUrl); List normalNoticeFormatDtos = rowsDto.buildNormalRowList(viewUrl); - log.info("[{}] 공지 개수 = {}", kuisNoticeInfo.getCategoryName(), importantNoticeFormatDtos.size() + normalNoticeFormatDtos.size()); + log.debug("[{}] 공지 개수 = {}", kuisNoticeInfo.getCategoryName(), importantNoticeFormatDtos.size() + normalNoticeFormatDtos.size()); noticeDtoList.add(new ComplexNoticeFormatDto(importantNoticeFormatDtos, normalNoticeFormatDtos)); } diff --git a/src/main/java/com/kustacks/kuring/worker/scrap/KuisNoticeScraperTemplate.java b/src/main/java/com/kustacks/kuring/worker/scrap/KuisNoticeScraperTemplate.java index dc075cfd..4f495d5b 100644 --- a/src/main/java/com/kustacks/kuring/worker/scrap/KuisNoticeScraperTemplate.java +++ b/src/main/java/com/kustacks/kuring/worker/scrap/KuisNoticeScraperTemplate.java @@ -29,7 +29,7 @@ private List requestWithKuisInfo(KuisNoticeInfo kuisNotic List requestResults = decisionMaker.apply(kuisNoticeRequestBody); long endTime = System.currentTimeMillis(); - log.info("[{}] 파싱에 소요된 초 = {}", kuisNoticeRequestBody.getCategoryName().getName(), (endTime - startTime) / 1000.0); + log.debug("[{}] 파싱에 소요된 초 = {}", kuisNoticeRequestBody.getCategoryName().getName(), (endTime - startTime) / 1000.0); return requestResults; } } diff --git a/src/main/java/com/kustacks/kuring/worker/scrap/client/ProxyJsoupClient.java b/src/main/java/com/kustacks/kuring/worker/scrap/client/ProxyJsoupClient.java index 2f9e3cb6..545142e4 100644 --- a/src/main/java/com/kustacks/kuring/worker/scrap/client/ProxyJsoupClient.java +++ b/src/main/java/com/kustacks/kuring/worker/scrap/client/ProxyJsoupClient.java @@ -64,14 +64,14 @@ public Document post(String url, int timeOut, Map requestBody) t private Document proxyTemplate(String url, int timeOut, MethodCallback callback) throws IOException { for (int idx = 0; idx < proxyQueue.size(); idx++) { ProxyInfo proxyInfo = proxyQueue.peek(); - log.info("[index:{}] proxy = {}:{}", idx, proxyInfo.getIp(), proxyInfo.getPort()); + log.debug("[index:{}] proxy = {}:{}", idx, proxyInfo.getIp(), proxyInfo.getPort()); try { return getDocument(url, timeOut, callback, proxyInfo); } catch (SocketTimeoutException e) { - log.error("Jsoup time out 오류 발생. {}", e.getMessage()); + log.warn("Jsoup time out 오류 발생. {}", e.getMessage()); } catch (IOException e) { - log.error("Jsoup 오류 발생. {}", e.getMessage()); + log.warn("Jsoup 오류 발생. {}", e.getMessage()); proxyQueue.poll(); } } @@ -85,7 +85,7 @@ private Document getDocument(String url, int timeOut, MethodCallback callback, P Document document = callback.sendRequest(connection, proxyInfo.getIp(), proxyInfo.getPort()); - log.info("{} 으로 성공!", proxyInfo.ip); + log.debug("{}로 우회 성공", proxyInfo.ip); return document; } diff --git a/src/main/java/com/kustacks/kuring/worker/scrap/client/notice/LatestPageNoticeApiClient.java b/src/main/java/com/kustacks/kuring/worker/scrap/client/notice/LatestPageNoticeApiClient.java index c080233a..b99d60a3 100644 --- a/src/main/java/com/kustacks/kuring/worker/scrap/client/notice/LatestPageNoticeApiClient.java +++ b/src/main/java/com/kustacks/kuring/worker/scrap/client/notice/LatestPageNoticeApiClient.java @@ -51,7 +51,7 @@ public List requestAll(DeptInfo deptInfo) throws InternalLogi ScrapingResultDto resultDto = getScrapingResultDto(deptInfo, totalNoticeSize, LATEST_SCRAP_ALL_TIMEOUT); return List.of(resultDto); } catch (IOException e) { - log.info("Department Scrap all IOException: {}", e.getMessage()); + log.warn("Department Scrap all IOException: {}", e.getMessage()); } catch (NullPointerException | IndexOutOfBoundsException e) { throw new InternalLogicException(ErrorCode.NOTICE_SCRAPER_CANNOT_PARSE, e); } @@ -59,10 +59,6 @@ public List requestAll(DeptInfo deptInfo) throws InternalLogi return Collections.emptyList(); } - private String buildUrlForTotalNoticeCount(DeptInfo deptInfo) { - return deptInfo.createRequestUrl(1, 1); - } - public int getTotalNoticeSize(String url) throws IOException, IndexOutOfBoundsException, NullPointerException { Document document = jsoupClient.get(url, LATEST_SCRAP_TIMEOUT); @@ -75,6 +71,10 @@ public int getTotalNoticeSize(String url) throws IOException, IndexOutOfBoundsEx return Integer.parseInt(totalNoticeSizeElement.ownText()); } + private String buildUrlForTotalNoticeCount(DeptInfo deptInfo) { + return deptInfo.createRequestUrl(1, 1); + } + private ScrapingResultDto getScrapingResultDto(DeptInfo deptInfo, int rowSize, int timeout) throws IOException { String requestUrl = deptInfo.createRequestUrl(START_PAGE_NUM, rowSize); String viewUrl = deptInfo.createViewUrl(); @@ -85,7 +85,7 @@ private ScrapingResultDto getScrapingResultDto(DeptInfo deptInfo, int rowSize, i Document document = jsoupClient.get(requestUrl, timeout); stopWatch.stop(); - log.info("[{}] takes {}millis to respond", deptInfo.getDeptName(), stopWatch.getTotalTimeMillis()); + log.debug("[{}] takes {}millis to respond", deptInfo.getDeptName(), stopWatch.getTotalTimeMillis()); return new ScrapingResultDto(document, viewUrl); } diff --git a/src/main/java/com/kustacks/kuring/worker/update/notice/DepartmentNoticeUpdater.java b/src/main/java/com/kustacks/kuring/worker/update/notice/DepartmentNoticeUpdater.java index 16258c58..1ad57367 100644 --- a/src/main/java/com/kustacks/kuring/worker/update/notice/DepartmentNoticeUpdater.java +++ b/src/main/java/com/kustacks/kuring/worker/update/notice/DepartmentNoticeUpdater.java @@ -49,7 +49,7 @@ public void update() { ).thenApply( scrapResults -> compareLatestAndUpdateDB(scrapResults, deptInfo.getDeptName()) ).thenAccept( - notificationService::sendNotificationList + notificationService::sendNotifications ); } } diff --git a/src/main/java/com/kustacks/kuring/worker/update/notice/KuisHomepageNoticeUpdater.java b/src/main/java/com/kustacks/kuring/worker/update/notice/KuisHomepageNoticeUpdater.java index dfde6545..a2ae416d 100644 --- a/src/main/java/com/kustacks/kuring/worker/update/notice/KuisHomepageNoticeUpdater.java +++ b/src/main/java/com/kustacks/kuring/worker/update/notice/KuisHomepageNoticeUpdater.java @@ -54,7 +54,7 @@ public void update() { ).thenApply( scrapResults -> compareLatestAndUpdateDB(scrapResults, kuisNoticeInfo.getCategoryName()) ).thenAccept( - notificationService::sendNotificationList + notificationService::sendNotifications ); } } @@ -77,7 +77,7 @@ public void updateAll() { private void updateLibrary() { List scrapResults = updateLibraryNotice(CategoryName.LIBRARY); List notices = compareLibraryLatestAndUpdateDB(scrapResults, CategoryName.LIBRARY); - notificationService.sendNotificationList(notices); + notificationService.sendNotifications(notices); } private List updateLibraryNotice(CategoryName categoryName) { diff --git a/src/main/java/com/kustacks/kuring/worker/update/notice/KuisNoticeUpdater.java b/src/main/java/com/kustacks/kuring/worker/update/notice/KuisNoticeUpdater.java index 46e443ca..917cdbe8 100644 --- a/src/main/java/com/kustacks/kuring/worker/update/notice/KuisNoticeUpdater.java +++ b/src/main/java/com/kustacks/kuring/worker/update/notice/KuisNoticeUpdater.java @@ -45,7 +45,7 @@ public void update() { CompletableFuture .supplyAsync(() -> updateKuisNoticeAsync(kuisNoticeInfo, KuisNoticeInfo::scrapLatestPageHtml), noticeUpdaterThreadTaskExecutor) .thenApply(scrapResults -> compareLatestAndUpdateDB(scrapResults, kuisNoticeInfo.getCategoryName())) - .thenAccept(notificationService::sendNotificationList); + .thenAccept(notificationService::sendNotifications); } } } @@ -53,7 +53,7 @@ public void update() { private void updateLibrary() { List scrapResults = updateLibraryNotice(CategoryName.LIBRARY); List notices = compareLatestAndUpdateDB(scrapResults, CategoryName.LIBRARY); - notificationService.sendNotificationList(notices); + notificationService.sendNotifications(notices); } private List updateLibraryNotice(CategoryName categoryName) { diff --git a/src/main/java/com/kustacks/kuring/worker/update/notice/NoticeUpdateSupport.java b/src/main/java/com/kustacks/kuring/worker/update/notice/NoticeUpdateSupport.java index 1feea775..ca258a6a 100644 --- a/src/main/java/com/kustacks/kuring/worker/update/notice/NoticeUpdateSupport.java +++ b/src/main/java/com/kustacks/kuring/worker/update/notice/NoticeUpdateSupport.java @@ -31,10 +31,10 @@ public List filteringSoonSaveNotices( newNotices.add(newNotice); } } catch (IncorrectResultSizeDataAccessException e) { - log.error("오류가 발생한 공지 정보"); - log.error("articleId = {}", notice.getArticleId()); - log.error("postedDate = {}", notice.getPostedDate()); - log.error("subject = {}", notice.getSubject()); + log.warn("오류가 발생한 공지 정보"); + log.warn("articleId = {}", notice.getArticleId()); + log.warn("postedDate = {}", notice.getPostedDate()); + log.warn("subject = {}", notice.getSubject()); } } @@ -63,10 +63,10 @@ public List filteringSoonSaveDepartmentNotices( newNotices.add(newDepartmentNotice); } } catch (IncorrectResultSizeDataAccessException e) { - log.error("오류가 발생한 공지 정보"); - log.error("articleId = {}", notice.getArticleId()); - log.error("postedDate = {}", notice.getPostedDate()); - log.error("subject = {}", notice.getSubject()); + log.warn("오류가 발생한 공지 정보"); + log.warn("articleId = {}", notice.getArticleId()); + log.warn("postedDate = {}", notice.getPostedDate()); + log.warn("subject = {}", notice.getSubject()); } } return newNotices; diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index e1042891..6d067661 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -87,9 +87,12 @@ logging: org: apache: coyote: - http11: info - file: - name: classpath:/kuring.log + http11: trace + +decorator: + datasource: + p6spy: + enable-logging: true management: endpoint: diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 2d21b4a7..5483bcc9 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -13,7 +13,7 @@ spring: batch_size: 100 format_sql: true defer-datasource-initialization: false - show-sql: false + show-sql: true open-in-view: false datasource: hikari: @@ -78,9 +78,12 @@ logging: org: apache: coyote: - http11: info - file: - name: classpath:/kuring.log + http11: trace + +decorator: + datasource: + p6spy: + enable-logging: true management: endpoint: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 3a0b8fcf..a3e27baa 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -43,14 +43,10 @@ spring: serialization: FAIL_ON_EMPTY_BEANS: false -logging: - level: - org: - apache: - coyote: - http11: info - file: - name: classpath:/kuring.log +decorator: + datasource: + p6spy: + enable-logging: false management: endpoint: diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 00000000..74d1c4a3 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + ${LOG_PATTERN} + + + + + + + + + + + + + + ${LOG_PATH}/kuring-info-${BY_DATE}.log + + + INFO + ACCEPT + DENY + + + ${LOG_PATTERN} + + + ${LOG_PATH}/info/kuring-info-%d{yyyy-MM-dd}-%i.log + 50MB + 10 + 500MB + + + + + ${LOG_PATH}/kuring-warn-or-more-${BY_DATE}.log + + WARN + + + ${LOG_PATTERN} + + + ${LOG_PATH}/warn-or-more/kuring-warn-or-more-%d{yyyy-MM-dd}-%i.log + 50MB + 10 + 500MB + + + + + + + + + diff --git a/src/test/java/com/kustacks/kuring/acceptance/AdminAcceptanceTest.java b/src/test/java/com/kustacks/kuring/acceptance/AdminAcceptanceTest.java index bd4f3e4b..70d99747 100644 --- a/src/test/java/com/kustacks/kuring/acceptance/AdminAcceptanceTest.java +++ b/src/test/java/com/kustacks/kuring/acceptance/AdminAcceptanceTest.java @@ -1,6 +1,7 @@ package com.kustacks.kuring.acceptance; import com.kustacks.kuring.admin.adapter.in.web.dto.RealNotificationRequest; +import com.kustacks.kuring.admin.adapter.in.web.dto.TestNotificationRequest; import com.kustacks.kuring.support.IntegrationTestSupport; import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; @@ -15,8 +16,6 @@ import static com.kustacks.kuring.acceptance.AuthStep.로그인_되어_있음; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; @DisplayName("인수 : 관리자") class AdminAcceptanceTest extends IntegrationTestSupport { @@ -48,7 +47,6 @@ void role_root_admin_search_feedbacks() { @Test void role_root_admin_create_test_notification() { // given - doNothing().when(firebaseNotificationService).sendNotificationByAdmin(any()); String accessToken = 로그인_되어_있음(ADMIN_LOGIN_ID, ADMIN_PASSWORD); // when @@ -57,7 +55,38 @@ void role_root_admin_create_test_notification() { .header("Authorization", "Bearer " + accessToken) .contentType(MediaType.APPLICATION_JSON_VALUE) .accept(MediaType.APPLICATION_JSON_VALUE) - .body(new RealNotificationRequest("테스트 공지", "테스트 공지입니다", "https://www.naver.com", ADMIN_PASSWORD)) + .body(new TestNotificationRequest("bachelor", "테스트 공지입니다", "1234")) + .when().post("/api/v2/admin/notices/dev") + .then().log().all() + .extract(); + + // then + assertAll( + () -> assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()), + () -> assertThat(response.jsonPath().getInt("code")).isEqualTo(200), + () -> assertThat(response.jsonPath().getString("message")).isEqualTo("테스트 공지 생성에 성공하였습니다"), + () -> assertThat(response.jsonPath().getString("data")).isNull() + ); + } + + /** + * given : 사전에 등록된 어드민이 있다 + * when : 실제 공지를 발송하면 + * then : 성공적으로 발송된다. + */ + @DisplayName("[v2] 실제 공지 발송") + @Test + void role_root_admin_create_real_notification() { + // given + String accessToken = 로그인_되어_있음(ADMIN_LOGIN_ID, ADMIN_PASSWORD); + + // when + var response = RestAssured + .given().log().all() + .header("Authorization", "Bearer " + accessToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .accept(MediaType.APPLICATION_JSON_VALUE) + .body(new RealNotificationRequest("real 공지", "real 공지입니다", "https://www.naver.com", ADMIN_PASSWORD)) .when().post("/api/v2/admin/notices/prod") .then().log().all() .extract(); diff --git a/src/test/java/com/kustacks/kuring/message/adapter/in/event/MessageAdminEventListenerTest.java b/src/test/java/com/kustacks/kuring/message/adapter/in/event/MessageAdminEventListenerTest.java index 61ef162e..f8c3ba2e 100644 --- a/src/test/java/com/kustacks/kuring/message/adapter/in/event/MessageAdminEventListenerTest.java +++ b/src/test/java/com/kustacks/kuring/message/adapter/in/event/MessageAdminEventListenerTest.java @@ -4,8 +4,6 @@ import com.kustacks.kuring.admin.application.port.in.dto.TestNotificationCommand; import com.kustacks.kuring.admin.application.service.AdminCommandService; import com.kustacks.kuring.auth.context.Authentication; -import com.kustacks.kuring.message.application.port.in.dto.AdminNotificationCommand; -import com.kustacks.kuring.message.application.port.in.dto.AdminTestNotificationCommand; import com.kustacks.kuring.notice.domain.CategoryName; import com.kustacks.kuring.support.IntegrationTestSupport; import org.junit.jupiter.api.DisplayName; @@ -18,7 +16,6 @@ import static com.kustacks.kuring.admin.domain.AdminRole.ROLE_ROOT; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; @DisplayName("[단위] 어드민 이벤트 리스너 테스트") class MessageAdminEventListenerTest extends IntegrationTestSupport { @@ -33,8 +30,6 @@ class MessageAdminEventListenerTest extends IntegrationTestSupport { @Test void sendNotificationEvent() { // given - doNothing().when(firebaseNotificationService).sendNotificationByAdmin(any(AdminNotificationCommand.class)); - RealNotificationCommand command = new RealNotificationCommand( "test title", "test body", @@ -54,8 +49,6 @@ void sendNotificationEvent() { @Test void sendTestNotificationEvent() { // given - doNothing().when(firebaseNotificationService).sendTestNotificationByAdmin(any(AdminTestNotificationCommand.class)); - TestNotificationCommand command = new TestNotificationCommand( CategoryName.STUDENT.getName(), "test body", diff --git a/src/test/java/com/kustacks/kuring/support/IntegrationTestSupport.java b/src/test/java/com/kustacks/kuring/support/IntegrationTestSupport.java index 21fa295c..e53036c7 100644 --- a/src/test/java/com/kustacks/kuring/support/IntegrationTestSupport.java +++ b/src/test/java/com/kustacks/kuring/support/IntegrationTestSupport.java @@ -1,7 +1,6 @@ package com.kustacks.kuring.support; -import com.kustacks.kuring.message.application.service.FirebaseNotificationService; import com.kustacks.kuring.message.application.service.FirebaseSubscribeService; import io.restassured.RestAssured; import org.junit.jupiter.api.BeforeEach; @@ -21,9 +20,6 @@ public class IntegrationTestSupport { @MockBean protected FirebaseSubscribeService firebaseSubscribeService; - @MockBean - protected FirebaseNotificationService firebaseNotificationService; - @LocalServerPort int port; diff --git a/src/test/java/com/kustacks/kuring/worker/update/notice/DepartmentNoticeUpdaterTest.java b/src/test/java/com/kustacks/kuring/worker/update/notice/DepartmentNoticeUpdaterTest.java index 907bc075..74ea6bff 100644 --- a/src/test/java/com/kustacks/kuring/worker/update/notice/DepartmentNoticeUpdaterTest.java +++ b/src/test/java/com/kustacks/kuring/worker/update/notice/DepartmentNoticeUpdaterTest.java @@ -46,7 +46,7 @@ class DepartmentNoticeUpdaterTest { void department_scrap_async_test() throws InterruptedException { // given doReturn(createDepartmentNoticesFixture()).when(scrapperTemplate).scrap(any(), any()); - doNothing().when(firebaseService).sendNotificationList(anyList()); + doNothing().when(firebaseService).sendNotifications(anyList()); // when departmentNoticeUpdater.update(); diff --git a/src/test/java/com/kustacks/kuring/worker/update/notice/KuisHomepageNoticeUpdaterTest.java b/src/test/java/com/kustacks/kuring/worker/update/notice/KuisHomepageNoticeUpdaterTest.java index 459936b5..6adb0275 100644 --- a/src/test/java/com/kustacks/kuring/worker/update/notice/KuisHomepageNoticeUpdaterTest.java +++ b/src/test/java/com/kustacks/kuring/worker/update/notice/KuisHomepageNoticeUpdaterTest.java @@ -25,8 +25,6 @@ import static org.assertj.core.api.Assertions.tuple; import static org.junit.jupiter.api.Assertions.assertAll; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; @@ -56,7 +54,6 @@ void notice_scrap_async_test() throws InterruptedException { // given doReturn(createNoticesFixture()).when(scrapperTemplate).scrap(any(), any()); doReturn(createLibraryFixture()).when(libraryNoticeApiClient).request(any()); - doNothing().when(firebaseNotificationService).sendNotificationList(anyList()); // when kuisHomepageNoticeUpdater.update(); diff --git a/src/test/java/com/kustacks/kuring/worker/update/notice/KuisNoticeUpdaterTest.java b/src/test/java/com/kustacks/kuring/worker/update/notice/KuisNoticeUpdaterTest.java index de2daa73..fecdcb21 100644 --- a/src/test/java/com/kustacks/kuring/worker/update/notice/KuisNoticeUpdaterTest.java +++ b/src/test/java/com/kustacks/kuring/worker/update/notice/KuisNoticeUpdaterTest.java @@ -51,7 +51,7 @@ void notice_scrap_async_test() throws InterruptedException { // given doReturn(createNoticesFixture()).when(scrapperTemplate).scrap(any(), any()); doReturn(createLibraryFixture()).when(libraryNoticeApiClient).request(any()); - doNothing().when(firebaseService).sendNotificationList(anyList()); + doNothing().when(firebaseService).sendNotifications(anyList()); // when kuisNoticeUpdater.update();