Files

5.3 KiB
Raw Permalink Blame History

doc_type, full_text
doc_type full_text
short sources/02_Logging.md

02_Logging 总结

核心主题

本文介绍 Python 标准库中的 logging 模块,说明如何用日志替代直接 print() 或静默忽略异常,从而让诊断信息的输出方式、详细程度和目的地变得可配置。相关主题可连接到 Python日志记录、concepts/异常处理、程序诊断。

为什么需要 logging

在解析文件或处理输入数据时,程序经常会遇到格式错误或类型转换失败。例如 parse() 或 parse_csv() 中可能捕获 ValueError。

传统处理方式有两个极端:

  • 直接打印错误信息:适合调试或提醒用户,但不够灵活。
  • 使用 pass 静默忽略:避免干扰用户,但可能掩盖问题。

这两种方式都不理想,因为真实程序往往需要根据运行环境选择不同策略:开发时希望看到详细原因,生产时可能只记录警告或只保留严重错误。logging 模块正是为这种可配置诊断而设计的。

logging 的基本用法

模块通常先创建一个 logger:

import logging
log = logging.getLogger(__name__)

使用 __name__ 创建 logger 的好处是日志来源会自动对应当前模块名,例如 fileparse。这使得不同模块的日志可以被分别控制。

常用日志级别包括:

log.critical(message, *args)
log.error(message, *args)
log.warning(message, *args)
log.info(message, *args)
log.debug(message, *args)

它们表示不同严重程度:

  • CRITICAL:最严重的问题。
  • ERROR:错误。
  • WARNING:警告,默认通常会显示。
  • INFO:普通运行信息。
  • DEBUG:调试细节。

日志消息采用 % 风格格式化:

log.warning("Couldn't parse : %s", line)

这比提前构造字符串更符合 logging 的使用习惯。

在异常处理中使用 logging

原来的异常处理可能是:

except ValueError as e:
    print("Couldn't parse :", line)
    print("Reason :", e)

改成 logging 后:

except ValueError as e:
    log.warning("Couldn't parse : %s", line)
    log.debug("Reason : %s", e)

这种写法把“发生了坏数据”作为 warning,把“具体异常原因”作为 debug。这样默认情况下用户可以看到主要问题,而开发者可以通过提高日志详细程度查看原因。

logging 配置与调用分离

本文强调一个重要设计原则:产生日志的代码和配置日志行为的代码应当分离。

模块内部只负责发出日志:

log.warning(...)
log.debug(...)

程序入口负责配置日志系统:

import logging
logging.basicConfig(
    filename='app.log',
    level=logging.INFO,
)

这种分离让库代码不必关心日志写到哪里、格式是什么、显示哪些级别;这些都由主程序或运行环境决定。该思想与 关注点分离 有关。

练习 8.2:给模块添加日志

练习要求修改 fileparse.py 中的 parse_csv(),把类型转换失败时的 print() 替换为 logging 调用。

原代码中,当某一行转换失败时:

print(f"Row {rowno}: Couldn't convert {row}")
print(f"Row {rowno}: Reason {e}")

修改后:

log.warning("Row %d: Couldn't convert %s", rowno, row)
log.debug("Row %d: Reason %s", rowno, e)

这样做的效果是:

  • 默认只看到 WARNING 及以上级别的信息。
  • 如果配置 logger 为 DEBUG,可以看到更详细的异常原因。
  • 如果设置为 CRITICAL,则普通警告和调试信息都会被关闭。

示例控制方式:

logging.getLogger('fileparse').setLevel(logging.DEBUG)

或关闭大部分信息:

logging.getLogger('fileparse').setLevel(logging.CRITICAL)

这说明 logger 可以按模块名独立控制,适合大型程序的诊断管理。

练习 8.3:给程序添加日志配置

要让整个应用使用 logging,需要在主程序启动阶段初始化日志系统。例如:

import logging
logging.basicConfig(
    filename='app.log',
    filemode='w',
    level=logging.WARNING,
)

配置项含义:

  • filename:日志输出文件;省略时通常输出到标准错误。
  • filemode:写入模式,w 表示覆盖,a 表示追加。
  • level:最低输出级别,如 DEBUG、INFO、WARNING、ERROR、CRITICAL。

本文提示应思考:在 report.py 这类主程序中,日志配置应放在程序启动入口,而不是放进通用工具模块。通常可放在 if __name__ == '__main__': 分支中。

关键收获

  1. logging 比 print() 更适合程序诊断,因为它可配置、可分级、可按模块管理。
  2. 日志级别允许区分警告、错误、调试细节等不同信息。
  3. 库模块应只发出日志,不应决定日志写到哪里或显示多少。
  4. 主程序负责一次性初始化 logging 配置。
  5. 在异常处理中,可以用 warning 记录用户应知道的问题,用 debug 记录开发者需要的细节。

相关概念