如何编写按天切割日志脚本

wen 实用脚本 30

本文目录导读:

如何编写按天切割日志脚本

  1. Linux Shell 脚本方案
  2. Python 脚本方案
  3. Java 方案(Log4j2)
  4. 使用定时任务
  5. Docker 容器方案
  6. 使用建议

Linux Shell 脚本方案

使用 logrotate(推荐)

#!/bin/bash
# 日志切割脚本 - logrotate方式
# 配置 logrotate
cat > /etc/logrotate.d/myapp << 'EOF'
/var/log/myapp/*.log {
    daily                    # 按天切割
    rotate 30               # 保留30天
    compress                # 压缩旧日志
    delaycompress           # 延迟一天压缩
    missingok              # 日志缺失不报错
    notifempty             # 空文件不切割
    create 0644 root root   # 创建新文件的权限
    sharedscripts           # 所有日志共享一个脚本
    postrotate
        # 通知应用程序重新打开日志文件
        kill -USR1 `cat /var/run/myapp.pid` 2>/dev/null || true
    endscript
}
EOF
# 手动运行测试
logrotate -vf /etc/logrotate.d/myapp

纯 Shell 脚本

#!/bin/bash
# 日志切割脚本 - 手动方式
LOG_DIR="/var/log/myapp"
LOG_FILE="app.log"
DATE_FORMAT=$(date +%Y%m%d)
RETENTION_DAYS=30
# 切割日志函数
rotate_log() {
    local log_path="$LOG_DIR/$LOG_FILE"
    local backup_path="$LOG_DIR/${LOG_FILE}_${DATE_FORMAT}"
    # 检查日志文件是否存在
    if [ ! -f "$log_path" ]; then
        echo "日志文件不存在: $log_path"
        exit 1
    fi
    # 复制当前日志到备份文件
    cp "$log_path" "$backup_path"
    # 清空当前日志文件
    > "$log_path"
    # 压缩旧日志(可选)
    gzip "$backup_path" 2>/dev/null || true
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] 日志已切割: $backup_path"
}
# 清理过期日志
cleanup_old_logs() {
    find "$LOG_DIR" -name "${LOG_FILE}_*" -type f -mtime +$RETENTION_DAYS -delete
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] 已清理 $RETENTION_DAYS 天前的旧日志"
}
# 主函数
main() {
    # 创建日志目录
    mkdir -p "$LOG_DIR"
    # 执行切割
    rotate_log
    # 清理旧日志
    cleanup_old_logs
}
main

Python 脚本方案

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import gzip
import shutil
import logging
from datetime import datetime, timedelta
from logging.handlers import TimedRotatingFileHandler
class DailyLogRotator:
    """按天切割日志"""
    def __init__(self, log_path, retention_days=30):
        self.log_path = log_path
        self.retention_days = retention_days
        self.logger = None
    def setup_logger(self, logger_name='myapp'):
        """设置按天切割的日志记录器"""
        handler = TimedRotatingFileHandler(
            filename=self.log_path,
            when='midnight',  # 每天午夜切割
            interval=1,       # 间隔1天
            backupCount=self.retention_days,  # 保留天数
            encoding='utf-8',
            delay=False
        )
        # 设置日志格式
        formatter = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        )
        handler.setFormatter(formatter)
        # 创建logger
        self.logger = logging.getLogger(logger_name)
        self.logger.setLevel(logging.INFO)
        self.logger.addHandler(handler)
        return self.logger
    def manual_rotate(self):
        """手动切割日志"""
        if not os.path.exists(self.log_path):
            print(f"日志文件不存在: {self.log_path}")
            return False
        # 生成备份文件名
        backup_name = f"{self.log_path}.{datetime.now().strftime('%Y%m%d')}"
        try:
            # 复制并清空原日志
            shutil.copy2(self.log_path, backup_name)
            with open(self.log_path, 'w') as f:
                f.truncate()
            # 压缩旧日志
            with open(backup_name, 'rb') as f_in:
                with gzip.open(f"{backup_name}.gz", 'wb') as f_out:
                    shutil.copyfileobj(f_in, f_out)
            # 删除未压缩的备份
            os.remove(backup_name)
            print(f"日志已切割并压缩: {backup_name}.gz")
            return True
        except Exception as e:
            print(f"日志切割失败: {e}")
            return False
    def cleanup_old_logs(self):
        """清理过期日志"""
        now = datetime.now()
        cutoff_date = now - timedelta(days=self.retention_days)
        for filename in os.listdir(os.path.dirname(self.log_path)):
            if filename.startswith(os.path.basename(self.log_path)):
                filepath = os.path.join(os.path.dirname(self.log_path), filename)
                # 解析日志中的日期
                try:
                    file_date = datetime.strptime(
                        filename.split('.')[-2],  # 假设格式为 app.log.20240101.gz
                        '%Y%m%d'
                    )
                    if file_date < cutoff_date:
                        os.remove(filepath)
                        print(f"已删除过期日志: {filename}")
                except (ValueError, IndexError):
                    continue
def main():
    """主函数"""
    # 配置
    config = {
        'log_path': '/var/log/myapp/app.log',
        'retention_days': 30
    }
    # 创建日志目录
    os.makedirs(os.path.dirname(config['log_path']), exist_ok=True)
    # 初始化日志切割器
    rotator = DailyLogRotator(
        log_path=config['log_path'],
        retention_days=config['retention_days']
    )
    # 手动切割
    rotator.manual_rotate()
    # 清理旧日志
    rotator.cleanup_old_logs()
    # 设置自动切割的logger(用于应用程序中)
    logger = rotator.setup_logger()
    # 测试日志
    logger.info("系统启动")
    logger.info("日志切割完成")
if __name__ == "__main__":
    main()

Java 方案(Log4j2)

<!-- log4j2.xml 配置 -->
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>
        <!-- 按天切割的 RollingFile -->
        <RollingFile name="RollingFile" 
                     fileName="/var/log/myapp/app.log"
                     filePattern="/var/log/myapp/app-%d{yyyy-MM-dd}.log.gz">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
            <Policies>
                <!-- 按天切割 -->
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
            </Policies>
            <!-- 保留30天 -->
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="RollingFile"/>
        </Root>
    </Loggers>
</Configuration>

使用定时任务

crontab 配置

# 每天0点执行日志切割
0 0 * * * /usr/local/bin/rotate_log.sh
# 如果需要更精确的时间(比如每天23:59)
59 23 * * * /usr/local/bin/rotate_log.sh
# 也可以每半小时执行一次检查
*/30 * * * * /usr/local/bin/rotate_log.sh

