Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
logging.py306 linesDownload Raw Back to rich
1from __future__ import annotations2 3import logging4import os5from datetime import datetime6from logging import Handler, LogRecord7from types import ModuleType8from typing import TYPE_CHECKING, ClassVar, Iterable, List, Optional, Type, Union9 10if TYPE_CHECKING:11    from ._log_render import FormatTimeCallable12    from .console import Console, ConsoleRenderable13    from .highlighter import Highlighter14    from .traceback import Traceback15 16from rich._null_file import NullFile17 18from . import get_console19from ._log_render import LogRender20from .highlighter import ReprHighlighter21from .text import Text22 23 24class RichHandler(Handler):25    """A logging handler that renders output with Rich. The time / level / message and file are displayed in columns.26    The level is color coded, and the message is syntax highlighted.27 28    Note:29        Be careful when enabling console markup in log messages if you have configured logging for libraries not30        under your control. If a dependency writes messages containing square brackets, it may not produce the intended output.31 32    Args:33        level (Union[int, str], optional): Log level. Defaults to logging.NOTSET.34        console (:class:`~rich.console.Console`, optional): Optional console instance to write logs.35            Default will use a global console instance writing to stdout.36        show_time (bool, optional): Show a column for the time. Defaults to True.37        omit_repeated_times (bool, optional): Omit repetition of the same time. Defaults to True.38        show_level (bool, optional): Show a column for the level. Defaults to True.39        show_path (bool, optional): Show the path to the original log call. Defaults to True.40        enable_link_path (bool, optional): Enable terminal link of path column to file. Defaults to True.41        highlighter (Highlighter, optional): Highlighter to style log messages, or None to use ReprHighlighter. Defaults to None.42        markup (bool, optional): Enable console markup in log messages. Defaults to False.43        rich_tracebacks (bool, optional): Enable rich tracebacks with syntax highlighting and formatting. Defaults to False.44        tracebacks_width (Optional[int], optional): Number of characters used to render tracebacks, or None for full width. Defaults to None.45        tracebacks_code_width (int, optional): Number of code characters used to render tracebacks, or None for full width. Defaults to 88.46        tracebacks_extra_lines (int, optional): Additional lines of code to render tracebacks, or None for full width. Defaults to None.47        tracebacks_theme (str, optional): Override pygments theme used in traceback.48        tracebacks_word_wrap (bool, optional): Enable word wrapping of long tracebacks lines. Defaults to True.49        tracebacks_show_locals (bool, optional): Enable display of locals in tracebacks. Defaults to False.50        tracebacks_suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.51        tracebacks_max_frames (int, optional): Optional maximum number of frames returned by traceback.52        locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.53            Defaults to 10.54        locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.55        log_time_format (Union[str, TimeFormatterCallable], optional): If ``log_time`` is enabled, either string for strftime or callable that formats the time. Defaults to "[%x %X] ".56        keywords (List[str], optional): List of words to highlight instead of ``RichHandler.KEYWORDS``.57    """58 59    KEYWORDS: ClassVar[Optional[List[str]]] = [60        "GET",61        "POST",62        "HEAD",63        "PUT",64        "DELETE",65        "OPTIONS",66        "TRACE",67        "PATCH",68    ]69    HIGHLIGHTER_CLASS: ClassVar[Type[Highlighter]] = ReprHighlighter70 71    def __init__(72        self,73        level: Union[int, str] = logging.NOTSET,74        console: Optional[Console] = None,75        *,76        show_time: bool = True,77        omit_repeated_times: bool = True,78        show_level: bool = True,79        show_path: bool = True,80        enable_link_path: bool = True,81        highlighter: Optional[Highlighter] = None,82        markup: bool = False,83        rich_tracebacks: bool = False,84        tracebacks_width: Optional[int] = None,85        tracebacks_code_width: Optional[int] = 88,86        tracebacks_extra_lines: int = 3,87        tracebacks_theme: Optional[str] = None,88        tracebacks_word_wrap: bool = True,89        tracebacks_show_locals: bool = False,90        tracebacks_suppress: Iterable[Union[str, ModuleType]] = (),91        tracebacks_max_frames: int = 100,92        locals_max_length: int = 10,93        locals_max_string: int = 80,94        log_time_format: Union[str, FormatTimeCallable] = "[%x %X]",95        keywords: Optional[List[str]] = None,96    ) -> None:97        super().__init__(level=level)98        self.console = console or get_console()99        self.highlighter = highlighter or self.HIGHLIGHTER_CLASS()100        self._log_render = LogRender(101            show_time=show_time,102            show_level=show_level,103            show_path=show_path,104            time_format=log_time_format,105            omit_repeated_times=omit_repeated_times,106            level_width=None,107        )108        self.enable_link_path = enable_link_path109        self.markup = markup110        self.rich_tracebacks = rich_tracebacks111        self.tracebacks_width = tracebacks_width112        self.tracebacks_extra_lines = tracebacks_extra_lines113        self.tracebacks_theme = tracebacks_theme114        self.tracebacks_word_wrap = tracebacks_word_wrap115        self.tracebacks_show_locals = tracebacks_show_locals116        self.tracebacks_suppress = tracebacks_suppress117        self.tracebacks_max_frames = tracebacks_max_frames118        self.tracebacks_code_width = tracebacks_code_width119        self.locals_max_length = locals_max_length120        self.locals_max_string = locals_max_string121        self.keywords = keywords122 123    def get_level_text(self, record: LogRecord) -> Text:124        """Get the level name from the record.125 126        Args:127            record (LogRecord): LogRecord instance.128 129        Returns:130            Text: A tuple of the style and level name.131        """132        level_name = record.levelname133        level_text = Text.styled(134            level_name.ljust(8), f"logging.level.{level_name.lower()}"135        )136        return level_text137 138    def emit(self, record: LogRecord) -> None:139        """Invoked by logging."""140        message = self.format(record)141        traceback = None142        if (143            self.rich_tracebacks144            and record.exc_info145            and record.exc_info != (None, None, None)146        ):147            exc_type, exc_value, exc_traceback = record.exc_info148            assert exc_type is not None149            assert exc_value is not None150            from .traceback import Traceback151 152            traceback = Traceback.from_exception(153                exc_type,154                exc_value,155                exc_traceback,156                width=self.tracebacks_width,157                code_width=self.tracebacks_code_width,158                extra_lines=self.tracebacks_extra_lines,159                theme=self.tracebacks_theme,160                word_wrap=self.tracebacks_word_wrap,161                show_locals=self.tracebacks_show_locals,162                locals_max_length=self.locals_max_length,163                locals_max_string=self.locals_max_string,164                suppress=self.tracebacks_suppress,165                max_frames=self.tracebacks_max_frames,166            )167            message = record.getMessage()168            if self.formatter:169                record.message = record.getMessage()170                formatter = self.formatter171                if hasattr(formatter, "usesTime") and formatter.usesTime():172                    record.asctime = formatter.formatTime(record, formatter.datefmt)173                message = formatter.formatMessage(record)174 175        message_renderable = self.render_message(record, message)176        log_renderable = self.render(177            record=record, traceback=traceback, message_renderable=message_renderable178        )179        if isinstance(self.console.file, NullFile):180            # Handles pythonw, where stdout/stderr are null, and we return NullFile181            # instance from Console.file. In this case, we still want to make a log record182            # even though we won't be writing anything to a file.183            self.handleError(record)184        else:185            try:186                self.console.print(log_renderable)187            except Exception:188                self.handleError(record)189 190    def render_message(self, record: LogRecord, message: str) -> ConsoleRenderable:191        """Render message text in to Text.192 193        Args:194            record (LogRecord): logging Record.195            message (str): String containing log message.196 197        Returns:198            ConsoleRenderable: Renderable to display log message.199        """200        use_markup = getattr(record, "markup", self.markup)201        message_text = Text.from_markup(message) if use_markup else Text(message)202 203        highlighter = getattr(record, "highlighter", self.highlighter)204        if highlighter:205            message_text = highlighter(message_text)206 207        if self.keywords is None:208            self.keywords = self.KEYWORDS209 210        if self.keywords:211            message_text.highlight_words(self.keywords, "logging.keyword")212 213        return message_text214 215    def render(216        self,217        *,218        record: LogRecord,219        traceback: Optional[Traceback],220        message_renderable: ConsoleRenderable,221    ) -> ConsoleRenderable:222        """Render log for display.223 224        Args:225            record (LogRecord): logging Record.226            traceback (Optional[Traceback]): Traceback instance or None for no Traceback.227            message_renderable (ConsoleRenderable): Renderable (typically Text) containing log message contents.228 229        Returns:230            ConsoleRenderable: Renderable to display log.231        """232        path = os.path.basename(record.pathname)233        level = self.get_level_text(record)234        time_format = None if self.formatter is None else self.formatter.datefmt235        log_time = datetime.fromtimestamp(record.created)236 237        log_renderable = self._log_render(238            self.console,239            [message_renderable] if not traceback else [message_renderable, traceback],240            log_time=log_time,241            time_format=time_format,242            level=level,243            path=path,244            line_no=record.lineno,245            link_path=record.pathname if self.enable_link_path else None,246        )247        return log_renderable248 249 250if __name__ == "__main__":  # pragma: no cover251    from time import sleep252 253    FORMAT = "%(message)s"254    # FORMAT = "%(asctime)-15s - %(levelname)s - %(message)s"255    logging.basicConfig(256        level="NOTSET",257        format=FORMAT,258        datefmt="[%X]",259        handlers=[RichHandler(rich_tracebacks=True, tracebacks_show_locals=True)],260    )261    log = logging.getLogger("rich")262 263    log.info("Server starting...")264    log.info("Listening on http://127.0.0.1:8080")265    sleep(1)266 267    log.info("GET /index.html 200 1298")268    log.info("GET /imgs/backgrounds/back1.jpg 200 54386")269    log.info("GET /css/styles.css 200 54386")270    log.warning("GET /favicon.ico 404 242")271    sleep(1)272 273    log.debug(274        "JSONRPC request\n--> %r\n<-- %r",275        {276            "version": "1.1",277            "method": "confirmFruitPurchase",278            "params": [["apple", "orange", "mangoes", "pomelo"], 1.123],279            "id": "194521489",280        },281        {"version": "1.1", "result": True, "error": None, "id": "194521489"},282    )283    log.debug(284        "Loading configuration file /adasd/asdasd/qeqwe/qwrqwrqwr/sdgsdgsdg/werwerwer/dfgerert/ertertert/ertetert/werwerwer"285    )286    log.error("Unable to find 'pomelo' in database!")287    log.info("POST /jsonrpc/ 200 65532")288    log.info("POST /admin/ 401 42234")289    log.warning("password was rejected for admin site.")290 291    def divide() -> None:292        number = 1293        divisor = 0294        foos = ["foo"] * 100295        log.debug("in divide")296        try:297            number / divisor298        except:299            log.exception("An error of some kind occurred!")300 301    divide()302    sleep(1)303    log.critical("Out of memory!")304    log.info("Server exited with code=-1")305    log.info("[bold]EXITING...[/bold]", extra=dict(markup=True))306 
codekingpro/portable-devtools · Team Ai