-
Notifications
You must be signed in to change notification settings - Fork 2
feat: prod 환경 error level log 디스코드 웹훅 전송 - #182 #183
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
Conversation
📝 WalkthroughSummary by CodeRabbit릴리스 노트
✏️ Tip: You can customize this high-level summary in your review settings. Walkthroughprod 환경에서 ERROR 레벨의 로그를 Discord 웹훅으로 전송하는 기능을 추가합니다. 새로운 DiscordErrorLogAppender 클래스를 구현하여 Logback에 통합하고, 메시지 포맷팅 유틸을 업데이트하며, 비동기 appender를 통해 로그 처리 성능을 최적화합니다. Changes
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
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.
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
📒 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=500과neverBlock=true조합으로 인해 폭발적인 에러 발생 시 일부 로그가 유실될 수 있다는 점은 인지해 주세요.
107-113: ASYNC_ERROR에서 neverBlock=false 설정에 유의하세요.에러 로그 파일 기록을 위해
neverBlock=false로 설정되어 있어, 큐가 가득 찰 경우 애플리케이션이 블로킹될 수 있습니다. 파일 I/O 성능 저하 시 영향이 있을 수 있으나, ERROR 로그 유실 방지 목적이라면 적절한 선택입니다.
...ermitserver/domain/reservationsession/core/component/ReservationSessionCleanupScheduler.java
Outdated
Show resolved
Hide resolved
src/main/java/com/permitseoul/permitserver/global/DiscordErrorLogAppender.java
Show resolved
Hide resolved
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.
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
📒 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 안전성을 적절히 제공합니다.
| 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```"); | ||
| } |
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.
🧩 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).
🔥Pull requests
⛳️ 작업한 브랜치
👷 작업한 내용
🚨 참고 사항