Onion - Identifying Incident-indicating Logs for Cloud Systems
- II-log: Incident-Indicating Log, 真正能告诉你故障在哪的那批 log;
- Log clique: 一组高度相似 / 频繁共现的 log template, 是 Onion 聚类的产物;
- Anomalous server: incident 时段触发了 alert 或 metric 异常的实例;
- Normal server: 同服务下未受影响的同伴实例;
- Bilateral: 双边对比, 这里指 anomalous 集 vs normal 集的对比;
前置: 为什么 raw log 不能直接喂给 RCA 系统
云服务一次 incident 的 log 量级通常是百万到亿条, 真正与故障有因果关系的可能只有几十条; 中间夹着大量噪声:
- routine heartbeat;
- 健康探针 (
GET /health 200); - 周期性 batch / cron 任务的常规 log;
- 跨实例都有的全局事件 (版本 reload, 集群配置 push);
- 上下游服务的非关键 warning;
如果直接把全量 log 倒给下游 (人或者 LLM 或者经典 RCA model), 信号会被噪声淹没; 所以先做一道 incident-indicating log 过滤是 RCA pipeline 的刚需;
Onion 解决的就是这一步; 它不试图直接给出 root cause, 只回答一个更窄的问题:
“在这堆 log 里, 哪几十条是真正跟这次事故相关的?”
论文定位
Onion 是 Microsoft 内部生产部署的一个 log filter, 在 ESEC/FSE 2021 发出来; 它有几个工业系统 paper 的硬指标特征:
- F1 = 0.95 (在 MS 真实事故数据上);
- 百万级 log 几分钟跑完 (单机 CPU);
- 不需要训练数据、不需要标注、没有 GPU 需求;
这三个数字组合直接告诉你: 它不是 DL, 不是 trained model, 是纯经典统计 + 图论 + 聚类; 这是它能在生产里活得下去的关键 — 部署门槛低, 在线开销低, 可解释性高;
Onion 的位置在 RCA pipeline 里非常清晰:
1 | raw logs ──► [Onion] ──► incident-indicating logs ──► [downstream RCA] |
它是前置过滤器, 不是 end-to-end RCA; 不要把它误读成"输入 log 输出 root cause";
论文解决的核心问题
具体回答的问题是:
- 怎么定义"跟 incident 相关"才能既精准又不依赖人工标注?
- 怎么把"判定相关"这件事做到既快又稳, 适合工业流量?
- 怎么处理"流量自然波动 / 周期任务 / 版本上线" 这些跟 incident 同时段但无关的噪声?
论文识别的两个 Technical Difficulty
paper 在 motivation 部分明确把"为什么这件事难"拆成两条, 这两条直接对应了后面三准则 + clique 设计的取舍, 值得单独拎出来:
Difficulty 1: 海量 + 多样 + 低信噪比的 log 数据
云服务的 log 同时具备三个让传统方法吃瘪的特征:
| 子属性 | 表现 | 为什么是技术难点 |
|---|---|---|
| Volume (海量) | 一次 incident 关联的 log 通常百万到亿级 | 任何需要 或更高复杂度的算法都直接不可行 |
| Diversity (多样) | 跨服务、跨组件、跨版本的 log 格式天然不一致 | 没法用一套硬编码规则去 match, 需要自适应的 representation |
| Overwhelming details (噪声压倒) | 99%+ 是健康路径上的 routine log (heartbeat, success ack, monitor probe) | 真正与故障相关的 log 比例极小, signal 被 noise 海量稀释 |
- Volume 难点 → 选统计加权 + 渐进聚类而非 DL/embedding, 保证百万 log 几分钟跑完;
- Diversity 难点 → incident-aware representation, 不依赖固定 schema, 跨服务跨格式都通用;
- Noise 难点 → 三个准则联合筛选 (Consistency + Impact + Bilateral-Diff), 单一指标过不了的噪声会被另一个滤掉;
Difficulty 2: Confounding events (混淆事件) 让朴素时间对比失效
这是 paper 的真正深度洞察, 也是最容易被忽略的难点:
incident 时段并不只有 incident 一件事在发生; 同时段往往叠加了:
- 流量自然波动: 周一上午、月底结算、热点事件爆发引发的全局流量增长;
- 周期性 batch / cron: 每小时备份、每日统计、每周 compaction 这种定时任务;
- 版本 deployment: 灰度发布、配置 push、客户端强制升级;
- 跨服务级联反应: 上游服务变更触发下游响应模式变化, 但不一定是 incident;
这些事件和 incident 在 log 上的"统计扰动"很相似 — 都会让某些 log template 出现频率突变, 都是"跟之前不一样"; 朴素的"incident 时段 vs baseline 时段"对比根本分不开:
1 | 朴素时间对比: |
这是 confounding events 给纯时间维度对比下的死结: 你想知道"是不是 incident 信号", 但你看到的统计变化既可能是 incident 也可能是上面任意一种 confounding event, 从单一时间序列里没法分离;
[!这一难点直接催生了 Bilateral-Difference]
Bilateral 用空间对比 (同时段不同实例: anomalous server vs normal server) 替代 / 补充 时间对比; confounding events 因为是全局事件, 会同时打在 anomalous 和 normal 实例上, 两边都涨, 差为零, 自然抵消; 而真正的 incident 信号是局部的 — 只在出问题的实例上密集出现, normal 实例上几乎没有, 空间差异显著; 这一招把 confounding 噪声从空间维度上滤掉, 是 Onion 整套方法论里最妙的一笔;
两个 Difficulty 怎么映射到三准则 + clique
把 Difficulty 和后面 design 的对应关系列出来:
| Technical Difficulty | 直接催生的设计 |
|---|---|
| Diff 1 (Volume + Noise) | Impact 准则 (用量级 + burst 度过滤低频次噪声) + 渐进聚类 (压缩 template 数) |
| Diff 1 (Diversity 跨服务) | Incident-aware Representation (统计特征跨格式通用) |
| Diff 1 (Cross-incident 泛化) | Consistency 准则 (跨历史 incident 的复现率) |
| Diff 2 (Confounding events) | Bilateral-Difference 准则 (空间对比代替时间对比) |
| Diff 2 (单 template 不稳) | Log Clique 聚合 (多个 template 一起算统计指标, ensemble 更稳) |
这是一个非常完整的 design rationale: 每一个核心机制都对应一个明确的 technical difficulty, 没有为了 fancy 而 fancy 的部分;
核心机制 1: 三个 incident-indicating 准则 (核心 insight)
这是 Onion 最有价值的方法论贡献, 把"什么 log 才算 incident-indicating" 拆成三个互相正交、各自打不同噪声的统计指标:
Consistency (一致性)
同类 incident 反复出现时, 这个 log 是不是反复出现?
衡量: 一个 log template 在历史相似 incident 里的复现率;
防的是什么:
- 偶发噪声: 某次 incident 凑巧出现一次的 log, Consistency 低, 不是真信号;
- incident-specific quirks: 单次 incident 的特殊记录, 不能泛化;
形式上类似:
高 Consistency 的 log = “这种事故反复出现的指纹”;
Impact (影响力)
这个 log 的出现规模和时间集中度够不够 prominent?
衡量: log template 在 incident 时段的出现量级 × 时间集中程度;
防的是什么:
- 低频偶发记录: 一次 incident 里只出现 2-3 次的 log, 即使 Consistency 高也大概率是噪声;
- 时间扩散的 log: 在 incident 时段均匀分布、看不出 burst 的 log, 跟 incident 关联弱;
形式上像:
burstiness 度量"这些 log 是不是集中在很短一段时间爆发", 高 burstiness 意味着事件性强;
高 Impact 的 log = “确实在 incident 时段被大量、集中触发”;
Bilateral-Difference (双边差异) ⭐
这个 log 在 anomalous 实例上和 normal 实例上的频率差距大吗?
衡量: 同一时段内, log template 在 anomalous server 集合上的频率 和在 normal server 集合上的频率 之间的差异;
形式上:
防的是什么 (这是 Onion 最有威力的设计):
| 噪声源 | 为什么 Bilateral 能滤掉它 |
|---|---|
| 流量自然涨跌 | 周一上午全部实例 log 都涨, anomalous/normal 同步涨, 差为 0 |
| batch / cron 任务 | 备份任务在所有实例上都跑, 抵消 |
| 版本上线 | 新格式日志在所有实例上都有, 抵消 |
| 全局配置 push | 同样所有实例都有, 抵消 |
[!这就是 Bilateral 的精髓]
Onion 用空间维度的对比 (同时段不同实例) 替代 / 补充 时间维度的对比 (同实例不同时段); 大量噪声会同时打在所有实例上, 空间对比让它们天然抵消, 留下来的差异才是 incident-specific 真信号;
三个准则的分工
这是为什么 Onion 用三个不是一个的核心:
| 准则 | 防什么噪声 | 单独够用吗 |
|---|---|---|
| Consistency | 偶发的 single-incident 噪声 | 不够, 高频但跟事故无关的 log 也能高 Consistency |
| Impact | 低频 / 时间扩散的非事件性 log | 不够, 周期性高频 log (cron) 也能高 Impact |
| Bilateral-Diff | 同时段所有实例都有的全局噪声 | 不够, 单实例独有的偶发 log 也能高 Bilateral |
三者 AND 起来才能锁定真信号; 任何单一准则都有它打不到的噪声死角, 三者互补才能拿到 0.95 F1;
核心机制 2: Incident-aware Representation
这是 Onion 把 log 变成数值向量的方式, 关键判断: 不是 word2vec, 不是学到的 embedding, 不需要训练;
它本质上是带权 TF-IDF 类的统计向量, 但权重是 incident-aware 的;
朴素 TF-IDF 不够用
如果直接用 TF-IDF, 每个 log template 的权重只反映它在所有 log 里的"罕见度"; 这跟 incident 没关系 — 一些极罕见但跟事故无关的 log (例如某种 admin command 偶尔出现) 反而会被推到高权重;
Incident-aware 的权重设计
Onion 的"incident-aware"体现在权重项里显式带入了 incident 上下文, 大致包含这些维度:
- 在 anomalous server 上的频率;
- 在 normal server 上的频率;
- 在 baseline (历史正常时段) 的频率;
- 这些频率的相对比值 / 差值;
- 可能再加 token 级别的 TF-IDF 风格分量;
把这些组合成每个 template 的向量分量, 这个向量自然就对 incident-relevant 信号敏感, 对全局噪声 (在 anomalous 和 normal 上都高频的) 不敏感;
这套表示的关键不在用了什么 fancy 模型, 而在特征工程层面就把 incident 上下文编码进去; 所以下游聚类不需要再"学" incident 是什么, 它直接在已经"incident-aware"的空间里做几何运算就够;
跟 word2vec / sentence-bert 的对比
| 维度 | word2vec / SBERT | Onion incident-aware repr |
|---|---|---|
| 是否需要训练 | 需要 | 不需要 |
| 表示语义 | 通用语义 | incident 相关性 |
| 维度 | 100-768 | 可以很低 (~10-30 个统计特征) |
| 跨服务迁移 | 好 (语义通用) | 好 (统计准则通用) |
| 推理速度 | 慢 (向量计算 + ANN) | 快 (统计特征) |
Onion 选这条路是工业生产的合理判断: 准确率不亚于 (在 RCA-relevant 任务上甚至更优), 但部署成本低一个数量级;
核心机制 3: Progressive Log Clique Algorithm
最后一步是把"打分了的 template" 聚类成有意义的事件单元;
为什么不直接 top-K template
直接对所有 template 按三准则打分排序取 top-K 也能产出结果, 但有几个问题:
- 同一个故障会触发一组相关 log (例如 connection pool exhausted 会同时引发"wait for connection", “request timeout”, “pool size N reached”), 把它们当独立 entry 报给下游会重复占 token;
- 单个 template 看起来 prominent, 跟它共现的其它 template 才能给出完整的故障故事;
所以 Onion 把"相关 template"打包成 log clique 再处理;
什么是 log clique
clique 是图论术语, 意思是一组两两都相邻的节点 — 在 Onion 里它意味着一组互相高度相关 / 频繁共现的 log template; 直觉上一个 clique 对应一个故障的 log 表现模式;
渐进式聚类怎么做
Onion 的"progressive log clustering"大概是这样的算法 (基于 paper 的描述推断):
1 | 初始状态: 每个 log template 自成一个单点 clique |
这是经典的 agglomerative hierarchical clustering, 加了 clique 性质约束作为停止条件; 整个过程不需要预设 K (cluster 数), 这是它叫 “progressive” 的来源 — 自适应增长, 直到自然收敛;
Contrast Analysis on Cliques
clique 形成后, 在 clique 这个粒度上重新计算三个准则, 而不是 template 粒度:
- 一个 clique 的 Consistency = 这一组 template 在历史 incident 里整体一致的程度;
- Impact = clique 内 template 总量 + 集中度;
- Bilateral-Diff = clique 在 anomalous vs normal server 上的整体差异;
最后取 top-K cliques (K 通常 5-10), 这些 clique 内部的所有 template 就是输出的 incident-indicating logs;
[!为什么 clique 比 template 更稳]
单个 template 的统计可能因为采样波动而不稳, 一个 clique 包含多个 template, 统计 ensemble 更稳健, 假阳性率显著低; 这是为什么 Onion 选择"在 clique 上做 contrast" 而不是"在 template 上做 contrast";
完整 pipeline 走一遍
把三个核心机制串起来:
1 | incident 时段所有 raw logs + anomalous/normal server 划分 |
整条 pipeline 没有任何 trained model, 没有 LLM, 没有 GPU 依赖;
跟下游 RCA 的衔接
Onion 的输出怎么进下游, 三种典型用法:
| 下游 | 衔接方式 |
|---|---|
| 人类 OCE | top-K clique 直接展示在 dashboard, OCE 看几条 log 就能起诊断假设 |
| RCACopilot 类 LLM | clique 输出 + metric + trace 拼成 prompt, LLM 推根因类别 |
| 传统 ML classifier | clique 的统计向量当 feature 喂给 SVM / Random Forest, 直接做分类 |
我们设计的 single-call RCA pipeline 就属于第二种 — Layer 2a 的 Onion-style 过滤直接对接 LLM 的 prompt;
一些值得讨论的局限
对 anomalous/normal split 的强依赖
bilateral-diff 准则成立的前提是有可信的实例划分; 几个边界情形:
- 全军覆没: 所有实例都坏了, 没有 normal 集做对比, bilateral 退化, 只能靠 Consistency + Impact;
- 单实例服务: 没有同伴可比, bilateral 完全失效;
- 划分错了: 把一个 normal 实例当 anomalous (或反之), bilateral 计算被污染;
paper 没特别讨论这些边界情形怎么 fallback;
对 Consistency 的历史依赖
Consistency 需要历史相似 incident 的 corpus, 冷启动时 (新部署系统 / 新故障类型) 用不了; 退化为只剩 Impact + Bilateral;
Log Parsing 的质量上限
整套依赖前置的 log template 提取; 如果 Drain 把本该不同的 log 合并成同一个 template (under-clustering), 或者反过来 (over-clustering), Onion 后面再聪明也救不回来;
纯静态特征的局限
Onion 的特征是手工统计指标, 一些需要语义理解的细微差别它抓不到; 例如两条 log “Failed to connect to db1” 和 “Failed to connect to db2”, token 级别看起来都是同一类, 但实际上分别指向两个不同后端 — 学到的 embedding 能区分, Onion 的 TF-IDF 类表示可能不能;
没有 trace 信号
Onion 只看 log, 不利用 metric 也不利用 trace; 在多模态 RCA 时代这是一个明显遗漏 — 它要求自己单一信号源就解决问题, 给后续工作留了空间 (LLMRCA, GALA 都补上了多模态);
把 Onion 浓缩成几句, 它的主流观点可以总结为:
- 云事故 RCA 的瓶颈不是"找根因模型", 而是"在百万 log 里先把噪声滤掉" — 这个步骤独立成方法论值得做;
- 用三个互相正交的统计准则 (Consistency / Impact / Bilateral-Difference) 联合判断 incident-indicating, 任何单一准则都有死角, 三者 AND 起来覆盖率才够;
- Bilateral-Difference 是最大的工程创新: 用同时段不同实例的空间对比, 替代易被流量波动 / 周期任务 / 版本上线干扰的纯时间对比;
- 表示层用incident-aware 加权统计, 不用 word2vec 等学到的 embedding, 牺牲一点语义换巨大的部署优势 (零训练成本 + 单机 CPU 跑百万 log 几分钟);
- 聚类层用 progressive agglomerative + clique 约束, 把单 template 的不稳统计 ensemble 成 clique 粒度, 假阳性大幅下降;
- Onion 是 filter 不是 classifier, 输出 incident-indicating logs 而非 root cause label, 跟下游任何 RCA 系统都好衔接 — 这是它能成为流水线中可复用模块的关键;
