3. 配置日志记录器:三种套路,你用哪种?

配置日志,说白了就是告诉 Python:「日志该怎么写、写到哪里、写多详细」。我见过不少新手,直接 logging.basicConfig 一把梭,跑起来没问题,一到生产就抓瞎。今天我把三个主流配置方式捋一遍,你根据自己的场景选就行。

3.1 basicConfig —— 最省事的入门配置

如果你只是写个脚本、做个原型,basicConfig 是你的好朋友。它一句话就能搞定日志的基本框架。

我习惯在 main 函数开头写,因为 basicConfig 默认只生效一次,之后再调用就没效果了。第一次的配置会覆盖后面的。

import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    filename='app.log',
    filemode='a'
)

logging.info("程序启动")

这里有个坑,你想想看——filename 指定了以后,日志就只往文件写,控制台不输出了。我曾经因为这个问题,排查了半天,以为是日志没写对……后来才发现是控制台被关了。

注意: basicConfig 不支持同时输出到文件和控制台,也不支持滚动日志。生产环境别只用这个。

3.2 字典配置 —— 生产环境的首选

说实话,这才是真正能用的配置方式。用字典配置,你可以精准控制 logger、handler、formatter 的每个细节。

import logging.config

config_dict = {
    'version': 1,
    'formatters': {
        'default': {
            'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        },
        'detailed': {
            'format': '%(asctime)s - %(name)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s'
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'level': 'DEBUG',
            'formatter': 'default',
            'stream': 'ext://sys.stdout'
        },
        'file': {
            'class': 'logging.handlers.RotatingFileHandler',
            'level': 'INFO',
            'formatter': 'detailed',
            'filename': 'app.log',
            'maxBytes': 10485760,
            'backupCount': 5
        }
    },
    'loggers': {
        'mymodule': {
            'level': 'DEBUG',
            'handlers': ['console', 'file'],
            'propagate': False
        }
    },
    'root': {
        'level': 'WARNING',
        'handlers': ['console']
    }
}

logging.config.dictConfig(config_dict)
logging.getLogger('mymodule').info("字典配置生效")

你看,handler 可以是控制台,也可以是滚动文件,还可以自己加网络 handler。formatter 也可以按模块区分。我个人习惯把 Root logger 只开 WARNING,各个业务模块独立的 logger 再开 INFO 或 DEBUG,这样不会被噪音淹没。

3.3 文件配置 —— 团队协作的利器

当项目大了,配置写成 Python 字典放代码里不方便修改。这时候用 fileConfig 就很香——把配置塞进 .ini 或 .conf 文件,运维同学直接改文件,不用动代码。

配置文件示例:logging.ini

[loggers]
keys=root,mymodule

[handlers]
keys=consoleHandler,fileHandler

[formatters]
keys=defaultFormatter,detailedFormatter

[logger_root]
level=WARNING
handlers=consoleHandler

[logger_mymodule]
level=DEBUG
handlers=consoleHandler,fileHandler
qualname=mymodule
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=defaultFormatter
args=(sys.stdout,)

[handler_fileHandler]
class=handlers.RotatingFileHandler
level=INFO
formatter=detailedFormatter
args=('app.log','a',10485760,5)

[formatter_defaultFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s

[formatter_detailedFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s

Python 代码里一行搞定:

import logging.config
logging.config.fileConfig('logging.ini')
logging.getLogger('mymodule').info("文件配置加载成功")

踩坑记录: fileConfig 默认不会加载 logging.handlers 里的类,如果你用 RotatingFileHandler,得在文件头的 [handler_fileHandler] 里把 class 写全:class=logging.handlers.RotatingFileHandler。

3.4 三种配置方式对比

配置方式 适用场景 灵活性 维护成本 我常用的理由
basicConfig 脚本、小工具、快速验证 低 低 写 Demo 时省心,但别上生产
字典配置 中大型项目、需要动态控制 高 中 可以按模块定制,无需额外文件
文件配置 团队项目、需要运维调整 中 低(文件维护) 分离配置,改日志不用改代码

3.5 一张图理清配置关系

下面这张图展示了三种配置方式与核心组件(Logger、Handler、Formatter)之间的关系——你选择一种方式,最终都是把这些组件组装起来。我画图时习惯把「文件配置」单独拎出来,因为它多了一层配置文件读取。

三种日志配置方式的关系图 basicConfig 字典配置 文件配置 logging.ini Logger + Handler + Formatter 日志输出(文件/控制台/网络) 示 basicConfig 和字典配置直接在代码中定义组件 文件配置通过配置文件间接定义组件

我的建议: 开发阶段用字典配置,灵活调试;上生产后如果可以,改成文件配置,方便运维调整。如果你一个人写小项目,basicConfig 也不是不行,但记得别用它来写循环日志——文件会一直膨胀,直到磁盘爆掉。

3.6 实战经验:什么时候选哪种?

  • 脚本/工具类 → basicConfig + 控制台输出,省事
  • 小网站/微服务 → 字典配置,handler 分控制台和文件,方便调试和回查
  • 大型微服务/分布式 → 文件配置 + 统一格式标准,团队每个人拉一份配置就能跑

三种配置方式没有的优劣,核心是理解配置的本质——告诉 logging 模块:什么日志该输出、用什么格式、写到哪儿。你掌握这三种,以后看任何框架的日志配置都不会懵。