From cc127f18769f900b3cf8caa914b910fd04ed5142 Mon Sep 17 00:00:00 2001 From: Dima Gerasimov Date: Thu, 29 Oct 2020 01:05:49 +0000 Subject: [PATCH] kython.klogging - move to core - add a proper description why it's useful - make default level INFO - use HPI_LOGS variable for easier log level control (https://github.com/seanbreckenridge/HPI/commit/abdc6df1eaa85a4fdf7321c83d6b17f3cc344a7d) --- lint | 1 - my/core/common.py | 2 +- my/core/logging.py | 91 +++++++++++++++++++++++++++++++++++++++++++ my/github/common.py | 6 +-- my/kython/klogging.py | 65 ------------------------------- 5 files changed, 95 insertions(+), 70 deletions(-) create mode 100644 my/core/logging.py delete mode 100644 my/kython/klogging.py diff --git a/lint b/lint index bb2f097..535a9aa 100755 --- a/lint +++ b/lint @@ -42,7 +42,6 @@ def subpackages(package: str) -> Iterable[str]: def core_modules() -> Iterable[str]: return [ *subpackages('my.core'), - *subpackages('my.kython'), 'my.config', 'my.cfg', 'tests/misc.py', diff --git a/my/core/common.py b/my/core/common.py index d57f63a..09629bd 100644 --- a/my/core/common.py +++ b/my/core/common.py @@ -111,7 +111,7 @@ def listify(fn=None, wrapper=list): # return listify(fn=fn, wrapper=md) -from ..kython.klogging import setup_logger, LazyLogger +from .logging import setup_logger, LazyLogger Paths = Union[Sequence[PathIsh], PathIsh] diff --git a/my/core/logging.py b/my/core/logging.py new file mode 100644 index 0000000..bc10dc0 --- /dev/null +++ b/my/core/logging.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +''' +Default logger is a bit, see 'test'/run this file for a demo +''' + +def test() -> None: + import logging + import sys + M = lambda s: print(s, file=sys.stderr) + + M(" Logging module's deafults are not great...'") + l = logging.getLogger('test_logger') + l.error("For example, this should be logged as error. But it's not even formatted properly, doesn't have logger name or level") + + M(" The reason is that you need to remember to call basicConfig() first") + logging.basicConfig() + l.error("OK, this is better. But the default format kinda sucks, I prefer having timestamps and the file/line number") + + M("") + M(" With LazyLogger you get a reasonable logging format, colours and other neat things") + + ll = LazyLogger('test') # No need for basicConfig! + ll.info("default level is INFO") + ll.debug(".. so this shouldn't be displayed") + ll.warning("warnings are easy to spot!") + ll.exception(RuntimeError("exceptions as well")) + + +import logging +from typing import Union, Optional +import os + +Level = int +LevelIsh = Optional[Union[Level, str]] + + +def mklevel(level: LevelIsh) -> Level: + glevel = os.environ.get('HPI_LOGS', None) + if glevel is not None: + level = glevel + if level is None: + return logging.NOTSET + if isinstance(level, int): + return level + return getattr(logging, level.upper()) + + +FORMAT = '{start}[%(levelname)-7s %(asctime)s %(name)s %(filename)s:%(lineno)d]{end} %(message)s' +FORMAT_COLOR = FORMAT.format(start='%(color)s', end='%(end_color)s') +FORMAT_NOCOLOR = FORMAT.format(start='', end='') +DATEFMT = '%Y-%m-%d %H:%M:%S' + + +def setup_logger(logger: logging.Logger, level: LevelIsh) -> None: + lvl = mklevel(level) + try: + import logzero # type: ignore[import] + except ModuleNotFoundError: + import warnings + warnings.warn("You might want to install 'logzero' for nice colored logs!") + logger.setLevel(lvl) + h = logging.StreamHandler() + h.setLevel(lvl) + h.setFormatter(logging.Formatter(fmt=FORMAT_NOCOLOR, datefmt=DATEFMT)) + logger.addHandler(h) + logger.propagate = False # ugh. otherwise it duplicates log messages? not sure about it.. + else: + formatter = logzero.LogFormatter( + fmt=FORMAT_COLOR, + datefmt=DATEFMT, + ) + logzero.setup_logger(logger.name, level=lvl, formatter=formatter) + + +class LazyLogger(logging.Logger): + def __new__(cls, name, level: LevelIsh = 'INFO'): + logger = logging.getLogger(name) + # this is called prior to all _log calls so makes sense to do it here? + def isEnabledFor_lazyinit(*args, logger=logger, orig=logger.isEnabledFor, **kwargs): + att = 'lazylogger_init_done' + if not getattr(logger, att, False): # init once, if necessary + setup_logger(logger, level=level) + setattr(logger, att, True) + return orig(*args, **kwargs) + + logger.isEnabledFor = isEnabledFor_lazyinit # type: ignore[assignment] + return logger + + +if __name__ == '__main__': + test() diff --git a/my/github/common.py b/my/github/common.py index 6e003d3..e37bd83 100644 --- a/my/github/common.py +++ b/my/github/common.py @@ -6,10 +6,12 @@ from typing import Optional, NamedTuple, Iterable, Set, Tuple import pytz -from ..core import warn_if_empty +from ..core import warn_if_empty, LazyLogger from ..core.error import Res +logger = LazyLogger(__name__) + class Event(NamedTuple): dt: datetime summary: str @@ -23,8 +25,6 @@ Results = Iterable[Res[Event]] @warn_if_empty def merge_events(*sources: Results) -> Results: - from ..kython.klogging import LazyLogger - logger = LazyLogger(__name__) from itertools import chain emitted: Set[Tuple[datetime, str]] = set() for e in chain(*sources): diff --git a/my/kython/klogging.py b/my/kython/klogging.py deleted file mode 100644 index 2c7601a..0000000 --- a/my/kython/klogging.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python3 -import logging -from typing import Union, Optional - -Level = int -LevelIsh = Optional[Union[Level, str]] - - -def mklevel(level: LevelIsh) -> Level: - if level is None: - return logging.NOTSET - if isinstance(level, int): - return level - return getattr(logging, level.upper()) - - -_FMT = '{start}[%(levelname)-7s %(asctime)s %(name)s %(filename)s:%(lineno)d]{end} %(message)s' -_FMT_COLOR = _FMT.format(start='%(color)s', end='%(end_color)s') -_FMT_NOCOLOR = _FMT.format(start='', end='') - - -def setup_logger(logger: logging.Logger, level: LevelIsh) -> None: - lvl = mklevel(level) - try: - import logzero # type: ignore - except ModuleNotFoundError: - import warnings - warnings.warn("You might want to install 'logzero' for nice colored logs!") - logger.setLevel(lvl) - h = logging.StreamHandler() - h.setLevel(lvl) - h.setFormatter(logging.Formatter(fmt=_FMT_NOCOLOR)) - logger.addHandler(h) - else: - formatter = logzero.LogFormatter( - fmt=_FMT_COLOR, - datefmt=None, # pass None to prevent logzero from messing with date format - ) - logzero.setup_logger(logger.name, level=lvl, formatter=formatter) - - -class LazyLogger(logging.Logger): - def __new__(cls, name, level: LevelIsh = 'DEBUG'): - logger = logging.getLogger(name) - # this is called prior to all _log calls so makes sense to do it here? - def isEnabledFor_lazyinit(*args, logger=logger, orig=logger.isEnabledFor, **kwargs): - att = 'lazylogger_init_done' - if not getattr(logger, att, False): # init once, if necessary - setup_logger(logger, level=level) - setattr(logger, att, True) - return orig(*args, **kwargs) - - logger.isEnabledFor = isEnabledFor_lazyinit # type: ignore[assignment] - return logger - - -def test(): - ll = LazyLogger('test') - ll.debug('THIS IS DEBUG') - ll.warning('THIS IS WARNING') - ll.exception(RuntimeError("oops")) - - -if __name__ == '__main__': - test()