Never thought to try this before, but yes you can do this using IPython.display.display
and a custom logging.Handler
which calls it:
import logging
from IPython.display import display, HTML
class DisplayHandler(logging.Handler):
def emit(self, record):
message = self.format(record)
display(message)
This could be used to display anything the notebook can display, including HTML, Markdown, images???, audio???? (if you want your notebook to read your logs to you).
I combined this with a custom logging.Formatter
that outputs an HTML
object for passing to display()
. It's not pretty but you can take the basic concept and improve it, or combine both classes into a single NotebookHTMLHandler
class or something like that:
class HTMLFormatter(logging.Formatter):
level_colors = {
logging.DEBUG: 'lightblue',
logging.INFO: 'dodgerblue',
logging.WARNING: 'goldenrod',
logging.ERROR: 'crimson',
logging.CRITICAL: 'firebrick'
}
def __init__(self):
super().__init__(
'<span style="font-weight: bold; color: green">{asctime}</span> '
'[<span style="font-weight: bold; color: {levelcolor}">{levelname}</span>] '
'{message}',
style='{'
)
def format(self, record):
record.levelcolor = self.level_colors.get(record.levelno, 'black')
return HTML(super().format(record))
Put all together you can use it like this:
log = logging.getLogger()
handler = DisplayHandler()
handler.setFormatter(HTMLFormatter())
log.addHandler(handler)
log.setLevel(logging.DEBUG)
Here's an example of how it looks:
For the HTML version you could tidy this up further, if the HTML grows complicated enough, by combining with an HTML templating engine like Jinja, or add JavaScript/Widgets to make log messages expandable to show more of the log record context, per your idea. A full solution I think would be beyond the scope of this answer.