168 lines
4.4 KiB
Python
168 lines
4.4 KiB
Python
import logging
|
|
import os
|
|
from datetime import datetime
|
|
from logging.handlers import TimedRotatingFileHandler
|
|
|
|
from colorlog import ColoredFormatter
|
|
|
|
|
|
LOGGER_NAME = "DDev"
|
|
|
|
|
|
class LevelAwareColoredFormatter(ColoredFormatter):
|
|
def __init__(self, normal_fmt, error_fmt, *args, **kwargs):
|
|
super().__init__(normal_fmt, *args, **kwargs)
|
|
self.normal_fmt = normal_fmt
|
|
self.error_fmt = error_fmt
|
|
|
|
def format(self, record):
|
|
original_fmt = self._style._fmt
|
|
try:
|
|
self._style._fmt = (
|
|
self.error_fmt if record.levelno >= logging.ERROR else self.normal_fmt
|
|
)
|
|
return super().format(record)
|
|
finally:
|
|
self._style._fmt = original_fmt
|
|
|
|
|
|
class DailyFileHandler(TimedRotatingFileHandler):
|
|
def __init__(self, base_dir, backupCount=30, encoding="utf-8", delay=True):
|
|
self.base_dir = base_dir
|
|
os.makedirs(self.base_dir, exist_ok=True)
|
|
filename = self._build_filename()
|
|
super().__init__(
|
|
filename=filename,
|
|
when="midnight",
|
|
interval=1,
|
|
backupCount=backupCount,
|
|
encoding=encoding,
|
|
delay=delay,
|
|
)
|
|
|
|
def _build_filename(self, dt=None):
|
|
dt = dt or datetime.now()
|
|
return os.path.join(self.base_dir, f"app_{dt:%Y_%m_%d}.log")
|
|
|
|
def doRollover(self):
|
|
if self.stream:
|
|
self.stream.close()
|
|
self.stream = None
|
|
|
|
self.baseFilename = os.path.abspath(self._build_filename())
|
|
self.rolloverAt = self.computeRollover(int(datetime.now().timestamp()))
|
|
|
|
if not self.delay:
|
|
self.stream = self._open()
|
|
|
|
|
|
def get_logger(level=logging.DEBUG):
|
|
logger = logging.getLogger(LOGGER_NAME)
|
|
logger.setLevel(level)
|
|
logger.propagate = False
|
|
return logger
|
|
|
|
|
|
def setup_console_logger(level=logging.DEBUG):
|
|
logger = get_logger(level)
|
|
|
|
has_console = any(getattr(h, "_ddev_console", False) for h in logger.handlers)
|
|
if has_console:
|
|
return logger
|
|
|
|
normal_fmt = "%(log_color)s%(levelname)-8s%(reset)s %(log_color)s%(message)s"
|
|
error_fmt = (
|
|
"%(log_color)s%(levelname)-8s%(reset)s %(log_color)s%(message)s "
|
|
"[%(pathname)s:%(lineno)d | %(funcName)s | Thread: %(threadName)s | PID: %(process)d]"
|
|
)
|
|
|
|
formatter = LevelAwareColoredFormatter(
|
|
normal_fmt=normal_fmt,
|
|
error_fmt=error_fmt,
|
|
datefmt="%Y/%m/%d %H:%M:%S",
|
|
reset=True,
|
|
log_colors={
|
|
"DEBUG": "cyan",
|
|
"INFO": "green",
|
|
"WARNING": "yellow",
|
|
"ERROR": "red",
|
|
"CRITICAL": "bold_red",
|
|
},
|
|
)
|
|
|
|
handler = logging.StreamHandler()
|
|
handler.setFormatter(formatter)
|
|
handler._ddev_console = True
|
|
|
|
logger.addHandler(handler)
|
|
return logger
|
|
|
|
|
|
def setup_file_logger(log_dir, level=logging.DEBUG):
|
|
logger = get_logger(level)
|
|
|
|
has_file = any(getattr(h, "_ddev_file", False) for h in logger.handlers)
|
|
if has_file:
|
|
return logger
|
|
|
|
formatter = logging.Formatter(
|
|
"%(asctime)s | %(levelname)s | %(name)s | %(message)s",
|
|
datefmt="%Y/%m/%d %H:%M:%S",
|
|
)
|
|
|
|
handler = DailyFileHandler(base_dir=log_dir, backupCount=30)
|
|
handler.setFormatter(formatter)
|
|
handler._ddev_file = True
|
|
|
|
logger.addHandler(handler)
|
|
return logger
|
|
|
|
|
|
def remove_file_handlers():
|
|
logger = get_logger()
|
|
for h in logger.handlers[:]:
|
|
if getattr(h, "_ddev_file", False):
|
|
logger.removeHandler(h)
|
|
h.close()
|
|
|
|
|
|
def start_logger(data_dir, subdir, level=logging.DEBUG, console=True, file=True):
|
|
log_dir = os.path.join(data_dir, subdir)
|
|
|
|
logger = get_logger(level)
|
|
|
|
if console:
|
|
setup_console_logger(level)
|
|
|
|
if file:
|
|
remove_file_handlers()
|
|
setup_file_logger(log_dir, level)
|
|
|
|
return logger
|
|
|
|
|
|
g_logger = setup_console_logger()
|
|
|
|
|
|
def debug(msg, *args, **kwargs):
|
|
g_logger.debug(msg, *args, stacklevel=2, **kwargs)
|
|
|
|
|
|
def info(msg, *args, **kwargs):
|
|
g_logger.info(msg, *args, stacklevel=2, **kwargs)
|
|
|
|
|
|
def warning(msg, *args, **kwargs):
|
|
g_logger.warning(msg, *args, stacklevel=2, **kwargs)
|
|
|
|
|
|
def error(msg, *args, exc_info=False, **kwargs):
|
|
g_logger.error(msg, *args, exc_info=exc_info, stacklevel=2, **kwargs)
|
|
|
|
|
|
def critical(msg, *args, exc_info=False, **kwargs):
|
|
g_logger.critical(msg, *args, exc_info=exc_info, stacklevel=2, **kwargs)
|
|
|
|
|
|
def set_level(level):
|
|
get_logger().setLevel(level) |