如何在Python中调用通过logging.config.dictConfig创建的处理程序的方法?

0 投票
2 回答
34 浏览
提问于 2025-04-14 18:37

我正在为我正在开发的应用程序设置一种特定的日志记录方案。为了实现这个目标,我希望能够根据自定义条件随意旋转日志文件。这样一来,内置的基于时间(使用 TimedRotatingFileHandler)或日志大小(使用 RotatingFileHandler)的旋转选项就不够用了。不过,这两个处理器都有一个叫 doRollover 的方法,我可以用它来实现我想要的功能。问题在于,我使用 logging.config.dictConfig 来设置我的日志配置,如下所示:

config = {
    "version": 1,
    "formatters": {
        "default_formatter": {
            "format": logging.BASIC_FORMAT,
        },
    },
    "handlers": {
        "file": {
            "class": "logging.handlers.RotatingFileHandler",
            "level": "NOTSET",
            "formatter": "default_formatter",
            "backupCount": 5,
            "filename": "log.txt",
            "encoding": "utf8",
        },
    },
    "loggers": {
        "": {
            "level": "NOTSET",
            "handlers": ["file"],
        },
    },
}
logging.config.dictConfig(config)

这样一来,logging.config.dictConfig 就负责创建 RotatingFileHandler 的实例,所以我根本没有机会保留对这个类实例(也就是对象)的引用。因此,我不太清楚该如何调用这个对象的方法。

我该如何在通过 logging.config.dictConfig 创建的处理器对象上调用一个方法(在我的情况下是 doRollover)?或者,如果这不可能的话,我该如何手动提供一个我通过直接调用构造函数实例化的处理器对象,以满足这个配置?

2 个回答

1

决定何时进行“轮换”的逻辑是在 BaseRotatingHandler.emit 这个地方写的:

 def emit(self, record):
    try:
        if self.shouldRollover(record):
            self.doRollover()
        logging.FileHandler.emit(self, record)
    except Exception:
        self.handleError(record)

你可以定义一个自定义的子类,基于 BaseRotatingHandler 或者 RotatingFileHandler,具体取决于你的需求。这个子类的 __init__ 方法可以接受配置字典中提供的额外参数,而 shouldRollover 方法则可以利用这些参数来实现你的轮换逻辑。

举个例子,

class NewbytesHandler(RotatingFileHandler):
    def __init__(self, *, foo, **kwargs):
        super().__init__(**kwargs)
        self.foo = foo

    def shouldRollover(self, record):
        if <some logic involving self.foo>:
            return True
        else:
            return False

然后在你的配置中,

config = {
    ...
    "handlers": {
        "file": {
            "class": "NewbytesHandler",
            "level": "NOTSET",
            "formatter": "default_formatter",
            "foo": 7,
            "filename": "log.txt",
            "encoding": "utf8",
        },
    },
    ...
}
logging.config.dictConfig(config)
0

这可以通过查看安装了处理器的记录器的 handlers 属性来实现。在上面的代码片段中,根记录器被分配了这个处理器,所以我们可以这样获取它:

file_handler_name = "file"
root_logger = logging.getLogger()

file_handler = next(
    handler for handler in root_logger.handlers if handler.name == file_handler_name
)
file_handler.doRollover()  # or whatever other method you want to call

之所以能这样做,是因为在问题的例子中,处理器的名字是“file”,在这里我们实际上是遍历 root_logger.handlers,直到找到一个 name 属性等于“file”的处理器。通过这个遍历返回的处理器就是由 logging.config.dictConfig 实例化的对象。

如果你不喜欢使用 next,用 for 循环也可以实现,代码如下:

file_handler_name = "file"
root_logger = logging.getLogger()

for handler in root_logger.handlers:
    if handler.name == "file":
        file_handler = handler
        break

file_handler.doRollover()  # or whatever other method you want to call

撰写回答