1
0
mirror of https://github.com/checktheroads/hyperglass synced 2024-05-11 05:55:08 +00:00

improve log handling

This commit is contained in:
checktheroads
2020-10-11 15:39:15 -07:00
parent 70e39102b0
commit 95cd9424fd
4 changed files with 55 additions and 40 deletions

View File

@@ -37,29 +37,27 @@ POSSIBILITY OF SUCH DAMAGE.
""" """
# Standard Library # Standard Library
import sys import logging
# Third Party # Third Party
import uvloop import uvloop
import rich.traceback
# Project # Project
from hyperglass.log import _get_rich
from hyperglass.util import set_app_path from hyperglass.util import set_app_path
from hyperglass.constants import METADATA from hyperglass.constants import METADATA
try: # Use Rich for traceback formatting.
# Third Party rich.traceback.install()
import stackprinter
except ImportError:
pass
else:
if sys.stdout.isatty():
_style = "darkbg2"
else:
_style = "plaintext"
stackprinter.set_excepthook(style=_style)
# Set Rich as the default logging handler.
logging.getLogger().handlers = [_get_rich(True)]
# Find hyperglass application directory.
set_app_path() set_app_path()
# Use Uvloop for performance.
uvloop.install() uvloop.install()
__name__, __version__, __author__, __copyright__, __license__ = METADATA __name__, __version__, __author__, __copyright__, __license__ = METADATA

View File

@@ -123,7 +123,8 @@ def start(build, direct, workers):
start(**kwargs) start(**kwargs)
elif not build and direct: elif not build and direct:
uvicorn_start(**kwargs) uvicorn_start(**kwargs)
except KeyboardInterrupt:
error("Stopping hyperglass due to keyboard interrupt.")
except BaseException as err: except BaseException as err:
error(str(err)) error(str(err))

View File

@@ -54,6 +54,23 @@ def _get_rich(debug: bool = False) -> RichHandler:
return RichHandler(**rich_kwargs) return RichHandler(**rich_kwargs)
def setup_lib_logging(debug: bool = False) -> None:
"""Override the logging handlers for dependency libraries."""
for name in (
"gunicorn",
"gunicorn.access",
"gunicorn.error",
"uvicorn",
"uvicorn.access",
"uvicorn.error",
"uvicorn.asgi",
"netmiko",
"scrapli",
"httpx",
):
logging.getLogger(name).handlers = [_get_rich(debug)]
def base_logger(): def base_logger():
"""Initialize hyperglass logging instance.""" """Initialize hyperglass logging instance."""
_loguru_logger.remove() _loguru_logger.remove()
@@ -75,31 +92,6 @@ def _log_success(self, message, *a, **kw):
logging.Logger.success = _log_success logging.Logger.success = _log_success
builtin_logging_config = {
"version": 1,
"formatters": {"basic": {"format": "%(message)s"}},
"root": {"level": "INFO", "handlers": ["rich"]},
"handlers": {
"rich": {
"level": "INFO",
"formatter": "basic",
"class": "rich.logging.RichHandler",
},
"console": {
"level": "INFO",
"formatter": "basic",
"class": "rich.logging.RichHandler",
},
},
"loggers": {
"": {"handlers": ["console"], "level": "INFO", "propagate": True},
"uvicorn": {"handlers": ["rich"], "level": "INFO", "propagate": True},
"uvicorn.access": {"handlers": ["rich"], "level": "INFO", "propagate": True},
"uvicorn.error": {"handlers": ["rich"], "level": "ERROR", "propagate": True},
"uvicorn.asgi": {"handlers": ["rich"], "level": "INFO", "propagate": True},
},
}
def set_log_level(logger, debug): def set_log_level(logger, debug):
"""Set log level based on debug state.""" """Set log level based on debug state."""

View File

@@ -4,14 +4,16 @@
import sys import sys
import math import math
import shutil import shutil
import logging
import platform import platform
# Third Party # Third Party
from gunicorn.arbiter import Arbiter from gunicorn.arbiter import Arbiter
from gunicorn.app.base import BaseApplication from gunicorn.app.base import BaseApplication
from gunicorn.glogging import Logger
# Project # Project
from hyperglass.log import log from hyperglass.log import log, setup_lib_logging
from hyperglass.constants import MIN_PYTHON_VERSION, __version__ from hyperglass.constants import MIN_PYTHON_VERSION, __version__
pretty_version = ".".join(tuple(str(v) for v in MIN_PYTHON_VERSION)) pretty_version = ".".join(tuple(str(v) for v in MIN_PYTHON_VERSION))
@@ -45,6 +47,23 @@ else:
loglevel = "WARNING" loglevel = "WARNING"
class StubbedGunicornLogger(Logger):
"""Custom logging to direct Gunicorn/Uvicorn logs to Loguru/Rich.
See: https://pawamoy.github.io/posts/unify-logging-for-a-gunicorn-uvicorn-app/
"""
def setup(self, cfg):
"""Override Gunicorn setup."""
handler = logging.NullHandler()
self.error_logger = logging.getLogger("gunicorn.error")
self.error_logger.addHandler(handler)
self.access_logger = logging.getLogger("gunicorn.access")
self.access_logger.addHandler(handler)
self.error_logger.setLevel(loglevel)
self.access_logger.setLevel(loglevel)
def check_redis_instance() -> bool: def check_redis_instance() -> bool:
"""Ensure Redis is running before starting server.""" """Ensure Redis is running before starting server."""
@@ -93,6 +112,8 @@ def cache_config():
def on_starting(server: Arbiter): def on_starting(server: Arbiter):
"""Gunicorn pre-start tasks.""" """Gunicorn pre-start tasks."""
setup_lib_logging(params.debug)
python_version = platform.python_version() python_version = platform.python_version()
required = ".".join((str(v) for v in MIN_PYTHON_VERSION)) required = ".".join((str(v) for v in MIN_PYTHON_VERSION))
log.info("Python {} detected ({} required)", python_version, required) log.info("Python {} detected ({} required)", python_version, required)
@@ -173,7 +194,10 @@ def start(**kwargs):
"timeout": math.ceil(params.request_timeout * 1.25), "timeout": math.ceil(params.request_timeout * 1.25),
"on_starting": on_starting, "on_starting": on_starting,
"on_exit": on_exit, "on_exit": on_exit,
"raw_env": ["testing=test"], "logger_class": StubbedGunicornLogger,
"accesslog": "-",
"errorlog": "-",
"logconfig_dict": {"formatters": {"generic": {"format": "%(message)s"}}},
**kwargs, **kwargs,
}, },
).run() ).run()