简体   繁体   English

Python:结合日志记录和wx,以便将日志记录流重定向到stdout / stderr框架

[英]Python: combine logging and wx so that logging stream is redirectet to stdout/stderr frame

Here's the thing: 这是事情:

I'm trying to combine the logging module with wx.App()'s redirect feature. 我正在尝试将日志记录模块与wx.App()的重定向功能结合起来。 My intention is to log to a file AND to stderr. 我的目的是登录文件AND stderr。 But I want stderr/stdout redirected to a separate frame as is the feature of wx.App. 但我希望stderr / stdout重定向到一个单独的框架,这是wx.App的功能。

My test code: 我的测试代码:

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()

Expected behavior is: 预期的行为是:
- a file is created named test.log - 创建名为test.log的文件
- the file contains logging messages with level DEBUG and INFO/ERROR/WARNING/CRITICAL - 该文件包含级别为DEBUG和INFO / ERROR / WARNING / CRITICAL的日志消息
- messages from type INFO and ERROR/WARNING/CRITICAL are either shown on the console or in a separate frame, depending on where they are created - INFO和ERROR / WARNING / CRITICAL类型的消息显示在控制台上或单独的框架中,具体取决于它们的创建位置
- logger messages that are not inside MyApp or MyFrame are displayed at the console - 不在MyApp或MyFrame内的记录器消息显示在控制台上
- logger messages from inside MyApp or MyFrame are shown in a separate frame - 来自MyApp或MyFrame内部的记录器消息显示在单独的框架中

Actual behavior is: 实际行为是:
- The file is created and contains: - 文件已创建并包含:

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.

- Console output is: - 控制台输出是:

main    INFO    Logger configured.
main.MyFrame    INFO    MyFrame.__init__() called.
main.MyApp      INFO    MyApp.OnInit() called.
main    INFO    Exiting program.

- No separate frame is opened, although the lines - 没有单独的框架打开,虽然线条

main.MyFrame    INFO    MyFrame.__init__() called.
main.MyApp      INFO    MyApp.OnInit() called.

shouldget displayed within a frame and not on the console. 应显示在框架内,而不是在控制台上。

It seems to me that wx.App can't redirect stderr to a frame as soon as a logger instance uses stderr as output. 在我看来,只要logger实例使用stderr作为输出,wx.App就无法将stderr重定向到一个帧。 wxPythons Docs claim the wanted behavior though, see here. wxPythons Docs虽然声称想要的行为,但请看这里。

Any ideas? 有任何想法吗?

Uwe 乌韦

When wx.App says it will redirect stdout/stderr to a popup window, what it means really is that it will redirect sys.stdout and sys.stderr, so if you directly write to sys.stdout or sys.stderr it will be redirected to a popup window eg try this 当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")

Problem here is that if wxApp is created after creating streamhandler, streamhandler is pointing to old(original) sys.stderr and sys.stdout not to the new ones which wxApp has set, so a simpler solution is to create wx.App before creating streap handler eg in code move app = MyApp(redirect = True) before logging initialization code. 这里的问题是,如果在创建streamhandler之后创建了wxApp,则streamhandler指向旧(原始)sys.stderr和sys.stdout而不是wxApp设置的新的,所以更简单的解决方案是在创建streap之前创建wx.App处理程序,例如在记录初始化代码之前移动app = MyApp(redirect = True)

Alternatively create a custom logging handler and write data to sys.stdout and sys.stderr or better create you own window and add data there. 或者,创建一个自定义日志记录处理程序并将数据写入sys.stdout和sys.stderr,或者更好地创建自己的窗口并在那里添加数据。 eg try this 试试这个

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)

The way I do this, which I think is more elegant, is to create a custom logging Handler subclass that posts its messages to a specific logging frame. 我这样做的方式,我认为更优雅,是创建一个自定义日志记录Handler子类,将其消息发布到特定的日志记录框架。

This makes it easier to turn GUI logging on/off at runtime. 这样可以更轻松地在运行时打开/关闭GUI日志记录。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM