4. 日志采集与传输:Filebeat/Loki的轻量级采集方案
日志采集这事儿,说白了就是给交易系统装个「黑匣子」。我见过太多团队,系统崩了之后连个日志都捞不出来——要么采集器把机器搞崩了,要么网络一断数据全丢。今天咱们就聊聊怎么用轻量级方案把日志稳稳当当送到后端。
4.1 为什么选轻量级采集器?
交易系统的机器,CPU和内存都很金贵。你想想看,一个高频交易节点上跑着几十个策略进程,要是再塞个重量级采集器,延迟立马就上去了。我个人习惯用Filebeat或Promtail(Loki的采集端),它们的内存占用通常只有10-20MB。
核心原则:采集器不能影响交易进程。如果采集器吃掉超过5%的CPU,那就是设计失败。
4.2 Filebeat:经典日志采集方案
Filebeat是Elastic生态里的轻量级选手。我最早接触它是在2018年,当时帮一家券商做日志平台迁移。嗯,这里要注意——Filebeat的配置其实很灵活,但很多人只用了它的基础功能。
4.2.1 基本配置示例
# filebeat.yml
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/trading/*.log
- /data/logs/strategy/*.log
# 多行日志合并(比如Java异常栈)
multiline.pattern: '^\d{4}-\d{2}-\d{2}'
multiline.negate: true
multiline.match: after
output.elasticsearch:
hosts: ["http://elasticsearch:9200"]
index: "trading-logs-%{+yyyy.MM.dd}"
这段配置里,multiline那块特别重要。我在项目中遇到过,Java的异常栈被拆成几十条日志,查问题的时候根本对不上。加上多行合并后,一条异常就是一个完整事件。
4.2.2 日志打标签(Tags)与元数据注入
标签是日志的「身份证」。没有标签的日志,就像没有名字的订单——你根本不知道它从哪来。
filebeat.inputs:
- type: log
paths: ["/var/log/trading/order.log"]
tags: ["order", "production", "shanghai"]
# 注入自定义字段
fields:
env: production
dc: shanghai
service: order-service
fields_under_root: true
我个人习惯把fields_under_root设为true,这样自定义字段会直接出现在日志根层级,查询时不用多一层嵌套。你想想看,查问题的时候多点一层鼠标,在Kibana里就是几秒钟的差距——紧急时刻这几秒能急死人。
| 标签类型 | 示例 | 用途 |
|---|---|---|
| 环境标签 | production, staging, dev | 区分不同环境 |
| 地域标签 | shanghai, hongkong, newyork | 定位物理位置 |
| 服务标签 | order, market-data, risk | 快速过滤服务 |
| 严重级别 | critical, warning, info | 告警优先级 |
4.3 Loki + Promtail:云原生时代的轻量方案
Loki是Grafana团队推出的日志系统,它和Prometheus的理念很像——不索引日志内容,只索引标签。说白了,就是牺牲了全文搜索能力,换来了极低的资源消耗。
4.3.1 Promtail配置示例
# promtail.yml
scrape_configs:
- job_name: trading-logs
static_configs:
- targets: [localhost]
labels:
job: trading
env: production
__path__: /var/log/trading/*.log
# 管道处理:提取字段
pipeline_stages:
- regex:
expression: '^(?P<timestamp>\S+) (?P<level>\S+) (?P<message>.*)'
- labels:
level:
- timestamp:
source: timestamp
format: RFC3339
这段配置里,pipeline_stages是Promtail的杀手锏。它可以在采集阶段就解析日志,提取出level、timestamp等字段作为标签。我在项目中用这个功能,把原本需要后端处理的解析工作前置了,效果很好。
我的经验:Loki适合日志量特别大的场景。比如我们每天产生几百GB的日志,用Elasticsearch成本太高,换成Loki后存储成本降了70%。但如果你需要频繁做全文搜索,还是老老实实用ES。
4.4 网络中断时的本地缓存与重试机制
这是最容易被忽视的问题。交易系统的网络环境很复杂,跨机房、跨地域的链路随时可能抖动。我曾经遇到过一次,网络中断了3分钟,结果采集器直接把日志丢了——因为默认配置下,Filebeat的缓存队列只有4MB。
4.4.1 Filebeat的缓存配置
# filebeat.yml
queue.mem:
events: 4096 # 内存队列大小
flush.min_events: 512 # 批量发送阈值
flush.timeout: 1s # 刷新超时
# 持久化队列(推荐)
queue.disk:
path: /data/filebeat-queue
max_size: 10GB
我个人强烈建议使用queue.disk持久化队列。它会把日志先写到磁盘,等网络恢复后再发送。10GB的磁盘空间,按我们交易系统的日志量,能缓存大约2小时的数据。
4.4.2 重试机制
output.elasticsearch:
hosts: ["http://elasticsearch:9200"]
worker: 2
bulk_max_size: 2048
# 重试配置
backoff.init: 1s
backoff.max: 60s
max_retries: 10
重试策略的核心是「指数退避」。第一次失败等1秒,第二次等2秒,第三次等4秒……直到最大60秒。这样既不会把后端打爆,也不会因为频繁重试浪费带宽。
避坑指南:我曾经遇到过一个问题——重试次数设了100次,结果网络断了1小时,Filebeat一直在重试,把磁盘IO打满了。后来我改成max_retries=10,配合持久化队列,问题就解决了。记住:重试不是万能的,该丢的数据要果断丢,别把采集器自己搞崩了。
4.5 知识体系结构图
4.6 实战建议总结
- 采集器选型:日志量小于100GB/天用Filebeat,大于100GB/天用Promtail+Loki
- 标签设计:至少包含env、service、dc三个标签,方便快速定位
- 缓存策略:必须开启持久化队列,磁盘空间预留日志量的2倍
- 重试配置:max_retries不超过10,backoff.max不超过60秒
- 监控采集器:采集器本身也要有健康检查,别让它成为新的故障点
最后说一句:日志采集是日志体系的「最后一公里」。这公里走不好,后面再好的分析平台都是白搭。我见过太多团队花大价钱搭了ELK,结果采集端三天两头丢数据——嗯,那感觉就像你修好了高速公路,但入口收费站天天塌方。