systemd timer (推荐)

# /etc/systemd/system/log-rotate.service
[Unit]
Description=Daily Log Rotation Service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/rotate_log.sh
User=root
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/log-rotate.timer
[Unit]
Description=Run log rotation daily
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target

Docker 容器方案

# Dockerfile
FROM alpine:latest
# 安装 logrotate
RUN apk add --no-cache logrotate bash
# 添加 logrotate 配置
COPY logrotate.conf /etc/logrotate.conf
COPY rotate_log.sh /usr/local/bin/rotate_log.sh
# 创建日志目录
RUN mkdir -p /var/log/myapp && \
    chmod +x /usr/local/bin/rotate_log.sh
# 添加定时任务
RUN echo "0 0 * * * /usr/local/bin/rotate_log.sh" >> /var/spool/cron/crontabs/root
CMD ["crond", "-f", "-l", "2"]

使用建议

  1. 推荐使用 logrotate:系统自带,配置简单,功能强大
  2. 考虑SIGHUP信号:许多应用(如Nginx)支持重新加载日志
  3. 压缩旧日志:节省存储空间
  4. 设置保留天数:避免磁盘占满
  5. 错误处理:确保日志切割失败时有告警
  6. 测试环境验证:先在小范围测试

选择哪种方案取决于你的具体需求和技术栈,对于大多数Linux系统,logrotate是最简单可靠的选择。

抱歉,评论功能暂时关闭!