怎么记日志才算专业,SRE、DEV 都应该了解的实践指南

Mike Shi 2025-12-05 16:14:09

坦白说:很多日志都只是无用信息…

[INFO] Starting process … probably.
[DEBUG] Made it to line 42 — still alive.
[TRACE] Function entered. Leaving soon.
[INFO] User clicked a button. Which one? No idea.
[WARN] Everything’s fine, just felt like warning you.
[DEBUG] Variable x = 7. Might change. Might not.
[INFO] Operation completed successfully (we think).
[TRACE] Loop iteration #12 of infinite sadness.
[DEBUG] Placeholder for meaningful message.
[INFO] Shutting down gracefully … except when not.

作为开发人员,我们常常像撒彩纸一样随意记录日志 —— 每个函数的入口、每个变量、每一次系统 “心跳” 都不放过。没过多久,数太字节的无意义日志行就会堆积如山,填满那些没人看的仪表盘。

我们向可观测性供应商支付数百万美元,却只是为了存储这些 “垃圾”。每一条无用的日志行都在消耗计算资源、磁盘空间和资金。没有明确目的的日志记录不是可观测性实践,而是 “乱扔垃圾”。

即便现在的可观测性平台借助列式存储大幅提升了压缩效率,也没有理由记录所有内容。这种无差别记录仍会让根本原因分析变成 “大海捞针”—— 稀释你真正需要的有效信号,而且你还得为此支付更高成本。

我们需要有选择性地记录日志:只记录那些有助于理解系统、调试实际问题或说明业务影响的信息,其他无关内容都不必记录。

日志记录的核心原则

每一条日志记录都是一个主动选择,而非下意识的操作。如果这条日志无法帮未来的你在凌晨 3 点排查出 bug,那就删掉它。日志记录不是记日记,要做到简洁、清晰且真正有用。

在敲下 logger.info(信息级日志记录代码)之前,先停下来问问自己:我之后真的会用 grep(一种文本搜索工具)查找这条日志吗?

如果答案是否定的,就删掉它。日志不是叙事流水账,而是证据。它们的存在,是为了在系统出现问题时,告诉你当时系统的运行状态。

日志不应被排在可观测性实践的末尾。它们不只是用于验证问题的 “显微镜”,更是用于发现问题的 “地图”。有时候,获取洞见最快的方式,就是直接探索原始文本 —— 通过 grep 搜索、筛选,跟着直觉去挖掘。

日志能激发探索欲:它们能展现出指标可能掩盖的细微差别,以及追踪数据无法传递的上下文信息。不要把日志当作最后的求助手段,而应将其视为 “实时输出的真相来源”,从一开始就主动去探索。

无上下文,不日志

只写 “发生错误”,却不包含输入数据、ID 或系统状态,这样的日志毫无意义。要添加足够的上下文信息,以便还原当时的场景 —— 比如请求 ID、用户 ID、输入参数、操作名称等。如今,借助 OpenTelemetry(一种开源可观测性框架),你可以免费获取追踪 ID(trace ID)和跨度 ID(span ID),一定要善用它们。通过追踪 ID 与追踪数据(甚至指标)关联起来的日志,其价值远胜于孤立的文本行。

日志不是可观测性实践中独立的一环,而是根本原因分析的 “收尾章节”。你通过指标触发告警,通过追踪数据展开调查,然后再查看日志,了解到底发生了什么。当日志通过追踪 ID 和跨度 ID 关联起来后,它们就不再是无用信息,而成为了精准、有上下文的证据 —— 直接与单个请求的路径相关联。这才是有明确目的的可观测性实践,而非一堆杂乱无章的文本。

结构化日志,而非自由文本

自由文本形式的日志早已过时。结构化日志(无论是 JSON、CSV 格式,还是键值对形式)不仅更易于查询,更是数据分析的基础。一旦日志具备了结构化格式,规律便会自然浮现

  • “这个错误从上周开始激增。”
  • “这个问题大多在事件 X 之后出现。”
  • “这个警告与某次特定部署相关。”

日志的未来发展方向,不是逐行阅读单条日志,而是从成千上万条日志中发现整体规律。

图片来自网络

结构化日志让图表展示变得简单高效。虽然很多可观测性平台支持 “读取时解析 schema”(schema on read),但这种灵活性是有代价的:每一次查询,系统都要逐行扫描并解析原始文本,推断出本该从一开始就存在的结构化格式。这类查询计算成本高、速度慢,而且用户编写查询语句也更困难。

