Skip to content

Latest commit

 

History

History
84 lines (59 loc) · 5.72 KB

File metadata and controls

84 lines (59 loc) · 5.72 KB

로깅 규약

시작 절차와 규칙은 CONTRIBUTING.md를 따릅니다. 이 문서는 로그를 어떻게 남기고, 요청을 traceId로 잇는가의 기준입니다. 운영에서 로그를 보는 방법(Grafana·Loki)은 infra/docs/monitoring.md를 따릅니다.

원칙

  • 로그는 운영에서 문제를 추적하기 위한 것입니다. 로그로 흐름 제어를 하지 않습니다.
  • 모든 요청 로그와 오류 응답은 같은 traceId를 공유해 서로 연결됩니다(에러 처리 규약).
  • 민감정보를 남기지 않습니다 — 비밀번호, 토큰, 인증 헤더, 개인정보, DB 자격증명. 필요하면 마스킹합니다.
  • 로그는 표준 출력(stdout) 으로 보냅니다. 파일로 직접 쓰지 않습니다. 컨테이너 로그를 인프라가 수집합니다(infra/backend-conventions).

traceId

traceId는 하나의 요청을 응답과 모든 로그에서 잇는 식별자입니다.

  • 요청마다 하나를 확보하고, 요청 처리 동안 MDC에 넣어 모든 로그에 자동으로 실리게 합니다.
  • 같은 값을 오류 응답 계약의 traceId 필드에 사용합니다(에러 처리 규약).
  • 요청이 끝나면 MDC를 정리해, 재사용되는 스레드에 값이 새지 않게 합니다.
  • 외부에서 온 상관관계 ID(X-Request-Id)가 있으면 검증을 통과한 경우에만 그 값을 씁니다. 없거나 통과하지 못하면 새로 생성합니다.

들어온 traceId는 검증합니다

클라이언트가 보낸 헤더를 그대로 쓰면 그 값이 로그 한 줄에 들어가는 통로가 됩니다. 개행을 섞으면 있지도 않은 로그 줄을 만들어 낼 수 있고(로그 위조), 길이 상한이 없으면 한 요청이 로그를 임의로 부풀립니다. 위의 "민감정보·개행 주입에 주의" 원칙이 적용되는 첫 번째 지점입니다.

global/web/TraceIdFilter가 허용 목록 방식으로 검사합니다.

항목
허용 문자 영숫자와 하이픈([A-Za-z0-9-]) — 제어문자·공백·구분자는 전부 거절
길이 1 ~ TraceIdFilter.MAX_LENGTH(64). UUID(36자)가 들어가는 크기
판정 부분 일치가 아니라 전체 일치. 일부만 맞는 값은 통과하지 못합니다
실패 시 요청을 실패시키지 않고 서버가 만든 UUID로 대체합니다
  • 거절한 값을 로그에 남기지 않습니다. 남기는 순간 막으려던 주입 경로가 다시 열립니다.
  • MDC·응답 헤더·오류 응답의 traceId에는 검증을 통과한 값만 실립니다. 세 곳이 항상 같은 값입니다.
  • 요청을 400으로 거절하지 않는 이유: 상관관계 ID는 부가 정보이고, 그 값이 이상하다고 정상 요청을 막을 이유가 없습니다.

로그 레벨

레벨 용도
ERROR 처리 실패로 사람이 조치해야 하는 상황. 처리되지 않은 예외, 외부 연동 실패 등
WARN 당장 실패는 아니지만 비정상·주의가 필요한 상황
INFO 운영상 의미 있는 사건(기동, 주요 상태 변화). 요청마다 남기지 않습니다
DEBUG 개발·조사용 상세. 운영 기본 레벨에서는 출력되지 않습니다
  • 예상된 도메인 오류(검증 실패, 404 등)는 ERROR로 남기지 않습니다. 정상 흐름의 일부입니다.
  • 처리되지 않은 예외(500)는 ERROR로 원인·스택을 남깁니다. 단, 이 정보는 로그에만 있고 응답 body에는 넣지 않습니다.

무엇을 어떻게 남기나

  • 사람이 아니라 기계와 검색을 위해 남깁니다. 메시지에 안정적인 키워드를 포함해 Loki에서 필터할 수 있게 합니다.
  • 예외를 로깅할 때는 예외 객체를 함께 넘겨 스택이 남게 합니다. 문자열로 이어붙이지 않습니다.
  • 같은 사건을 여러 계층에서 중복으로 로깅하지 않습니다. 처리 지점에서 한 번 남깁니다.
  • 로그 메시지에 사용자 입력을 그대로 넣을 때는 민감정보·개행 주입에 주의합니다.

운영 관측과의 연결

수집된 로그는 Grafana의 Loki에서 조회합니다(상세는 infra/docs/monitoring.md).

{namespace="pinlog-prod", app="<서비스명>"}

# 에러만
{namespace="pinlog-prod", app="<서비스명>"} |= "ERROR"
  • 헬스체크 요청 로그는 인프라에서 걸러집니다. 매 요청 INFO 로그로 관측을 오염시키지 않습니다.
  • 특정 요청을 추적할 때는 응답으로 받은 traceId로 로그를 검색합니다.

테스트

  • 로깅 자체를 단정하는 테스트는 최소화합니다. 대신 오류 응답의 traceId 계약을 테스트해 로그 상관관계의 전제를 지킵니다(에러 처리 규약).
  • 민감정보가 로그로 나가지 않아야 하는 지점(인증 등)은, 그 값이 응답·로그 경로에 실리지 않는지 검증합니다.

체크리스트

  • 요청마다 traceId를 MDC에 넣고 응답 계약과 같은 값을 쓴다
  • 요청 종료 시 MDC를 정리한다
  • 외부에서 온 traceId는 형식·길이를 검증하고, 통과하지 못하면 새로 생성한다
  • 비밀번호·토큰·인증 헤더·개인정보를 로그에 남기지 않는다
  • 예상된 도메인 오류를 ERROR로 남기지 않는다
  • 예외는 객체로 넘겨 스택을 남기고, 응답 body에는 노출하지 않는다
  • 로그는 stdout으로 보낸다