Plain Notes技术札记
← 全部笔记
开发方法

让一条日志回答一个具体问题

日志的价值不在于数量,而在于它能否回答问题。发生了什么、处于哪个阶段、花了多久、最终结果是什么,这四项往往比一长段“正在执行”更有用。

记录阶段,而不只是记录异常

一次处理可能包含读取、解析、转换和写入。如果只在最终失败时记录错误,就很难判断问题从哪里开始。

import logging
import time

log = logging.getLogger(__name__)

def run_job(job_id, operation):
    started = time.perf_counter()
    log.info("job_started id=%s", job_id)
    try:
        result = operation()
    except Exception:
        log.exception("job_failed id=%s elapsed_ms=%.1f",
                      job_id, (time.perf_counter() - started) * 1000)
        raise
    log.info("job_finished id=%s elapsed_ms=%.1f",
             job_id, (time.perf_counter() - started) * 1000)
    return result

用于计算耗时的计时器应关注时间间隔。展示事件发生时间则使用统一的时区和时间格式。两者用途不同,不需要混为一谈。

让同一次操作有共同标识

多个任务并发时,日志会交错输出。为一次操作分配共同标识,能把相关事件重新串起来。这个标识应该是内部生成的随机值或任务编号,而不是邮箱、手机号或访问凭据。

给日志设置边界

不应直接输出完整配置、请求头或表单内容。调试时需要的信息,通常可以缩减为字段名称、数据类型、长度或经过脱敏的摘要。

根据问题选择粒度

怀疑某一步慢,就记录各阶段耗时;怀疑输入不符合要求,就记录校验失败的字段;怀疑任务重复执行,就记录开始、结束与任务标识。

一份好的排错记录应当能让另一个人复核结论,同时不需要接触原始敏感数据。

先提出明确的问题,再决定记录什么,通常比“把所有东西都打印出来”更快找到答案。