祝大家2026新年快乐,身体健康,今天来思考一个有趣的问题:——在大厂,日志类系统以前是怎么做的?现在因为AI变得有多简单?在大厂做后端的那几年,我几乎没有遇到过「不需要看日志」的系统。监控和告警解决的是“哪里出问题了”,日志解决的永远是那个更难的问题:为什么会出问题?当
祝大家2026新年快乐,身体健康,今天来思考一个有趣的问题: —— 在大厂,日志类系统以前是怎么做的?现在因为 AI 变得有多简单?
在大厂做后端的那几年,我几乎没有遇到过「不需要看日志」的系统。
监控和告警解决的是 “哪里出问题了”, 日志解决的永远是那个更难的问题:
为什么会出问题?
当系统规模还小时,人肉分析日志勉强能扛; 但一旦到了高 QPS、多服务、多团队的环境,日志分析就会变成一种持续消耗工程师精力的体力活。
先说一个很多人可能不知道的事实:
大厂并不是“不用看日志”,而是尽量“让人少看日志”。
在大厂时期,我们会先做几件基础设施层面的事情:
这一步的目标是: 让日志“可查询”,而不是“可理解”。
接下来是最重的一步:
这些规则的特点是:
本质上,这是在用规则模拟语义理解。
即使做了所有这些事情:
日志分析的瓶颈,从来不在机器,而在人。
直到这两年,大模型真正可用之后,我才意识到一件事:
我们当年用大量规则、人工经验、复杂系统去解决的问题, 本质上就是“文本语义理解”。
而这,正是 AI 最擅长的事情。
于是,一个想法就变得非常自然:
能不能把“理解日志”这件事,直接交给模型?
先看一个可以真正跑在生产环境里的最小架构:

这个架构的核心思想只有一句话:
让机器做理解、归类和总结,人只看结果。
这是 Rust 非常擅长的部分。
use tokio::fs::File;
use tokio::io::{AsyncBufReadExt, BufReader};
pub async fn read_logs(path: &str) -> anyhow::Result<Vec<String>> {
let file = File::open(path).await?;
let reader = BufReader::new(file);
let mut lines = reader.lines();
let mut logs = Vec::new();
while let Some(line) = lines.next_line().await? {
logs.push(line);
}
Ok(logs)
}
在真实系统里:
这和传统日志系统的处理方式完全一致。
过去我们用规则,现在直接用 embedding。
#[derive(Serialize)]
struct EmbeddingRequest {
model: String,
input: Vec<String>,
}
async fn embed_logs(logs: Vec<String>) -> Vec<Vec<f32>> {
// 调用 embedding API,返回语义向量
}
这一层的意义在于:
日志不再是字符串,而是“语义坐标”。
fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
let dot = a.iter().zip(b).map(|(x, y)| x * y).sum::<f32>();
let norm_a = a.iter().map(|x| x * x).sum::<f32>().sqrt();
let norm_b = b.iter().map(|x| x * x).sum::<f32>().sqrt();
dot / (norm_a * norm_b)
}
只要设定一个阈值,就能自动把:
自然聚到一起。
这一步,在过去几乎不可能低成本实现。
以下是同一类错误日志,请总结问题原因,并给出可能的排查方向:
- log1
- log2
- log3
模型给出的结果,往往已经接近一个合格工程师的分析结论。
回头看会发现:
不同的是:
AI 并没有改变系统工程本身, 它只是把“最难的一环”变成了可调用的能力。

如果你维护过一个日志量巨大的系统, 你会知道:
真正值钱的不是日志,而是结论。
AI 的出现,让我们第一次可以:
而 Rust,正好是把这套能力稳定落地的最好工具之一。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!