기술 · 실용 가이드
Python 로그 회전에서 줄이 사라졌나요? inode 전환과 레코드 합집합 검증
회전 주체를 하나로 두고 열린 디스크립터와 경로를 대조한 뒤 이전·새 파일의 번호 레코드를 정확히 한 번씩 확인합니다.

logrotate가 성공하고 새 app.log가 생겨도 Python이 이전 inode에 쓰기를 멈췄다는 증거는 아닙니다. WatchedFileHandler는 Unix 계열에서 emit 전에 장치와 inode를 확인하므로 디스크립터·경로·레코드 세 단계로 검증해야 합니다.
회전 주체는 하나만 사용합니다. 외부 rename-create와 WatchedFileHandler 또는 내부 회전 핸들러 중 하나를 선택하고, 경계의 번호 레코드가 누락·중복 없는 하나의 순서가 되는지 확인합니다.
1. 회전 주체 하나 선택
logrotate가 app.log를 이름 변경하고 새로 만든다면 WatchedFileHandler 하나만 연결하고 내부 회전 핸들러를 겹치지 않습니다.
Windows나 inode 의미가 다른 파일시스템에서는 실제 마운트와 서비스 계정으로 시험하기 전 적용을 가정하지 않습니다.
2. 디스크립터와 경로 신원 기록
회전 전에 flush하고 os.fstat와 os.stat의 장치 번호·inode를 기록합니다. 두 값은 같은 객체를 가리켜야 합니다.
대상 디렉터리가 사라지면 다시 열기가 실패할 수 있으므로 부모 권한과 새 파일 소유자도 기록합니다.
3. 이름 변경·생성 후 한 건 기록
app.log를 app.log.1로 바꾸고 다음 쓰기 전에 검토한 소유권과 권한으로 새 app.log를 만듭니다.
번호 센티널 한 건을 기록하고 flush한 뒤 새 경로와 디스크립터 inode가 같고 회전 파일이 이전 inode를 유지하는지 확인합니다.
4. 모든 번호를 정확히 한 번 검증
회전 전 seq-001~003, 뒤 seq-004~006을 쓰고 두 파일을 모두 읽습니다. 합집합은 기대 집합과 같고 각 값은 한 번만 있어야 합니다.
CPython 3.12.14 로컬 시험에서는 누락·중복이 없고 회전 뒤 디스크립터가 새 경로 inode와 일치했습니다.
5. 경계를 숨기는 지름길 거부
copytruncate는 무손실 대안이 아닙니다. 공식 설명은 복사와 절단 사이 손실 구간을 경고합니다.
전체 줄 수만 비교하거나 새 경로 존재만으로 성공을 판단하지 않습니다. 한 누락과 한 중복은 같은 줄 수가 될 수 있습니다.
6. 결과 범위 기록
이 시험은 특정 Python, 파일시스템, 서비스 계정과 설정만 증명하며 원격 FS, 컨테이너 볼륨, Windows는 증명하지 않습니다.
버전, 핸들러, 정책, 마운트, 권한, 계정이 바뀌면 다시 시험하고 다시 열기 예외를 감시합니다.
