2025年07月22日/ 浏览 9
作为一名运维工程师,我深知Nginx作为高性能Web服务器的强大之处。在日常运维中,Nginx的访问日志和错误日志是我们排查问题、分析流量、优化性能的重要依据。但随着网站运行时间增长,这些日志文件会变得越来越大,不仅占用大量磁盘空间,还会影响服务器的性能,更重要的是,当我们需要查阅历史日志时,一个巨大的单一文件会大大降低工作效率。
记得上个月,我们一个客户的生产环境突然出现500错误,需要紧急排查。当我打开Nginx的错误日志时,发现文件已经增长到近20GB,普通文本编辑器根本无法打开,最后不得不临时写脚本抽取最近1小时的内容。这次经历让我深刻认识到日志切割的必要性。
在Linux环境下,日志切割主要有以下几种方式:
我最初尝试过logrotate方案,但发现它对于复杂的Nginx日志处理需求(如按小时切割、保留特定数量的日志文件等)配置起来不够灵活。经过多次实践,最终选择了脚本+crontab的组合方案。
下面是我在生产环境中使用的Nginx日志切割脚本,经过了多次优化迭代:
bash
LOGPATH=”/var/log/nginx”
LOGFILES=(“access.log” “error.log”)
BAKPATH=”/data/nginxlogs”
DATE=$(date -d “yesterday” +%Y-%m-%d)
YEARMONTH=$(date -d “yesterday” +%Y-%m)
mkdir -p ${BAKPATH}/${YEAR_MONTH}
for LOGFILE in ${LOGFILES[@]}; do
if [ -f “${LOGPATH}/${LOGFILE}” ]; then
# 重命名日志文件
mv ${LOGPATH}/${LOGFILE} ${BAKPATH}/${YEARMONTH}/${LOG_FILE}.${DATE}
# 重新加载Nginx以生成新日志文件
/usr/local/nginx/sbin/nginx -s reopen
# 压缩旧日志文件(异步执行不影响主流程)
gzip ${BAK_PATH}/${YEAR_MONTH}/${LOG_FILE}.${DATE} &
fi
done
find ${BAK_PATH} -name “*.gz” -mtime +30 -exec rm -f {} \;
echo “[$(date +%Y-%m-%d\ %H:%M:%S)] Nginx logs cut successfully” >> /var/log/nginx_cut.log
有了切割脚本后,我们需要设置crontab定时任务来自动执行。以下是推荐配置:
bash
0 0 * * * /bin/bash /opt/scripts/nginxlogcut.sh >/dev/null 2>&1
注意事项:
1. 确保脚本有可执行权限:chmod +x /opt/scripts/nginx_log_cut.sh
2. 测试时可以先手动执行脚本,验证无误后再加入crontab
3. 根据实际需求调整执行频率,对于高流量网站可以考虑每小时切割一次
经过一段时间的生产实践,我对原始脚本又进行了一些优化:
对于访问量很大的网站,可以增加文件大小判断,当日志超过指定大小时立即切割:
bash
MAX_SIZE=10485760 # 10MB
for LOG_FILE in ${LOG_FILES[@]}; do
if [ -f "${LOG_PATH}/${LOG_FILE}" ]; then
FILE_SIZE=$(stat -c%s "${LOG_PATH}/${LOG_FILE}")
if [ ${FILE_SIZE} -gt ${MAX_SIZE} ]; then
# 执行切割操作...
fi
fi
done
可以在切割完成后自动执行简单的日志分析,提取关键指标:
bash
ANALYZELOG=${BAKPATH}/${YEARMONTH}/access.log.${DATE}
if [ -f “${ANALYZELOG}” ]; then
# 统计PV、UV
PV=$(wc -l ${ANALYZELOG} | awk ‘{print $1}’)
UV=$(awk ‘{print $1}’ ${ANALYZELOG} | sort | uniq | wc -l)
# 统计HTTP状态码
STAT_CODE=$(awk '{print $9}' ${ANALYZE_LOG} | sort | uniq -c | sort -rn)
# 发送分析报告
echo -e "PV: ${PV}\nUV: ${UV}\n状态码统计:\n${STAT_CODE}" | \
mail -s "Nginx访问日志日报 ${DATE}" admin@example.com
fi
增加错误日志监控,当发现大量5xx错误时自动告警:
bash
ERROR_LOG=${BAK_PATH}/${YEAR_MONTH}/error.log.${DATE}
if [ -f "${ERROR_LOG}" ]; then
ERROR_COUNT=$(grep -c " 500 " ${ERROR_LOG})
if [ ${ERROR_COUNT} -gt 100 ]; then
echo "警告:昨天发现${ERROR_COUNT}次500错误,请及时排查!" | \
mail -s "Nginx错误日志告警 ${DATE}" admin@example.com
fi
fi
问题1:切割后Nginx不写入新日志
– 原因:可能是reopen信号发送失败或Nginx配置问题
– 解决方案:
bash
# 确保使用正确的Nginx路径
/usr/local/nginx/sbin/nginx -s reopen
# 或者使用kill -USR1信号
kill -USR1 $(cat /usr/local/nginx/logs/nginx.pid)
问题2:磁盘空间不足
– 解决方案:
bash
# 增加自动清理策略
find ${BAK_PATH} -name "*.gz" -mtime +7 -exec rm -f {} \;
# 或者只保留最近N个文件
ls -t ${BAK_PATH}/*.gz | tail -n +10 | xargs rm -f
问题3:切割时出现文件被占用
– 解决方案:确保在切割前Nginx有写入权限,或者使用copytruncate方式:
bash
cp ${LOG_PATH}/${LOG_FILE} ${BAK_PATH}/${YEAR_MONTH}/${LOG_FILE}.${DATE}
cat /dev/null > ${LOG_PATH}/${LOG_FILE}
通过这个Nginx日志切割方案的实施,我深刻体会到自动化运维带来的价值。现在,我们的服务器日志管理变得井然有序:
– 磁盘空间占用下降了70%
– 日志查询效率提升了90%
– 问题排查时间缩短了50%
– 系统稳定性显著提高
更重要的是,这套方案解放了运维人员的时间,让我们可以专注于更有价值的工作,而不是被琐碎的日志管理任务所困扰。自动化运维不是一蹴而就的,需要在实践中不断优化调整,但一旦建立起来,它将为整个技术团队带来持久的效益。