import logging
import os
import socket
from dataclasses import dataclass
from logging import handlers
from typing import Iterable, Union
from utensil import constant
[docs]def parse_log_level(level):
if isinstance(level, str):
if level.upper() == "NOTSET":
return logging.NOTSET
if level.upper() == "DEBUG":
return logging.DEBUG
if level.upper() == "INFO":
return logging.INFO
if level.upper() == "WARNING":
return logging.WARNING
if level.upper() == "WARN":
return logging.WARN
if level.upper() == "ERROR":
return logging.ERROR
if level.upper() == "FATAL":
return logging.FATAL
if level.upper() == "CRITICAL":
return logging.CRITICAL
raise ValueError(level)
try:
return int(level)
except ValueError as e:
raise ValueError(f"invalid log level: '{level}'") from e
[docs]@dataclass
class LoggerConfig:
level: Union[str, int] = constant.LOG.get("Level")
handlers: Iterable[logging.Handler] = (None,)
format: str = (
"{asctime:s}.{msecs:06.0f} "
f'{constant.HOST_INFO.get("HostName")} {socket.gethostname()} '
"{processName:s}({process:d}) "
"{threadName:s}({thread:d}) {levelname:s} "
"({name:s}.{funcName:s}) {message:s}")
style: str = "{"
datefmt: str = "%Y-%m-%d %I:%M:%S"
def __post_init__(self):
_logger_handlers = []
if constant.LOG.get("Stream", "NOTSET") != "NOTSET":
handler = logging.StreamHandler()
handler.setLevel(
parse_log_level(constant.LOG.get("Stream", "NOTSET")))
_logger_handlers.append(handler)
if constant.LOG.get("Syslog", "NOTSET") != "NOTSET":
handler = handlers.SysLogHandler()
handler.setLevel(
parse_log_level(constant.LOG.get("Syslog", "NOTSET")))
_logger_handlers.append(handler)
if constant.LOG.get("File", "NOTSET") != "NOTSET":
log_file_name = constant.LOG.get("FilePrefix", "log")
if not os.path.isdir(os.path.dirname(log_file_name)):
os.makedirs(os.path.dirname(log_file_name))
handler = handlers.WatchedFileHandler(log_file_name)
handler.setLevel(parse_log_level(constant.LOG.get("File",
"NOTSET")))
_logger_handlers.append(handler)
self.level = parse_log_level(self.level)
if self.handlers == (None,):
self.handlers = _logger_handlers
[docs]class BraceString(str):
def __mod__(self, other):
return self.format(*other)
def __str__(self): # pylint: disable=invalid-str-returned
# `self` is a string
return self
[docs]class StyleAdapter(logging.LoggerAdapter):
def __init__(self, logger, extra=None):
super().__init__(logger, extra)
[docs] def process(self, msg, kwargs):
if kwargs.pop('style', "{") == "{": # optional
msg = BraceString(msg)
return msg, kwargs
[docs]def get_logger(name, logger_config=None):
try:
from loguru import logger
logger.opt(lazy=True)
return logger
except ImportError:
pass
if logger_config is None:
logger_config = LoggerConfig()
logger = logging.getLogger(name)
logger.setLevel(logger_config.level)
formatter = logging.Formatter(
fmt=logger_config.format,
datefmt=logger_config.datefmt,
style=logger_config.style,
)
for handler in logger_config.handlers:
handler.setFormatter(formatter)
logger.addHandler(handler)
return StyleAdapter(logger)
class _DummyLogger:
def setLevel(self, level):
pass
def debug(self, msg, *args, **kwargs):
pass
def info(self, msg, *args, **kwargs):
pass
def warning(self, msg, *args, **kwargs):
pass
def warn(self, msg, *args, **kwargs):
pass
def error(self, msg, *args, **kwargs):
pass
def exception(self, msg, *args, exc_info=True, **kwargs):
pass
def critical(self, msg, *args, **kwargs):
pass
fatal = critical
def log(self, level, msg, *args, **kwargs):
pass
def findCaller(self, stack_info=False, stacklevel=1):
pass
def makeRecord(
self,
name,
level,
fn,
lno,
msg,
args,
exc_info,
func=None,
extra=None,
sinfo=None,
):
pass
def _log(
self,
level,
msg,
args,
exc_info=None,
extra=None,
stack_info=False,
stacklevel=1,
):
pass
def handle(self, record):
pass
def addHandler(self, hdlr):
pass
def removeHandler(self, hdlr):
pass
def hasHandlers(self):
pass
def callHandlers(self, record):
pass
def getEffectiveLevel(self):
pass
def isEnabledFor(self, level):
pass
def getChild(self, suffix):
pass
DUMMY_LOGGER = _DummyLogger()