前言
大家好,我是倔强青铜三 。欢迎关注我,微信公众号:倔强青铜三。点赞、收藏、关注,一键三连!
欢迎来到苦练Python第61天!
今天咱们把 Python 的"黑匣子"------logging 模块,从"只会 print"的原始人进化成"专业运维"的现代人!
学会它,你将优雅搞定:
- 控制台彩色输出
- 文件按大小/日期切分
- 多模块日志统一格式
- 线上环境一键静默/调试
- 日志热重载不重启进程
一句话:别再 print 了,print 不能当饭吃,logging 才能救命!
🎯 今日收获预览
- 基础四件套:
basicConfig
、getLogger
、debug/info/warning/error/critical
、Formatter
- 进阶四件套:
FileHandler
、RotatingFileHandler
、TimedRotatingFileHandler
、Filter
- 实战三板斧:多模块日志、日志分级热切换、异常堆栈完整保留
- 常见坑位:重复日志、中文乱码、父子 logger 传播、线程安全一次排雷
🧊 热身:为什么抛弃 print?
灵魂三问:
- 你要不要在终端和文件同时看到日志?
- 你要不要在测试环境 DEBUG,线上只留 WARNING?
- 你要不要保留堆栈、时间、行号、函数名?
logging
模块全帮你封装好了 ,还自带线程安全锁!
🗡️ 第一式:基础四件套
1. 最速上手 ------ basicConfig
python
# quick_start.py
import logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s [%(levelname)s] %(name)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logging.info("服务启动成功!")
logging.warning("磁盘剩余空间不足 10%")
logging.error("数据库连接超时")
运行:
yaml
2025-08-14 20:10:00 [INFO] root: 服务启动成功!
2025-08-14 20:10:00 [WARNING] root: 磁盘剩余空间不足 10%
2025-08-14 20:10:00 [ERROR] root: 数据库连接超时
一句话记忆:basicConfig = 一行代码搞定 80% 需求!
🧰 第二式:八大参数拆解
basicConfig 关键字速查
参数 | 作用 | 示例 |
---|---|---|
level | 全局阈值 | logging.INFO |
format | 日志格式 | '%(asctime)s %(message)s' |
datefmt | 时间格式 | '%H:%M:%S' |
filename | 写文件 | 'app.log' |
filemode | 文件模式 | 'a' /'w' |
stream | 指定流 | sys.stdout |
handlers | 多个处理器 | [fh, ch] |
force | 强制覆盖 | True |
🧬 第三式:自定义格式完全指南
1. 让日志同时落地文件 + 控制台
python
# dual_output.py
import logging
logger = logging.getLogger('my_app')
logger.setLevel(logging.DEBUG)
# 控制台
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
# 文件
fh = logging.FileHandler('app.log', encoding='utf-8')
fh.setLevel(logging.DEBUG)
fmt = '%(asctime)s [%(levelname)s] %(filename)s:%(lineno)d - %(message)s'
formatter = logging.Formatter(fmt)
ch.setFormatter(formatter)
fh.setFormatter(formatter)
logger.addHandler(ch)
logger.addHandler(fh)
logger.debug("调试信息只在文件里")
logger.info("普通信息两边都有")
logger.warning("警告信息两边都有")
运行后:
- 控制台只看到 INFO 及以上
app.log
里 DEBUG 也保留,且中文正常
2. 按大小切分日志
python
# rotate_size.py
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger('size_log')
logger.setLevel(logging.DEBUG)
# 单个文件最大 1KB,保留 3 个备份
rh = RotatingFileHandler('size.log', maxBytes=1024, backupCount=3, encoding='utf-8')
rh.setFormatter(logging.Formatter('%(asctime)s %(message)s'))
logger.addHandler(rh)
for i in range(100):
logger.info(f"这是第 {i} 条日志")
运行后:
matlab
size.log
size.log.1
size.log.2
size.log.3
3. 按日期切分日志
python
# rotate_time.py
import logging
from logging.handlers import TimedRotatingFileHandler
logger = logging.getLogger('time_log')
logger.setLevel(logging.INFO)
# 每天 0 点切分,保留 7 天
th = TimedRotatingFileHandler('daily.log', when='midnight', backupCount=7, encoding='utf-8')
th.setFormatter(logging.Formatter('%(asctime)s %(message)s'))
logger.addHandler(th)
logger.info("新的一天开始了")
🧪 第四式:日志分级热切换
python
# hot_level.py
import logging
import sys
import time
logging.basicConfig(stream=sys.stdout, level=logging.INFO,
format='%(levelname)s: %(message)s')
logger = logging.getLogger()
def toggle_level():
current = logger.level
new_level = logging.DEBUG if current == logging.INFO else logging.INFO
logger.setLevel(new_level)
logger.warning(f"日志级别已切换为 {logging.getLevelName(new_level)}")
# 模拟运行
for i in range(10):
logger.debug("调试信息")
logger.info("普通信息")
if i == 4:
toggle_level()
time.sleep(0.5)
运行后:前 5 秒看不到 DEBUG,后 5 秒全部出现!
🎯 实战三板斧
实战 1:多模块共享同一配置
css
project/
├── main.py
└── utils.py
python
# utils.py
import logging
log = logging.getLogger(__name__)
def work():
log.debug("工具模块调试")
log.info("工具模块运行")
python
# main.py
import logging
import utils
logging.basicConfig(
level=logging.DEBUG,
format='[%(name)s] %(levelname)s: %(message)s'
)
logger = logging.getLogger(__name__)
logger.info("主程序启动")
utils.work()
运行:
csharp
[__main__] INFO: 主程序启动
[utils] DEBUG: 工具模块调试
[utils] INFO: 工具模块运行
实战 2:异常堆栈完整保留
python
# full_traceback.py
import logging
import sys
logging.basicConfig(
level=logging.DEBUG,
stream=sys.stdout,
format='%(asctime)s %(levelname)s\n%(message)s'
)
try:
1 / 0
except Exception:
logging.exception("发生异常,但我不崩溃")
运行:
vbnet
2025-08-14 20:11:00 ERROR
发生异常,但我不崩溃
Traceback (most recent call last):
File "full_traceback.py", line 11, in <module>
1 / 0
ZeroDivisionError: division by zero
实战 3:Filter 过滤指定关键字
python
# keyword_filter.py
import logging
class KeywordFilter(logging.Filter):
def __init__(self, keyword):
super().__init__()
self.keyword = keyword
def filter(self, record):
return self.keyword in record.getMessage()
logger = logging.getLogger('filter')
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.addFilter(KeywordFilter('重要'))
ch.setFormatter(logging.Formatter('%(message)s'))
logger.addHandler(ch)
logger.info("这是一条普通信息")
logger.info("这是一条重要信息")
运行:
这是一条重要信息
🧭 常见坑位汇总
场景 | 症状 | 解决 |
---|---|---|
重复日志 | 一条记录输出 N 次 | 只给 root 加 Handler,或 propagate=False |
中文乱码 | 文件里是 \xe4\xbd\xa0 |
encoding='utf-8' |
父子 logger | 子 logger 日志翻倍 | 合理命名 + propagate=False |
线程安全 | 日志穿插错乱 | logging 自带锁,放心用 |
🧩 类比时间:logging 和快递中心
- Logger = 收件小哥
- Handler = 不同快递公司(顺丰、圆通、EMS)
- Formatter = 快递面单格式
- Filter = 安检口:带水的顺丰不收
- Level = 快递优先级:VIP 优先发货
最后感谢阅读!欢迎关注我,微信公众号:
倔强青铜三
。欢迎点赞、收藏、关注,一键三连!!