Linux logrotate 로그 순환 설정

애플리케이션 로그 파일을 자동으로 순환(rotate), 압축, 정리하기 위한 logrotate 설정 템플릿입니다. 크기/시간 기반 순환, 압축, 사후 처리 스크립트 등을 포함합니다.

Gist
# /etc/logrotate.d/my-app
# logrotate 설정 파일 — 애플리케이션 로그 순환 설정
#
# 수동 실행 (테스트):  logrotate -d /etc/logrotate.d/my-app   (dry-run)
# 수동 강제 실행:      logrotate -f /etc/logrotate.d/my-app
# 전체 상태 확인:      cat /var/lib/logrotate/status
#
# logrotate는 기본적으로 cron에 의해 하루 한 번 실행됨
# /etc/cron.daily/logrotate 또는 systemd timer(logrotate.timer)


# ──────────────────────────────────────────────────
# 애플리케이션 일반 로그 (access, error)
# 와일드카드로 여러 파일을 하나의 블록으로 처리
# ──────────────────────────────────────────────────
/var/log/my-app/access.log
/var/log/my-app/error.log
/var/log/my-app/app-*.log {
    # 순환 주기: daily(매일), weekly(매주), monthly(매월), yearly(매년)
    daily

    # 보관할 이전 로그 파일 수 (rotate 5 = 5개 보관 후 가장 오래된 것 삭제)
    rotate 30

    # 순환된 파일을 gzip으로 압축 (디스크 절약)
    compress

    # 압축을 한 사이클 늦게 적용 (현재 순환된 파일은 다음 번에 압축)
    # 이유: 일부 애플리케이션이 아직 이전 로그에 쓰고 있을 수 있음
    delaycompress

    # 로그 파일이 없어도 오류 발생시키지 않음
    missingok

    # 로그 파일이 비어있으면 순환 건너뜀
    notifempty

    # 날짜를 파일명에 포함 (my-app.log-20240101.gz 형식)
    dateext
    # dateformat -%Y%m%d  # 날짜 형식 커스터마이즈 (기본: -%Y%m%d)

    # 로그 파일을 복사 후 원본을 비움 (copytruncate)
    # 주의: 애플리케이션이 파일 디스크립터를 재열기하지 않는 경우에만 사용
    # copytruncate

    # 권한 설정: 새 로그 파일 생성 시 적용할 권한, 소유자, 그룹
    create 0640 nodeapp nodeapp

    # 특정 크기 초과 시에도 순환 (시간 조건과 AND 조건)
    # size 100M: 100MB 초과 시 즉시 순환 (주기와 관계없이)
    # minsize 10M: 이 크기 이상일 때만 순환 (주기 + 크기 조건)
    minsize 10M

    # 순환 후 실행할 스크립트 (서비스에 새 로그 파일 열도록 신호 전송)
    postrotate
        # 방법 1: systemd 서비스에 SIGUSR1 신호 보내기 (새 로그 파일 열기)
        systemctl kill -s SIGUSR1 my-app.service 2>/dev/null || true

        # 방법 2: PID 파일로 직접 신호 전송
        # if [ -f /run/my-app.pid ]; then
        #     kill -USR1 $(cat /run/my-app.pid) 2>/dev/null || true
        # fi

        # 방법 3: nginx 방식 (로그 재열기)
        # nginx -s reopen 2>/dev/null || true
    endscript

    # 순환 전에 실행할 스크립트 (예: 마지막 로그 라인에 구분자 추가)
    # prerotate
    #     echo "=== 로그 순환: $(date) ===" >> /var/log/my-app/access.log
    # endscript
}


# ──────────────────────────────────────────────────
# 크기 기반 순환 (시간과 무관하게 크기 기준)
# 빠르게 쌓이는 디버그 로그에 적합
# ──────────────────────────────────────────────────
/var/log/my-app/debug.log {
    # 크기 기준 순환: 지정 크기 초과 시 즉시 순환 (주기 무시)
    size 50M

    # 소규모 보관: 최근 5개만 유지
    rotate 5

    compress
    delaycompress
    missingok
    notifempty
    dateext
    create 0640 nodeapp nodeapp

    postrotate
        systemctl kill -s SIGUSR1 my-app.service 2>/dev/null || true
    endscript
}


# ──────────────────────────────────────────────────
# 감사 로그 (장기 보관, 압축 필수)
# 규정 준수(Compliance) 목적의 로그 — 삭제 없이 장기 보관
# ──────────────────────────────────────────────────
/var/log/my-app/audit.log {
    weekly

    # 1년치(52주) 보관
    rotate 52

    compress
    # delaycompress 미적용 (감사 로그는 즉시 압축)
    missingok
    notifempty
    dateext

    # 압축 방식 변경 (기본 gzip 대신 bzip2, 더 높은 압축률)
    compresscmd /usr/bin/bzip2
    compressoptions -9
    uncompresscmd /usr/bin/bunzip2
    compressext .bz2

    # 권한 강화: 감사 로그는 root만 읽기 가능
    create 0600 root root

    # 마지막 로그 파일 삭제 시 실행할 스크립트
    # (예: 보관 완료 알림)
    lastaction
        echo "감사 로그 순환 완료: $(date)" | mail -s "감사 로그 순환 알림" security@example.com
    endscript
}


# ──────────────────────────────────────────────────
# Nginx 액세스/에러 로그 (참고용)
# ──────────────────────────────────────────────────
/var/log/nginx/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    sharedscripts

    # sharedscripts: 여러 파일이 매칭되어도 post/prerotate를 한 번만 실행
    postrotate
        # nginx에 USR1 신호로 로그 재열기
        if [ -f /run/nginx.pid ]; then
            kill -USR1 $(cat /run/nginx.pid)
        fi
    endscript
}