Following discussion with @jasongrout, a rough version of how I accomplished logging to an Output widget.
Benefits of this method are:
with output: and print throughout codeThe particular configuration I used here means that logging messages always appear on top of the Output widget, and force newer messages to the bottom.
Credit: https://stackoverflow.com/questions/3290292/read-from-a-log-file-as-its-being-written-using-python for the code to subclass the handler and extend the emit() method.
import logging
from colorama import Fore
import ipywidgets as ipyw
out = ipyw.Output(layout=ipyw.Layout(width='600px', height='160px', border='solid'))
class log_viewer(logging.Handler):
""" Class to redistribute python logging data """
# have a class member to store the existing logger
logger_instance = logging.getLogger("__name__")
def __init__(self, *args, **kwargs):
# Initialize the Handler
logging.Handler.__init__(self, *args)
# optional take format
# setFormatter function is derived from logging.Handler
for key, value in kwargs.items():
if "{}".format(key) == "format":
self.setFormatter(value)
# make the logger send data to this class
self.logger_instance.addHandler(self)
def emit(self, record):
""" Overload of logging.Handler method """
record = self.format(record)
out.outputs = ({'name': 'stdout', 'output_type': 'stream', 'text': (Fore.BLACK + (record + '\n'))},) + out.outputs
main_logger = logging.getLogger(__name__)
main_logger.addHandler(log_viewer())
main_logger.setLevel(20) # log at info level.
You can log to the widget from anywhere (provided you have imported main_logger) by calling `main_logger.info('msg text'), and possibly alter the color (hence colorama).
This is great! There's an issue for centralising the documentation on the output widget (issue #1935 ). Maybe this recipe should go in there?
Would it also make sense to add a convenience method to the Output widget to give you back one of these log viewer instances specific to the widget?
Thanks for posting this!
This block:
# optional take format
# setFormatter function is derived from logging.Handler
for key, value in kwargs.items():
if "{}".format(key) == "format":
self.setFormatter(value)
is probably more natural as
```python
if 'format' in kwargs:
self.setFormatter(kwargs['format'])
````
Setting to good first issue - we can both add it to the output docs (see #1935) and add a convenience method to return a logger.
See also https://github.com/jupyterlab/jupyterlab/issues/4319#issuecomment-397840503 for another example.
I've created a pull request https://github.com/jupyter-widgets/ipywidgets/pull/2268 to implement the convenience function, if it looks good, I'll update the docs based on the function.