Skip to content

fix: 요청 로그 매칭을 위해 requestId를 명시#621

Merged
dh2906 merged 3 commits into
developfrom
fix/620-request-log-correlation
May 4, 2026
Merged

fix: 요청 로그 매칭을 위해 requestId를 명시#621
dh2906 merged 3 commits into
developfrom
fix/620-request-log-correlation

Conversation

@dh2906
Copy link
Copy Markdown
Contributor

@dh2906 dh2906 commented Apr 29, 2026

🔍 개요


🚀 주요 변경 내용

  • runtime.error Slack 알림 본문에 MDC requestId를 명시합니다.
  • 요청 상세 DEBUG 로그의 Request, Headers, Query String, Body 라인에도 같은 requestId를 명시합니다.
  • 예상하지 못한 예외 경로에서 Slack 에러 알림과 DEBUG 요청 로그가 같은 requestId를 공유하는지 테스트로 고정합니다.

💬 참고 사항

  • 콘솔/파일 로그 패턴에는 이미 requestId가 포함되어 있었지만, runtime.error 로거는 별도 Slack appender를 사용하고 additivity=false라 Slack 알림 자체에는 식별자가 남지 않았습니다.
  • DEBUG 비활성화 시 요청 상세 로그를 계산하지 않는 기존 동작은 유지했습니다.
  • 검증: CI=true ./gradlew test --tests 'gg.agit.konect.unit.global.exception.GlobalExceptionHandlerTest' --rerun-tasks, CI=true ./gradlew checkstyleMain

✅ Checklist (완료 조건)

  • 코드 스타일 가이드 준수
  • 테스트 코드 포함됨
  • Reviewers / Assignees / Labels 지정 완료
  • 보안 및 민감 정보 검증 (API 키, 환경 변수, 개인정보 등)

- 런타임 에러 Slack 알림과 DEBUG 요청 로그가 같은 요청을 가리키는지 확정할 수 있도록 requestId를 함께 남긴다.

- runtime.error 로거가 별도 Slack appender만 사용해 콘솔/파일 로그 패턴의 MDC만으로는 매칭이 어려운 구조를 보완한다.

- DEBUG 요청 상세 로그에도 같은 requestId를 포함해 동시 요청 상황에서도 에러와 요청 본문을 구분할 수 있게 한다.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 2026

Warning

Rate limit exceeded

@dh2906 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 53 minutes and 29 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9d6c95df-fae4-4e42-930c-6748ebde3efa

📥 Commits

Reviewing files that changed from the base of the PR and between 6e9175a and 8c7afd0.

📒 Files selected for processing (2)
  • src/main/java/gg/agit/konect/global/exception/GlobalExceptionHandler.java
  • src/test/java/gg/agit/konect/unit/global/exception/GlobalExceptionHandlerTest.java
📝 Walkthrough

Walkthrough

GlobalExceptionHandler가 SLF4J MDC의 requestId를 읽어 Slack 에러 메시지와 요청 디버그 로그에 포함하도록 변경되었습니다. 예외 위치 계산과 requestId 읽기를 별도 헬퍼로 분리하고, 디버그 로깅 오버로드를 추가했습니다.

Changes

Exception Handler + Tests

