From 82b350d569f5467a9c8b7931b658a9b13f168c9f Mon Sep 17 00:00:00 2001 From: Jonathan Slenders Date: Tue, 22 Mar 2022 23:52:39 +0100 Subject: [PATCH] Add PromptToolkitLogHandler. --- examples/logging/colored_logs.py | 23 ++++++++ src/prompt_toolkit/shortcuts/logging.py | 74 +++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 examples/logging/colored_logs.py create mode 100644 src/prompt_toolkit/shortcuts/logging.py diff --git a/examples/logging/colored_logs.py b/examples/logging/colored_logs.py new file mode 100644 index 000000000..dd2ee1b91 --- /dev/null +++ b/examples/logging/colored_logs.py @@ -0,0 +1,23 @@ +""" +Example of a custom logging handler that uses prompt_toolkit to render nicely +colored logs. +""" +import logging +import time + +from prompt_toolkit.shortcuts.logging import PromptToolkitLogHandler + + +def main() -> None: + logging.basicConfig(level=logging.DEBUG, handlers=[PromptToolkitLogHandler()]) + + for i in range(10): + logging.debug(f"test debug {i}") + logging.info(f"test info {i}") + logging.warning(f"test warning {i}") + logging.error(f"test error {i}") + time.sleep(0.5) + + +if __name__ == "__main__": + main() diff --git a/src/prompt_toolkit/shortcuts/logging.py b/src/prompt_toolkit/shortcuts/logging.py new file mode 100644 index 000000000..a08b612f3 --- /dev/null +++ b/src/prompt_toolkit/shortcuts/logging.py @@ -0,0 +1,74 @@ +""" +Utility for printing colored logs. +""" +import datetime +import logging +from typing import Callable, Optional, TextIO, Union + +from prompt_toolkit.formatted_text import HTML +from prompt_toolkit.layout.containers import AnyContainer, VSplit, Window +from prompt_toolkit.layout.controls import FormattedTextControl +from prompt_toolkit.styles import Style + +from .utils import print_container, print_formatted_text + +__all__ = ["PromptToolkitLogHandler"] + + +def default_template(record: logging.LogRecord) -> HTML: + now = datetime.datetime.now() + return HTML( + f"<{record.levelname}>" + " {level:10} {msg}" + f"" + ).format( + now=now.strftime("%m/%d/%y %H:%M:%S"), + level=record.levelname, + msg=record.msg, + ) + + +def default_style() -> Style: + return Style.from_dict( + { + "time": "ansimagenta", + "warning": "#aa8888", + "warning level": "reverse", + "debug": "#888888", + "error": "ansired", + "error level": "reverse", + "info": "#00aa00", + } + ) + + +class PromptToolkitLogHandler(logging.StreamHandler): + """ + Print all logging messages using the given template. + + Usage:: + + import logging + logging.basicConfig( + level=logging.DEBUG, + handlers=[PromptToolkitHandler()] + ) + """ + # NOTE: Previously, I tried using `print_container` instead of + # `print_formatted_text`. This doesn't work unfortunately, because + # we'd try to create/access an event loop, which causes asyncio to + # log new messages. We can't log anything while printing a log + # message. + + def __init__( + self, + template: Callable[[logging.LogRecord], HTML] = default_template, + style: Style = default_style(), + stream: Optional[TextIO] = None, + ) -> None: + self.template = template + self.style = style + super().__init__(stream=stream) + + def emit(self, record) -> None: + print_formatted_text(self.template(record), style=self.style, file=self.stream)