From f1043676da414b036674eaff92b8789e6b709600 Mon Sep 17 00:00:00 2001 From: skshetry <18718008+skshetry@users.noreply.github.com> Date: Thu, 9 Feb 2023 09:34:14 +0545 Subject: [PATCH] =?UTF-8?q?[WIP]=20logging:=20change=20from=20dictConfig?= =?UTF-8?q?=20to=20equivalent=20code=20for=20creating=20logge=E2=80=A6=20(?= =?UTF-8?q?#8985?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit logging: change from dictConfig to equivalent code for creating loggers/handlers/formatters dictConfig clears all handlers, and potentially disables existing loggers (we disabled this functionality though). There's much more code than necessary in dictConfig than what we need. I have tried to keep the setup equivalent to what we had in the config, so I have not tried to cleanup the code. --- dvc/logger.py | 132 +++++++++++++------------------------------------- 1 file changed, 35 insertions(+), 97 deletions(-) diff --git a/dvc/logger.py b/dvc/logger.py index 7aa294a98..09b69f85f 100644 --- a/dvc/logger.py +++ b/dvc/logger.py @@ -1,7 +1,9 @@ """Manages logging configuration for DVC repo.""" +import logging import logging.config import logging.handlers +import sys import colorama @@ -66,12 +68,11 @@ class LoggingException(Exception): super().__init__(msg) -def excludeFilter(level): - class ExcludeLevelFilter(logging.Filter): - def filter(self, record): # noqa: A003 - return record.levelno < level +def exclude_filter(level: int): + def filter_fn(record: "logging.LogRecord") -> bool: + return record.levelno < level - return ExcludeLevelFilter + return filter_fn class ColorFormatter(logging.Formatter): @@ -203,21 +204,36 @@ def set_loggers_level(level: int = logging.INFO) -> None: logging.getLogger(name).setLevel(level) -def _is_jupyter() -> bool: - try: - ipython = get_ipython() # type: ignore[name-defined] - except (NameError, TypeError): - return False - - shell = ipython.__class__.__name__ - return shell == "ZMQInteractiveShell" or "google.colab" in str(ipython.__class__) - - def setup(level: int = logging.INFO) -> None: - is_jupyter = _is_jupyter() - if not is_jupyter: - # initialize colorama, so that tty detection works if we are not inside jupyter - colorama.init() + colorama.init() + formatter = ColorFormatter() + + console_info = LoggerHandler(sys.stdout) + console_info.setLevel(logging.INFO) + console_info.setFormatter(formatter) + console_info.addFilter(exclude_filter(logging.WARNING)) + + console_debug = LoggerHandler(sys.stdout) + console_debug.setLevel(logging.DEBUG) + console_debug.setFormatter(formatter) + console_debug.addFilter(exclude_filter(logging.INFO)) + + addLoggingLevel("TRACE", logging.DEBUG - 5) + + console_trace = LoggerHandler(sys.stdout) + console_trace.setLevel(logging.TRACE) # type: ignore[attr-defined] + console_trace.setFormatter(formatter) + console_trace.addFilter(exclude_filter(logging.DEBUG)) + + console_errors = LoggerHandler(sys.stderr) + console_errors.setLevel(logging.WARNING) + console_errors.setFormatter(formatter) + + for name in ["dvc", "dvc_objects", "dvc_data"]: + logger = logging.getLogger(name) + logger.setLevel(level) + for handler in [console_info, console_debug, console_trace, console_errors]: + logger.addHandler(handler) if level >= logging.DEBUG: # Unclosed session errors for asyncio/aiohttp are only available @@ -229,81 +245,3 @@ def setup(level: int = logging.INFO) -> None: # action. logging.getLogger("asyncio").setLevel(logging.CRITICAL) logging.getLogger("aiohttp").setLevel(logging.CRITICAL) - - addLoggingLevel("TRACE", logging.DEBUG - 5) - logging.config.dictConfig( - { - "version": 1, - "filters": { - "exclude_errors": {"()": excludeFilter(logging.WARNING)}, - "exclude_info": {"()": excludeFilter(logging.INFO)}, - "exclude_debug": {"()": excludeFilter(logging.DEBUG)}, - }, - "formatters": {"color": {"()": ColorFormatter}}, - "handlers": { - "console_info": { - "class": "dvc.logger.LoggerHandler", - "level": "INFO", - "formatter": "color", - "stream": "ext://sys.stdout", - "filters": ["exclude_errors"], - }, - "console_debug": { - "class": "dvc.logger.LoggerHandler", - "level": "DEBUG", - "formatter": "color", - "stream": "ext://sys.stdout", - "filters": ["exclude_info"], - }, - "console_trace": { - "class": "dvc.logger.LoggerHandler", - "level": "TRACE", - "formatter": "color", - "stream": "ext://sys.stdout", - "filters": ["exclude_debug"], - }, - "console_errors": { - "class": "dvc.logger.LoggerHandler", - "level": "WARNING", - "formatter": "color", - "stream": "ext://sys.stderr", - }, - }, - "loggers": { - "dvc": { - "level": level, - "handlers": [ - "console_info", - "console_debug", - "console_trace", - "console_errors", - ], - }, - "dvc_objects": { - "level": level, - "handlers": [ - "console_info", - "console_debug", - "console_trace", - "console_errors", - ], - }, - "dvc_data": { - "level": level, - "handlers": [ - "console_info", - "console_debug", - "console_trace", - "console_errors", - ], - }, - }, - "disable_existing_loggers": False, - } - ) - - if is_jupyter: - # NOTE: colorama init needs to go after logging config to avoid potential - # conflicts with libs that modify logging (like tensorflow/abseil) - # https://github.com/iterative/dvc/issues/8387 - colorama.init()