← 뒤로

프로젝트 요약

문제(AS-IS)

목표(TO-BE)

  1. JSON 형식의 구조화 로깅으로 로그 파싱·검색 용이성 확보
  2. 환경별(dev/staging/prod) 로그 레벨·출력 형식 전략 수립
  3. 로그 필드 표준화(timestamp, level, message, traceId, spanId 등)
  4. MDC 기반 traceId/spanId로 분산 추적 가능하게 구성
  5. 민감 정보 마스킹 규칙 수립
  6. 로그 수집 스택 선택 및 연동 방향 결정
  7. HTTP Request 컨텍스트 로깅(method, path, status, duration 등)
  8. User/Client 컨텍스트 로깅(user_id, client_ip, user_agent 등)
  9. WebSocket 컨텍스트 로깅(websocket.command, websocket.session_id, websocket.destination 등)

설계/선택(Key decisions)

구분 선택 이유
JSON 인코더 logstash-logback-encoder 필드명·마스킹 커스터마이징 풍부, Collector 경유 없음
traceId/spanId Micrometer Tracing Spring Boot 3 네이티브, W3C 표준, Zipkin/Tempo 연동 가능
HTTP 컨텍스트 ObservationHandler 메트릭·트레이스와 동일 Observation 기반, Filter 불필요
User/Client 컨텍스트 Handler + ArgumentResolver client_ip/user_agent는 Handler, user_id/auth_session_id는 Resolver(검증 로직 중복 없음)
WebSocket 컨텍스트 WebSocketTracingChannelInterceptor 확장 traceId와 동일 생명 주기, 별도 Interceptor 불필요
MDC 필드 네이밍 http.*, websocket.* 프로토콜.필드 패턴으로 일관성, Loki 필터링 용이
완료 로그 레벨 HTTP INFO, WebSocket CONNECT/SUBSCRIBE/SEND INFO, heartbeat DEBUG 운영에서 유저 요청 추적 가능, heartbeat 로그량 억제

결과(Impact)

추후 디벨롭

  1. 목표 6(로그 수집 스택) 구현 및 Loki 연동 검증
  2. 로그 레벨·샘플링 등 고트래픽 대응 전략 수립

목차


구현 상세

1) JSON 구조화 로깅

배경: Spring Boot 기본 Logback으로 평문 로그가 출력됨. Loki 등과 연동하려면 파싱 가능한 형식이 필요함.

후보 비교:

결정: logstash-logback-encoder — 필드명·패턴·마스킹 풍부, Collector 경유 없어 성능 우수, trace_id는 MDC 별도 주입

2) 환경별 로그 형식

적용: LOGGING_FORMAT 환경변수로 출력 형식 지정(json/plain). application.propertieslogging.format=${LOGGING_FORMAT:json}

3) MDC 기반 traceId/spanId

목적: 요청 단위로 로그를 묶어 Loki 등에서 trace_id로 검색 시 동일 요청의 모든 로그를 한 번에 조회.

후보: (1) 수동 MDC Filter, (2) OTel Java Agent, (3) Micrometer Tracing

결정: Micrometer Tracing — trace_id/span_id 자동 주입, Spring Boot 3.x 네이티브, W3C 표준, Zipkin/Tempo 연동 시 유리. 의존성 1개·설정 1줄·코드 0줄로 도입 부담 낮음.

4) MDC 전파 (비동기·WebSocket·Scheduled·Redisson)

배경: MDC는 ThreadLocal 기반이라 스레드가 바뀌면 traceId/spanId가 사라짐.

상황 처리
비동기 (@Async, AsyncRunner) ContextPropagatingTaskDecorator 적용
WebSocket (clientInboundChannel) WebSocketTracingChannelInterceptor — preSend에서 span 생성·MDC 설정, afterSendCompletion에서 정리
@Scheduled, ThreadPoolTaskScheduler SpanRunner.runWithSpan() / ScheduledSpanAspect
Redisson topic listener AsyncRunner.runAsync로 콜백 본문을 taskExecutor에서 SpanRunner.runWithSpan + publishEvent 처리

5) 민감 정보 마스킹

목적: 비밀번호, 토큰, 시크릿 등 민감 정보가 로그에 노출되지 않도록 마스킹.

결정: 필드 기반 마스킹(PathMask / FieldNameBasedFieldMasker) 우선 적용. logstash-logback-encoder 7.0+ MaskingJsonGeneratorDecorator 사용.

마스킹 대상: password, secret, token, accessToken, refreshToken, authorization, cookie, apiKey, api_key

6) HTTP Request 컨텍스트

목적: HTTP 요청 처리 시 로그에 method, path, status, duration 포함.

결정: Micrometer Observation — Spring Boot 3 관찰성 모델과 일치, traceId/spanId와 동일한 Observation 기반. ObservationHandler<ServerRequestObservationContext> 구현.

MDC 필드: http.method, http.path, http.status_code, http.duration_ms

7) User/Client 컨텍스트

목적: 감사·디버깅·보안 분석을 위해 사용자·클라이언트 식별 정보 포함.

결정: Handler 확장 + ArgumentResolver 조합

MDC 필드: user_id, auth_session_id, client_ip, user_agent

8) WebSocket 컨텍스트

목적: WebSocket 메시지 처리 시 websocket.command, session_id, destination 등으로 Loki에서 필터·추적 가능하게 함.

결정: WebSocketTracingChannelInterceptor 확장 — traceId/spanId와 동일한 메시지 단위 생명 주기. StompHeaderAccessor로 command, sessionId, destination, sessionAttributes 추출.

MDC 필드: websocket.command, websocket.session_id, websocket.destination, websocket.start_time, websocket.end_time, websocket.duration_ms

완료 로그 레벨: heartbeat(null command) → DEBUG, CONNECT/SUBSCRIBE/SEND → INFO (로그량 억제)


어떠한 날카로운 피드백이더라도 환영합니다. 사소한 의견도 괜찮습니다.

citron0137@gmail.com 또는 LinkedIn 을 통해 피드백을 보내주세요.