Python项目全局日志配置问题及自定义日志模块无输出排查
解决Python全局日志配置及无输出问题
先帮你搞定当前日志没输出的问题,再给你实现一次性全局配置的方案,彻底摆脱重复配置的麻烦。
为什么你的日志没输出?
你代码里有个很容易踩的logging坑:只设置了Handler的级别,却没设置Logger本身的级别。
Python日志是分层过滤的:日志消息得先过Logger自己的级别关,才能传到Handler。默认情况下,Logger的级别是WARNING,所以哪怕你在Dev环境把Handler级别设为DEBUG,logger.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




