-
Notifications
You must be signed in to change notification settings - Fork 0
feat: 로깅 기능 구현 #244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: 로깅 기능 구현 #244
Conversation
Summary of ChangesHello @yongjun0511, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 이 PR은 애플리케이션에 강력한 로깅 인프라를 도입하고 이를 주요 서비스에 통합합니다. 변경 사항은 시스템의 관찰 가능성을 높여 애플리케이션 동작을 모니터링하고, 문제를 디버깅하며, 사용자 활동을 추적하는 것을 용이하게 합니다. 또한, 지속적인 로깅을 위한 Docker 배포를 포함하여 PLG 스택 관련 CI/CD 구성을 포함합니다. Highlights
Ignored Files
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. 코드 흐름 따라, 로그는 길을 밝히네, 오류는 숨지 못해. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
이번 PR은 로깅 기능 구현과 CI/CD 관련 작업을 중심으로 진행되었습니다. 전반적으로 로깅 인프라(AOP, MDC, 이벤트 기반 로깅, Logback 설정)가 체계적으로 잘 구축되었습니다. 특히 트랜잭션 커밋 후에만 로그를 남기는 이벤트 리스너와 요청 추적을 위한 MDC 사용은 매우 좋은 접근 방식입니다. 다만, 몇 가지 중요한 보안 및 설정 관련 개선점이 보입니다. 민감한 사용자 정보(이메일, 문의 내용, 비밀번호 등)가 로그에 노출될 수 있는 부분이 있어 수정이 필요합니다. 또한, Logback 설정에서 로그 보관 기간과 파일 로그 포맷을 개선하여 운영 환경에서의 안정성과 추적 용이성을 높이는 것을 제안합니다. 아래에 자세한 리뷰를 남겼으니 확인 부탁드립니다.
| String otherArgsJson = IntStream.range(0, args.length) | ||
| .filter(i -> !(args[i] instanceof HttpServletRequest)) | ||
| .filter(i -> !(args[i] instanceof CustomUserDetails)) | ||
| .filter(i -> !(args[i] instanceof org.springframework.validation.BindingResult)) | ||
| .mapToObj(i -> { | ||
| String name = (paramNames != null && i < paramNames.length) ? paramNames[i] : "arg" + i; | ||
| Object arg = args[i]; | ||
| try { | ||
| String value = objectMapper.writeValueAsString(arg); | ||
| if (value.length() > 200) value = value.substring(0, 200) + "...(truncated)"; | ||
| return name + "=" + value; | ||
| } catch (Exception e) { | ||
| return name + "=" + String.valueOf(arg); | ||
| } | ||
| }) | ||
| .collect(Collectors.joining(", ")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
컨트롤러의 모든 요청 인자를 JSON으로 변환하여 로그에 기록하고 있습니다. 이 방식은 디버깅에 유용하지만, 회원가입이나 로그인 시 비밀번호, 또는 개인정보 수정 시 민감한 정보가 그대로 로그 파일에 노출될 수 있는 심각한 보안 취약점을 야기합니다. objectMapper.writeValueAsString(arg)를 호출하기 전에, 로깅할 객체에 민감한 정보가 포함되어 있는지 확인하고 마스킹 처리하는 로직이 반드시 필요합니다. 예를 들어, 특정 필드명(e.g., 'password', 'secret')을 기준으로 마스킹하거나, DTO 필드에 @LogMask와 같은 어노테이션을 추가하여 선택적으로 마스킹하는 방법을 고려해볼 수 있습니다. 지금 상태로는 운영 환경에서 심각한 정보 유출로 이어질 수 있습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
커스텀 어노테이션 만들어서 마스킹 해주었어요
| eventPublisher.publishEvent(LogEvent.of(String.format( | ||
| "Inquiry created: id=%d, userId=%d, email=%s, content=%s", | ||
| saved.getId(), user.getId(), request.getEmail(), request.getContent()))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사용자 문의 내용과 이메일 주소와 같은 민감한 개인정보를 로그에 직접 기록하고 있습니다. INFO 레벨 로그는 보통 수집 및 분석 대상이 되므로, 개인정보가 포함될 경우 정보 유출의 위험이 있습니다. 문의 내용(request.getContent())과 이메일(request.getEmail()) 대신, 문의 ID와 사용자 ID 같은 식별자만 로그에 남기는 것을 권장합니다. 만약 내용 로깅이 꼭 필요하다면, 길이를 제한하거나 일부를 마스킹 처리하는 방안을 고려해 보세요.
| eventPublisher.publishEvent(LogEvent.of(String.format( | |
| "Inquiry created: id=%d, userId=%d, email=%s, content=%s", | |
| saved.getId(), user.getId(), request.getEmail(), request.getContent()))); | |
| eventPublisher.publishEvent(LogEvent.of(String.format( | |
| "Inquiry created: id=%d, userId=%d", | |
| saved.getId(), user.getId()))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
반영했슴다 이거
| eventPublisher.publishEvent(LogEvent.of( | ||
| String.format("User withdrawn: userId=%d, email=%s, nickname=%s", | ||
| user.getId(), | ||
| user.getEmail(), | ||
| user.getNickname()) | ||
| )); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
반영완
| <file>${LOG_PATH}/${APP_NAME}.log</file> | ||
| <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> | ||
| <fileNamePattern>${LOG_PATH}/${APP_NAME}-%d{yyyy-MM-dd}.log</fileNamePattern> | ||
| <maxHistory>1</maxHistory> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
로그 파일의 maxHistory가 1로 설정되어 있어, 로그가 하루만 보관되고 삭제됩니다. 운영 환경에서 장애 추적이나 데이터 분석을 위해 최소 7일에서 30일 정도의 로그를 보관하는 것이 일반적입니다. 현재 설정은 과거 문제 발생 시 원인 분석을 어렵게 할 수 있으므로, 서비스 정책에 맞게 보관 기간을 늘리는 것을 검토해 보세요. 이 설정은 ERROR_FILE appender(38라인)에도 동일하게 적용하는 것이 좋습니다.
| <maxHistory>1</maxHistory> | |
| <maxHistory>30</maxHistory> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이건 파일로 저장을 안하면서 없어졌어요~
| <maxHistory>1</maxHistory> | ||
| </rollingPolicy> | ||
| <encoder> | ||
| <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%level] [%thread] %logger{36} - %msg%n</pattern> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
파일에 기록되는 로그(MAIN_FILE)의 패턴에 requestId가 빠져있습니다. 콘솔 로그에는 [reqId=%X{requestId}]가 포함되어 있어 요청 추적이 용이하지만, 파일 로그에는 이 정보가 없어 특정 요청과 관련된 로그들을 묶어서 확인하기 어렵습니다. 파일 로그 패턴에도 requestId를 추가하여 디버깅 및 모니터링 효율을 높이는 것을 권장합니다. ERROR_FILE appender(46라인)의 패턴에도 동일하게 적용해주세요.
| <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%level] [%thread] %logger{36} - %msg%n</pattern> | |
| <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%level] [%thread] [reqId=%X{requestId}] %logger{36} - %msg%n</pattern> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이것도 해결했어요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
확인했습니다! 수고하셨어요! 망나니 개발자님 블로그랑 코드 같이 보니까 재밌네욤!! 굳굳
| .average() | ||
| .orElse(0.0); | ||
|
|
||
| if (!reviews.isEmpty() && averageRating == 0.0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rating이 0.0일때는 log.warn이 오는게 맞을까요..? 어제 회의에서는 0.0일때도 화면 그냥 보여주는거 같아서 뭐가 더 적절할지 고민이 되네요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분은 리뷰가 존재함에도 불구하고 rating이 0.0이 나온다면 "리뷰 데이터에 문제가 있지 않을까?" 라고 생각을 했어요!
리뷰를 달려면 별점을 입력해야 하는 것 같아서.. 혹시 맞을까요!?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
네네 맞아요! 별점 입력해야 리뷰 달기 가능이에요!
두 조건 다 검증하는 것으로 가시쥬 지금 그대로 기기해도 좋습니다!
Dockerfile
Outdated
| WORKDIR /app | ||
|
|
||
| # 로그 디렉토리 미리 생성 | ||
| RUN mkdir -p /app/logs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오 로그 쌓는거 너무 좋은데요! 근데 로그가 너무 많이 쌓이면 ec2자체가 터져버릴 수도 있는 상황도 생각해본다면! 혹시 cron으로 주기적으로 로그를 청소해보는건 어떨까 하는 생각을 해봤어요.! 근데 나중에 결정적일때 로그가 없어져버리는 상황도 생각해야겠네요 ㅋㅎㅋㅎ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
헛 이 부분은 로그를 출력하는 방식을 변경해서 함께 삭제를 해야하는데 반영을 못했네욥! 삭제하겠습니다!
로그 청소에 대해서 조금 알아봤는데요,
지금 처럼 따로 로그에 대한 설정을 하지 않는 경우 도커 데몬의 json-file 드라이버 의해 관리됩니다.
- (모든 컨테이너의 로그에 대한 json을 도커 엔진이 관리하게 됨.)
보통은 docker run을 할 때 롤링 정책을 설정할 수 있는 것 같아서 그 방법으로 용량을 관리해도 좋아 보입니다.
https://docs.docker.com/engine/logging/drivers/local/
- Cloud Front/Promtail 같은 외부 수집기를 사용할 경우는 로컬에 많은 용량을 저장하고 있을 필요는 없어보이고 로컬 엔진에서 20M* 5로테이션 = 100 MB 정도만 저장하고 있어서 저렇게 최근 로그만 가지고 있어도 괜찮아 보입니다! ( 20GB 정도의 용량이 남아있어서 사용하면서 불편한 점이 있다면 수정해도 좋을 것 같습니다.)
--log-driver=json-file \
--log-opt max-size=20m \
--log-opt max-file=5 \
이 부분을 Docker Run에 추가해 두었습니다 !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 좋아요!!
|
|
||
| @Target({ElementType.FIELD, ElementType.RECORD_COMPONENT}) | ||
| @Retention(RetentionPolicy.RUNTIME) | ||
| public @interface LogMask { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오왕 이거 진짜 만드신거구나 짱짱!
|
=> 이 부분은 결국 비동기처리 된다면 로그가 시간순으로 쌓일수 없는것으로 이해했는데요! => 혹시 괜찮으시다면 시간도 찍어주실수 있나요? 소요시간 말고 요청한 시간입니다 이를테면 2025-09-29:13:49:21 이런식으로요~! 혹시 이 부분이 불요하거나 이미 관리하고 있다면 말씀해주시어요 |
| .average() | ||
| .orElse(0.0); | ||
|
|
||
| if (!reviews.isEmpty() && averageRating == 0.0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
네네 맞아요! 별점 입력해야 리뷰 달기 가능이에요!
두 조건 다 검증하는 것으로 가시쥬 지금 그대로 기기해도 좋습니다!
Dockerfile
Outdated
| WORKDIR /app | ||
|
|
||
| # 로그 디렉토리 미리 생성 | ||
| RUN mkdir -p /app/logs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 좋아요!!
|
로그 시간까지 다 확인했습니다-! dev로 머지해도 될것같아요! 수고하셨어요~!! |

#️⃣ Issue Number
📝 요약(Summary)
💬 공유사항 to 리뷰어
로깅 기능 구현 Details
logback-spring-xml
로그 공통 형식을 다음과 같이 설정했습니다.
래핑 비동기 처리 관련
로그는 이렇게 처리했어요
Controller Layer
컨르롤러의 로그는 대부분 비슷한 형식을 가지고 있을 것이라고 생각했습니다.
ControllerLogAspect에서 구현됩니다.ex)
ex)
Service Layer
⭐ 영속에 대한 Service Log 비동기 처리
LogEvent를 발행하고 AfterCommit으로 비동기 처리했습니다.LogEvent내부를 구현했습니다.✅ PR Checklist
PR이 다음 요구 사항을 충족하는지 확인하세요.