Layer / File(s) Summary
Data / Constants
src/main/java/gg/agit/konect/global/exception/GlobalExceptionHandler.java
REQUEST_ID_MDC_KEY = "requestId" 상수 추가 및 MDC에서 읽기 위한 기반을 마련.
Core Implementation
src/main/java/gg/agit/konect/global/exception/GlobalExceptionHandler.java
handleException(...)이 MDC에서 requestId를 얻어 Slack 메시지에 Request ID 라인으로 포함하도록 변경. 예외 위치 포맷팅을 getExceptionLocation(Exception)으로 분리.
Logging / Wiring
src/main/java/gg/agit/konect/global/exception/GlobalExceptionHandler.java
기존 requestDebugLogging(HttpServletRequest)requestId 인자를 받는 오버로드 추가. getRequestId() 헬퍼는 MDC에서 requestId를 읽고 없으면 " - " 반환. 요청 관련 디버그 로그 라인들이 Request [requestId: ...] 형식으로 프리픽스됨.
Tests
src/test/java/gg/agit/konect/unit/global/exception/GlobalExceptionHandlerTest.java
각 테스트에서 MDC.put("requestId", ...) 설정 및 MDC.clear() 정리 추가. 로그 어서션이 Request ID: \...` 및 requestId-프리픽스 형식(Request [requestId: ...], Query String [requestId: ...], Body [requestId: ...])을 검증하도록 업데이트. 스택 트레이스가 없을 때 위치는 unknown:0`으로 검증.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 나는 토끼, 로그 속을 뛰며,
requestId로 자국을 남기네,
Slack 알림엔 이름표를 붙이고,
디버그엔 꼬리를 달아주네.
누가 어느 요청인지 이제 한눈에!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 PR의 주요 변경 사항을 명확하게 설명합니다. MDC requestId를 로그에 명시하여 요청 매칭을 개선하는 핵심 변경 내용을 담고 있습니다.
Description check ✅ Passed 설명이 변경 사항과 관련성 있게 작성되었습니다. Slack 알림에 requestId 추가, DEBUG 로그에 requestId 포함, 테스트 검증 등 구체적인 변경 내용을 설명합니다.
Linked Issues check ✅ Passed PR은 #620 이슈의 모든 요구사항을 충족합니다. Slack 알림에 requestId 명시, DEBUG 로그에 requestId 포함, 동시 요청 테스트 검증이 모두 구현되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경 사항이 #620 이슈 범위 내에 있습니다. GlobalExceptionHandler와 테스트만 수정되었으며, requestId 통합 목표와 직접 관련된 변경입니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/620-request-log-correlation

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 53 minutes and 29 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/gg/agit/konect/global/exception/GlobalExceptionHandler.java (1)

207-213: ⚠️ Potential issue | 🔴 Critical

[LEVEL: high] 예외 핸들러에서 스택트레이스 첫 요소를 무조건 참조하고 있습니다.
문제: Line 208의 e.getStackTrace()[0]는 스택트레이스가 비어있는 예외에서 ArrayIndexOutOfBoundsException을 유발할 수 있습니다.
영향: 런타임 예외 처리 중 2차 예외가 발생하면 Slack 알림과 표준 에러 응답 생성이 깨져 장애 분석이 어려워집니다.
제안: 스택트레이스 길이를 검사해 비어있을 때 안전한 기본 location 값을 사용하세요.

최소 수정 예시
-        StackTraceElement origin = e.getStackTrace()[0];
+        StackTraceElement[] stackTrace = e.getStackTrace();
+        StackTraceElement origin = stackTrace.length > 0 ? stackTrace[0] : null;
@@
-        String location = String.format("%s:%d", origin.getFileName(), origin.getLineNumber());
+        String location = origin == null
+            ? "unknown:0"
+            : String.format("%s:%d", origin.getFileName(), origin.getLineNumber());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/gg/agit/konect/global/exception/GlobalExceptionHandler.java`
around lines 207 - 213, handleException currently does e.getStackTrace()[0]
(origin) which throws if the stack trace is empty; change handleException to
check e.getStackTrace() length (or null) before indexing and use a safe default
location string (e.g., "Unknown:0" or the exception class name) when empty, then
build uri, exception, location and message using that safe value so no secondary
ArrayIndexOutOfBoundsException occurs during error handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/test/java/gg/agit/konect/unit/global/exception/GlobalExceptionHandlerTest.java`:
- Around line 105-106: The test in GlobalExceptionHandlerTest currently checks
.doesNotContain("Body: {\"name\":\"KONECT\"}") which misses the actual log
format that can leak sensitive data; update the assertion on the logger output
(the chain containing .doesNotContain(...)) to either replace or add a check for
.doesNotContain("Body [requestId:") so the test fails if the log contains the
"Body [requestId:"-style payload; locate the assertion in
GlobalExceptionHandlerTest and modify the doesNotContain call(s) accordingly.

---

Outside diff comments:
In `@src/main/java/gg/agit/konect/global/exception/GlobalExceptionHandler.java`:
- Around line 207-213: handleException currently does e.getStackTrace()[0]
(origin) which throws if the stack trace is empty; change handleException to
check e.getStackTrace() length (or null) before indexing and use a safe default
location string (e.g., "Unknown:0" or the exception class name) when empty, then
build uri, exception, location and message using that safe value so no secondary
ArrayIndexOutOfBoundsException occurs during error handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e1778ff5-f10e-4c29-85ed-82c98be9199c

