日志分析 + 搜索全家桶
凌晨 3 点翻日志的工程师的 10 件套:结构化日志库、ship→store 全链路(Fluent Bit → Loki / Elasticsearch / ClickHouse)、本地 SQL 查日志的 lnav、Sentry 错误聚合,再加 MCP server 让 AI agent 直接查 trace 和告警。
这个 pack 解决什么
凌晨 3 点。寻呼器响了,5xx 飙了。你 SSH 上去,tail -f 一个已经轮转的文件,grep 一个其实是三个异常共用一段子串的错,40 分钟后你只缩小到「checkout 模块某个地方」。这就是这个 pack 干掉的痛点。
目标不是「可观测性表演」——不需要 15 个没人打开的 dashboard。目标是:结构化日志从一端进去,问题从另一端出来,并且这个问题可以由你、你队友、或者一个有 MCP 权限的 AI agent 来问。
每一个选品都是开源或有可自建的开源核心。整条流水线在一台中型 VM 上能撑到 ~50 GB/天 的日志量;超过这个量级再把 Loki / ClickHouse 分到独立机器。不绑厂商、不踩按量计费的坑。
推荐安装顺序
- winston (Node) 或 Loguru (Python) — 从应用层的结构化日志开始。JSON 输出,每个事件一行,每行都有
timestamp、level、service、trace_id。源头不结构化,后面所有工具都在和你的 formatter 打架,干不了正事。 - Fluent Bit — 日志搬运工。tail 文件 / journald / Docker 日志,解析 JSON,加 host label,攒批,重试,发往你的存储。C 写的小二进制,常驻内存 ~5 MB,sidecar 或 DaemonSet 都行。中间层非它不可。
- Grafana Loki — 存储默认选项。只索引 label(不索引正文),用对象存储,跑起来便宜。最适合「结构化 JSON 进,按
service=checkout level=error查」的场景。LogQL 语法像 PromQL——懂 Prometheus 5 分钟上手。 - Elasticsearch — 当你需要在日志正文里全文搜索、而不只是按 label 时的替代存储。重(JVM、磁盘开销大)但在「找所有提到
OrderId=abc-123的日志」这类问题上无敌。配 Kibana 作 UI。 - ClickHouse — 当日志量特别大(>100 GB/天)且需要 SQL 时的替代存储。列存,吃压缩 JSON 跟玩似的,Elasticsearch 跑 30 秒的查询它 1 秒出。规模上来后的正确答案。
- lnav — 终端本地日志导航器。直接对日志文件跑 SQL 查询,实时 tail,自动识别格式,错误高亮。SSH 到某一台机器、中心存储指不上时反手就用它。单个二进制,无 daemon。
- Sentry — 错误聚合 + 告警。和 Loki/ES/CH 角色不同——那三个存所有日志;Sentry 专门抓异常和 stack trace,按指纹智能聚合去重,新错误出现或量突增时告警。可自建。
- SigNoz MCP Server — Model Context Protocol 桥接。让 Claude / ChatGPT / Cursor 用自然语言查 SigNoz 的 trace、日志、告警。「过去一小时最慢的接口是哪个?」→ 真数据真答案,不是幻觉。
- ClickHouse MCP — 存储是 ClickHouse 时更安全的 MCP 选择。默认只读、防 drop table、参数化查询。交给 agent 也不用担心它把
DROP DATABASE production给执行了。
流水线怎么协同
[ 你的应用 ]
│
▼ winston / Loguru (输出结构化 JSON 到 stdout)
│
[ Fluent Bit ] (解析、加 label、攒批)
│
├──▶ Loki ← 便宜,按 label 索引
├──▶ Elasticsearch ← 重正文全文搜索
└──▶ ClickHouse ← 大量级 SQL 分析
│
├──▶ Sentry ← 只收异常,聚合 + 告警
│
▼ 查询入口:
- lnav (本地文件,无 daemon)
- Grafana (Loki UI)
- Kibana (ES UI)
- SigNoz MCP (AI agent → trace/日志/告警)
- ClickHouse MCP (AI agent → SQL,只读)
关键认知:三个存储选一个,不要全装。Loki 是 80% 团队的正确默认。只有当「在正文里全文搜」是日常需求才换 Elasticsearch;只有当日志量 + 查询延迟把 Loki 推爆才换 ClickHouse。pack 里列三个是因为正确答案取决于你的流量形态——不是让你三个都装。
你会遇到的取舍
- Loki vs Elasticsearch vs ClickHouse — Loki 最便宜最易运维,但全文搜索是真的弱(百万行子串匹配很慢)。Elasticsearch 反过来:重,但「全文找这个字符串」是它主场。ClickHouse 是 SQL 核选项——聚合查询飞快但你写的是 SQL 不是 LogQL/KQL。选哪个就看你日常的问题更接近哪一种。
- winston vs Loguru vs pino vs zap — winston 是 Node 默认但 pino 更快(生态也追上了)。Loguru 是 Python 默认但
structlog在复杂上下文绑定时更灵活。本 pack 选默认;真撞到瓶颈再换。 - Sentry vs 日志存储 — Sentry 在错误捕获上和日志存储有重叠。两个都跑值得:Sentry 走「新错误出现 → 告警值班」回路;日志存储走「重建请求时序」回路。两套独立活儿。
- MCP server vs 自定义 agent 工具 — MCP 标准化了 agent 调用工具的方式,任何 MCP 兼容客户端(Claude Desktop、Cursor、ChatGPT 自定义 GPT)都能复用同一套 SigNoz/ClickHouse 接入。自己写 OpenAI function-calling 更灵活但不可移植。任何要给多个 agent runtime 用的工具,MCP 都赢。
常见踩坑
- 日志写字符串而不是结构化字段 —
log.info("user " + userId + " failed")没法搜。log.info({ event: "login_failed", userId })在三种存储里都能查。这一个改动让后面整套栈 80% 的价值才落得了地。 - Fluent Bit 没做反压控制 — 流量突增时 tail input 会 OOM。先设
Mem_Buf_Limit并打开文件 buffer,不要等生产环境出事才补。 - Loki label 高基数 — 千万别拿
user_id、request_id、trace_id当 label。Loki 存储成本和唯一 label 集数量成线性关系,一个失手的高基数 label 直接让账单涨 100 倍。label 只留service、env、level、host。 - Sentry 采样率默认 100% — 平时没问题,直到某个后台 job 10 分钟刷同一个错 5 万次把你配额打爆。在 SDK 的
before_send钩子里把这种暴力循环在源头去重。 - MCP server 默认读写权限暴露 — 几乎所有 MCP server 文档都先给读写示例。ClickHouse MCP 这种尤其重要:agent 在对面时,只读模式(env 里设)是唯一安全默认。配置必审。
- 把日志正文当 schema 索引 — ES/CH 会让你把每个 JSON 字段都映射成列。半年后你有 12000 个字段,其中一半是某个 buggy 服务的 typo。事件名和字段名在 logger 层归一化,不要在存储层处理。
10 个资产打包就绪
常见问题
Loki / Elasticsearch / ClickHouse 真的要三个都装吗?
不要——选一个。pack 里列三个是因为正确答案取决于你的形态。Loki 是 ~80% 团队的默认:便宜、按 label 索引、好运维。日常问题是「在任意一条日志正文里找这个字符串」就上 Elasticsearch(重正文全文搜索更强)。日志量超过 ~100 GB/天 或要在日志上做真正的 SQL 分析就上 ClickHouse。三个并行跑做选型对比一两周没问题,作为长期常驻栈就太痛了。
这套栈里 AI 到底起什么作用——SigNoz MCP 就是个聊天 UI 吗?
不止是聊天 UI。MCP server 把 trace、日志、告警暴露成 agent 可以自主调用的工具。实际场景举例:Claude agent 接到一条 Sentry 告警,自己去 SigNoz 查 trace、从 Loki 拉对应日志、写一段事故摘要进工单——全程一个 prompt。ClickHouse MCP 在 SQL 风格的日志分析上是同样的角色,并且强制只读,agent 想 drop 表也下不去手。
为啥用 winston/Loguru 而不是直接 `print` JSON?
三个理由。一:结构化字段通过 API 添加而不是字符串拼接,全代码库一致。二:日志等级、采样、传输(文件 / stdout / 网络)和调用点解耦。三:生态——winston 有 100+ 传输 plugin,Loguru 在 FastAPI/Django 里开箱即用。当然你可以自己 json.dumps,但一个月内就会把上面这些功能重新发明一遍。
如果错误日志已经进了 Loki,Sentry 是不是多余?
不多余,分工不同。Loki/ES/CH 不加区分地存一切,回答「给我看看这个请求前后的时序」。Sentry 按 stack trace 指纹去重异常,把它们聚合成「issue」,跟踪首次出现 / 回归 / 量突增,并在新 issue 出现时叫值班。把 Sentry 当作你的错误收件箱,把日志存储当作目击证人——两套服务你,谁也替代不了谁。
整套能不能跑在一台 VM 上,还是必须 Kubernetes?
一台中型 VM(16 vCPU、32 GB 内存、500 GB SSD)能舒服撑到 ~20 GB/天 的日志量,Loki + Fluent Bit + Sentry 自建全在一台上。超过 50 GB/天 再把 Loki 的对象存储拆到 S3 兼容存储,给 ClickHouse/Elasticsearch 独立机器。不用 Kubernetes——docker-compose 完全够,50 GB/天 以下甚至更可取。等你有运维精力维护 k8s 再上,不是日志流水线本身要求 k8s。