Skip to content

chore: 클라이언트 조기 연결 종료 로그 노이즈 제거 - #45

Merged
JayongLee merged 1 commit into
mainfrom
chore/disconnected-client-log-noise
Aug 21, 2026
Merged

JayongLee merged 1 commit into
mainfrom
chore/disconnected-client-log-noise

Conversation

@JayongLee

Copy link
Copy Markdown
Member

문제

다음 예외의 스택트레이스가 로그에 노출된다:

AsyncRequestNotUsableException: ServletOutputStream failed to flush:
java.io.IOException: Connection reset by peer
  at ...UTF8JsonGenerator.flush
  at ...AbstractJackson2HttpMessageConverter.writeInternal
  at ...ServletInvocableHandlerMethod.invokeAndHandle

Controller가 정상 응답을 만들어 직렬화하는 도중, 소켓 반대편이 이미 닫혀 있어 발생한다. 클라이언트가 응답 수신 전에 연결을 끊은 상황으로, 서버가 조치할 수 있는 것이 없다.

전형적 원인: 사용자의 페이지 이동/새로고침, 클라이언트 read timeout, ALB/Nginx idle timeout, 스캐너의 조기 종료.

조사 결과

Spring 6.2.17의 DisconnectedClientHelper는 이 예외를 이름 기준 화이트리스트에 명시적으로 포함한다 (바이트코드 확인):

EXCEPTION_TYPE_NAMES = { AbortedException, ClientAbortException,
                         EOFException, EofException,
                         AsyncRequestNotUsableException }
EXCEPTION_PHRASES    = { "broken pipe", "connection reset by peer" }

ExceptionHandlerExceptionResolver가 checkAndLogClientDisconnectedException()으로 이를 감지하면 빈 ModelAndView를 반환하고 즉시 종료한다.

따라서 이 예외는 GlobalExceptionHandler.handleUnexpected에 도달하지 않으며, Discord 알림도 발송되지 않는다. 알림 노이즈 우려는 실제로는 발생하지 않는 것으로 확인했다.

남는 것은 Spring 전용 DisconnectedClient 카테고리(DEBUG)와, 예외가 컨테이너까지 전파된 경우 Tomcat이 찍는 스택트레이스다.

변경

Spring이 이 목적으로 분리해 둔 두 카테고리를 OFF로 내렸다. 코드 변경 없이 logback-spring.xml만 수정한다.

<logger name="org.springframework.web.servlet.handler.DisconnectedClient" level="OFF"/>
<logger name="org.springframework.web.servlet.mvc.method.annotation.DisconnectedClient" level="OFF"/>

springProfile 블록 밖에 두어 dev/prod 모두에 적용된다.

트레이드오프

발생 빈도가 급증하면 그것은 신호다. 특정 엔드포인트에 몰린다면 응답이 느려져 클라이언트 타임아웃에 걸린다는 뜻이고, 이는 실제 성능 문제다. 이번 변경은 그 신호도 함께 가린다.

빈도 추적이 필요하다면 OFF 대신 WARN으로 조정하면 된다. 위에서 확인했듯 이 예외는 알림 경로를 타지 않으므로 WARN이어도 Discord로 새지 않는다. 리뷰에서 의견 주시면 조정하겠다.

검증

  • ./gradlew test 통과 (Logback 설정 오류 시 컨텍스트 로딩 단계에서 실패하므로 설정 유효성도 함께 확인됨)
  • ./gradlew ktlintCheck 통과

AsyncRequestNotUsableException(ServletOutputStream flush 실패, connection
reset by peer)은 클라이언트가 응답 수신 전에 연결을 끊은 상황으로 서버가
조치할 수 있는 것이 없다.

Spring 6.2의 DisconnectedClientHelper는 이 예외를 이름 기준 화이트리스트에
포함하므로, ExceptionHandlerExceptionResolver가 감지하면 빈 ModelAndView를
반환하고 종료한다. GlobalExceptionHandler에 도달하지 않아 Discord 알림도
발송되지 않는다.

Spring이 이 목적으로 분리해 둔 두 카테고리를 OFF로 내려 로그만 정리했다.
@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@JayongLee, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 59 minutes

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

Wait for the limit to reset, then comment @coderabbitai review or push new commits to the PR.

An organization admin can change what happens after included review limits in Billing.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d89656c3-4b36-4063-a816-bceced7bafd7

📥 Commits

Reviewing files that changed from the base of the PR and between fe6ba16 and ec03a54.

📒 Files selected for processing (1)
  • src/main/resources/logback-spring.xml

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.

@JayongLee JayongLee self-assigned this Aug 21, 2026
@JayongLee
JayongLee merged commit ea6608b into main Aug 21, 2026
2 checks passed
@JayongLee
JayongLee deleted the chore/disconnected-client-log-noise branch August 21, 2026 11:24
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.

1 participant