Skip to content

Conversation

@sjk4618
Copy link
Member

@sjk4618 sjk4618 commented Dec 5, 2025

🔥Pull requests

⛳️ 작업한 브랜치

👷 작업한 내용

  • prod 환경 error level log 디스코드 웹훅 전송
  • dev 환경 디스코드 stacktrace 포함 전공

🚨 참고 사항

@sjk4618 sjk4618 self-assigned this Dec 5, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 5, 2025

📝 Walkthrough

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 오류 로그를 Discord를 통해 실시간으로 알림받는 기능 추가
    • 오류 발생 시 예외 정보 및 스택 추적 세부 정보 캡처 및 전송
  • 성능 개선

    • 비동기 로깅 처리 추가로 시스템 응답 성능 향상

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

prod 환경에서 ERROR 레벨의 로그를 Discord 웹훅으로 전송하는 기능을 추가합니다. 새로운 DiscordErrorLogAppender 클래스를 구현하여 Logback에 통합하고, 메시지 포맷팅 유틸을 업데이트하며, 비동기 appender를 통해 로그 처리 성능을 최적화합니다.

Changes

Cohort / File(s) 변경 사항
Discord 에러 로그 Appender 구현
src/main/java/com/permitseoul/permitserver/global/DiscordErrorLogAppender.java
새로운 DiscordErrorLogAppender 클래스 추가 (UnsynchronizedAppenderBase 확장). ERROR 레벨 로그를 Discord 웹훅으로 전송하며, MDC 필드(trace_id, uri, method, status), Seoul 타임존 타임스탐프, 로거/스레드 정보, 메시지를 포함. 예외 발생 시 스택트레이스를 Markdown 코드블록으로 포맷. URL, 연결 타임아웃, 읽기 타임아웃 설정 메서드 제공.
Logback 프로덕션 설정 업데이트
src/main/resources/logback/logback-prod-file.xml
DISCORD_ERROR appender를 HttpAppender에서 DiscordErrorLogAppender로 변경. 웹훅 URL 및 타임아웃 설정(각 3000ms) 추가. 비동기 appender 3개(ASYNC_INFO, ASYNC_ERROR, ASYNC_DISCORD_ERROR) 신규 도입하여 루트 로거에 적용. 기존 INFO_FILE, ERROR_FILE 주석 정리.
Discord 메시지 포맷터 확장
src/main/java/com/permitseoul/permitserver/global/external/discord/util/DiscordMessageFormatterUtil.java
Dev HTTP 로그 포맷에 FIELD_EXCEPTION, FIELD_STACKTRACE 필드 추가. 예외 및 스택트레이스 값을 JSON에서 읽어 Markdown 출력에 조건부로 추가.
기타 로깅 추가
src/main/java/com/permitseoul/permitserver/global/redis/RedisTicketTypeCountInitializer.java
RedisTicketTypeCountInitializer.run 메서드에 ERROR 레벨 로그 한 줄 추가.

Sequence Diagram

sequenceDiagram
    participant App as Application<br/>(Logger)
    participant Appender as DiscordErrorLogAppender
    participant Discord as Discord Webhook<br/>(API)

    App->>Appender: ERROR level log event
    Appender->>Appender: Extract MDC fields<br/>(trace_id, uri, method, status)
    Appender->>Appender: Format timestamp<br/>(Asia/Seoul timezone)
    Appender->>Appender: Build log content<br/>(message + stack trace)
    Appender->>Appender: JSON escape & wrap<br/>content field
    Appender->>Discord: HTTP POST webhook
    activate Discord
    Discord-->>Appender: Response (2xx or error)
    deactivate Discord
    alt 2xx Status
        Appender->>App: Success (silent)
    else Non-2xx Status
        Appender->>Appender: Log warning response
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • 주의 대상 영역:
    • DiscordErrorLogAppender의 JSON 이스케이프 로직 및 HTTP 전송 예외 처리 검증
    • 스택트레이스 길이 제한(1700자) 및 포맷팅 정확성 확인
    • Logback 비동기 appender 설정의 queueSize, discardingThreshold, neverBlock 파라미터 적절성 검토
    • MDC 필드 추출 시 null/missing 값 처리 안정성

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 Pull Request의 주요 변경사항을 명확하게 설명합니다. prod 환경에서 error 레벨 로그를 Discord 웹훅으로 전송하는 기능 추가로, 전체 변경사항의 주된 목표와 일치합니다.
Description check ✅ Passed 설명이 Pull Request의 변경사항과 관련이 있습니다. prod 환경 error 레벨 로그 Discord 전송과 dev 환경 stacktrace 포함 전송이라는 작업 내용이 실제 코드 변경사항과 부합합니다.
Linked Issues check ✅ Passed Pull Request가 연결된 이슈 #182의 요구사항을 충족합니다. DiscordErrorLogAppender 클래스 추가, logback 설정 변경, 그리고 메시지 포매터 업데이트로 prod 환경 error 로그 Discord 전송 기능이 구현되었습니다.
Out of Scope Changes check ✅ Passed 변경사항이 모두 연결된 이슈 #182의 범위 내에 있습니다. DiscordErrorLogAppender, DiscordMessageFormatterUtil, logback 설정, RedisTicketTypeCountInitializer의 모든 변경은 Discord 웹훅 로그 전송 기능 구현과 관련이 있습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#182

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

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