预先结构化的日志则能彻底扭转这种低效局面。当你的数据本身就具备结构化格式时,就可以充分利用列式存储和原生聚合功能 —— 在毫秒级时间内完成事件的查询、可视化和关联分析,而无需等待数分钟。

明确何时该统计度量,而非一味记录日志

并非所有事件都适合写入日志流。有些信息需要更规范的结构和时间维度记录 —— 这正是跨度(span)和指标(metric)的用途。如果你要统计延迟时间、用户流程或分布式系统中的因果关系,就应该生成跨度数据而非日志。跨度能捕捉耗时、上下文以及服务间的关联关系,告诉你 “为什么某个操作变慢或出错”;而日志只能告诉你 “某个问题发生了”。

同样的逻辑也适用于指标:将重复出现的日志转化为可告警、可高效聚合的有效信号。如果你发现自己每秒都在记录数百条相同的日志信息,那你就不是在进行可观测性实践,只是在浪费存储资源。对这类信息,只需统计一次、进行汇总,然后让指标和追踪数据来承担主要的分析工作即可。

日志级别是给人看的,而非给机器看的

日志记录不是个人的调试日记,而是留给未来团队成员的共享资料。每一条日志都应能让他人清晰理解当时发生了什么,无需猜测你的意图。记录日志时,要为下一次故障排查做准备,而非受当下情绪影响。你的日志记录着系统的运行历程,要让这段 “历程” 值得一读。例如:

  • 错误(ERROR):需要人员介入处理。系统出现故障。
  • 警告(WARN):出现意外情况,但系统可正常运行。后续需调查。
  • 信息(INFO):系统的常规运行状态,有记录价值。
  • 调试 / 追踪(DEBUG/TRACE):开发人员临时用于排查问题的信息 —— 不应轻易提交到生产环境。

日志记录要审慎。除非确实需要采取行动,否则不要将某条日志标记为 “错误” 级别。滥用 “错误” 级别会让告警机制失去敏感度,导致团队对真正重要的问题视而不见。每个日志级别都应传递明确的意图:哪些问题需要立即修复,哪些需要持续关注,哪些可以忽略。

不过,追踪级日志也有其用武之地。例如,在 ClickHouse Cloud(一款云原生数据库)的后台运行中,我们会大量使用追踪级日志,帮助工程师排查性能问题,并为大规模用户提供支持。这是一种经过深思熟虑的例外情况 —— 当你运营着一个实时为数千个工作负载提供服务的分布式数据库时,这种日志级别是必要的。但对于大多数应用而言,如此详细的日志记录并非可观测性实践,只是在制造无用信息。

助力高效日志记录的工具

现在已有丰富的软件开发工具包(SDK)和强大的过滤工具,无需再 “无差别记录所有日志”,要善用这些工具。

现代的 OpenTelemetry Collector SDK(收集器软件开发工具包)能让你精准控制日志记录内容:你可以对代码进行埋点配置,只生成有意义的日志行,然后在数据摄入或收集阶段过滤或丢弃所有无关日志。

例如:

  • 过滤处理器(filter processor)支持使用 OpenTelemetry 转换语言(OTTL),根据日志的严重级别、资源属性或内容模式等条件,丢弃不需要的日志、指标或追踪数据。
  • 如果你的可观测性平台支持,还可以在代理(agent)、收集器网关(collector gateway)或数据摄入阶段进行过滤,确保无用日志不会被写入、存储或建立索引 —— 从而节省计算资源、存储成本和查询成本。
  • 如果管理员发现有用户在生成无用日志,可以在数据处理流程中强力过滤这些日志,或者强制推行 “最小日志记录” 策略。即便你使用的是专有平台,它们通常也会提供类似的过滤或摄入控制工具,只是可能不会公开宣传这些功能。

有目的地面向日志,否则不如不记录

可观测性的关键不在于数据量,而在于清晰度。每一条日志都应凭借其独特价值占据一席之地 —— 即解释那些指标和追踪数据无法传递的信息。没有明确目的的日志记录,只会浪费资金,并掩盖有价值的洞见。

日志记录要审慎,要采用结构化格式,要添加上下文信息,要明确何时该统计度量、何时该进行追踪、何时该保持 “沉默”。现代工具让规范的日志记录变得比以往更简单,但最终的自律仍需来自你自身。

归根结底,出色的日志记录并非要捕捉所有发生的事情,而是要捕捉关键信息。

原文链接:https://thenewstack.io/when-to-log-and-when-to-shut-up/

快猫星云 联系方式 快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云
OpenSource
开源版
Flashcat
Flashcat
Flashduty
Flashduty