Python:将日志记录和wx组合,使日志流重定向到stdout / stderr帧

3

事情是这样的:

我正在尝试将日志模块与wx.App()的重定向功能相结合。我的意图是同时记录到文件stderr。但我希望stderr/stdout被重定向到一个单独的框架,就像wx.App的特性一样。

我的测试代码:

import logging
import wx

class MyFrame(wx.Frame):
    def __init__(self):
        self.logger = logging.getLogger("main.MyFrame")
        wx.Frame.__init__(self, parent = None, id = wx.ID_ANY, title = "MyFrame")
        self.logger.debug("MyFrame.__init__() called.")

    def OnExit(self):
        self.logger.debug("MyFrame.OnExit() called.")

class MyApp(wx.App):
    def __init__(self, redirect):
        self.logger = logging.getLogger("main.MyApp")
        wx.App.__init__(self, redirect = redirect)
        self.logger.debug("MyApp.__init__() called.")

    def OnInit(self):
        self.frame = MyFrame()
        self.frame.Show()
        self.SetTopWindow(self.frame)
        self.logger.debug("MyApp.OnInit() called.")
        return True

    def OnExit(self):
        self.logger.debug("MyApp.OnExit() called.")

def main():
    logger_formatter = logging.Formatter("%(name)s\t%(levelname)s\t%(message)s")
    logger_stream_handler = logging.StreamHandler()
    logger_stream_handler.setLevel(logging.INFO)
    logger_stream_handler.setFormatter(logger_formatter)
    logger_file_handler = logging.FileHandler("test.log", mode = "w")
    logger_file_handler.setLevel(logging.DEBUG)
    logger_file_handler.setFormatter(logger_formatter)
    logger = logging.getLogger("main")
    logger.setLevel(logging.DEBUG)
    logger.addHandler(logger_stream_handler)
    logger.addHandler(logger_file_handler)
    logger.info("Logger configured.")

    app = MyApp(redirect = True)
    logger.debug("Created instance of MyApp. Calling MainLoop().")
    app.MainLoop()
    logger.debug("MainLoop() ended.")
    logger.info("Exiting program.")

    return 0

if (__name__ == "__main__"):
    main()

期望的行为是:
- 创建一个名为test.log的文件
- 文件包含级别为DEBUG和INFO/ERROR/WARNING/CRITICAL的日志信息
- 来自类型INFO和ERROR/WARNING/CRITICAL的消息要么显示在控制台上,要么显示在单独的框架中,具体取决于它们被创建的位置
- 不在MyApp或MyFrame内部的记录器消息将在控制台上显示
- 在MyApp或MyFrame内部的记录器消息将显示在单独的框架中

实际的行为是:
- 文件已创建并包含:

main    INFO    Logger configured.
main.MyFrame    DEBUG   MyFrame.__init__() called.
main.MyFrame    INFO    MyFrame.__init__() called.
main.MyApp  DEBUG   MyApp.OnInit() called.
main.MyApp  INFO    MyApp.OnInit() called.
main.MyApp  DEBUG   MyApp.__init__() called.
main    DEBUG   Created instance of MyApp. Calling MainLoop().
main.MyApp  DEBUG   MyApp.OnExit() called.
main    DEBUG   MainLoop() ended.
main    INFO    Exiting program.

- 控制台输出为:
main    INFO    Logger configured.
main.MyFrame    INFO    MyFrame.__init__() called.
main.MyApp      INFO    MyApp.OnInit() called.
main    INFO    Exiting program.

- 没有单独的框架被打开,尽管这些行中有标签。
main.MyFrame    INFO    MyFrame.__init__() called.
main.MyApp      INFO    MyApp.OnInit() called.

应该在框架内显示,而不是在控制台上显示。

我觉得当记录器实例使用stderr作为输出时,wx.App不能将stderr重定向到框架。然而,wxPython文档声称想要的行为是这样的,请参见此处。

有什么想法吗?

Uwe

2个回答

1
当wx.App说它将重定向stdout/stderr到一个弹出窗口时,它实际上是指它将重定向sys.stdout和sys.stderr,因此如果您直接写入sys.stdout或sys.stderr,它将被重定向到一个弹出窗口,例如尝试这个。
print "this will go to wx msg frame"
sys.stdout.write("yes it goes")
sys.stderr.write("... and this one too")

问题在于如果在创建流处理程序之后创建wxApp,则流处理程序将指向旧的(原始的)sys.stderr和sys.stdout,而不是wxApp设置的新值,因此一个更简单的解决方案是在创建stream handler之前创建wx.App,例如在代码中将app = MyApp(redirect = True)移动到日志初始化代码之前。
另一种方法是创建一个自定义的日志处理程序,将数据写入sys.stdout和sys.stderr,或者更好地创建自己的窗口并将数据添加到其中。例如,请尝试这样做。
class LogginRedirectHandler(logging.Handler):
        def __init__(self,):
            # run the regular Handler __init__
            logging.Handler.__init__(self)

        def emit(self, record):
            sys.stdout.write(record.message)

loggingRedirectHandler = LogginRedirectHandler()
logger_file_handler.setLevel(logging.DEBUG)
logger.addHandler(loggingRedirectHandler)

@Uwe,我已经更新了答案,你需要在Streamhandler创建之前移动wx.App的创建。 - Anurag Uniyal
很不幸,这也行不通,因为在MyApp或MyFrame中创建的所有日志记录器实例都无法连接到实际的日志记录器"main"。 "main"的流被重定向到wx的重定向框架,但是"main.MyApp"和"main.MyFrame"日志记录器并不是"main"的子级,因为在创建"main.MyApp"和"main.MyFrame"时,"main"并不存在。由于它们没有设置格式化程序和处理程序,它们不知道在MyApp和MyFrame内部的调试消息应该放在哪里。 - Uwe
你唯一的选择就是编写自定义处理程序。 - Anurag Uniyal
@Uwe,但这与wx重定向有什么关系? - Anurag Uniyal
在这种情况下,您可以使用良好的编辑器打开日志文件或“tail -f xxx.log”,但无论如何,您的问题应该说明,我回答了问题而不是您隐藏的目标。 - Anurag Uniyal
显示剩余3条评论

1
我认为更优雅的做法是创建一个自定义日志处理程序子类,将其消息发布到特定的日志框架中。
这样可以更轻松地在运行时打开/关闭GUI日志记录。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接