diff --git a/hyperglass/__init__.py b/hyperglass/__init__.py index 443e25f..b0ab6ba 100644 --- a/hyperglass/__init__.py +++ b/hyperglass/__init__.py @@ -36,20 +36,13 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ -# Standard Library -import logging - # Third Party import uvloop # Project -from hyperglass.log import _get_rich from hyperglass.util import set_app_path from hyperglass.constants import METADATA -# Set Rich as the default logging handler. -logging.getLogger().handlers = [_get_rich(True)] - # Find hyperglass application directory. set_app_path() diff --git a/hyperglass/exceptions.py b/hyperglass/exceptions.py index 16a46d7..8aee847 100644 --- a/hyperglass/exceptions.py +++ b/hyperglass/exceptions.py @@ -1,19 +1,13 @@ """Custom exceptions for hyperglass.""" # Standard Library -import sys import json as _json from typing import Dict, List, Union, Optional -# Third Party -from rich.console import Console - # Project from hyperglass.log import log from hyperglass.constants import STATUS_CODE_MAP -console = Console() - def validation_error_message(*errors: Dict) -> str: """Parse errors return from pydantic.ValidationError.errors().""" @@ -47,12 +41,6 @@ class HyperglassError(Exception): else: log.info(repr(self)) - if all(sys.exc_info()): - # Rich will raise a ValueError if print_exception() is used - # outside of a try/except block. Only use Rich for traceback - # printing if the exception is caught. - console.print_exception(extra_lines=6) - def __str__(self) -> str: """Return the instance's error message.""" return self._message diff --git a/hyperglass/log.py b/hyperglass/log.py index b58468e..f0eec59 100644 --- a/hyperglass/log.py +++ b/hyperglass/log.py @@ -2,21 +2,18 @@ # Standard Library import os +import sys import logging from datetime import datetime # Third Party from loguru import logger as _loguru_logger -from rich.theme import Theme -from rich.console import Console -from rich.logging import RichHandler -_FMT_FILE = ( +_FMT = ( "[{level}] {time:YYYYMMDD} {time:HH:mm:ss} | {name}:" "{line} | {function} {message}" ) _DATE_FMT = "%Y%m%d %H:%M:%S" -_FMT_STDOUT = "{message}" _FMT_BASIC = "{message}" _LOG_LEVELS = [ {"name": "TRACE", "no": 5, "color": ""}, @@ -28,33 +25,8 @@ _LOG_LEVELS = [ {"name": "CRITICAL", "no": 50, "color": ""}, ] -_RICH_THEME = Theme( - { - "logging.level.debug": "bold grey50", - "logging.level.info": "bold blue", - "logging.level.success": "bold green", - "logging.level.warning": "bold yellow", - "logging.level.error": "bold dark_goldenrod", - "logging.level.critical": "bold red", - } -) -_RICH_CONSOLE = Console(theme=_RICH_THEME, log_time_format=_DATE_FMT) - - -def _get_rich(debug: bool = False) -> RichHandler: - rich_kwargs = { - "level": "INFO", - "markup": True, - "rich_tracebacks": True, - "console": _RICH_CONSOLE, - } - if debug: - rich_kwargs["level"] = "DEBUG" - return RichHandler(**rich_kwargs) - - -def setup_lib_logging(debug: bool = False) -> None: +def setup_lib_logging() -> None: """Override the logging handlers for dependency libraries.""" for name in ( "gunicorn", @@ -68,13 +40,13 @@ def setup_lib_logging(debug: bool = False) -> None: "scrapli", "httpx", ): - logging.getLogger(name).handlers = [_get_rich(debug)] + _loguru_logger.bind(logger_name=name) -def base_logger(): +def base_logger(level: str = "INFO"): """Initialize hyperglass logging instance.""" _loguru_logger.remove() - _loguru_logger.add(_get_rich(), format=_FMT_BASIC, level="INFO", enqueue=True) + _loguru_logger.add(sys.stdout, format=_FMT, level=level, enqueue=True) _loguru_logger.configure(levels=_LOG_LEVELS) return _loguru_logger @@ -97,9 +69,7 @@ def set_log_level(logger, debug): """Set log level based on debug state.""" if debug: os.environ["HYPERGLASS_LOG_LEVEL"] = "DEBUG" - logger.remove() - logger.add(_get_rich(True), format=_FMT_BASIC, level="DEBUG", enqueue=True) - logger.configure(levels=_LOG_LEVELS) + base_logger("DEBUG") if debug: logger.debug("Debugging enabled") @@ -139,7 +109,7 @@ def enable_file_logging(logger, log_directory, log_format, log_max_size): logger.add( log_file, - format=_FMT_FILE, + format=_FMT, rotation=log_max_size, serialize=structured, enqueue=True, diff --git a/hyperglass/main.py b/hyperglass/main.py index 0f55e4d..51c4b4b 100644 --- a/hyperglass/main.py +++ b/hyperglass/main.py @@ -112,7 +112,7 @@ def cache_config(): def on_starting(server: Arbiter): """Gunicorn pre-start tasks.""" - setup_lib_logging(params.debug) + setup_lib_logging() python_version = platform.python_version() required = ".".join((str(v) for v in MIN_PYTHON_VERSION)) diff --git a/poetry.lock b/poetry.lock index 36bbe09..5765a9c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -117,7 +117,7 @@ cffi = ">=1.1" six = ">=1.4.1" [package.extras] -tests = ["pytest (>=3.2.1,<3.3.0 || >3.3.0)"] +tests = ["pytest (>=3.2.1,!=3.3.0)"] typecheck = ["mypy"] [[package]] @@ -213,7 +213,7 @@ optional = false python-versions = "*" [package.extras] -test = ["flake8 (3.7.8)", "hypothesis (3.55.3)"] +test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] [[package]] name = "contextvars" @@ -239,12 +239,12 @@ cffi = ">=1.8,<1.11.3 || >1.11.3" six = ">=1.4.1" [package.extras] -docs = ["sphinx (>=1.6.5,<1.8.0 || >1.8.0,<3.1.0 || >3.1.0,<3.1.1 || >3.1.1)", "sphinx-rtd-theme"] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] idna = ["idna (>=2.1)"] pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] ssh = ["bcrypt (>=3.1.5)"] -test = ["pytest (>=3.6.0,<3.9.0 || >3.9.0,<3.9.1 || >3.9.1,<3.9.2 || >3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,<3.79.2 || >3.79.2)"] +test = ["pytest (>=3.6.0,!=3.9.0,!=3.9.1,!=3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] [[package]] name = "cssselect2" @@ -310,7 +310,7 @@ starlette = "0.13.4" all = ["requests (>=2.24.0,<3.0.0)", "aiofiles (>=0.5.0,<0.6.0)", "jinja2 (>=2.11.2,<3.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<2.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "graphene (>=2.1.8,<3.0.0)", "ujson (>=3.0.0,<4.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn (>=0.11.5,<0.12.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)"] dev = ["python-jose[cryptography] (>=3.1.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn (>=0.11.5,<0.12.0)", "graphene (>=2.1.8,<3.0.0)"] doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=5.4.0,<6.0.0)", "markdown-include (>=0.5.1,<0.6.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.2.0)", "typer (>=0.3.0,<0.4.0)", "typer-cli (>=0.0.9,<0.0.10)", "pyyaml (>=5.3.1,<6.0.0)"] -test = ["pytest (5.4.3)", "pytest-cov (2.10.0)", "mypy (0.782)", "black (19.10b0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.4.0)", "orjson (>=3.2.1,<4.0.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "aiofiles (>=0.5.0,<0.6.0)", "flask (>=1.1.2,<2.0.0)"] +test = ["pytest (==5.4.3)", "pytest-cov (==2.10.0)", "mypy (==0.782)", "black (==19.10b0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.4.0)", "orjson (>=3.2.1,<4.0.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "aiofiles (>=0.5.0,<0.6.0)", "flask (>=1.1.2,<2.0.0)"] [[package]] name = "favicons" @@ -615,7 +615,7 @@ optional = false python-versions = "*" [package.extras] -test = ["Cython (0.29.14)"] +test = ["Cython (==0.29.14)"] [[package]] name = "httpx" @@ -995,7 +995,7 @@ six = "*" [package.extras] docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] -tests = ["pytest (>=3.2.1,<3.3.0 || >3.3.0)", "hypothesis (>=3.27.0)"] +tests = ["pytest (>=3.2.1,!=3.3.0)", "hypothesis (>=3.27.0)"] [[package]] name = "pyserial" @@ -1101,7 +1101,7 @@ paramiko = "*" [[package]] name = "scrapli" -version = "2020.10.10" +version = "2020.12.31" description = "Fast, flexible, sync/async, Python 3.6+ screen scraping client specifically for network devices" category = "main" optional = false @@ -1113,12 +1113,12 @@ scrapli-asyncssh = {version = ">=2020.10.10", optional = true, markers = "extra [package.extras] asyncssh = ["scrapli-asyncssh (>=2020.10.10)"] community = ["scrapli-community (>=2020.09.19)"] -full = ["textfsm (>=1.1.0,<2.0.0)", "ntc-templates (>=1.1.0,<2.0.0)", "ttp (>=0.4.0,<1.0.0)", "scrapli-paramiko (>=2020.10.10)", "scrapli-ssh2 (>=2020.10.10)", "scrapli-asyncssh (>=2020.10.10)", "scrapli-community (>=2020.09.19)", "genie (>=20.2)", "pyats (>=20.2)"] +full = ["textfsm (>=1.1.0,<2.0.0)", "ntc-templates (>=1.1.0,<2.0.0)", "ttp (>=0.5.0,<1.0.0)", "scrapli-paramiko (>=2020.10.10)", "scrapli-ssh2 (>=2020.10.24)", "scrapli-asyncssh (>=2020.10.10)", "scrapli-community (>=2020.09.19)", "genie (>=20.2)", "pyats (>=20.2)"] genie = ["genie (>=20.2)", "pyats (>=20.2)"] paramiko = ["scrapli-paramiko (>=2020.10.10)"] -ssh2 = ["scrapli-ssh2 (>=2020.10.10)"] +ssh2 = ["scrapli-ssh2 (>=2020.10.24)"] textfsm = ["textfsm (>=1.1.0,<2.0.0)", "ntc-templates (>=1.1.0,<2.0.0)"] -ttp = ["ttp (>=0.4.0,<1.0.0)"] +ttp = ["ttp (>=0.5.0,<1.0.0)"] [[package]] name = "scrapli-asyncssh" @@ -1294,7 +1294,7 @@ python-versions = ">=3.6" click = ">=7.1.1,<7.2.0" [package.extras] -test = ["pytest-xdist (>=1.32.0,<2.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "mypy (0.782)", "black (>=19.10b0,<20.0b0)", "isort (>=5.0.6,<6.0.0)", "shellingham (>=1.3.0,<2.0.0)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "coverage (>=5.2,<6.0)"] +test = ["pytest-xdist (>=1.32.0,<2.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "mypy (==0.782)", "black (>=19.10b0,<20.0b0)", "isort (>=5.0.6,<6.0.0)", "shellingham (>=1.3.0,<2.0.0)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "coverage (>=5.2,<6.0)"] all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"] dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)"] doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=5.4.0,<6.0.0)", "markdown-include (>=0.5.1,<0.6.0)"] @@ -1318,7 +1318,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] brotli = ["brotlipy (>=0.6.0)"] secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "uvicorn" @@ -1419,12 +1419,12 @@ python-versions = ">=3.6" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [metadata] lock-version = "1.1" python-versions = ">=3.6.1,<4.0" -content-hash = "87de34ae3f9c6bf6d10e23e60a251ad2d02b966c30941d01d51fcfe8c5e7a4bf" +content-hash = "020be9857d03222d73418b6c7e4b966cd42d5a020f4b3e3a3dddeef6d8f29535" [metadata.files] aiocontextvars = [ @@ -2044,8 +2044,8 @@ scp = [ {file = "scp-0.13.2.tar.gz", hash = "sha256:ef9d6e67c0331485d3db146bf9ee9baff8a48f3eb0e6c08276a8584b13bf34b3"}, ] scrapli = [ - {file = "scrapli-2020.10.10-py3-none-any.whl", hash = "sha256:852cbc0768f4b7a67b9545e03caa5e0ae58db30506578891eb848ac637f9a7df"}, - {file = "scrapli-2020.10.10.tar.gz", hash = "sha256:91528b423ffd69714eced6dc966cfa07d922bfaca656d1055d3af94b3fd1f5fe"}, + {file = "scrapli-2020.12.31-py3-none-any.whl", hash = "sha256:9edf53a5ccfb9265bd34758565801713ca02d2add7798e1d71f57ba07c74b583"}, + {file = "scrapli-2020.12.31.tar.gz", hash = "sha256:657a7874775cd4f8ef4ebd87b474292f785e5a1c5652351cfaf645b66105b32e"}, ] scrapli-asyncssh = [ {file = "scrapli_asyncssh-2020.10.10-py3-none-any.whl", hash = "sha256:46713e4d93bf2c784a33c336ae7ddce401969213097f8d03b48e96c1850e4e38"}, diff --git a/pyproject.toml b/pyproject.toml index f749b07..a083cf1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,6 @@ py-cpuinfo = "^7.0.0" pydantic = "^1.4" python = ">=3.6.1,<4.0" redis = "^3.5.3" -rich = "^8.0" scrapli = {extras = ["asyncssh"], version = "^2020.9.26"} uvicorn = "^0.11" uvloop = "^0.14.0"