Files
coding-mentor-agent/kb/python-course-kb-practical-python/wiki/summaries/02_Logging.md
T

186 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
doc_type: short
full_text: sources/02_Logging.md
---
# 02_Logging 总结
## 核心主题
本文介绍 Python 标准库中的 `logging` 模块,说明如何用日志替代直接 `print()` 或静默忽略异常,从而让诊断信息的输出方式、详细程度和目的地变得可配置。相关主题可连接到 Python日志记录、[[concepts/异常处理]]、程序诊断。
## 为什么需要 logging
在解析文件或处理输入数据时,程序经常会遇到格式错误或类型转换失败。例如 `parse()` 或 `parse_csv()` 中可能捕获 `ValueError`。
传统处理方式有两个极端:
- 直接打印错误信息:适合调试或提醒用户,但不够灵活。
- 使用 `pass` 静默忽略:避免干扰用户,但可能掩盖问题。
这两种方式都不理想,因为真实程序往往需要根据运行环境选择不同策略:开发时希望看到详细原因,生产时可能只记录警告或只保留严重错误。`logging` 模块正是为这种可配置诊断而设计的。
## logging 的基本用法
模块通常先创建一个 logger:
```python
import logging
log = logging.getLogger(__name__)
```
使用 `__name__` 创建 logger 的好处是日志来源会自动对应当前模块名,例如 `fileparse`。这使得不同模块的日志可以被分别控制。
常用日志级别包括:
```python
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`:调试细节。
日志消息采用 `%` 风格格式化:
```python
log.warning("Couldn't parse : %s", line)
```
这比提前构造字符串更符合 logging 的使用习惯。
## 在异常处理中使用 logging
原来的异常处理可能是:
```python
except ValueError as e:
print("Couldn't parse :", line)
print("Reason :", e)
```
改成 logging 后:
```python
except ValueError as e:
log.warning("Couldn't parse : %s", line)
log.debug("Reason : %s", e)
```
这种写法把“发生了坏数据”作为 `warning`,把“具体异常原因”作为 `debug`。这样默认情况下用户可以看到主要问题,而开发者可以通过提高日志详细程度查看原因。
## logging 配置与调用分离
本文强调一个重要设计原则:产生日志的代码和配置日志行为的代码应当分离。
模块内部只负责发出日志:
```python
log.warning(...)
log.debug(...)
```
程序入口负责配置日志系统:
```python
import logging
logging.basicConfig(
filename='app.log',
level=logging.INFO,
)
```
这种分离让库代码不必关心日志写到哪里、格式是什么、显示哪些级别;这些都由主程序或运行环境决定。该思想与 关注点分离 有关。
## 练习 8.2:给模块添加日志
练习要求修改 `fileparse.py` 中的 `parse_csv()`,把类型转换失败时的 `print()` 替换为 `logging` 调用。
原代码中,当某一行转换失败时:
```python
print(f"Row {rowno}: Couldn't convert {row}")
print(f"Row {rowno}: Reason {e}")
```
修改后:
```python
log.warning("Row %d: Couldn't convert %s", rowno, row)
log.debug("Row %d: Reason %s", rowno, e)
```
这样做的效果是:
- 默认只看到 `WARNING` 及以上级别的信息。
- 如果配置 logger 为 `DEBUG`,可以看到更详细的异常原因。
- 如果设置为 `CRITICAL`,则普通警告和调试信息都会被关闭。
示例控制方式:
```python
logging.getLogger('fileparse').setLevel(logging.DEBUG)
```
或关闭大部分信息:
```python
logging.getLogger('fileparse').setLevel(logging.CRITICAL)
```
这说明 logger 可以按模块名独立控制,适合大型程序的诊断管理。
## 练习 8.3:给程序添加日志配置
要让整个应用使用 logging,需要在主程序启动阶段初始化日志系统。例如:
```python
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` 记录开发者需要的细节。
## 相关概念
- Python日志记录
- [[concepts/异常处理]]
- 程序诊断
- 关注点分离
- 模块化程序设计
## Related Concepts
- [[concepts/测试-日志与调试]]
- [[concepts/库接口设计]]
- [[concepts/main-函数与脚本结构]]
- [[concepts/CSV-数据处理]]
- [[concepts/文件读写]]
- [[concepts/模块与-import]]
- [[concepts/Python-输入输出]]