Skip to content

Conversation

@sjk4618
Copy link
Member

@sjk4618 sjk4618 commented Nov 4, 2025

🔥Pull requests

⛳️ 작업한 브랜치

👷 작업한 내용

prod 환경 logback error level 디스코드 웹훅 전송

🚨 참고 사항

@sjk4618 sjk4618 self-assigned this Nov 4, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 4, 2025

📝 Walkthrough

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • Discord를 통한 에러 알림 기능 추가
  • 개선 사항

    • 로그 추적 정보 확대 (URI, 메서드, 상태 코드 포함)
    • 로그 파일 관리 개선 및 정리 정책 추가
    • 헬스 체크 URL 필터링으로 로그 노이즈 감소

Walkthrough

헬스체크 요청을 건너뛰고 traceId를 확보한 뒤 MDC에 uri·method·trace_id를 설정하고 체인 실행 후 status를 MDC에 기록하며 finally에서 개별 MDC 키들을 제거하도록 필터가 변경되었고, logback-prod에 INFO/ERROR 필터·MDC 확장·비동기 appenders·Discord HTTP appender가 추가되며 Gradle에 Discord appender 의존성이 추가되었습니다.

Changes

Cohort / File(s) 변경 사항
MDC 필터 강화
src/main/java/com/permitseoul/permitserver/global/filter/MDCLoggingFilter.java
헬스체크 URL 조기 스킵 추가; TRACE_ID, URI, METHOD, STATUS 상수 정리; traceId 확보 후 MDC에 uri·method 설정; 체인 실행 후 status 설정; finally에서 개별 MDC 키별 제거로 변경(전체 clear 제거)
Logback 프로덕션 설정 확장
src/main/resources/logback/logback-prod-file.xml
INFO/ERROR appender에 uri,method,status MDC 추가; INFO는 ERROR 제외(LevelFilter), ERROR는 ERROR 전용 필터 적용; stackTrace maxDepthPerThrowable 10→20; RollingPolicy 파일패턴을 .log로 변경하고 cleanHistoryOnStart 적용; DISCORD_ERROR HTTP appender(웹훅 POST, JSON 페이로드, 타임아웃) 추가; ASYNC appenders(ASYNC_INFO,ASYNC_ERROR,ASYNC_DISCORD_ERROR) 추가 및 루트 로거에 연결
빌드 의존성 변경
build.gradle
maven { url 'https://jitpack.io' } 리포지토리 추가 및 implementation 'com.github.napstr:logback-discord-appender:1.0.0' 의존성 추가

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client
    participant Filter as MDCLoggingFilter
    participant Chain as FilterChain
    participant App as Application
    rect rgb(245,255,245)
    Client->>Filter: HTTP Request
    note right of Filter: 1) health-check 검사\n2) traceId 확인/생성\n3) MDC에 trace_id, uri, method 설정
    end
    alt health-check URL
        Filter-->>Client: short-circuit (skip logging)
    else normal request
        Filter->>Chain: proceed(request)
        Chain->>App: handle request
        App-->>Chain: response(status)
        Chain-->>Filter: response
        note right of Filter: 4) MDC에 status 설정\n5) 로그 이벤트 발생 → 비동기 appenders (INFO/ERROR/Discord)
        Filter-->>Client: response
    end
    rect rgb(255,250,245)
    note right of Filter: finally: 개별 MDC 제거(status, method, uri, trace_id)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • 주의 검토 항목:
    • 헬스체크 판정 로직과 예외적 요청에서의 스킵 영향 (MDCLoggingFilter.java).
    • MDC 설정/제거가 예외 상황에서도 누수 없이 동작하는지 점검.
    • DISCORD_ERROR appender의 페이로드 포맷, HTTP 타임아웃, 레벨 필터 동작 검증 (logback-prod-file.xml).
    • Async appender queue/neverBlock 설정과 운영 시 로깅 손실/지연 영향.
    • RollingPolicy의 파일패턴 변경 및 cleanHistoryOnStart 동작 확인.

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경 사항을 명확하게 반영하고 있으며, prod 환경에서 logback error 레벨을 Discord 웹훅으로 연동하는 내용을 정확히 설명합니다.
Description check ✅ Passed PR 설명이 작업 내용(prod 환경 logback error level Discord 웹훅 전송)을 명시하고 있어 변경 사항과 관련이 있습니다.
Linked Issues check ✅ Passed PR은 logback-discord-appender 의존성 추가, Discord 에러 어펜더 설정, MDC 필드 확장, 비동기 처리 구현을 통해 Issue #155의 prod 환경 Discord 연동 요구사항을 완전히 충족합니다.
Out of Scope Changes check ✅ Passed 모든 변경 사항(MDC 키 추가, Discord 어펜더 설정, 로깅 설정 개선)이 Issue #155의 Discord 에러 로그 연동이라는 범위 내에서 이루어졌습니다.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#155

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5514aa9 and 5b268f8.

