前言
日志是排查问题的第一手资料。但日志管理不好,要么磁盘被占满,要么关键时刻找不到日志。Linux提供了logrotate、journald等工具管理日志,但在分布式环境下,还需要集中收集和分析。
这篇文章从单机日志管理到集中收集方案,系统性地讲解如何构建一个高效的日志管理体系,让日志真正成为排查问题的利器。
一、logrotate:日志轮转的基石
1.1 基本配置
logrotate是Linux系统自带的日志轮转工具,通过cron定期执行。
# 查看logrotate配置cat/etc/logrotate.conf# 查看应用特定配置ls/etc/logrotate.d/默认配置示例:
/var/log/nginx/*.log { daily # 每天轮转 rotate 7 # 保留7个文件 compress # 压缩旧日志 delaycompress # 延迟压缩(不压缩最新一个) missingok # 文件不存在不报错 notifempty # 空文件不轮转 create 0640 nginx nginx # 创建新文件权限 sharedscripts # 所有日志轮转完再执行脚本 postrotate /bin/kill -USR1 `cat /var/run/nginx.pid 2> /dev/null` 2> /dev/null || true endscript }1.2 常用选项
| 选项 | 说明 | 示例 |
|---|---|---|
daily/weekly/monthly | 轮转周期 | daily |
rotate N | 保留N个文件 | rotate 30 |
size +100M | 按大小轮转 | size 100M |
compress | 压缩旧日志 | compress |
delaycompress | 延迟压缩最新文件 | delaycompress |
dateext | 使用日期后缀 | dateext |
dateformat | 日期格式 | dateformat .%Y%m%d |
copytruncate | 复制后清空(不重启服务) | copytruncate |
missingok | 文件不存在不报错 | missingok |
notifempty | 空文件不轮转 | notifempty |
1.3 实战配置示例
Nginx日志轮转:
/var/log/nginx/*.log { daily rotate 30 compress delaycompress missingok notifempty create 0640 www-data www-data sharedscripts postrotate if [ -f /var/run/nginx.pid ]; then kill -USR1 `cat /var/run/nginx.pid` fi endscript }应用日志轮转:
/var/log/app/*.log { size 100M # 按大小轮转 rotate 10 compress delaycompress missingok notifempty copytruncate # 复制后清空,避免重启应用 dateext dateformat .%Y%m%d }MySQL慢查询日志:
/var/log/mysql/mysql-slow.log { daily rotate 7 compress delaycompress missingok notifempty create 0640 mysql mysql postrotate /usr/bin/mysqladmin flush-logs endscript }1.4 手动测试与调试
# 手动执行logrotate(测试模式,不实际轮转)logrotate-d /etc/logrotate.d/nginx# 强制执行(即使未到时间)logrotate-f /etc/logrotate.d/nginx# 查看logrotate状态cat/var/lib/logrotate/status1.5 常见问题
问题1:日志轮转后服务不重新打开文件
# 方案1:使用postrotate发送信号postrotatekill-USR1`cat/var/run/app.pid`endscript# 方案2:使用copytruncate(但可能丢失数据)copytruncate# 方案3:应用支持日志重载(如Nginx的USR1信号)问题2:轮转后磁盘空间未释放
# 检查文件是否被进程占用lsof|grepdeleted# 如果文件被占用,需要重启进程或发送信号让其重新打开文件二、journald:systemd日志管理
2.1 基本使用
journald是systemd的日志系统,统一管理systemd服务的日志。
# 查看所有日志journalctl# 查看指定服务日志journalctl -u nginx# 实时跟踪journalctl -u nginx -f# 查看最近100行journalctl -u nginx -n100# 查看今天的日志journalctl -u nginx --since today# 查看指定时间范围journalctl -u nginx --since"2024-01-01 00:00:00"--until"2024-01-02 00:00:00"# 查看内核日志journalctl -k# 查看系统启动日志journalctl -b2.2 日志级别过滤
# 只看错误级别及以上journalctl -u nginx -p err# 级别说明:# 0: emerg(紧急)# 1: alert(警报)# 2: crit(严重)# 3: err(错误)# 4: warning(警告)# 5: notice(通知)# 6: info(信息)# 7: debug(调试)2.3 日志存储配置
# 查看日志占用空间journalctl --disk-usage# 保留最近1周journalctl --vacuum-time=1week# 限制大小500Mjournalctl --vacuum-size=500M# 配置文件:/etc/systemd/journald.conf[Journal]SystemMaxUse=500M# 系统日志最大占用SystemKeepFree=1G# 保留空闲空间SystemMaxFileSize=100M# 单个日志文件最大MaxRetentionSec=1week# 保留时间2.4 持久化存储
默认情况下,journald日志存储在/run/log/journal(内存中),重启后丢失。
启用持久化:
# 创建持久化目录mkdir-p /var/log/journalchownroot:systemd-journal /var/log/journalchmod2755/var/log/journal# 重启journaldsystemctl restart systemd-journald# 验证ls-la /var/log/journal/三、日志分析工具:快速定位问题
3.1 grep:基础搜索
# 搜索包含"error"的行grep-i error /var/log/app.log# 搜索多个关键词grep-E"error|exception|failed"/var/log/app.log# 显示上下文(前后5行)grep-C5"error"/var/log/app.log# 统计出现次数grep-c"error"/var/log/app.log# 只显示文件名和匹配行数grep-l"error"/var/log/*.log3.2 awk:字段提取与统计
# 提取特定字段(如第5列)awk'{print $5}'/var/log/app.log# 按字段过滤(第3列是"ERROR")awk'$3 == "ERROR"'/var/log/app.log# 统计每个IP的访问次数awk'{print $1}'/var/log/nginx/access.log|sort|uniq-c|sort-rn# 统计错误类型awk'/ERROR/ {print $NF}'/var/log/app.log|sort|uniq-c# 计算平均值(如响应时间)awk'{sum+=$NF; count++} END {print sum/count}'/var/log/app.log3.3 sed:文本替换与提取
# 提取时间范围(如10:00-11:00)sed-n'/2024-01-01 10:00:00/,/2024-01-01 11:00:00/p'/var/log/app.log# 替换文本sed's/old/new/g'/var/log/app.log# 删除包含特定内容的行sed'/error/d'/var/log/app.log# 提取特定行号范围sed-n'100,200p'/var/log/app.log3.4 组合使用:实战案例
案例1:统计API错误率
# 统计5xx错误数量grep-E" 5[0-9]{2} "/var/log/nginx/access.log|wc-l# 计算错误率total=$(wc-l</var/log/nginx/access.log)errors=$(grep-E" 5[0-9]{2} "/var/log/nginx/access.log|wc-l)rate=$(echo"scale=2;$errors* 100 /$total"|bc)echo"错误率:${rate}%"案例2:找出最慢的请求
# 假设日志格式:时间 响应时间 状态码 URLawk'{print $2, $4}'/var/log/nginx/access.log|sort-rn|head-10案例3:按时间段统计请求量
# 提取小时字段并统计awk'{print substr($1, 1, 13)}'/var/log/nginx/access.log|sort|uniq-c四、日志集中收集:分布式环境必备
4.1 为什么需要集中收集
单机日志的问题:
- 服务器多,逐个登录查看效率低
- 日志分散,关联分析困难
- 服务器故障时日志可能丢失
集中收集的优势:
- 统一查询和分析
- 日志持久化存储
- 支持全文搜索和可视化
4.2 方案选择
| 方案 | 特点 | 适用场景 |
|---|---|---|
| ELK Stack | 功能强大,资源占用高 | 大规模日志分析 |
| Loki + Grafana | 轻量级,查询快 | 中小规模,与Grafana集成 |
| Fluentd/Fluent Bit | 轻量级采集器 | 容器环境 |
| rsyslog | 系统自带,配置简单 | 系统日志集中 |
4.3 rsyslog:系统日志集中
服务端配置(/etc/rsyslog.conf):
# 启用TCP/UDP接收 module(load="imtcp") input(type="imtcp" port="514") module(load="imudp") input(type="imudp" port="514") # 按主机名和程序名分类存储 $template RemoteLogs,"/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log" *.* ?RemoteLogs # 重启rsyslog systemctl restart rsyslog客户端配置:
# 发送到远程服务器 *.* @@192.168.1.100:514 # TCP(@@)或UDP(@)4.4 Filebeat + ELK:应用日志集中
Filebeat配置(filebeat.yml):
filebeat.inputs:-type:logenabled:truepaths:-/var/log/app/*.logfields:log_type:applicationenvironment:productionfields_under_root:trueoutput.logstash:hosts:["logstash:5044"]# 或直接输出到Elasticsearchoutput.elasticsearch:hosts:["elasticsearch:9200"]index:"app-logs-%{+yyyy.MM.dd}"Logstash配置(处理日志):
input{beats{port=>5044}}filter{if[fields][log_type]=="application"{grok{match=>{"message"=>"%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:message}"}}date{match=>["timestamp","yyyy-MM-dd HH:mm:ss"]}}}output{elasticsearch{hosts=>["elasticsearch:9200"]index=>"app-logs-%{+yyyy.MM.dd}"}}4.5 Loki + Promtail:轻量级方案
Promtail配置(采集日志):
server:http_listen_port:9080grpc_listen_port:0positions:filename:/tmp/positions.yamlclients:-url:http://loki:3100/loki/api/v1/pushscrape_configs:-job_name:appstatic_configs:-targets:-localhostlabels:job:app__path__:/var/log/app/*.logGrafana查询:
# 查询错误日志 {job="app"} |= "error" # 统计错误数量 sum(count_over_time({job="app"} |= "error" [5m])) # 按级别统计 sum by (level) (count_over_time({job="app"} [5m]))五、日志轮转与集中收集结合
5.1 轮转后自动收集
方案1:Filebeat自动检测
Filebeat会自动检测日志轮转,无需额外配置。
方案2:logrotate后触发收集
/var/log/app/*.log { daily rotate 7 compress delaycompress sharedscripts postrotate # 通知Filebeat重新读取 systemctl reload filebeat || true endscript }5.2 压缩日志处理
# Filebeat支持读取压缩日志filebeat.inputs:-type:logpaths:-/var/log/app/*.log.gzexclude_lines:['^DEBUG']# 排除调试日志六、跨网络日志收集
6.1 场景:多机房日志集中
如果日志服务器和应用服务器在不同网络环境(不同机房、不同云),直接连接可能受限。
解决方案:
- VPN/专线:稳定但部署周期长
- 组网工具:WireGuard、ZeroTier、星空组网等,快速组建虚拟内网
使用组网工具后,不同网络的服务器可以通过虚拟IP直接通信,日志收集配置更简单:
# Filebeat配置(通过虚拟IP连接)output.logstash:hosts:["10.0.0.100:5044"]# 虚拟内网IP# rsyslog配置(通过虚拟IP发送)*.*@@10.0.0.100:514优势:
- 配置简单,无需考虑网络环境差异
- 统一网络后,可以用Ansible批量配置日志收集
- 支持服务发现,动态添加节点
6.2 批量配置日志收集
# 使用Ansible批量配置(通过虚拟内网)ansible all -m copy -a"src=filebeat.yml dest=/etc/filebeat/filebeat.yml"ansible all -m systemd -a"name=filebeat state=restarted"七、日志分析实战案例
7.1 案例:API错误分析
需求:找出API错误最多的接口
步骤:
# 1. 提取错误日志grep"ERROR"/var/log/app.log>errors.log# 2. 提取接口路径(假设日志格式包含路径)awk'{print $7}'errors.log|sort|uniq-c|sort-rn|head-10# 3. 使用ELK或Loki查询# ELK:# GET /_search# {"query": {"match": {"level": "ERROR"}}, "aggs": {"by_endpoint": {"terms": {"field": "endpoint"}}}}## Loki:# sum by (endpoint) (count_over_time({job="app"} |= "ERROR" [1h]))7.2 案例:性能问题定位
需求:找出响应时间最慢的请求
步骤:
# 1. 提取响应时间(假设日志包含响应时间字段)awk'{print $NF}'/var/log/nginx/access.log|sort-rn|head-10# 2. 找出对应的请求awk'$NF > 5.0 {print}'/var/log/nginx/access.log|head-20# 3. 使用ELK分析# 查询: response_time:>5.0# 排序: 按response_time降序7.3 案例:安全审计
需求:找出异常访问(如频繁失败登录)
步骤:
# 1. 提取登录失败日志grep"login failed"/var/log/auth.log# 2. 统计每个IP的失败次数grep"login failed"/var/log/auth.log|awk'{print $NF}'|sort|uniq-c|sort-rn# 3. 找出失败次数超过阈值的IPgrep"login failed"/var/log/auth.log|awk'{print $NF}'|sort|uniq-c|awk'$1 > 10 {print $2}'八、日志管理最佳实践
8.1 日志级别规范
| 级别 | 使用场景 | 示例 |
|---|---|---|
| DEBUG | 调试信息 | 变量值、函数调用 |
| INFO | 正常流程 | 请求处理、状态变更 |
| WARN | 警告信息 | 配置缺失、性能下降 |
| ERROR | 错误信息 | 异常捕获、业务失败 |
| FATAL | 致命错误 | 系统崩溃、无法恢复 |
8.2 日志格式规范
推荐格式:
时间戳 级别 [线程] 类名 - 消息内容 key1=value1 key2=value2示例:
2024-01-14 10:30:45.123 INFO [http-nio-8080-exec-1] com.example.Controller - 处理请求 method=GET path=/api/users status=200 duration=45ms好处:
- 结构化,便于解析
- 包含关键信息(方法、路径、状态、耗时)
- 支持字段查询和聚合
8.3 日志轮转策略
| 日志类型 | 轮转策略 | 保留时间 |
|---|---|---|
| 应用日志 | 按大小(100M)或按天 | 7-30天 |
| 访问日志 | 按天 | 30-90天 |
| 错误日志 | 按大小(50M)或按天 | 90天 |
| 审计日志 | 按天 | 1年+ |
| 调试日志 | 按大小(10M) | 3-7天 |
8.4 日志存储优化
# 1. 使用压缩存储compress delaycompress# 2. 限制日志大小size 100M# 3. 定期清理旧日志find/var/log -name"*.log.*"-mtime +30 -delete# 4. 集中存储使用对象存储(S3、OSS)归档# 定期将旧日志上传到对象存储aws s3sync/var/log/archived/ s3://logs-bucket/archived/九、总结
| 管理方向 | 关键工具/方法 | 预期效果 | 注意事项 |
|---|---|---|---|
| 日志轮转 | logrotate | 防止磁盘占满 | 需要应用支持日志重载 |
| 系统日志 | journald | 统一管理systemd日志 | 默认内存存储,需启用持久化 |
| 日志分析 | grep/awk/sed | 快速定位问题 | 需要熟悉工具语法 |
| 集中收集 | ELK/Loki/rsyslog | 统一查询和分析 | 需要额外资源 |
| 跨网络收集 | 组网工具统一网络 | 配置简化 | 需要安全审计 |
| 日志格式 | 结构化日志 | 便于解析和查询 | 需要应用层支持 |
核心思路:
- 单机日志用logrotate轮转,防止磁盘占满
- 系统日志用journald管理,启用持久化存储
- 日志分析用grep/awk/sed组合,快速定位问题
- 分布式环境用集中收集方案(ELK/Loki)
- 跨网络场景用组网工具统一网络后再收集
注意事项:
- 日志轮转后需要应用重新打开文件
- 集中收集需要额外资源,合理规划
- 日志格式要结构化,便于后续分析
- 跨网络收集可以用组网工具简化配置