苦练Python第61天:logging模块——让Python日志“有迹可循”的瑞士军刀

前言

大家好,我是倔强青铜三 。欢迎关注我,微信公众号:倔强青铜三。点赞、收藏、关注,一键三连!

欢迎来到苦练Python第61天

今天咱们把 Python 的"黑匣子"------logging 模块,从"只会 print"的原始人进化成"专业运维"的现代人!

学会它,你将优雅搞定:

  • 控制台彩色输出
  • 文件按大小/日期切分
  • 多模块日志统一格式
  • 线上环境一键静默/调试
  • 日志热重载不重启进程

一句话:别再 print 了,print 不能当饭吃,logging 才能救命!


🎯 今日收获预览

  • 基础四件套:basicConfiggetLoggerdebug/info/warning/error/criticalFormatter
  • 进阶四件套:FileHandlerRotatingFileHandlerTimedRotatingFileHandlerFilter
  • 实战三板斧:多模块日志、日志分级热切换、异常堆栈完整保留
  • 常见坑位:重复日志、中文乱码、父子 logger 传播、线程安全一次排雷

🧊 热身:为什么抛弃 print?

灵魂三问:

  1. 你要不要在终端和文件同时看到日志?
  2. 你要不要在测试环境 DEBUG,线上只留 WARNING?
  3. 你要不要保留堆栈、时间、行号、函数名?

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 优先发货

最后感谢阅读!欢迎关注我,微信公众号:倔强青铜三

欢迎点赞、收藏、关注,一键三连!!

相关推荐
Testopia2 小时前
AI与敏捷开发管理系列3:敏捷方法在AI项目中的应用案例
人工智能·ai编程·敏捷流程·#人工智能学习
Testopia2 小时前
AI与敏捷开发管理1:传统方法失灵?人工智能项目的新法则
人工智能·项目管理·敏捷开发·敏捷流程
倔强青铜三2 小时前
苦练Python第60天:json模块——让Python和JSON“无缝互译”的神兵利器
人工智能·python·面试
孤客网络科技工作室2 小时前
Python - 100天从新手到大师:第二十七天Python操作PDF文件
开发语言·python·pdf
悬剑13143 小时前
python简易程序跑NLPIR模型
python·nlpir
wheeldown3 小时前
【Leetcode高效算法】用双指针策略打破有效三角形的个数
python·算法·leetcode
真的想不出名儿3 小时前
登录前验证码校验实现
java·前端·python
做运维的阿瑞3 小时前
Python原生数据结构深度解析:从入门到精通
开发语言·数据结构·后端·python·系统架构
Ivanqhz3 小时前
LR算法中反向最右推导(Reverse RightMost Derivation)
人工智能·算法