📒 Files selected for processing (1)
  • build.gradle (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • build.gradle

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: 0

🧹 Nitpick comments (1)
src/main/resources/logback/logback-prod-file.xml (1)

133-138: Discord 전송 실패 시 에러 로그 유실 가능성을 고려하세요.

queueSize=500은 Discord webhook이 지연되거나 실패할 때 버퍼 역할을 하기에 작을 수 있습니다. 네트워크 장애나 Discord API 장애 시 많은 에러 로그가 유실될 수 있습니다.

권장 사항:

  • Discord 알림의 중요도에 따라 queueSize를 1000-2000으로 증가시키는 것을 고려하세요.
  • 또는 Discord 전송 실패가 허용 가능하다면(ERROR_FILE에 이미 기록되므로) 현재 설정을 유지하세요.

Line 113-114의 timeout 설정(3초)이 적절한지도 검토하세요. Discord 응답이 느린 경우 큐가 빠르게 쌓일 수 있습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 802835a and 67964bb.

📒 Files selected for processing (2)
  • src/main/java/com/permitseoul/permitserver/global/filter/MDCLoggingFilter.java (1 hunks)
  • src/main/resources/logback/logback-prod-file.xml (5 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.
🧬 Code graph analysis (1)
src/main/java/com/permitseoul/permitserver/global/filter/MDCLoggingFilter.java (1)
src/main/java/com/permitseoul/permitserver/global/Constants.java (1)
  • Constants (3-24)
🔇 Additional comments (9)
src/main/java/com/permitseoul/permitserver/global/filter/MDCLoggingFilter.java (4)

24-29: LGTM! MDC 키 상수 선언이 올바릅니다.

Java 컨벤션에 맞게 private static final 순서로 수정되었고, 새로운 MDC 키들(URI, METHOD, STATUS)이 logback 설정과 일관되게 정의되었습니다.


36-40: 헬스 체크 필터링 로직이 적절합니다.

헬스 체크 엔드포인트에 대해 MDC 처리를 건너뛰어 불필요한 로깅 오버헤드를 방지하고 있습니다. null 체크도 올바르게 구현되어 있습니다.


47-52: 예외 발생 시 STATUS MDC 누락 가능성을 확인하세요.

현재 구조에서는 filterChain.doFilter() 실행 중 예외가 발생하면 Line 52의 STATUS가 MDC에 설정되지 않습니다. 예외가 발생한 요청의 에러 로그에 STATUS 정보가 누락될 수 있습니다. 필요하다면 finally 블록이나 catch 블록에서 response.getStatus()를 호출하여 STATUS를 설정하는 것을 고려해보세요.


54-57: 개별 MDC 제거로의 변경이 더 안전합니다.

MDC.clear() 대신 개별 키를 제거하는 방식은 다른 필터가 설정한 MDC 엔트리에 영향을 주지 않아 더 안전합니다. 좋은 개선입니다.

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

7-11: INFO와 ERROR 로그 분리가 올바르게 구현되었습니다.

LevelFilter를 통해 INFO_FILE에서 ERROR 레벨을 제외하고, ERROR_FILE에서만 ERROR를 수용하여 로그가 명확하게 분리됩니다.


25-27: MDC 컨텍스트 확장이 MDCLoggingFilter와 일관됩니다.

uri, method, status 필드가 추가되어 로그에 요청 컨텍스트 정보가 풍부해졌습니다. MDCLoggingFilter에서 설정하는 키들과 정확히 일치합니다.

Also applies to: 58-60


66-66: 스택 트레이스 깊이 증가가 디버깅에 도움이 됩니다.

maxDepthPerThrowable을 10에서 20으로 증가시켜 복잡한 예외 체인의 근본 원인을 더 잘 파악할 수 있습니다. 로그 크기 증가는 합리적인 수준입니다.


88-115: Discord JSON 페이로드의 특수 문자 처리 문제 재확인

제공된 컨트롤러 검색 결과를 바탕으로 URI 패턴을 검증했습니다. 대부분의 경로 변수(eventId, ticketCode, paymentKey 등)는 숫자나 안전한 코드 형식이지만, 로그백 설정의 근본적인 문제는 여전히 존재합니다.

현재 문제:
Line 108의 패턴에서 trace_id=%X{trace_id} uri=%X{uri} method=%X{method} status=%X{status}가 JSON 문자열 내에 직접 삽입되고 있습니다. %message%xEx%replace로 개행을 처리하지만, MDC 값들에는 이스케이핑이 적용되지 않아 다음과 같은 경우에 JSON이 깨질 수 있습니다:

  • URI 쿼리 파라미터에 큰따옴표나 백슬래시 포함 시
  • 특수 제어 문자가 포함된 경우

권장 개선 사항:

  1. LoggingEventCompositeJsonEncoder 사용하여 구조화된 JSON 생성 (선호)
  2. 또는 MDC 값에 대한 이스케이핑 추가: %replace(%X{uri}){'\"','\\\"'}

126-131: ASYNC_ERROR의 블로킹 동작은 의도된 설계 입니다 (에러 로그 유실 방지 우선)

검증 결과, 파일의 주석에 명시된 대로 ASYNC_ERROR의 neverBlock=false 설정은 의도된 것입니다. 에러 로그 유실을 방지하기 위해 큐가 가득 찰 경우 요청 처리 스레드의 블로킹을 허용하는 설계입니다.

현재 설정의 트레이드오프:

  • 장점: 에러 로그 손실 방지 (discardingThreshold=0)
  • 위험: 에러 급증 시 요청 스레드 블로킹 가능성 (queueSize=2000)

권장 검토 항목:

  • 프로덕션 환경에서 실제 에러 발생 빈도가 이 트레이드오프를 정당화하는지 확인
  • 필요시 queueSize 증가 또는 neverBlock=true + discardingThreshold 조정을 고려

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67964bb and 9316de4.

📒 Files selected for processing (1)
  • src/main/resources/logback/logback-prod-file.xml (4 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 (4)
src/main/resources/logback/logback-prod-file.xml (4)

34-34: 파일명 패턴 변경(ZIP→LOG)과 cleanHistoryOnStart 활성화의 운영 영향을 확인하세요.

  • .zip 형식에서 .log 형식으로 변경되어 로그가 자동으로 압축되지 않습니다. 저장 공간 비용이 증가할 수 있습니다.
  • cleanHistoryOnStart=true가 추가되어 애플리케이션 시작 시 이전 로그 기록이 삭제됩니다(maxHistory 초과분). 운영 환경에서 로그 보존 정책과 일치하는지 확인이 필요합니다.

이 변경사항이 프로덕션 환경의 로그 보존 정책(maxHistory: 30일)과 일치하는지, 그리고 저장 공간 계획에 포함되었는지 확인해 주세요.

Also applies to: 80-80


117-131: 비동기 Appender 큐 설정 및 드롭/블로킹 전략이 적절하게 구성되었습니다.

각 로그 레벨별 우선순위와 앱 성능 보호의 균형이 잘 맞춰져 있습니다:

  • INFO: 큐 가득 시 WARN 이하 드롭 + 논블로킹 (지연 방지)
  • ERROR: 큐 가득 시 블로킹 (에러 손실 방지)
  • DISCORD: 큐 가득 시 드롭 + 논블로킹 (HTTP 타임아웃 시 앱 지연 방지)

단, 실제 운영 환경에서 큐 크기(INFO: 5000, ERROR: 2000, DISCORD: 500)가 예상 처리량에 충분한지 모니터링하시기 바랍니다.


7-11: INFO_FILE에서 ERROR 레벨 로그 필터링이 올바르게 설정되었습니다.

LevelFilter 설정(DENY on ERROR, NEUTRAL on mismatch)으로 INFO/WARN 파일과 ERROR 파일이 명확하게 분리됩니다.


25-27: MDC 필드 설정 및 정리가 일관성 있게 구현되어 있습니다.

검증 결과, MDCLoggingFilter에서 logback-prod-file.xml에 추가된 모든 MDC 필드(uri, method, status)가 정확하게 설정되고 있습니다:

  • uri: request.getRequestURI()로 설정
  • method: request.getMethod()로 설정
  • status: 필터 체인 실행 후 response.getStatus()로 설정

finally 블록에서 모든 MDC 키(trace_id, uri, method, status)가 올바르게 제거되고 있으므로 요청 간 MDC 정보 누수가 없습니다.

Comment on lines 88 to 115
<!-- Discord 웹훅 전송(error level만) -->
<appender name="DISCORD_ERROR" class="net.logstash.logback.appender.HttpAppender">
<url>${DISCORD_WEBHOOK_URL}</url> <!-- 환경변수로 주입 -->
<method>POST</method>
<headers>
<header>
<name>Content-Type</name>
<value>application/json</value>
</header>
</headers>

<!-- ERROR 이상만 전송 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>

<!-- Discord 단문 텍스트: 2000자 제한 대비 요약 포맷 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
{
"content": "[PERMIT-PROD] %level at %d{yyyy-MM-dd HH:mm:ss.SSS,Asia/Seoul}\n%logger{36}\n trace_id=%X{trace_id} uri=%X{uri} method=%X{method} status=%X{status}\n%replace(%message){'\r?\n',' \\n '}%n%replace(%xEx{5}){'\r?\n',' \\n '}
}
</pattern>
</encoder>

<connectTimeout>3000</connectTimeout>
<readTimeout>3000</readTimeout>
</appender>
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

Discord JSON payload은 PatternLayoutEncoder의 수동 JSON 구성으로 인한 특수문자 이스케이프 위험이 있습니다.

PatternLayoutEncoder로 수동 JSON을 구성할 때, 메시지나 MDC 값에 큰따옴표("), 백슬래시() 등이 포함되면 JSON 구조가 깨질 수 있습니다. 예를 들어 trace_id나 uri에 특수문자가 있으면 Discord 전송이 실패할 수 있습니다.

다음과 같이 JSON 인코더를 명시적으로 사용하거나, 패턴 필터를 개선해 특수문자를 완전히 이스케이프하는 것이 안전합니다:

<!-- Option 1: JsonLayout 사용 (권장) -->
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
  <includeContext>false</includeContext>
  <customFields>{"source":"PERMIT-PROD","environment":"production"}</customFields>
</encoder>

또는

<!-- Option 2: 기존 패턴에 더 강한 이스케이프 추가 -->
<pattern>
{
"content": "[PERMIT-PROD] %level at %d{yyyy-MM-dd HH:mm:ss.SSS,Asia/Seoul}\n%logger{36}\n trace_id=%replace(%X{trace_id}){'"'}{''}' uri=%replace(%X{uri}){'"'}{''}' method=%X{method} status=%X{status}\n...
}
</pattern>

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

HttpAppender 클래스 가용성과 환경변수 설정 확인 필요합니다.

Discord 웹훅 통합을 위해 logstash-logback-encoder에 포함된 HttpAppender가 필요합니다. 또한 ${DISCORD_WEBHOOK_URL} 환경변수가 프로덕션 환경에 반드시 설정되어야 합니다.


🏁 Script executed:

#!/bin/bash
# 의존성 확인: logstash-logback-encoder에 HttpAppender 포함 확인
grep -r "HttpAppender" --include="pom.xml" --include="build.gradle"

# MDC 필드 설정 확인: MDCLoggingFilter에서 uri, method, status 설정 여부
rg -A 10 "class MDCLoggingFilter" src/main/java

# 환경변수 문서 확인
find . -name "*.md" -o -name "*.yml" -o -name "*.yaml" | xargs grep -l "DISCORD_WEBHOOK_URL" 2>/dev/null || echo "Environment variable documentation not found"

Length of output: 1412


추가 검증이 필요합니다. 현재까지의 결과를 바탕으로:

logstash-logback-encoder는 HttpAppender를 제공하지 않으며, HttpAppender는 별도의 logstash-logback-http 라이브러리에서 제공됩니다.


🏁 Script executed:

#!/bin/bash
# 1. pom.xml에서 의존성 확인
cat pom.xml | grep -A 5 "logstash-logback"

# 2. MDCLoggingFilter에서 MDC.put 호출 확인
rg "MDC\.put" src/main/java/com/permitseoul/permitserver/global/filter/MDCLoggingFilter.java -A 2 -B 2

# 3. 배포 설정/환경변수 문서 검색
find . -type f \( -name "*.md" -o -name "*deployment*" -o -name "*environment*" -o -name "*.yml" \) | head -20

Length of output: 666


추가 검증을 진행하겠습니다.


🏁 Script executed:

#!/bin/bash
# 1. build.gradle에서 의존성 확인
find . -name "build.gradle*" -type f | head -5 | xargs cat 2>/dev/null | grep -A 5 -B 2 "logstash\|HttpAppender"

# 2. 프로덕션 배포 워크플로우에서 환경변수 설정 확인
cat .github/workflows/DOCKER-PROD-CD.yml 2>/dev/null | grep -i "discord\|webhook\|env" -A 2 -B 2

# 3. README에서 환경변수 설정 가이드 확인
cat README.md 2>/dev/null | grep -i "discord\|webhook\|env" -A 2 -B 2 || echo "No Discord/webhook config in README"

# 4. HttpAppender 클래스 실제 사용 확인
rg "HttpAppender" --type=java --type=xml src/

Length of output: 898


최종 검증을 위해 HttpAppender의 정확한 클래스명을 확인하겠습니다.


🏁 Script executed:

#!/bin/bash
# 다른 HttpAppender 구현체 확인
rg "HttpAppender" build.gradle --context 3

Length of output: 0


🌐 Web query:

logstash-logback-encoder 7.4 HttpAppender support

💡 Result:

Short answer: No — logstash-logback-encoder 7.4 does not provide a built-in HttpAppender. The project’s docs and release notes list the provided appenders (TCP, UDP, async, file/rolling via encoders, etc.) but no HttpAppender. [1][2]

If you need to send JSON logs over HTTP, common options are:

  • Use LogstashTcpSocketAppender (or another provided appender) and feed a Logstash TCP input. [1]
  • Use a third‑party Logback HTTP appender (or implement a small custom appender) that uses net.logstash.logback.encoder.LogstashEncoder to format JSON before posting.
  • Write logs to a file/rolling appender and have an external agent ship them via HTTP.

Sources: project README (appenders/encoders) and 7.4 release notes. [1][2]


HttpAppender 의존성이 누락되어 있습니다. 필수 라이브러리를 추가해야 합니다.

logstash-logback-encoder 7.4는 HttpAppender를 제공하지 않습니다. 현재 build.gradle에는 logstash-logback-encoder:7.4만 있으며, logback-prod-file.xml의 net.logstash.logback.appender.HttpAppender 클래스는 런타임에 ClassNotFoundException을 발생시킬 것입니다.

필요한 조치:

  • build.gradle에 HTTP 전송을 지원하는 Logback appender 라이브러리 추가 (예: logstash-logback-http 또는 대체 구현체)
  • DISCORD_WEBHOOK_URL 환경변수 설정 가이드를 README 또는 배포 문서에 추가
  • MDC 필드(trace_id, uri, method, status)는 MDCLoggingFilter에서 올바르게 설정되고 있으므로 변경 불필요

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: 3

🧹 Nitpick comments (2)
src/main/resources/logback/logback-prod-file.xml (2)

100-102: ThresholdFilter 설정이 일관성 없습니다.

ERROR_FILE은 LevelFilter(LINE 73-77)로 ERROR만 수락하지만, DISCORD_ERROR는 ThresholdFilter(LINE 100-102)로 ERROR "이상"을 수락합니다. ThresholdFilter는 더 높은 레벨(FATAL 등)도 포함하므로, 의도가 명확하지 않습니다. Discord에 ERROR 레벨만 전송한다면 LevelFilter 사용을 권장합니다.

LevelFilter로 변경하여 일관성을 높이십시오:

<filter class="ch.qos.logback.classic.filter.LevelFilter">
    <level>ERROR</level>
    <onMatch>ACCEPT</onMatch>
    <onMismatch>DENY</onMismatch>
</filter>

132-137: ASYNC_DISCORD_ERROR의 neverBlock: true 설정 검토가 필요합니다.

현재 설정:

  • discardingThreshold: 0 (ERROR 로그 드롭 없음)
  • neverBlock: true (큐 가득 시 블로킹 안 함)

이 두 설정이 충돌합니다. neverBlock: true이면 큐가 가득 찬 경우 로그가 드롭될 수 있으며, discardingThreshold: 0의 의도가 훼손됩니다.

권장:
Discord 전송이 실패해도 로그 손실을 방지하려면 neverBlock: false로 변경하십시오 (ASYNC_ERROR와 동일):

<appender name="ASYNC_DISCORD_ERROR" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="DISCORD_ERROR"/>
    <queueSize>500</queueSize>
    <discardingThreshold>0</discardingThreshold>
    <neverBlock>false</neverBlock>
</appender>

다만 이 경우 Discord 웹훅 지연 시 요청 스레드가 블로킹될 수 있으므로, 큐 크기와 타임아웃을 모니터링하십시오.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9316de4 and 1a841d1.

📒 Files selected for processing (2)
  • build.gradle (1 hunks)
  • src/main/resources/logback/logback-prod-file.xml (4 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 (6)
src/main/resources/logback/logback-prod-file.xml (6)

7-11: INFO_FILE에 LevelFilter 추가는 적절합니다.

ERROR 레벨 로그를 제외하는 설정이 올바르게 추가되었습니다. 이로써 INFO_FILE과 ERROR_FILE의 역할이 명확히 분리됩니다.


112-113: 타임아웃 설정이 적절합니다.

connectTimeout 3초, readTimeout 3초 설정으로 Discord 웹훅 호출 시 무한 대기를 방지합니다.


34-34: fileNamePattern 변경이 합리적입니다.

.zip 압축 형식에서 .log 형식으로 변경되어 로그 접근성이 향상됩니다.

Also applies to: 80-80


38-38: cleanHistoryOnStart 추가가 적절합니다.

서버 재시작 시 오래된 로그 정리가 자동으로 수행되어 디스크 관리가 개선됩니다.

Also applies to: 84-84


66-66: maxDepthPerThrowable 증가(10 → 20)는 스택트레이스 추적성 향상에 도움이 됩니다.

더 깊은 호출 스택을 기록함으로써 디버깅 정보가 풍부해집니다. 다만 JSON 페이로드 크기 증가에 유의하십시오.


24-27: MDC 필드 구현이 올바르게 완료되었습니다.

검증 결과, MDCLoggingFilter에서 모든 MDC 필드가 정확하게 설정되고 있습니다:

  • MDC.put(URI, request.getRequestURI()) (47줄)
  • MDC.put(METHOD, request.getMethod()) (48줄)
  • MDC.put(STATUS, String.valueOf(response.getStatus())) (52줄)

logback-prod-file.xml의 설정(24-27줄, 57-60줄)과 일치하며, 클린업도 적절히 구현되어 있습니다.

build.gradle Outdated
Comment on lines 79 to 80
implementation 'net.logstash.logback:logstash-logback-encoder:7.4'
implementation 'net.logstash.logback:logstash-logback-http:1.0.0'
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

의존성 버전 호환성 검증이 필요합니다.

logstash-logback-http:1.0.0과 logstash-logback-encoder:7.4의 호환성을 확인하십시오. 특히 HttpAppender가 logback의 encoder/provider와 올바르게 협력하는지 검증해야 합니다. 또한 최신 버전을 사용하고 있는지 확인하시기 바랍니다.


🌐 Web query:

What is the latest version of logstash-logback-http?

💡 Result:

The latest released version of de.idealo.whitelabels:logstash-logback-http is 1.1.1 (released Sep 22, 2022). [1][2]

Sources:
[1] MVNRepository — de.idealo.whitelabels:logstash-logback-http 1.1.1.
[2] Maven Central listing for de.idealo.whitelabels:logstash-logback-http.


잘못된 Maven 아티팩트 그룹 ID를 수정하세요.

Line 80의 net.logstash.logback:logstash-logback-http:1.0.0은 존재하지 않습니다. 올바른 의존성은 de.idealo.whitelabels:logstash-logback-http:1.2입니다.

🤖 Prompt for AI Agents
In build.gradle around lines 79 to 80, the dependency on line 80 uses a
non-existent Maven artifact groupId and coordinates; replace the incorrect
dependency `net.logstash.logback:logstash-logback-http:1.0.0` with the correct
artifact `de.idealo.whitelabels:logstash-logback-http:1.2` so the build resolves
the HTTP encoder dependency properly.

Comment on lines +90 to +91
<url>${DISCORD_WEBHOOK_URL}</url>
<method>POST</method>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

${DISCORD_WEBHOOK_URL} 환경변수 설정 가이드가 필요합니다.

프로덕션 배포 시 이 환경변수가 필수입니다. 미설정 시 HttpAppender 초기화 실패 또는 403/404 에러가 발생할 수 있습니다. 다음을 확인하십시오:

  1. 배포 문서(README 또는 wiki)에 환경변수 설정 방법 추가
  2. CI/CD 파이프라인(예: GitHub Actions, Docker)에서 환경변수 주입 설정 확인
  3. 선택: 환경변수 미설정 시의 graceful fallback 처리 (예: 로그 비활성화)

🏁 Script executed:

#!/bin/bash
# 1. 배포 관련 문서 확인
find . -type f \( -name "README*" -o -name "DEPLOY*" -o -name "*.md" \) | xargs grep -l "DISCORD\|webhook\|환경변수" 2>/dev/null || echo "배포 문서에서 환경변수 설정 가이드 미발견"

# 2. GitHub Actions 워크플로우 확인
find .github/workflows -name "*.yml" -o -name "*.yaml" 2>/dev/null | xargs grep -l "DISCORD\|env" 2>/dev/null || echo "GitHub Actions에서 환경변수 설정 미발견"

Length of output: 96


추가 검증이 필요합니다. GitHub Actions 워크플로우 파일과 logback 설정의 상세 내용을 확인하겠습니다.


---

🏁 Script executed:

```shell
#!/bin/bash
# 1. GitHub Actions 워크플로우에서 DISCORD_WEBHOOK_URL 설정 확인
echo "=== DOCKER-DEV-CD.yml 내용 ==="
grep -A5 -B5 "DISCORD\|webhook" .github/workflows/DOCKER-DEV-CD.yml 2>/dev/null || echo "DISCORD 관련 설정 없음"

echo ""
echo "=== DOCKER-PROD-CD.yml 내용 ==="
grep -A5 -B5 "DISCORD\|webhook" .github/workflows/DOCKER-PROD-CD.yml 2>/dev/null || echo "DISCORD 관련 설정 없음"

# 2. 전체 워크플로우 파일에서 env 섹션 확인 (env 변수 주입 여부)
echo ""
echo "=== DOCKER-PROD-CD.yml env 섹션 ==="
grep -A30 "^env:" .github/workflows/DOCKER-PROD-CD.yml 2>/dev/null || echo "env 섹션 없음"

# 3. logback-prod-file.xml 전체 내용 확인 (HttpAppender 및 오류 처리)
echo ""
echo "=== logback-prod-file.xml 전체 내용 ==="
cat src/main/resources/logback/logback-prod-file.xml 2>/dev/null || echo "파일 없음"

Length of output: 181


logback-prod-file.xml 파일을 찾을 수 없습니다. 리포지토리 전체에서 logback 설정 파일을 검색하겠습니다.


---

🏁 Script executed:

```shell
#!/bin/bash
# 1. 모든 logback 관련 파일 찾기
echo "=== logback 관련 파일 검색 ==="
find . -type f -name "*logback*" 2>/dev/null

# 2. 모든 XML 파일 검색 (resources/logback 디렉토리)
echo ""
echo "=== resources/logback 디렉토리 확인 ==="
find . -type d -path "*/resources/logback" 2>/dev/null

# 3. 전체 resources 디렉토리 구조
echo ""
echo "=== resources 디렉토리 구조 ==="
find . -type d -path "*/resources" 2>/dev/null | head -20

Length of output: 324


파일을 찾았습니다. 이제 logback-prod-file.xml의 내용을 확인하여 HttpAppender 설정과 오류 처리를 검증하겠습니다.


---

🏁 Script executed:

```shell
#!/bin/bash
# logback-prod-file.xml 파일 내용 확인
cat src/main/resources/logback/logback-prod-file.xml

Length of output: 5824


배포 문서 및 CI/CD 파이프라인에서 DISCORD_WEBHOOK_URL 환경변수 설정이 누락되어 있습니다.

검증 결과:

  1. 배포 문서: README, DEPLOY 관련 문서에서 DISCORD_WEBHOOK_URL 설정 가이드 없음
  2. CI/CD 파이프라인: GitHub Actions (DOCKER-PROD-CD.yml, DOCKER-DEV-CD.yml)에서 DISCORD_WEBHOOK_URL 환경변수 주입 설정 없음
  3. Logback 설정: HttpAppender에 오류 처리나 graceful fallback 메커니즘 미구현

다음을 작업하십시오:

  • 배포 문서(README 또는 wiki)에 환경변수 설정 방법 추가
  • CI/CD 파이프라인 (GitHub Actions)에서 DISCORD_WEBHOOK_URL 환경변수 주입 설정
  • 선택: logback 설정에 HttpAppender 오류 처리 추가 (예: conditional appender, fallback 로그레벨 조정)

Comment on lines +104 to +110
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
{
"content": "[PERMIT-PROD] %level at %d{yyyy-MM-dd HH:mm:ss.SSS,Asia/Seoul}\n%logger{36}\n trace_id=%replace(%X{trace_id}){"([\"\\])"}{\\\\$1} uri=%replace(%X{uri}){"([\"\\])"}{\\\\$1} method=%X{method} status=%X{status}\n%replace(%message){"([\"\\])"}{\\\\$1}%n%replace(%xEx{5}){'\r?\n',' \\n '}
}
</pattern>
</encoder>
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

Discord JSON 페이로드의 특수문자 이스케이프에 주의가 필요합니다.

패턴: %replace(%X{trace_id}){"([\"\\])"}{\\\\$1}

이 정규식은 큰따옴표와 백슬래시만 처리하지만, 로그 메시지에 개행(\n), 탭(\t), 제어문자 등이 포함되면 JSON 구조가 깨질 수 있습니다. 특히 메시지의 개행(%n)이 JSON 문자열 내부에서 실제 개행이 되면 JSON 파싱이 실패합니다(LINE 108의 %message에도 개행 포함 가능).

권장 개선사항:

  1. PatternLayoutEncoder 대신 LogstashEncoder 사용 (자동 이스케이프)
  2. 또는 정규식을 강화하여 모든 제어문자 처리
  3. 또는 사전 검증을 통해 JSON 유효성 테스트

LogstashEncoder 사용을 권장합니다:

<encoder class="net.logstash.logback.encoder.LogstashEncoder">
    <includeContext>false</includeContext>
    <customFields>{"source":"PERMIT-PROD","environment":"production"}</customFields>
</encoder>
🤖 Prompt for AI Agents
In src/main/resources/logback/logback-prod-file.xml around lines 104 to 110, the
PatternLayoutEncoder JSON pattern only escapes quotes and backslashes and can
produce invalid JSON when log fields contain newlines, tabs or other control
chars; replace the PatternLayoutEncoder with a LogstashEncoder
(net.logstash.logback.encoder.LogstashEncoder) configured with
includeContext=false and appropriate customFields to ensure proper JSON
encoding, or alternatively enhance the current %replace chains to escape all
control characters (newline, tab, carriage return, etc.) for every injected
field (%X{trace_id}, %X{uri}, %message, %xEx) so that the output is always a
valid single-line JSON payload.

@sjk4618 sjk4618 merged commit eb5e6c6 into dev Nov 4, 2025
2 checks passed
@sjk4618 sjk4618 deleted the feat/#155 branch November 4, 2025 15:02
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 환경 logback error level 디스코드 연동

2 participants