开发者

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. My intention is to log to a file AND to stderr. But I want stderr/stdout redirected to a separate frame as is the feature of 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

- the file contains logging messages with level DEBUG and 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

- logger messages that are not inside MyApp or MyFrame are displayed at the console

- logger messages from inside MyApp or MyFrame are shown in a separate frame

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 confi开发者_运维百科gured.
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. wxPythons Docs claim the wanted behavior though, see here.

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 e.g. try this

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 e.g. in code move app = MyApp(redirect = True) before logging initialization code.

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. e.g. 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.

This makes it easier to turn GUI logging on/off at runtime.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