#!/bin/bash
# ==============================================================================
# 脚本名称: clean_apache_logs.sh
# 描述: 用于清理和归档企业内网 Apache 服务的日志文件。
# 功能: 
#   1. 压缩超过指定天数（默认3天）的未压缩日志文件。
#   2. 删除超过指定天数（默认14天）的旧日志文件。
#   3. 优雅地重载 Apache 服务，避免日志句柄失效。
# 使用限制: 需要 root 权限运行。
# ==============================================================================

set -euo pipefail

# 默认配置
LOG_DIR="/var/log/apache2"
COMPRESS_DAYS=3
KEEP_DAYS=14
DRY_RUN=false
RELOAD_SERVICE=true
SERVICE_NAME="apache2"

# 打印帮助信息
show_help() {
    cat << EOF
用法: $(basename "$0") [选项]

选项:
  -d DIR          指定 Apache 日志目录 (默认: $LOG_DIR)
  -c DAYS         压缩超过指定天数的日志 (默认: $COMPRESS_DAYS 天)
  -k DAYS         删除超过指定天数的旧日志 (默认: $KEEP_DAYS 天)
  -s SERVICE      指定 Apache 服务名称 (默认: $SERVICE_NAME)
  -n              测试模式 (Dry run)，仅打印将要执行的操作，不实际修改文件或服务
  -r              清除日志后不重载 Apache 服务
  -h              显示帮助信息

示例:
  sudo $0 -d /var/log/httpd -k 30 -c 7
EOF
}

# 解析命令行参数
while getopts "d:c:k:s:nrh" opt; do
    case "$opt" in
        d) LOG_DIR="$OPTARG" ;;
        c) COMPRESS_DAYS="$OPTARG" ;;
        k) KEEP_DAYS="$OPTARG" ;;
        s) SERVICE_NAME="$OPTARG" ;;
        n) DRY_RUN=true ;;
        r) RELOAD_SERVICE=false ;;
        h) show_help; exit 0 ;;
        *) show_help; exit 1 ;;
    esac
done

# 日志输出函数
log_info() {
    echo -e "\033[32m[INFO] $(date '+%Y-%m-%d %H:%M:%S') - $1\033[0m"
}

log_warn() {
    echo -e "\033[33m[WARN] $(date '+%Y-%m-%d %H:%M:%S') - $1\033[0m"
}

log_error() {
    echo -e "\033[31m[ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $1\033[0m" >&2
}

# 权限和目录检查
if [ "$EUID" -ne 0 ]; then
    log_error "此脚本必须以 root 权限运行 (例如使用 sudo)。"
    exit 1
fi

if [ ! -d "$LOG_DIR" ]; then
    log_error "指定的日志目录不存在: $LOG_DIR"
    exit 1
fi

# 参数合理性校验
if ! [[ "$COMPRESS_DAYS" =~ ^[0-9]+$ ]] || ! [[ "$KEEP_DAYS" =~ ^[0-9]+$ ]]; then
    log_error "天数必须是正整数。"
    exit 1
fi

if [ "$COMPRESS_DAYS" -ge "$KEEP_DAYS" ]; then
    log_warn "压缩天数 ($COMPRESS_DAYS) 大于或等于保留天数 ($KEEP_DAYS)。这会导致文件在压缩前就被删除。"
fi

log_info "开始清理 Apache 日志..."
log_info "日志目录: $LOG_DIR"
log_info "压缩天数阈值: $COMPRESS_DAYS 天"
log_info "删除天数阈值: $KEEP_DAYS 天"
if [ "$DRY_RUN" = true ]; then
    log_warn "--- 处于测试模式 (Dry run)，不会修改实际文件 ---"
fi

# 1. 压缩旧日志 (*.log, 且排除已压缩的 *.gz 以及当前正在写入的活动日志)
# 通常 Apache 活动日志是 access.log, error.log 等，我们需要压缩带有日期后缀或者过期的日志。
# 为了安全起见，我们只压缩修改时间超过 COMPRESS_DAYS 天的 *.log 文件。
log_info "步骤 1: 压缩超过 $COMPRESS_DAYS 天的日志文件..."
if [ "$DRY_RUN" = true ]; then
    find "$LOG_DIR" -type f -name "*.log" -mtime +"$COMPRESS_DAYS" -exec echo "[Dry Run] 将要压缩: {}" \;
else
    # 查找并压缩
    # 使用 gzip 压缩，并保持原文件权限，-f 强制覆盖已存在的压缩包
    find "$LOG_DIR" -type f -name "*.log" -mtime +"$COMPRESS_DAYS" -print0 | while IFS= read -r -d '' file; do
        log_info "正在压缩: $file"
        gzip -f "$file"
    done
fi

# 2. 删除过期的旧日志 (包括 *.log 和 *.gz)
log_info "步骤 2: 删除超过 $KEEP_DAYS 天的旧日志文件..."
if [ "$DRY_RUN" = true ]; then
    find "$LOG_DIR" -type f \( -name "*.log" -o -name "*.gz" \) -mtime +"$KEEP_DAYS" -exec echo "[Dry Run] 将要删除: {}" \;
else
    find "$LOG_DIR" -type f \( -name "*.log" -o -name "*.gz" \) -mtime +"$KEEP_DAYS" -print0 | while IFS= read -r -d '' file; do
        log_info "正在删除: $file"
        rm -f "$file"
    done
fi

# 3. 释放被删除文件但仍被 Apache 占用的文件句柄（如果适用）
# 如果直接删除了活动的日志文件，Apache 句柄可能不会释放，需要重载服务。
# 另外，我们也可以清理空日志文件并重建（如果需要），但通常 reload 服务即可。
if [ "$RELOAD_SERVICE" = true ]; then
    log_info "步骤 3: 尝试优雅重载 Apache 服务以释放文件句柄..."
    if [ "$DRY_RUN" = true ]; then
        echo "[Dry Run] 将要执行: systemctl reload $SERVICE_NAME"
    else
        if systemctl is-active --quiet "$SERVICE_NAME"; then
            log_info "正在重载服务: $SERVICE_NAME"
            if systemctl reload "$SERVICE_NAME"; then
                log_info "服务重载成功。"
            else
                log_warn "服务重载失败，请检查服务名称或状态。"
            fi
        else
            log_warn "服务 $SERVICE_NAME 未运行，跳过重载。"
        fi
    fi
else
    log_info "步骤 3: 已跳过重载服务。"
fi

log_info "Apache 日志清理完成。"
