You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Python项目全局日志配置问题及自定义日志模块无输出排查

解决Python全局日志配置及无输出问题

先帮你搞定当前日志没输出的问题,再给你实现一次性全局配置的方案,彻底摆脱重复配置的麻烦。

为什么你的日志没输出?

你代码里有个很容易踩的logging坑:只设置了Handler的级别,却没设置Logger本身的级别

Python日志是分层过滤的:日志消息得先过Logger自己的级别关,才能传到Handler。默认情况下,Logger的级别是WARNING,所以哪怕你在Dev环境把Handler级别设为DEBUGlogger.info()这类低于WARNING的日志,会被Logger直接过滤掉,根本到不了Handler环节。

另外你的导入路径也有笔误:日志文件是log.py,但你写的是from log.logging import get_logger,正确导入应该是from log import get_logger

修复代码+实现全局配置

要实现全局生效的日志配置,不需要每次调用get_logger(),咱们可以在程序启动时一次性配置好Root Logger,之后所有模块直接拿logger就能用,自动继承配置。

方案1:配置Root Logger(推荐)

把你的log.py改成初始化日志的函数,不再返回logger实例:

# log.py
import os
import logging
from dotenv import load_dotenv
from utils import get_timestamp

def setup_logging():
    load_dotenv(".env")
    env = os.getenv('ENV_DEV', "development")
    
    # 获取全局的root logger
    root_logger = logging.getLogger()
    # 清空默认handler,避免重复输出
    root_logger.handlers.clear()
    # 设置root logger的最低级别,确保所有符合条件的日志都能进入处理流程
    root_logger.setLevel(logging.DEBUG)
    
    log_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    
    if env == "prod":
        # 生产环境:仅输出WARNING及以上级别到文件
        handler = logging.FileHandler(f'log/{get_timestamp("%Y%m%d")}_app.log')
        handler.setLevel(logging.WARNING)
        handler.setFormatter(log_format)
        root_logger.addHandler(handler)
    else:
        # 开发环境:输出DEBUG及以上级别到控制台
        handler = logging.StreamHandler()
        handler.setLevel(logging.DEBUG)
        handler.setFormatter(log_format)
        root_logger.addHandler(handler)

然后在主程序入口(比如main.py)里,只需要调用一次这个初始化函数:

# main.py
from log import setup_logging
import logging

# 程序启动时初始化一次即可
setup_logging()

# 直接获取logger就能用,无需重复配置
logger = logging.getLogger(__name__)
logger.debug("这是DEBUG级别的日志")
logger.info("这是INFO级别的日志")
logger.warning("这是WARNING级别的日志")

其他模块里也一样,直接拿logger就生效:

# other_module.py
import logging

logger = logging.getLogger(__name__)
logger.info("其他模块的INFO日志,自动继承全局配置")

方案2:单例Logger(不想改Root Logger时用)

如果你不想修改Root Logger,也可以把get_logger()改成单例模式,确保每次调用返回同一个配置好的实例:

# log.py
import os
import logging
from dotenv import load_dotenv
from utils import get_timestamp

# 单例实例,确保全局唯一
_global_logger = None

def get_logger():
    global _global_logger
    if _global_logger is not None:
        return _global_logger
    
    load_dotenv(".env")
    env = os.getenv('ENV_DEV', "development")
    logger = logging.getLogger("my_app_global_logger")
    logger.setLevel(logging.DEBUG)  # 必须设置Logger级别!
    logger.handlers.clear()  # 避免重复添加handler导致日志重复输出
    
    log_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    
    if env == "prod":
        handler = logging.FileHandler(f'log/{get_timestamp("%Y%m%d")}_app.log')
        handler.setLevel(logging.WARNING)
        handler.setFormatter(log_format)
        logger.addHandler(handler)
    else:
        handler = logging.StreamHandler()
        handler.setLevel(logging.DEBUG)
        handler.setFormatter(log_format)
        logger.addHandler(handler)
    
    _global_logger = logger
    return logger

这样不管在哪个模块调用get_logger(),拿到的都是同一个配置好的logger,不用重复写配置代码。

关键知识点总结

  • Logger和Handler级别都要设置:Logger决定哪些日志会被处理,Handler决定哪些日志会被输出,两者缺一不可。
  • Root Logger是全局父节点:所有自定义Logger都是它的子节点,会自动继承它的配置(除非单独设置自己的级别或handler)。
  • 避免重复添加Handler:多次调用配置函数会重复添加Handler,导致日志重复输出,所以要先清空handlers或者用单例模式。

内容的提问来源于stack exchange,提问作者Ragnar

火山引擎 最新活动