Skip to content

Logging to an output widget (as opposed to print with context manager) #1936

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
andymcarter opened this issue Jan 26, 2018 · 6 comments
Open

Comments

@andymcarter
Copy link

andymcarter commented Jan 26, 2018

Following discussion with @jasongrout, a rough version of how I accomplished logging to an Output widget.

Benefits of this method are:

  • Avoid repeated calls to with output: and print throughout code
  • Can configure several logging with different handlers; route your logging messages to different Output widgets.

The 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).

@pbugnion
Copy link
Member

pbugnion commented Jan 27, 2018

This is great! There's an issue for centralising the documentation on the output widget (issue #1935 ). Maybe this recipe should go in there?

@jasongrout
Copy link
Member

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!

@jasongrout
Copy link
Member

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

# set an optional formatter
if 'format' in kwargs:
    self.setFormatter(kwargs['format'])

@jasongrout
Copy link
Member

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.

@jasongrout
Copy link
Member

See also jupyterlab/jupyterlab#4319 (comment) for another example.

@sthapa
Copy link

sthapa commented Nov 18, 2018

I've created a pull request #2268 to implement the convenience function, if it looks good, I'll update the docs based on the function.

@vidartf vidartf modified the milestones: Patch release, 8.1 Aug 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants