본문으로 건너뛰기

11.1 로그

Patroni 는 데몬으로 상시 동작하므로, 장애가 지나간 뒤 무슨 일이 있었는지 재구성할 1차 자료는 로그다. 어느 시점에 leader lock 을 잃었는지, 왜 demote 를 결정했는지는 DCS 에도 메트릭에도 남지 않고 로그에만 남는다. 로그 동작은 patroni.ymllog 섹션에서 제어한다.

출력 형식과 레벨

log.typeplain(기본)과 json 중 하나다. json 을 쓰려면 python-json-logger 패키지가 필요하다. plain 형식의 기본 포맷은 "%(asctime)s %(levelname)s: %(message)s" 이고 log.format 으로 바꾼다. json 형식에서는 format 에 문자열 대신 리스트를 주어 어떤 필드를 내보낼지 정의하고, key-value 표기로 필드 이름을 바꿔 붙일 수도 있다.

레벨은 둘로 나뉜다. log.level(기본 INFO)이 일반 로그 레벨이고, 예외 스택 트레이스를 어느 레벨로 기록할지는 log.traceback_level(기본 ERROR)이 따로 정한다. timestamp 형식은 log.dateformat 으로 조정한다.

json 전용 옵션인 log.static_fields 는 모든 레코드에 고정 필드를 추가한다. 여러 노드의 로그를 중앙 수집기로 모을 때 노드 식별자를 넣어 두면 출처 구분이 쉬워진다.

log:
  type: json
  level: INFO
  traceback_level: ERROR
  static_fields:
    node: patroni-node1
  loggers:
    patroni.postmaster: WARNING

log.loggers 는 모듈별 레벨 오버라이드다. 전체 레벨은 그대로 두고 유난히 소란한 모듈만 WARNING 으로 낮추거나, 추적 중인 모듈만 DEBUG 로 올리는 식으로 쓴다.

파일 로깅과 로테이션

log.dir 을 지정하면 stderr 대신 파일로 기록한다. 디렉토리는 미리 존재해야 하고 Patroni 를 실행하는 사용자가 쓸 수 있어야 한다. 파일 로깅의 기본 로테이션은 25MB 파일 4개다. 파일 크기는 log.file_size, 보관 개수는 log.file_num 으로 조정한다. log.mode 는 로그 파일 권한(예: 0644)이고, 지정하지 않으면 umask 를 따른다.

log:
  dir: /var/log/patroni
  file_num: 8
  mode: 0644
기본 보관량 100MB(25MB x 4)는 로그가 조용한 클러스터에서는 며칠 분량이지만, DEBUG 레벨을 켜거나 장애로 오류가 쏟아지는 상황에서는 빠르게 밀려난다. 장애 분석에 필요한 구간이 로테이션으로 사라지기 전에 수집기로 옮기거나 file_num 을 여유 있게 잡아 두는 편이 안전하다.

비동기 로깅 큐

Patroni 의 로깅은 2단계다. 로그 레코드를 먼저 메모리 큐에 넣고, 별도의 로깅 스레드가 큐에서 꺼내 stderr 또는 파일에 쓴다. 로그 저장소의 I/O 가 느려져도 HA 루프를 도는 스레드가 쓰기에 직접 막히지 않도록 한 구조다.

    flowchart TD
  MAIN["Patroni 스레드"] -->|레코드 추가| Q["메모리 큐<br/>max_queue_size 1000"]
  Q -->|꺼내서 기록| LT["로깅 스레드"]
  LT --> OUT1["stderr"]
  LT --> OUT2["log.dir 파일<br/>25MB x 4 로테이션"]
  

큐 크기는 log.max_queue_size 로 정하며 기본 1000 레코드다. 기본 설정 기준으로 약 1시간 20분 분량의 로그에 해당한다.

heartbeat 로그와 deduplicate_heartbeat_logs

Patroni 는 매 HA 루프마다 자신의 판단을 로그로 남긴다. 안정 상태에서는 매 loop_wait(기본 10초)마다 “leader lock 을 유지했고 할 일이 없다"는 취지의 같은 내용이 반복되므로, 로그 대부분이 이 heartbeat 로 채워진다. log.deduplicate_heartbeat_logs(기본 false)를 true 로 켜면 내용이 같은 heartbeat 로그의 반복을 억제한다.

heartbeat 로그의 반복은 노이즈이기만 한 것이 아니다. 각 레코드의 timestamp 간격이 곧 HA 루프가 제때 돌았다는 증거라서, 루프가 I/O 대기 등으로 정체된 구간을 로그 간격이 벌어진 지점으로 찾아내는 진단이 가능하다. deduplicate_heartbeat_logs 를 켜면 저장 공간을 아끼는 대신 이 타이밍 정보를 잃는다.

장애 분석에서 보는 로그 패턴

로그 메시지의 정확한 문구는 버전에 따라 달라지므로, 특정 문자열을 외우기보다 패턴 단위로 보는 접근이 오래 간다. 장애 시각 전후로 보통 다음 순서로 검사한다.

  • heartbeat 간격이 loop_wait 를 크게 벗어난 구간이 있는지. 있다면 그 시각에 Patroni 프로세스나 호스트 자체가 정체된 것이다.
  • promote 또는 demote 결정이 기록된 지점과 그 직전의 판단 근거. role 전환 로그는 failover 재구성의 축이 된다.
  • DCS 통신 오류와 재시도 기록. leader lock 갱신 실패로 이어졌다면 시나리오 A의 전개를 따라간다.

json 로깅을 쓰면 이 검사를 수집기 쿼리로 대신할 수 있다.

정리

  • 로그 동작은 log 섹션 하나로 제어한다. type(plain/json), leveltraceback_level, format, json 전용 static_fields, 모듈별 loggers 오버라이드가 있다.
  • dir 을 지정하면 파일 로깅으로 전환되고 기본 25MB x 4 로테이션이 적용된다. file_size, file_num, mode 로 조정한다.
  • 로깅은 메모리 큐(기본 1000 레코드)와 별도 스레드를 거치는 비동기 구조다.
  • heartbeat 로그의 timestamp 간격은 HA 루프 타이밍의 진단 자료다. deduplicate_heartbeat_logs 는 이 정보와 저장 공간을 맞바꾸는 설정이다.