Difficult-Rocket/libs/msdnicrosoft_logger/shenjack.py
2022-07-16 20:20:23 +08:00

265 lines
7.7 KiB
Python

"""
@author shenjackyuanjie
@contact 3695888@qq.com
"""
import atexit
import threading
from time import strftime
from typing import Optional, Union, List, Iterable
from logging import NOTSET, DEBUG, INFO, WARNING, ERROR, FATAL
from Difficult_Rocket.utils.thread import ThreadLock
# 如果想要直接使用 logger 来 logging
# 直接调用 logger.debug() 即可
# 默认配置会有
# ----------
# 配置方式一
# 直接使用 logger.Logger()
# 将会创建一个空 logger
# 可以自行通过
# 配置方式二
#
#
color_reset_suffix = "\033[0m"
"""
logging.py
CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0
"""
DETAIL = 5
class LogFileCache:
"""日志文件缓存"""
def __init__(self, file_name: str = 'logs//log.log', flush_time: Optional[Union[int, float]] = 1, log_cache_lens_max: int = 10):
"""
@param file_name: 日志文件名称
@param flush_time: 刷新日志缓存,写入文件的时长间隔
@param log_cache_lens_max: 日志缓存在自动写入前的最大缓存长度
"""
# 配置相关
self._logfile_name = file_name # log 文件名称
self.flush_time = flush_time # 缓存刷新时长
self.cache_entries_num = log_cache_lens_max
# 日志缓存表
self._log_cache = []
# 同步锁
self.cache_lock = threading.Lock() # 主锁
self.write_lock = threading.Lock() # 写入锁
self.with_thread_lock = ThreadLock(self.cache_lock, time_out=1 / 60) # 直接用于 with 的主锁
self.threaded_write = threading.Timer(1, self._log_file_time_write) # 基于 timer 的多线程
def end_thread(self) -> None:
"""结束日志写入进程,顺手把目前的缓存写入"""
self.cache_lock.acquire(blocking=True)
self.threaded_write.cancel()
self._log_file_time_write()
def start_thread(self) -> None:
self.threaded_write.start()
atexit.register(self.end_thread)
@property
def logfile_name(self) -> str:
return self._logfile_name
@logfile_name.setter
def logfile_name(self, value: str) -> None:
with self.with_thread_lock:
self._logfile_name = value
@property
def log_caches(self) -> list:
return self._log_cache
@log_caches.setter
def log_caches(self, value: Union[str, Iterable[str]]):
if type(value) == str:
with self.with_thread_lock:
self._log_cache.append(value)
return
elif isinstance(value, Iterable):
with self.with_thread_lock:
list(map(self._log_cache.append, value))
...
def _log_file_time_write(self) -> None:
"""使用 threading.Timer 调用的定时写入日志文件的函数"""
if self.log_caches:
with self.with_thread_lock:
if self.log_caches:
...
...
def write_logs(self, string: str, wait_for_cache: bool = True) -> None:
if wait_for_cache:
with self.with_thread_lock and open(file=self.logfile_name, encoding='utf-8', mode='a') as log_file:
log_file.writelines(self._log_cache)
log_file.write(string)
...
else:
...
class Logger:
"""shenjack logger"""
def __init__(self, **kwargs) -> None:
"""
配置模式: 使用 kwargs 配置
@param config: 字典格式的配置
@param kwargs: key word 格式的配置
"""
self.name = 'root'
self.level = DEBUG
self.colors = None
if kwargs is not None: # 使用 kwargs 尝试配置
if name := kwargs.pop('name', False): # 顺手把获取到的配置填入临时变量 如果成功获取再填入 self
self.name = name
if level := kwargs.pop('level', False):
self.level = level
else:
...
self.file_cache = LogFileCache()
self.warn = self.warning
self.fine = self.detail
def make_log(self, *values: object,
level: int,
sep: Optional[str] = ' ',
end: Optional[str] = '\n',
flush: Optional[bool] = False) -> None:
if level < self.level:
return None
print(level, values, sep, end, flush, sep='|')
write_text = sep.join(*values)
print(write_text)
...
def detail(self, *values: object,
sep: Optional[str] = ' ',
end: Optional[str] = '\n',
flush: Optional[bool] = False) -> None:
self.make_log(*values, level=DETAIL, sep=sep, end=end, flush=flush)
def debug(self,
*values: object,
sep: Optional[str] = ' ',
end: Optional[str] = '\n',
flush: Optional[bool] = False) -> None:
self.make_log(*values, level=DEBUG, sep=sep, end=end, flush=flush)
def info(self,
*values: object,
sep: Optional[str] = ' ',
end: Optional[str] = '\n',
flush: Optional[bool] = False) -> None:
self.make_log(*values, level=INFO, sep=sep, end=end, flush=flush)
def warning(self,
*values: object,
sep: Optional[str] = ' ',
end: Optional[str] = '\n',
flush: Optional[bool] = False) -> None:
self.make_log(*values, level=WARNING, sep=sep, end=end, flush=flush)
def error(self,
*values: object,
sep: Optional[str] = ' ',
end: Optional[str] = '\n',
flush: Optional[bool] = False) -> None:
self.make_log(*values, level=ERROR, sep=sep, end=end, flush=flush)
def fatal(self,
*values: object,
sep: Optional[str] = ' ',
end: Optional[str] = '\n',
flush: Optional[bool] = False) -> None:
self.make_log(*values, level=FATAL, sep=sep, end=end, flush=flush)
logger_configs = {
'Logger': {
'root': {
'level': DETAIL,
'color': {
DEBUG: '\033[0m'
},
'file': 'main_log_file',
},
},
'Color': {
'detail'
},
'File': {
'main_log_file': {
'mode': 'a',
'encoding': 'utf-8',
'level': DEBUG,
'file_name': '{main_time}_logs.md'
},
},
'Formatter': {
'main_time': {'strftime': '%Y-%m-%d %H-%M-%S'},
'version': 'game.version',
'level': 'level',
'encoding': 'utf-8',
...: ...
}
}
def add_dict_config_to_global(some_dict: Union[dict, list, str], name: str) -> dict:
"""
提前声明,这个函数很有可能搞坏 config
请使用 add_kwargs_to_global 来修改配置
如果你不知道你在改什么,请**务必不要**用这个函数来修改配置
@param some_dict: 一个你丢进来的 logger 设置
@param name: 这个 logger 设置的名称
@return: 修改过的 logger 配置
"""
logger_configs[name] = some_dict
return logger_configs # 修改过的 logger 配置
def add_kwargs_to_global(**kwargs) -> dict:
"""
@param kwargs: 你要改的 logger配置
@return: 修改过的 logger 配置
"""
...
def get_logger(name: str = 'name') -> Logger:
"""
此函数用于从 global_config 中取出对应的配置建立一个相应的 logger
@param name: logger的名称
@return: 创建好的 logger
"""
...
if __name__ == "__main__":
# 在这里可以使用 add_kwargs_to_global
some_logger = Logger(name='aaa')
some_logger.level = DETAIL
some_logger.warn('aaaa', 'aaaa')
...