这就是事情:
我正在尝试将日志记录模块与wx.App()的重定向功能结合起来。我的目的是登录文件 AND 到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.
应显示在框架内,而不是显示在控制台上。
在我看来,只要logger实例使用stderr作为输出,wx.App就无法将stderr重定向到帧。 wxPythons Docs声称想要的行为,see here.
有什么想法吗?
乌韦
答案 0 :(得分: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")
问题在于,如果在创建streamhandler之后创建了wxApp,则streamhandler指向旧的(原始)sys.stderr和sys.stdout而不是wxApp设置的新的,所以更简单的解决方案是创建wx.App在创建streap处理程序之前在代码中,在记录初始化代码之前移动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)
答案 1 :(得分:1)
我这样做的方式,我认为更优雅,就是创建一个自定义日志记录Handler子类,将其消息发布到特定的日志记录框架。
这使得在运行时更容易打开/关闭GUI日志。