本文目录导读:

Linux Shell 脚本方式
使用 logrotate(推荐)
# 创建日志轮转配置文件
sudo tee /etc/logrotate.d/myapp << 'EOF'
/var/log/myapp/*.log {
daily # 每天轮转
rotate 30 # 保留30天
compress # 压缩旧日志
delaycompress # 延迟压缩
missingok # 如果日志丢失,不报错
notifempty # 如果日志为空,不轮转
create 644 www-data www-data # 创建新日志文件的权限
dateext # 使用日期作为后缀
dateformat -%Y%m%d # 日期格式
copytruncate # 先复制再清空原文件
postrotate
# 重启应用(如果有必要)
/usr/bin/systemctl reload myapp 2>/dev/null || true
endscript
}
EOF
手动 Shell 脚本
#!/bin/bash
# 日志按天分割脚本 - log_splitter.sh
LOG_DIR="/var/log/myapp"
LOG_FILE="app.log"
DATE_SUFFIX=$(date +%Y%m%d)
YESTERDAY_SUFFIX=$(date -d "yesterday" +%Y%m%d)
MAX_KEEP_DAYS=30
GZIP_DAYS=7 # 超过7天的日志压缩
# 创建日志目录
mkdir -p "$LOG_DIR"
# 函数:分割当前日志
split_log() {
local current_log="$LOG_DIR/$LOG_FILE"
local archive_log="$LOG_DIR/${LOG_FILE}.${DATE_SUFFIX}"
if [ -f "$current_log" ]; then
# 复制当前日志到归档文件
cp "$current_log" "$archive_log"
# 清空当前日志(不删除文件)
: > "$current_log"
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 日志已分割: $archive_log"
fi
}
# 函数:压缩旧日志
compress_old_logs() {
local cutoff_date=$(date -d "$GZIP_DAYS days ago" +%Y%m%d)
find "$LOG_DIR" -name "${LOG_FILE}.[0-9]*" -type f | while read log_file; do
local file_date=$(echo "$log_file" | grep -oP '\d{8}$')
if [ -n "$file_date" ] && [ "$file_date" -le "$cutoff_date" ]; then
if [[ ! "$log_file" =~ \.gz$ ]]; then
gzip "$log_file"
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 已压缩: ${log_file}.gz"
fi
fi
done
}
# 函数:清理过期日志
cleanup_old_logs() {
local cutoff_date=$(date -d "$MAX_KEEP_DAYS days ago" +%Y%m%d)
find "$LOG_DIR" -name "${LOG_FILE}.*" -type f | while read log_file; do
local file_date=""
# 提取日期
if [[ "$log_file" =~ [0-9]{8} ]]; then
file_date=$(echo "$log_file" | grep -oP '\d{8}')
fi
if [ -n "$file_date" ] && [ "$file_date" -le "$cutoff_date" ]; then
rm -f "$log_file"
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 已删除过期日志: $log_file"
fi
done
}
# 主逻辑
main() {
echo "=== 日志分割开始 ==="
split_log
compress_old_logs
cleanup_old_logs
echo "=== 日志分割完成 ==="
}
# 执行主逻辑
main
# 添加定时任务(每天0点执行)
# crontab -e
# 0 0 * * * /path/to/log_splitter.sh
Python 脚本方式
简单的日志旋转
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import gzip
import shutil
import logging
from datetime import datetime, timedelta
from logging.handlers import TimedRotatingFileHandler
class DailyLogRotator:
"""日志按天轮转处理"""
def __init__(self, log_dir, log_name, max_days=30):
self.log_dir = log_dir
self.log_name = log_name
self.max_days = max_days
# 确保日志目录存在
os.makedirs(log_dir, exist_ok=True)
def rotate_log(self):
"""轮转日志文件"""
current_log = os.path.join(self.log_dir, self.log_name)
if not os.path.exists(current_log):
print(f"日志文件不存在: {current_log}")
return
# 生成新的日志文件名
today = datetime.now().strftime("%Y%m%d")
archive_log = os.path.join(self.log_dir, f"{self.log_name}.{today}")
try:
# 复制当前日志
shutil.copy2(current_log, archive_log)
# 清空当前日志
with open(current_log, 'w') as f:
f.truncate(0)
print(f"日志已轮转: {archive_log}")
except Exception as e:
print(f"日志轮转失败: {e}")
def compress_old_logs(self, days_old=7):
"""压缩旧日志"""
cutoff_date = datetime.now() - timedelta(days=days_old)
for filename in os.listdir(self.log_dir):
if filename.startswith(self.log_name + ".") and not filename.endswith('.gz'):
filepath = os.path.join(self.log_dir, filename)
# 提取日期
date_str = filename.split('.')[-1]
try:
file_date = datetime.strptime(date_str, "%Y%m%d")
if file_date < cutoff_date:
# 压缩文件
with open(filepath, 'rb') as f_in:
with gzip.open(f"{filepath}.gz", 'wb') as f_out:
shutil.copyfileobj(f_in, f_out)
# 删除原文件
os.remove(filepath)
print(f"已压缩: {filename}")
except ValueError:
continue # 跳过不符合日期格式的文件
def cleanup_old_logs(self):
"""清理过期日志"""
cutoff_date = datetime.now() - timedelta(days=self.max_days)
for filename in os.listdir(self.log_dir):
if filename.startswith(self.log_name + "."):
filepath = os.path.join(self.log_dir, filename)
# 提取日期
date_str = filename.split('.')[-1].replace('.gz', '')
try:
file_date = datetime.strptime(date_str, "%Y%m%d")
if file_date < cutoff_date:
os.remove(filepath)
print(f"已删除过期日志: {filename}")
except ValueError:
continue
# 使用Python日志模块的TimedRotatingFileHandler
def setup_daily_logging(log_dir, log_name):
"""配置按天轮转的日志"""
import logging.handlers
# 创建日志目录
os.makedirs(log_dir, exist_ok=True)
# 日志文件路径
log_file = os.path.join(log_dir, log_name)
# 创建日志记录器
logger = logging.getLogger("MyApp")
logger.setLevel(logging.INFO)
# 创建按天轮转的文件处理器
handler = TimedRotatingFileHandler(
log_file,
when="midnight", # 每天午夜轮转
interval=1, # 间隔1天
backupCount=30, # 保留30天
encoding="utf-8"
)
# 设置日志格式
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
# 添加处理器
logger.addHandler(handler)
return logger
if __name__ == "__main__":
# 使用示例
rotator = DailyLogRotator("/var/log/myapp", "app.log")
rotator.rotate_log()
rotator.compress_old_logs()
rotator.cleanup_old_logs()
Java (Logback) 配置方式
<!-- logback.xml -->
<configuration>
<!-- 按天轮转的日志配置 -->
<appender name="DAILY_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/var/log/myapp/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天轮转,保留日期格式 -->
<fileNamePattern>/var/log/myapp/app.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 保留30天 -->
<maxHistory>30</maxHistory>
<!-- 总日志大小限制(可选) -->
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="DAILY_LOG" />
</root>
</configuration>
Docker 日志轮转
# docker-compose.yml
version: '3.8'
services:
myapp:
image: myapp:latest
logging:
driver: "json-file"
options:
max-size: "10m" # 单个日志文件最大10MB
max-file: "3" # 最多保留3个文件
compress: "true" # 压缩旧日志
Node.js (Winston) 配置
// logger.js
const winston = require('winston');
const DailyRotateFile = require('winston-daily-rotate-file');
// 配置按天轮转
const dailyRotateTransport = new DailyRotateFile({
filename: '/var/log/myapp/app-%DATE%.log',
datePattern: 'YYYY-MM-DD',
maxSize: '20m', // 单个文件最大20MB
maxFiles: '30d', // 保留30天
zippedArchive: true, // 压缩归档
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
)
});
const logger = winston.createLogger({
level: 'info',
transports: [
dailyRotateTransport,
new winston.transports.Console()
]
});
module.exports = logger;
使用建议
-
自动化:设置cron定时任务
# 每天0点执行 crontab -e 0 0 * * * /path/to/log_splitter.sh
-
监控:监控磁盘使用情况
# 查看日志目录大小 du -sh /var/log/myapp/ # 设置磁盘告警阈值 df -h | grep /var/log
-
安全性:设置适当的权限
# 设置日志目录权限 chmod 755 /var/log/myapp chown -R www-data:www-data /var/log/myapp
选择哪种方式取决于你的具体需求和环境,对于大多数场景,我推荐使用 logrotate(Linux)或 TimedRotatingFileHandler(Python/Java),因为它们更成熟可靠。