1. 日志泛滥的典型场景
某天凌晨3点,你的手机突然收到Disk Space Alert短信——OpenResty所在服务器的/var/log分区使用率超过95%。此时的你,可能正面临以下典型场景:
- 突发流量导致access.log每小时增长10GB
- 调试模式忘记关闭产生海量error日志
- 日志切割配置失效导致单个文件撑爆磁盘
- 第三方模块异常输出冗余调试信息
- 业务日志与访问日志混合存储
(某电商曾因促销活动未及时清理日志,导致支付接口日志写满磁盘,交易中断37分钟)
2. 紧急救援三板斧
2.1 快速释放空间(外科手术)
$ cd /usr/local/openresty/nginx/logs
$ du -sh * | sort -rh | head -n 5 # 按大小倒序显示前5
# 应急清理方案
$ truncate -s 0 access.log # 清空但不删除文件(保持inode)
$ find . -name "*.log" -size +1G -exec tee {} \; # 安全清空大文件
$ rm -f debug_*.log.(2020-2022) # 删除历史调试日志
2.2 动态日志级别调整(不停机修复)
-- OpenResty Lua动态配置示例
local function toggle_log_level()
local logger = ngx.log
local current_level = ngx.config.ngx_lua_version > 9000 and 4 or 3 -- 兼容版本差异
-- 当磁盘剩余空间<10%时切换为ERROR级别
if get_disk_free_percent() < 10 then
ngx.log = function(level, ...)
if level < ngx.ERR then return end
logger(level, ...)
end
else
ngx.log = logger -- 恢复原始函数
end
end
-- 挂载到定时器
local timer = require "resty.timer"
timer.every(60, toggle_log_level)
2.3 临时日志重定向(空间转移术)
# nginx.conf临时配置
http {
lua_shared_dict log_redirect 10m;
init_by_lua_block {
local free = get_disk_free("/var")
if free < 1024*1024*1024 then -- 小于1GB时
ngx.shared.log_redirect:set("emergency_mode", true)
end
}
log_by_lua_block {
local is_emergency = ngx.shared.log_redirect:get("emergency_mode")
if is_emergency then
-- 将日志分流到临时存储
local tmp_logger = require "ngx.pipe".spawn("tee -a /mnt/tmpfs/emergency.log")
tmp_logger:send(ngx.var.request_uri .. "\n")
end
}
}
3. 长效预防机制
3.1 智能日志切割方案
#!/bin/bash
# 自适应日志切割脚本(Shell示例)
LOG_DIR="/usr/local/openresty/nginx/logs"
MAX_SIZE="2G" # 单个文件上限
RETENTION_DAYS=7 # 保留天数
DISK_WATERMARK=80 # 磁盘使用率阈值
current_usage=$(df -h $LOG_DIR | awk 'NR==2 {print $5}' | tr -d '%')
# 动态调整保留策略
if [ $current_usage -ge $DISK_WATERMARK ]; then
EXTRA_ARGS="--remove-oldest --compress"
else
EXTRA_ARGS="--compress"
fi
/usr/sbin/logrotate -f /etc/logrotate.d/openresty $EXTRA_ARGS
3.2 日志生命周期管理
-- 基于LJIT的日志分析模块
local function log_gc()
local access = io.popen("ls -t /logs/access*.log | tail -n +4")
for old_log in access:lines() do
os.remove(old_log)
ngx.log(ngx.NOTICE, "Purged old log: "..old_log)
end
-- 自动清理错误率<0.1%的日志
local err_count = count_errors_last_hour()
local total_req = count_requests_last_hour()
if err_count/total_req < 0.001 then
os.execute("rm -f /logs/error.log.$(date -d '1 day ago' +%Y%m%d)")
end
end
4. 高阶技巧:日志分级存储
# 分级存储配置示例
map $time_iso8601 $log_year {
~^(?<year>\d{4})- $year;
}
map $status $log_level {
~^[23] 1; # 正常日志
~^4 2; # 客户端错误
~^5 3; # 服务端错误
default 0; # 特殊日志
}
server {
access_log /var/log/nginx/access_$log_level.$log_year.log combined;
error_log /var/log/nginx/error.log;
error_log /var/log/nginx/error_debug.log debug; # 调试日志单独存储
}
5. 技术方案对比
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
定时清理 | 实现简单 | 可能误删重要日志 | 稳定环境 |
日志分级 | 精准控制 | 增加配置复杂度 | 关键业务系统 |
动态采样 | 节省90%以上空间 | 丢失部分细节 | 高并发场景 |
云原生存储 | 弹性扩展 | 依赖基础设施 | 云环境部署 |
日志聚合 | 方便集中管理 | 增加网络开销 | 分布式系统 |
6. 避坑指南
- inode耗尽危机:删除大文件前检查
df -i
- 日志切割时间差:使用
delaycompress
避免压缩期间丢失日志 - 异步写入风险:确保
flush_time
设置合理 - 符号链接陷阱:软连接目标分区需同步监控
- 审计合规要求:医疗/金融行业注意保留期限
7. 实战总结
经过多次深夜救火,总结出日志管理的"三要三不要"原则:
要:
- 实现磁盘空间动态感知
- 建立日志分级熔断机制
- 定期演练日志清理流程
不要:
- 在业务高峰期运行find -delete
- 直接rm正在写入的日志文件
- 过度依赖日志聚合忽略本地存储
8. 终极解决方案展望
未来的智能日志系统应该具备:
- 基于机器学习的异常日志识别
- 自动生成日志保留策略建议
- 实时预测磁盘使用趋势
- 跨集群的日志生命周期协同