📥 Commits

Reviewing files that changed from the base of the PR and between c6b6dcc and 5e65295.

📒 Files selected for processing (2)
  • src/main/java/gg/agit/konect/global/exception/GlobalExceptionHandler.java
  • src/test/java/gg/agit/konect/unit/global/exception/GlobalExceptionHandlerTest.java
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: coverage
  • GitHub Check: Code Style Check
  • GitHub Check: Analyze (java-kotlin)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.java

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.java: Java 코드에서 import로 해결할 수 있는 경우 FQCN(Full Qualified Class Name)을 사용하지 않도록 지적한다
JPA/QueryDSL 조회 변경 시 N+1, 잘못된 fetch join, count 쿼리 왜곡, pagination 깨짐, distinct 누락을 확인한다
권한 로직은 관리자 우회, 요청자와 대상자 관계, 클럽/채팅방/공지/일정의 소속 검증이 빠지지 않았는지 확인한다
soft delete, 탈퇴 사용자, 차단/제외 조건, 중복 제거가 필요한 조회에서는 응답에 노출되면 안 되는 데이터가 포함되는지 확인한다
DTO 응답 변경은 기존 클라이언트가 기대하는 필드명, nullability, enum/string 값, 정렬 순서를 깨지 않는지 확인한다
조건이 2개 이상 결합된 비즈니스 규칙, 권한 조건, soft delete 제외, 중복 제거, fallback 우선순위, 대표값 선택, DTO 변환, count 쿼리 분리, fetch join 선택 이유처럼 코드만으로 의도가 숨겨지는 지점에는 주석을 권장한다
단순 생성자 호출, 필드 매핑, 컬렉션 반환, 이름만으로 명확한 분기에는 주석을 요구하지 않는다

Files:

  • src/main/java/gg/agit/konect/global/exception/GlobalExceptionHandler.java
  • src/test/java/gg/agit/konect/unit/global/exception/GlobalExceptionHandlerTest.java
**/*.{sql,java}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

데이터베이스 변경에서는 마이그레이션 순서, 기존 데이터 호환성, nullable/default 처리, 롤백 난이도, 인덱스 필요성을 확인한다

Files:

  • src/main/java/gg/agit/konect/global/exception/GlobalExceptionHandler.java
  • src/test/java/gg/agit/konect/unit/global/exception/GlobalExceptionHandlerTest.java
src/main/java/**/*.java

⚙️ CodeRabbit configuration file

src/main/java/**/*.java: 아래 원칙으로 리뷰 코멘트를 작성한다.

  • 코멘트는 반드시 한국어로 작성한다.
  • 반드시 수정이 필요한 항목만 코멘트로 남기고, 단순 취향 차이는 지적하지 않는다.
  • 각 코멘트 첫 줄에 심각도를 [LEVEL: high|medium|low] 형식으로 반드시 표기한다.
  • 심각도 기준: high=운영 장애 가능, medium=품질 저하, low=개선 권고.
  • 각 코멘트는 "문제 -> 영향 -> 제안" 순서로 3문장 이내로 간결하게 작성한다.
  • 가능하면 재현 조건 및 실패 시나리오도 포함한다.
  • 제안은 현재 코드베이스(Spring Boot + JPA + Flyway) 패턴과 일치해야 한다.
  • 보안, 트랜잭션 경계, 예외 처리, N+1, 성능 회귀 가능성을 우선 점검한다.
  • 가독성: 변수/메서드 이름이 의도를 바로 드러내는지, 중첩과 메서드 길이가 과도하지 않은지 점검한다.
  • 단순화: 불필요한 추상화, 중복 로직, 과한 방어 코드가 있으면 더 단순한 대안을 제시한다.
  • 확장성: 새 요구사항 추가 시 변경 범위가 최소화되는 구조인지(하드코딩 분기/값 여부 포함) 점검한다.

Files:

  • src/main/java/gg/agit/konect/global/exception/GlobalExceptionHandler.java
