4、日志监控与采集:日志分类与ELK技术栈实战
日志这东西,说简单也简单,说复杂真能坑死人。我做了这么多年风控系统,见过太多因为日志没搞好,线上出问题查半天找不到根因的案例。今天咱们就把日志监控与采集这块彻底聊透。
4.1 日志分类:三种日志各司其职
先说说日志分类。很多团队一开始就把所有日志往一个文件里写,结果排查问题时像大海捞针。我个人习惯把日志分成三类:业务日志、系统日志、审计日志。
| 日志类型 | 核心用途 | 典型内容 | 保留周期 |
|---|---|---|---|
| 业务日志 | 记录业务流转过程 | 用户请求、规则命中、决策结果 | 30-90天 |
| 系统日志 | 监控系统运行状态 | CPU/内存、接口耗时、异常堆栈 | 7-30天 |
| 审计日志 | 满足合规与追溯需求 | 操作人、操作时间、变更内容 | 1年以上 |
关键点:业务日志和审计日志虽然看起来像,但用途完全不同。业务日志关注「发生了什么」,审计日志关注「谁干的」。千万别混在一起。
我在项目中遇到过一件事:某次线上风控误杀率突然飙升,业务方催着要排查。结果发现业务日志和审计日志写到了同一个索引里,查询时互相干扰,花了整整半天才定位到问题。从那以后,我强制要求三类日志必须物理隔离。
4.2 ELK技术栈搭建:从零到可用的实战
ELK说白了就是三个组件:Elasticsearch存数据、Logstash收数据、Kibana看数据。我建议再加个Filebeat做轻量采集,这样架构更稳。
先看整体架构图:
搭建ELK其实不复杂,但有几个坑得提前知道。我直接给出一份可用的docker-compose配置:
version: '3'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.0
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms2g -Xmx2g
ports:
- "9200:9200"
volumes:
- es_data:/usr/share/elasticsearch/data
logstash:
image: docker.elastic.co/logstash/logstash:7.17.0
ports:
- "5044:5044"
volumes:
- ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
depends_on:
- elasticsearch
kibana:
image: docker.elastic.co/kibana/kibana:7.17.0
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
depends_on:
- elasticsearch
volumes:
es_data:
注意:ES的JVM堆内存千万别超过物理内存的50%,否则会被系统OOM Killer干掉。我曾经在生产环境吃过这个亏,半夜三点爬起来调参数。
4.3 日志采集Agent设计:轻量、可靠、可扩展
采集Agent是日志系统的「触角」。我推荐用Filebeat,原因很简单:它够轻,资源占用不到20MB内存,对业务几乎零影响。
但光用Filebeat还不够,你得设计好采集策略。我总结了几条原则:
- 就近采集:Agent部署在应用服务器本地,不走网络转发
- 断点续传:Filebeat自带这个能力,但要注意磁盘空间
- 多行合并:Java异常栈跨多行,需要配置multiline规则
给你看一个我常用的Filebeat配置:
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/business/*.log
- /var/log/system/*.log
multiline:
pattern: '^\d{4}-\d{2}-\d{2}'
negate: true
match: after
fields:
log_type: business
output.logstash:
hosts: ["logstash:5044"]
loadbalance: true
小技巧:多行合并的正则一定要测试充分。我见过有人把时间戳格式写错,结果所有异常栈都被拆成了单行,排查问题时根本没法看。
4.4 日志格式规范:统一才有价值
日志格式不统一,ELK再强也白搭。我强制团队使用JSON格式,原因很简单:Logstash解析JSON比正则快10倍以上。
这是我推荐的业务日志格式:
{
"timestamp": "2024-01-15T10:30:00.123+08:00",
"level": "INFO",
"trace_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"service": "risk-engine",
"module": "rule-evaluate",
"action": "decision",
"user_id": "u_10086",
"request_id": "req_202401151030001",
"rule_id": "rule_001",
"rule_name": "高频交易检测",
"decision": "reject",
"cost_ms": 45,
"extra": {
"ip": "192.168.1.100",
"device_id": "device_abc123"
}
}
为什么要有trace_id?你想想看,一个请求经过网关、风控引擎、规则库、数据库,如果没有trace_id,你怎么串起整个调用链?我在项目中遇到过,线上有个请求超时了,但日志里只有零散的信息,查了三个小时才发现是某个规则调了外部接口超时。
系统日志的格式相对简单,但一定要包含线程名和类名:
{
"timestamp": "2024-01-15T10:30:00.456+08:00",
"level": "ERROR",
"thread": "http-nio-8080-exec-3",
"logger": "com.risk.engine.RuleEngine",
"message": "规则执行异常: rule_001",
"stack_trace": "java.lang.Exception: ...",
"cost_ms": 5000
}
核心原则:日志是给人看的,也是给机器查的。JSON格式既方便Logstash解析,也方便Kibana做聚合查询。别再用那种「2024-01-15 INFO 用户登录成功」的纯文本格式了,那是十年前的做法。
嗯,日志这块内容不少,但核心就三点:分类清晰、采集可靠、格式统一。把这三点做到位,你的风控系统监控就有了坚实的基础。下次线上出问题,你就能在几分钟内定位到根因,而不是像无头苍蝇一样乱翻日志。
公众号:蓝海资料掘金营,微信deep3321