Python脚本如何区分日志输出级别:从基础到高级的完整指南
目录导读
- 为什么需要日志级别控制?
- Python内置logging模块的核心概念
- 日志级别的定义与含义(DEBUG/INFO/WARNING/ERROR/CRITICAL)
- 日志输出级别配置的三种方式
- 如何动态调整日志级别
- 日志格式化与自定义Handler
- 常见陷阱与最佳实践
- 问答环节:解决你的实际疑问
为什么需要日志级别控制?
在实际开发中,脚本可能产生大量调试信息、错误报告、状态更新等,如果不加区分地输出所有信息,会导致:

- 日志文件迅速膨胀,重要错误被海量调试信息淹没
- 线上环境不希望看到
DEBUG级别日志,但本地调试时需要 - 无法快速筛选问题(如只关注
ERROR级别)
通过日志级别将输出信息分级,是专业Python脚本的必备能力。
Python内置logging模块的核心概念
Python标准库logging提供了完善的日志系统,主要组件包括:
- Logger:日志记录器,应用程序直接调用它的
info()、debug()等方法 - Handler:日志处理器,决定日志输出到控制台、文件或网络
- Formatter:格式化器,定义日志内容的格式(时间、级别、消息等)
- Filter:过滤器,实现更精细的级别控制
层级关系:Logger通过setLevel()设置级别,Handler也独立拥有级别,最终输出取两者的“较高”级别(即数值较大、更严重的级别)。
日志级别的定义与含义
Python定义了五个标准级别(数值从小到大,严重程度递增):
| 级别 | 数值 | 使用场景 |
|---|---|---|
| DEBUG | 10 | 调试信息,如变量值、函数调用 |
| INFO | 20 | 普通信息,如程序启动、完成 |
| WARNING | 30 | 警告信息,如配置文件缺失 |
| ERROR | 40 | 错误信息,如数据库连接失败 |
| CRITICAL | 50 | 严重错误,如内存泄漏 |
注意:设置级别为INFO时,DEBUG级别的信息不会输出,而WARNING及更高级别的会输出。
日志输出级别配置的三种方式
basicConfig() 快速配置
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logging.info("程序启动")
logging.debug("调试信息") # 不会输出,因为级别为INFO
Logger对象手动配置
logger = logging.getLogger("my_app")
logger.setLevel(logging.DEBUG)
# 控制台Handler
ch = logging.StreamHandler()
ch.setLevel(logging.WARNING) # 只输出WARNING及以上
logger.addHandler(ch)
# 文件Handler
fh = logging.FileHandler('app.log')
fh.setLevel(logging.ERROR) # 只记录错误
logger.addHandler(fh)
配置文件(适合大型项目)
# logging.conf [loggers] keys=root [logger_root] level=INFO handlers=consoleHandler [handler_consoleHandler] class=StreamHandler level=WARNING formatter=basicFormatter [formatters] keys=basicFormatter [formatter_basicFormatter] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
然后通过logging.config.fileConfig('logging.conf')加载。
如何动态调整日志级别
生产环境中,经常需要在不重启进程的情况下改变日志级别。
import logging
def set_logger_level(logger_name: str, level: int):
logger = logging.getLogger(logger_name)
logger.setLevel(level)
for handler in logger.handlers:
handler.setLevel(level)
更高级的做法是结合信号处理或配置中心,比如监听SIGUSR1信号:
import signal
def handle_signal(signum, frame):
logging.getLogger().setLevel(logging.DEBUG)
signal.signal(signal.SIGUSR1, handle_signal)
日志格式化与自定义Handler
自定义格式化示例
formatter = logging.Formatter(
'%(asctime)s | %(levelname)-8s | %(filename)s:%(lineno)d | %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
自定义Handler(如输出到Redis)
class RedisHandler(logging.Handler):
def __init__(self, redis_client, key='logs'):
super().__init__()
self.redis = redis_client
self.key = key
def emit(self, record):
msg = self.format(record)
self.redis.lpush(self.key, msg)
常见陷阱与最佳实践
陷阱一:忘记设置root logger的级别
# 错误示例
logging.basicConfig() # 默认级别为WARNING
logging.info("不会输出") # 结果:没有输出
解决:明确设置level=logging.DEBUG或更低级别。
陷阱二:多次调用basicConfig()
logging.basicConfig(level=logging.INFO) # 第一次 logging.basicConfig(level=logging.DEBUG) # 第二次无效
解决:使用logger = logging.getLogger(__name__)并在应用入口一次性配置。
最佳实践:
- 每个模块使用
logger = logging.getLogger(__name__)获取独立logger - 生产环境日志保留
INFO及以上级别,开发环境可开启DEBUG - 日志文件使用滚动机制(RotatingFileHandler)防止无限增长
- 敏感信息(密码、密钥)绝对不要记录在日志中
问答环节:解决你的实际疑问
问:为什么我设置了logger.setLevel(logging.DEBUG),但控制台还是看不到DEBUG信息?
答:检查是否添加了Handler,以及Handler的级别是否高于DEBUG,如果只添加了StreamHandler而没设置其级别,默认是WARNING,需要在Handler上显式调用setLevel(logging.DEBUG)。
问:如何只在特定函数内部临时提升日志级别? 答:可以使用上下文管理器:
import contextlib
@contextlib.contextmanager
def logging_level(level):
old_level = logging.getLogger().level
logging.getLogger().setLevel(level)
try:
yield
finally:
logging.getLogger().setLevel(old_level)
with logging_level(logging.ERROR):
# 这段代码内只输出ERROR及以上
...
问:多线程脚本中日志级别如何处理?
答:logging是线程安全的,可以直接在多线程中使用,但注意日志输出顺序可能交错,建议使用QueueHandler+QueueListener组合实现线程安全的异步日志。
问:如何根据环境(开发/测试/生产)自动设置级别? 答:通过环境变量:
import os
log_level = os.getenv('LOG_LEVEL', 'WARNING').upper()
logging.basicConfig(level=getattr(logging, log_level, logging.WARNING))
通过合理运用日志级别,你的Python脚本将获得专业级的可观测性,好的日志系统能让故障定位时间缩短80%以上,从今天开始,在每一个print()出现的地方,思考它应该属于哪个级别吧!