Copy link

@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: 2

🧹 Nitpick comments (1)
src/main/java/com/permitseoul/permitserver/global/DiscordErrorLogAppender.java (1)

122-150: new URL(url) 생성자는 Java 20+에서 deprecated되었습니다.

현재 Java 버전에 따라 경고가 발생할 수 있습니다. URI.create(url).toURL()을 사용하는 것이 권장됩니다.

-            final URL webhookUrl = new URL(url);
+            final URL webhookUrl = java.net.URI.create(url).toURL();
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b1112b and 4e8339d.

📒 Files selected for processing (4)
  • src/main/java/com/permitseoul/permitserver/domain/reservationsession/core/component/ReservationSessionCleanupScheduler.java (1 hunks)
  • src/main/java/com/permitseoul/permitserver/global/DiscordErrorLogAppender.java (1 hunks)
  • src/main/java/com/permitseoul/permitserver/global/external/discord/util/DiscordMessageFormatterUtil.java (2 hunks)
  • src/main/resources/logback/logback-prod-file.xml (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: sjk4618
Repo: PERMIT-SEOUL/permit-server PR: 48
File: src/main/java/com/permitseoul/permitserver/domain/guest/core/domain/entity/GuestEntity.java:31-37
Timestamp: 2025-07-15T09:37:32.765Z
Learning: sjk4618 prefers to implement factory methods or public constructors for entities when they are actually needed, rather than creating them proactively.
🔇 Additional comments (8)
src/main/java/com/permitseoul/permitserver/global/DiscordErrorLogAppender.java (2)

42-69: append() 메서드 구현이 적절합니다.

방어적 체크(시작 상태, URL 유효성)와 ERROR 레벨 필터링이 잘 구현되어 있습니다. addError()를 사용한 예외 처리도 Logback 방식에 맞습니다.


103-117: 스택 트레이스 처리 로직이 잘 구현되어 있습니다.

Discord 메시지 길이 제한을 고려하여 1700자로 truncation하고, Markdown 코드 블록으로 감싸는 처리가 적절합니다.

src/main/java/com/permitseoul/permitserver/global/external/discord/util/DiscordMessageFormatterUtil.java (3)

45-51: null/missing 노드 처리가 적절합니다.

isMissingNode() 체크와 asText(null) 사용으로 JSON 필드가 없거나 null인 경우를 안전하게 처리하고 있습니다.


64-73: Stacktrace 길이 제한을 고려해 보세요.

Discord 메시지는 2000자 제한이 있습니다. DiscordErrorLogAppender에서는 스택트레이스를 1700자로 truncation하고 있는데, 이 유틸리티에서도 동일한 처리가 필요할 수 있습니다. Dev 환경에서 긴 스택트레이스가 발생할 경우 메시지 전송이 실패할 수 있습니다.


75-99: 마크다운 포맷팅이 잘 구성되어 있습니다.

조건부로 exception과 stacktrace 섹션을 추가하는 방식이 깔끔하며, 기존 필드들과 일관성 있게 유지되어 있습니다.

src/main/resources/logback/logback-prod-file.xml (3)

83-97: Discord 웹훅 환경변수 설정을 확인해 주세요.

${DISCORD_ERROR_WEBHOOK_URL} 환경변수가 prod 환경에 설정되어 있는지 확인이 필요합니다. 미설정 시 DiscordErrorLogAppender의 guard 조건(url == null || url.isBlank())으로 인해 로그 전송이 무시되지만, 명시적인 설정 확인이 권장됩니다.


115-121: ASYNC_DISCORD_ERROR 설정이 적절합니다.

discardingThreshold=0으로 ERROR 로그 유실 방지, neverBlock=true로 애플리케이션 블로킹 방지가 잘 설정되어 있습니다. 다만 queueSize=500neverBlock=true 조합으로 인해 폭발적인 에러 발생 시 일부 로그가 유실될 수 있다는 점은 인지해 주세요.


107-113: ASYNC_ERROR에서 neverBlock=false 설정에 유의하세요.

에러 로그 파일 기록을 위해 neverBlock=false로 설정되어 있어, 큐가 가득 찰 경우 애플리케이션이 블로킹될 수 있습니다. 파일 I/O 성능 저하 시 영향이 있을 수 있으나, ERROR 로그 유실 방지 목적이라면 적절한 선택입니다.

Copy link

@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

🧹 Nitpick comments (1)
src/main/java/com/permitseoul/permitserver/global/DiscordErrorLogAppender.java (1)

30-40: 설정 값에 대한 사전 검증을 추가하는 것을 고려해보세요.

현재 URL과 타임아웃 값은 append() 시점에만 확인됩니다. start() 메서드를 오버라이드하여 다음을 검증하면 설정 오류를 조기에 발견할 수 있습니다:

  • URL이 null이 아니고 유효한 형식인지
  • 타임아웃 값이 양수인지

예시:

@Override
public void start() {
    if (url == null || url.isBlank()) {
        addError("Discord webhook URL is required");
        return;
    }
    if (connectTimeout <= 0 || readTimeout <= 0) {
        addError("Timeouts must be positive");
        return;
    }
    super.start();
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e8339d and 262885b.

📒 Files selected for processing (2)
  • src/main/java/com/permitseoul/permitserver/global/DiscordErrorLogAppender.java (1 hunks)
  • src/main/java/com/permitseoul/permitserver/global/redis/RedisTicketTypeCountInitializer.java (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/main/java/com/permitseoul/permitserver/global/redis/RedisTicketTypeCountInitializer.java
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: sjk4618
Repo: PERMIT-SEOUL/permit-server PR: 48
File: src/main/java/com/permitseoul/permitserver/domain/guest/core/domain/entity/GuestEntity.java:31-37
Timestamp: 2025-07-15T09:37:32.765Z
Learning: sjk4618 prefers to implement factory methods or public constructors for entities when they are actually needed, rather than creating them proactively.
🔇 Additional comments (4)
src/main/java/com/permitseoul/permitserver/global/DiscordErrorLogAppender.java (4)

18-28: 클래스 구조와 필드 선언이 적절합니다.

Logback appender 구현에 적합한 상속 구조이며, KST 타임스탬프 포맷터와 합리적인 기본 타임아웃 값이 잘 설정되어 있습니다.


42-69: 메인 로직이 잘 구현되어 있습니다.

ERROR 레벨 필터링과 방어적인 가드 절, 예외 처리가 적절하게 구현되어 있습니다. JSON 이스케이프 처리도 올바르게 적용되었습니다.


122-150: HTTP 전송 로직이 견고하게 구현되어 있습니다.

리소스 관리, 타임아웃 설정, 에러 처리가 모두 적절합니다. setFixedLengthStreamingMode를 사용하여 성능도 최적화되어 있습니다.


152-163: 헬퍼 메서드가 올바르게 구현되어 있습니다.

이전 리뷰에서 지적된 탭 문자 처리가 추가되어 JSON 이스케이프 로직이 완성되었습니다. safe() 메서드도 null 안전성을 적절히 제공합니다.

Comment on lines +103 to +117
final IThrowableProxy throwableProxy = event.getThrowableProxy();
if (throwableProxy != null) {
String stackTrace = ThrowableProxyUtil.asString(throwableProxy);

final int maxStackLength = 1700;
if (stackTrace.length() > maxStackLength) {
stackTrace = stackTrace.substring(0, maxStackLength) + "\n... (truncated)";
}

sb.append("\n\n")
.append("```")
.append("\n")
.append(stackTrace)
.append("\n```");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Discord API message character limit

💡 Result:

Short answer: 2,000 characters per plain message (Nitro-expanded messages can be up to ~4,000 where the feature is available). Embeds and webhooks use different limits (embed fields/titles/descriptions and a 6,000 total embed-character cap). [1][2]

Sources:

  • Discord community/support discussion confirming 2,000 (and experimental/ Nitro 4,000) message limit. [1]
  • Discord/embed limits (embed title/description/field limits and 6,000 total-embed-character rule). [2]

🏁 Script executed:

# First, let's examine the full buildContent method to understand the complete message structure
cd src/main/java/com/permitseoul/permitserver/global && cat -n DiscordErrorLogAppender.java | sed -n '71,120p'

Repository: PERMIT-SEOUL/permit-server

Length of output: 2242


Stack trace truncation may still exceed Discord's 2000-character message limit.

Discord enforces a 2000-character limit on plain text messages. The current 1700-character stack trace limit, combined with the message prefix (logger name, timestamp, thread, trace ID, URI, method, status, and formatted message), can total 2000+ characters depending on the lengths of variable fields like logger name and message content.

Consider reducing maxStackLength to 1500 characters to maintain a safe buffer below the Discord limit.

🤖 Prompt for AI Agents
In
src/main/java/com/permitseoul/permitserver/global/DiscordErrorLogAppender.java
around lines 103 to 117, the stack trace truncation uses maxStackLength=1700
which can still push the total Discord message over the 2000-character limit
when combined with the log header; change maxStackLength to 1500 (or compute
remaining allowance dynamically) so the appended stackTrace is limited to 1500
chars and still adds "\n... (truncated)" when cut, and ensure the final composed
message is validated against 2000 chars before sending (truncate the tail if
necessary).

@sjk4618 sjk4618 merged commit 853d2e7 into dev Dec 5, 2025
2 checks passed
@sjk4618 sjk4618 deleted the feat/#182 branch December 5, 2025 10:27
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.

feat: prod 환경 error level log 디스코드 웹훅 전송

2 participants