Today I was reading the wxPython Google group / mailing list and there was someone asking about how to make Python’s logging module write its output to file and to a TextCtrl. It turns out that you need to create a custom logging handler to do it. At first, I tried just using a normal StreamHandler and redirecting stdout via the sys module (sys.stdout) to my text control, but that would only redirect my print statements, not the log messages.
Let’s take a look at what I ended up with:
import logging import logging.config import wx ######################################################################## class CustomConsoleHandler(logging.StreamHandler): """""" #---------------------------------------------------------------------- def __init__(self, textctrl): """""" logging.StreamHandler.__init__(self) self.textctrl = textctrl #---------------------------------------------------------------------- def emit(self, record): """Constructor""" msg = self.format(record) self.textctrl.WriteText(msg + "\n") self.flush() ######################################################################## class MyPanel(wx.Panel): """""" #---------------------------------------------------------------------- def __init__(self, parent): """Constructor""" wx.Panel.__init__(self, parent) self.logger = logging.getLogger("wxApp") self.logger.info("Test from MyPanel __init__") logText = wx.TextCtrl(self, style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL) btn = wx.Button(self, label="Press Me") btn.Bind(wx.EVT_BUTTON, self.onPress) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(logText, 1, wx.EXPAND|wx.ALL, 5) sizer.Add(btn, 0, wx.ALL, 5) self.SetSizer(sizer) txtHandler = CustomConsoleHandler(logText) self.logger.addHandler(txtHandler) #---------------------------------------------------------------------- def onPress(self, event): """ """ self.logger.error("Error Will Robinson!") self.logger.info("Informational message") ######################################################################## class MyFrame(wx.Frame): """""" #---------------------------------------------------------------------- def __init__(self): """Constructor""" wx.Frame.__init__(self, None, title="Logging test") panel = MyPanel(self) self.logger = logging.getLogger("wxApp") self.Show() #---------------------------------------------------------------------- def main(): """ """ dictLogConfig = { "version":1, "handlers":{ "fileHandler":{ "class":"logging.FileHandler", "formatter":"myFormatter", "filename":"test.log" }, "consoleHandler":{ "class":"logging.StreamHandler", "formatter":"myFormatter" } }, "loggers":{ "wxApp":{ "handlers":["fileHandler", "consoleHandler"], "level":"INFO", } }, "formatters":{ "myFormatter":{ "format":"%(asctime)s - %(name)s - %(levelname)s - %(message)s" } } } logging.config.dictConfig(dictLogConfig) logger = logging.getLogger("wxApp") logger.info("This message came from main!") app = wx.App(False) frame = MyFrame() app.MainLoop() if __name__ == "__main__": main()
You will note that I ended up using Python’s logging.config module. The dictConfig method was added in Python 2.7, so if you don’t have that or better, then this code won’t work for you. Basically you set up your logging handler and formatters and what-not inside of dictionary and then pass it to logging.config. If you run this code, you will notice that the first couple of messages go to stdout and the log, but not to the text control. At the end of the panel class’s __init__, we add our custom handler and that’s when redirecting logging messages to the text control begins. You can press the button to see it in action!
You may also want to take a look at some of the references below. They help explain what I’m doing in more detail.
Related Articles
- wxPython: Redirecting stdout and stderr
- Python 101: An Intro to logging
- Python logging – how to log to multiple locations
- Official documentation on the logging modules dict config
- StackOverflow: How to Write Custom Python Logging Handler
That’s really useful thanks ! Never thought of it.
btw why do you do ‘stream = self.stream’ in the emit method ? You are not using ‘stream’ after that…
You might be interested in the same functionality which I provided in a Stack Overflow answer:
http://stackoverflow.com/questions/2819791/redirect-logging-output-using-custom-logging-handler/2821097#2821097
That looks cool! Thanks!