You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pythonic方式实现返回自定义日志对象,替代劫持__new__的不规范用法

问题核心根源

你当前的实现本质是把CustomLogger类伪装成了一个无参数的工厂函数,通过劫持__new__方法绕过了Python类的实例化流程:

  • __new__的设计作用是创建并返回当前CustomLogger类的实例,只有返回值是CustomLogger实例时,才会自动调用__init__方法完成属性初始化
  • 你直接返回了外部的logging.Logger实例,自然无法通过__init__扩展参数,也不符合Python面向对象的设计规范
  • 额外还有隐藏问题:每次调用CustomLogger()都会重复给root logger添加handler,会导致同一条日志多次输出的bug
正确实现思路及逻辑

1. 用工厂函数替代伪装类

你的需求本质是生成预配置的Logger实例,属于典型的工厂模式场景,完全不需要套类外壳,直接定义独立的工厂函数即可:

  • 所有原本写在__new__里的配置逻辑都可以直接迁移到函数中
  • 要扩展参数直接在函数定义时添加即可,比如支持传入日志级别、是否添加文件handler、自定义日志级别列表等,没有任何限制
  • 可以内置重复配置检测,避免重复添加handler、重复注册日志级别

2. 拆分通用工具逻辑

原本类中的addLoggingLevel、CustomFormatter都是通用的无状态工具,可以独立拆分:

  • addLoggingLevel可以定义为独立函数,也可以加@staticmethod装饰后放到工具类中
  • CustomFormatter作为独立的Formatter子类存在,和工厂逻辑解耦,方便后续单独修改格式规则

3. 优化调用逻辑

你现有的模块级日志判断逻辑可以直接封装到工厂函数中,外部调用无需重复写if __name__ == "__main__"的判断,减少冗余代码。

极简示例参考
import logging
from datetime import datetime

# 独立的自定义日志级别注册函数
def addLoggingLevel(levelName:str, levelNumber:int, methodName:str=None, exitAfter:bool=False,force:bool=False):
    # 你原有的实现不变,此处省略完整代码
    pass

# 独立的自定义格式化类
class CustomFormatter(logging.Formatter):
    # 你原有的实现不变,此处省略完整代码
    pass

# 日志工厂函数,支持自定义参数扩展
def get_custom_logger(name: str = None, default_level: int = logging.WARNING, add_console_handler: bool = True):
    # 注册自定义日志级别,全局只执行一次
    if not hasattr(logging, 'TRACE'):
        addLoggingLevel(levelName='VERBOSE',levelNumber=15)
        addLoggingLevel(levelName='FATAL',levelNumber=15,exitAfter=True,force=True)
        addLoggingLevel(levelName='TRACE',levelNumber=9)
    
    logger = logging.getLogger(name)
    logger.setLevel(default_level)

    # 只给root logger添加控制台handler,避免重复添加导致日志重复输出
    if add_console_handler and name is None and not any(isinstance(h, logging.StreamHandler) for h in logger.handlers):
        consoleHandler = logging.StreamHandler()
        consoleHandler.setFormatter(CustomFormatter())
        logger.addHandler(consoleHandler)
    
    return logger

调用方式更简洁:

class Example():
    def __init__(self):
        # 主进程传name=None获取配置好的root logger,其他模块传__name__获取对应模块的logger
        nlog = get_custom_logger(name=__name__ if __name__ != "__main__" else None)
        nlog.setLevel(20)

内容的提问来源于stack exchange,提问作者Nick W.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 11:24:01