一、日志分析基础:日志的作用与分类、常见日志格式、日志采集工具
做SRE这些年,我越来越觉得日志这东西就像飞机的黑匣子。平时没人搭理它,一出事所有人都在喊“快看日志”。说白了,日志就是系统运行时的“心电图”,记录着每一秒发生了什么、哪里不对劲。
我个人习惯是,接手一个新系统,第一件事不是看代码,而是先翻日志。日志里藏着的东西,比任何文档都真实。你想想看,代码可能撒谎,但日志不会。
1.1 日志的作用与分类
日志到底能干啥?我总结下来就三件事:
- 故障排查:系统挂了,日志是第一现场。我记得有一次线上服务半夜报警,CPU飙到99%,查了一圈没头绪。最后翻nginx的access日志,发现某个接口的请求量突然暴涨了100倍——原来是爬虫在搞鬼。
- 性能分析:响应慢、吞吐低,日志里的时间戳就是最好的“测速仪”。从请求进来、到处理、到返回,每一步的耗时都能从日志里抠出来。
- 安全审计:谁在什么时候干了什么,日志一清二楚。合规检查的时候,日志就是你的“不在场证明”。
日志按来源分,大致有这几类:
| 分类 | 典型代表 | 我关注的点 |
|---|---|---|
| 应用日志 | Java的log4j、Python的logging | 异常堆栈、业务逻辑错误 |
| 访问日志 | Nginx access log、Apache access log | 请求量、响应时间、状态码分布 |
| 系统日志 | /var/log/messages、dmesg | OOM、磁盘I/O错误、内核panic |
| 数据库日志 | MySQL slow query log、PostgreSQL log | 慢查询、死锁、连接数异常 |
| 中间件日志 | Redis log、Kafka log | 内存使用、队列积压、主从同步延迟 |
1.2 常见日志格式
日志格式这东西,看着简单,但坑不少。我见过最离谱的,有人把日志写成了一行JSON,结果换行符没处理好,整条日志被截断了。嗯,这里要注意。
Apache/Nginx 的访问日志格式
先说最经典的Apache combined格式。说白了就是一行文本,用空格分隔各个字段:
192.168.1.1 - - [10/Oct/2024:13:55:36 +0800] "GET /api/users HTTP/1.1" 200 2326 "https://example.com" "Mozilla/5.0"
每个字段的含义:
192.168.1.1— 客户端IP- -— 身份认证信息(通常为空)[10/Oct/2024:13:55:36 +0800]— 请求时间"GET /api/users HTTP/1.1"— 请求方法、路径、协议200— 状态码2326— 响应体大小(字节)"https://example.com"— Referer来源"Mozilla/5.0"— User-Agent
Nginx的格式和Apache几乎一样,只是多了几个字段。我个人习惯在Nginx日志里加上$request_time和$upstream_response_time,这两个字段对性能分析太关键了:
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" $request_time $upstream_response_time';
JSON 格式日志
现在越来越多的应用开始用JSON格式写日志。为什么?因为好解析、好扩展。你想想看,一行文本格式的日志,想加个字段就得改解析规则,但JSON加个key就行了。
{
"timestamp": "2024-10-10T13:55:36.123Z",
"level": "ERROR",
"logger": "com.example.UserService",
"thread": "http-nio-8080-exec-3",
"message": "数据库连接超时",
"context": {
"userId": "12345",
"requestId": "abc-def-ghi",
"duration": 5032
},
"stackTrace": "java.sql.SQLTimeoutException: ..."
}
JSON格式的好处很明显:
- 结构化:每个字段都有明确的含义,不用猜
- 可扩展:加字段不影响已有解析逻辑
- 易查询:Elasticsearch里直接按字段搜索,效率高
但JSON也有缺点——体积大。同样的信息,JSON比文本格式大30%-50%。所以我的建议是:开发环境用JSON,生产环境用文本格式。或者折中一下,用Logstash的kv过滤器把文本转成JSON。
1.3 日志采集工具介绍
日志采集,说白了就是把分散在各台机器上的日志,统一收集到一个地方。我见过有人用scp手动拷贝的,也见过用rsync定时同步的。小规模还行,一旦上了百台机器,这些土办法就废了。
目前业界最主流的组合是 Filebeat + Logstash + Elasticsearch,也就是所谓的ELK栈。我重点说说前两个。
Filebeat:轻量级日志采集器
Filebeat是Elastic公司出的一个Go语言写的采集器。为什么选它?就一个字——轻。它占用的内存只有几十MB,CPU也几乎可以忽略不计。我曾在128MB内存的云服务器上跑过,稳得很。
Filebeat的核心逻辑很简单:
- 监听日志文件的变化(类似
tail -f) - 读取新增的日志行
- 发送到指定的输出端(Logstash、Elasticsearch、Kafka等)
一个典型的Filebeat配置长这样:
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/nginx/access.log
fields:
service: nginx
env: production
output.logstash:
hosts: ["192.168.1.100:5044"]
Logstash:日志处理管道
Logstash是ELK栈里的“数据加工厂”。它做的事情就是:收进来、洗干净、送出去。
Logstash的架构是典型的管道模式:
输入(Input) → 过滤(Filter) → 输出(Output)
我画了一张图,帮你理解这个流程:
Logstash最强大的地方是它的过滤插件。我常用的几个:
- grok:用正则表达式解析非结构化文本。比如把Nginx日志拆成IP、时间、URL等字段。说实话,grok的正则写起来挺痛苦的,但一旦写好,一劳永逸。
- mutate:字段类型转换、重命名、删除。比如把字符串类型的数字转成整数,方便后面做聚合统计。
- date:时间戳解析。日志里的时间格式五花八门,这个插件能把它们统一成ISO 8601格式。
一个Logstash配置示例:
input {
beats {
port => 5044
}
}
filter {
grok {
match => { "message" => "%{COMBINEDAPACHELOG}" }
}
date {
match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
target => "@timestamp"
}
mutate {
remove_field => ["message"]
}
}
output {
elasticsearch {
hosts => ["http://localhost:9200"]
index => "nginx-access-%{+YYYY.MM.dd}"
}
}
小结
日志分析的第一步,就是把日志“管起来”。选对格式、用对工具,后面的事情才能顺起来。我个人觉得,Filebeat+Logstash这套组合拳,对于大多数场景已经够用了。如果你追求极致性能,可以试试用Vector或者Fluentd替代Logstash,但那是后话了。
记住一句话:日志不是用来存的,是用来查的。采集的时候多花点心思设计格式和字段,后面分析的时候能省十倍的时间。