**/*

⚙️ CodeRabbit configuration file

**/*: 공통 리뷰 톤 가이드:

  • 모든 코멘트는 첫 줄에 [LEVEL: ...] 태그를 포함한다.
  • 과장된 표현 없이 사실 기반으로 작성한다.
  • 한 코멘트에는 하나의 이슈만 다룬다.
  • 코드 예시가 필요하면 최소 수정 예시를 제시한다.
  • 가독성/단순화/확장성 이슈를 발견하면 우선순위를 높여 코멘트한다.

Files:

  • src/main/java/gg/agit/konect/global/exception/GlobalExceptionHandler.java
  • src/test/java/gg/agit/konect/unit/global/exception/GlobalExceptionHandlerTest.java
🧠 Learnings (1)
📚 Learning: 2026-04-25T06:58:54.405Z
Learnt from: CR
Repo: BCSDLab/KONECT_BACK_END PR: 0
File: src/main/java/gg/agit/konect/domain/notification/AGENTS.md:0-0
Timestamp: 2026-04-25T06:58:54.405Z
Learning: Applies to src/main/java/gg/agit/konect/domain/notification/**/*IntegrationTest*.java : Add regression tests for: club application event rollback resulting in zero notifications created (AFTER_COMMIT integration test), inbox list/unread count/bulk read excluding chat-related types (repository integration test), Expo push partial ticket failures logged without whole-request exception propagation, group chat token vs target user count mismatch in current policy.

Applied to files:

  • src/test/java/gg/agit/konect/unit/global/exception/GlobalExceptionHandlerTest.java

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 29, 2026

🧪 JaCoCo Coverage Report (Changed Files)

변경된 main Java 소스 파일에 대한 JaCoCo 데이터가 없습니다.

📊 View Workflow Run

- 예외 핸들러가 스택트레이스 첫 요소에 바로 접근하면 2차 예외로 장애 분석 로그와 응답 생성이 깨질 수 있어 길이 검사를 추가한다.
- 스택트레이스가 비어 있을 때는 고정된 기본 위치를 남겨 Slack 알림 포맷을 유지한다.
- DEBUG 비활성화 테스트는 현재 Body 로그 포맷을 기준으로 민감 정보 로깅 회귀를 검출하도록 정리한다.
@dh2906
Copy link
Copy Markdown
Contributor Author

dh2906 commented May 4, 2026

CodeRabbit 리뷰 반영했습니다.

  • GlobalExceptionHandler가 빈 스택트레이스 예외를 처리할 때 unknown:0 위치를 사용하도록 방어했습니다.
  • DEBUG OFF 테스트의 Body 검증 문자열을 현재 로그 포맷 기준으로 수정했습니다.
  • 빈 스택트레이스 회귀 테스트를 추가했습니다.

검증:

  • ./gradlew test --tests gg.agit.konect.unit.global.exception.GlobalExceptionHandlerTest --rerun-tasks
  • CI=true ./gradlew test --rerun-tasks
  • ./gradlew checkstyleMain

@dh2906
Copy link
Copy Markdown
Contributor Author

dh2906 commented May 4, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

- 예외 타입이 스택트레이스를 null로 반환해도 예외 핸들러가 2차 예외를 만들지 않도록 기본 위치를 사용한다.
- 빈 스택트레이스와 같은 fallback 포맷을 유지해 Slack 알림과 에러 응답 흐름을 안정적으로 보존한다.
- null 스택트레이스 회귀 테스트를 추가해 예외 처리 경계 조건을 고정한다.
@dh2906
Copy link
Copy Markdown
Contributor Author

dh2906 commented May 4, 2026

추가 확인 후 null 스택트레이스 케이스까지 반영했습니다.

  • getExceptionLocation()에서 stackTrace == null || stackTrace.length == 0 모두 unknown:0으로 처리합니다.
  • getStackTrace()가 null을 반환하는 예외 회귀 테스트를 추가했습니다.

검증:

  • ./gradlew test --tests gg.agit.konect.unit.global.exception.GlobalExceptionHandlerTest --rerun-tasks
  • ./gradlew checkstyleMain

@dh2906 dh2906 merged commit 208e6e0 into develop May 4, 2026
5 checks passed
@dh2906 dh2906 deleted the fix/620-request-log-correlation branch May 4, 2026 04:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: 에러 알림과 요청 디버그 로그 requestId 매칭

1 participant