add
This commit is contained in:
159
ccxt/static_dependencies/ethereum/utils/logging.py
Normal file
159
ccxt/static_dependencies/ethereum/utils/logging.py
Normal file
@@ -0,0 +1,159 @@
|
||||
import contextlib
|
||||
from functools import (
|
||||
cached_property,
|
||||
)
|
||||
import logging
|
||||
from typing import (
|
||||
Any,
|
||||
Dict,
|
||||
Iterator,
|
||||
Tuple,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
|
||||
from .toolz import (
|
||||
assoc,
|
||||
)
|
||||
|
||||
DEBUG2_LEVEL_NUM = 8
|
||||
|
||||
TLogger = TypeVar("TLogger", bound=logging.Logger)
|
||||
|
||||
|
||||
class ExtendedDebugLogger(logging.Logger):
|
||||
"""
|
||||
Logging class that can be used for lower level debug logging.
|
||||
"""
|
||||
|
||||
@cached_property
|
||||
def show_debug2(self) -> bool:
|
||||
return self.isEnabledFor(DEBUG2_LEVEL_NUM)
|
||||
|
||||
def debug2(self, message: str, *args: Any, **kwargs: Any) -> None:
|
||||
if self.show_debug2:
|
||||
self.log(DEBUG2_LEVEL_NUM, message, *args, **kwargs)
|
||||
else:
|
||||
# When we find that `DEBUG2` isn't enabled we completely replace
|
||||
# the `debug2` function in this instance of the logger with a noop
|
||||
# lambda to further speed up
|
||||
self.__dict__["debug2"] = lambda message, *args, **kwargs: None
|
||||
|
||||
def __reduce__(self) -> Tuple[Any, ...]:
|
||||
# This is needed because our parent's implementation could
|
||||
# cause us to become a regular Logger on unpickling.
|
||||
return get_extended_debug_logger, (self.name,)
|
||||
|
||||
|
||||
def setup_DEBUG2_logging() -> None:
|
||||
"""
|
||||
Installs the `DEBUG2` level logging levels to the main logging module.
|
||||
"""
|
||||
if not hasattr(logging, "DEBUG2"):
|
||||
logging.addLevelName(DEBUG2_LEVEL_NUM, "DEBUG2")
|
||||
logging.DEBUG2 = DEBUG2_LEVEL_NUM # type: ignore
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _use_logger_class(logger_class: Type[logging.Logger]) -> Iterator[None]:
|
||||
original_logger_class = logging.getLoggerClass()
|
||||
logging.setLoggerClass(logger_class)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
logging.setLoggerClass(original_logger_class)
|
||||
|
||||
|
||||
def get_logger(name: str, logger_class: Union[Type[TLogger], None] = None) -> TLogger:
|
||||
if logger_class is None:
|
||||
return cast(TLogger, logging.getLogger(name))
|
||||
else:
|
||||
with _use_logger_class(logger_class):
|
||||
# The logging module caches logger instances. The following code
|
||||
# ensures that if there is a cached instance that we don't
|
||||
# accidentally return the incorrect logger type because the logging
|
||||
# module does not *update* the cached instance in the event that
|
||||
# the global logging class changes.
|
||||
#
|
||||
# types ignored b/c mypy doesn't identify presence of
|
||||
# manager on logging.Logger
|
||||
manager = logging.Logger.manager
|
||||
if name in manager.loggerDict:
|
||||
if type(manager.loggerDict[name]) is not logger_class:
|
||||
del manager.loggerDict[name]
|
||||
return cast(TLogger, logging.getLogger(name))
|
||||
|
||||
|
||||
def get_extended_debug_logger(name: str) -> ExtendedDebugLogger:
|
||||
return get_logger(name, ExtendedDebugLogger)
|
||||
|
||||
|
||||
THasLoggerMeta = TypeVar("THasLoggerMeta", bound="HasLoggerMeta")
|
||||
|
||||
|
||||
class HasLoggerMeta(type):
|
||||
"""
|
||||
Assigns a logger instance to a class, derived from the import path and name.
|
||||
|
||||
This metaclass uses `__qualname__` to identify a unique and meaningful name
|
||||
to use when creating the associated logger for a given class.
|
||||
"""
|
||||
|
||||
logger_class = logging.Logger
|
||||
|
||||
def __new__(
|
||||
mcls: Type[THasLoggerMeta],
|
||||
name: str,
|
||||
bases: Tuple[Type[Any]],
|
||||
namespace: Dict[str, Any],
|
||||
) -> THasLoggerMeta:
|
||||
if "logger" in namespace:
|
||||
# If a logger was explicitly declared we shouldn't do anything to
|
||||
# replace it.
|
||||
return super().__new__(mcls, name, bases, namespace)
|
||||
if "__qualname__" not in namespace:
|
||||
raise AttributeError("Missing __qualname__")
|
||||
|
||||
with _use_logger_class(mcls.logger_class):
|
||||
logger = logging.getLogger(namespace["__qualname__"])
|
||||
|
||||
return super().__new__(mcls, name, bases, assoc(namespace, "logger", logger))
|
||||
|
||||
@classmethod
|
||||
def replace_logger_class(
|
||||
mcls: Type[THasLoggerMeta], value: Type[logging.Logger]
|
||||
) -> Type[THasLoggerMeta]:
|
||||
return type(mcls.__name__, (mcls,), {"logger_class": value})
|
||||
|
||||
@classmethod
|
||||
def meta_compat(
|
||||
mcls: Type[THasLoggerMeta], other: Type[type]
|
||||
) -> Type[THasLoggerMeta]:
|
||||
return type(mcls.__name__, (mcls, other), {})
|
||||
|
||||
|
||||
class _BaseHasLogger(metaclass=HasLoggerMeta):
|
||||
# This class exists to a allow us to define the type of the logger. Once
|
||||
# python3.5 is deprecated this can be removed in favor of a simple type
|
||||
# annotation on the main class.
|
||||
logger = logging.Logger("") # type: logging.Logger
|
||||
|
||||
|
||||
class HasLogger(_BaseHasLogger):
|
||||
pass
|
||||
|
||||
|
||||
HasExtendedDebugLoggerMeta = HasLoggerMeta.replace_logger_class(ExtendedDebugLogger)
|
||||
|
||||
|
||||
class _BaseHasExtendedDebugLogger(metaclass=HasExtendedDebugLoggerMeta): # type: ignore
|
||||
# This class exists to a allow us to define the type of the logger. Once
|
||||
# python3.5 is deprecated this can be removed in favor of a simple type
|
||||
# annotation on the main class.
|
||||
logger = ExtendedDebugLogger("") # type: ExtendedDebugLogger
|
||||
|
||||
|
||||
class HasExtendedDebugLogger(_BaseHasExtendedDebugLogger):
|
||||
pass
|
||||
Reference in New Issue
Block a user