logger主体部分完成!
顺手修了thread log的毛病
This commit is contained in:
parent
d3a2ea2a0c
commit
0dabb05224
@ -15,7 +15,7 @@ from os import PathLike
|
|||||||
from time import strftime
|
from time import strftime
|
||||||
from logging import NOTSET, DEBUG, INFO, WARNING, ERROR, FATAL
|
from logging import NOTSET, DEBUG, INFO, WARNING, ERROR, FATAL
|
||||||
from types import FrameType
|
from types import FrameType
|
||||||
from typing import Optional, Union, Dict, Iterable, Tuple, List, Callable
|
from typing import Optional, Union, Dict, Iterable, Tuple, Any, List, Callable
|
||||||
|
|
||||||
print(os.path.abspath(os.curdir))
|
print(os.path.abspath(os.curdir))
|
||||||
os.chdir('../../')
|
os.chdir('../../')
|
||||||
@ -88,34 +88,46 @@ logger_configs = {
|
|||||||
'color': 'main_color',
|
'color': 'main_color',
|
||||||
'file': 'main_log_file',
|
'file': 'main_log_file',
|
||||||
},
|
},
|
||||||
|
'client': {
|
||||||
|
'level': TRACE,
|
||||||
|
'color': 'main_color',
|
||||||
|
'file': 'main_log_file',
|
||||||
|
},
|
||||||
|
'server': {
|
||||||
|
'level': TRACE,
|
||||||
|
'color': 'main_color',
|
||||||
|
'file': 'main_log_file',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'Color': {
|
'Color': {
|
||||||
'main_color': {
|
'main_color': {
|
||||||
'date': {},
|
'date': '\033[38;2;201;222;56m',
|
||||||
TRACE: {'info': '\033[34;48;2;44;44;54m', 'message': '\033[34;48;2;40;40;40m'},
|
'file': '\033[38;2;0;255;180m',
|
||||||
|
'line': '\033[38;2;0;255;180m',
|
||||||
|
'logger': '\033[0m',
|
||||||
|
TRACE: {'info': '\033[34;48;2;44;44;54m', 'message': '\033[34;48;2;40;40;70m'},
|
||||||
FINE: {'info': '\033[35;48;2;44;44;54m', 'message': '\033[35m'},
|
FINE: {'info': '\033[35;48;2;44;44;54m', 'message': '\033[35m'},
|
||||||
DEBUG: {'info': '\033[38;2;133;138;149;48;2;44;44;54m', 'message': '\033[38;2;133;138;149m'},
|
DEBUG: {'info': '\033[38;2;133;138;149m', 'message': '\033[38;2;133;138;149m'},
|
||||||
INFO: {'info': '\033[32;48;2;44;44;54m', 'message': '\033[32m'},
|
INFO: {'info': '\033[0m', 'message': '\033[0m'},
|
||||||
WARNING: {'info': '\033[33;48;2;44;44;54m', 'message': '\033[33m'},
|
WARNING: {'info': '\033[33m', 'message': '\033[33m'},
|
||||||
ERROR: {'info': '\033[31;48;2;44;44;54m', 'message': '\033[31m'},
|
ERROR: {'info': '\033[31m', 'message': '\033[31m'},
|
||||||
FATAL: {'info': '\033[38;2;255;200;72;48;2;120;10;10m', 'message': '\033[38;2;255;200;72;48;2;120;10;10m'}
|
FATAL: {'info': '\033[38;2;255;255;0;48;2;120;10;10m', 'message': '\033[38;2;255;255;0;48;2;120;10;10m'}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'File': {
|
'File': {
|
||||||
'main_log_file': {
|
'main_log_file': {
|
||||||
'mode': 'a',
|
'mode': 'a',
|
||||||
'encoding': 'utf-8',
|
'encoding': 'utf-8',
|
||||||
'level': DEBUG,
|
'level': TRACE,
|
||||||
'file_name': '{file_time}_logs.md'
|
'file_name': './logs/{file_time}_logs.md'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'Formatter': {
|
'Formatter': {
|
||||||
'MESSAGE': {
|
'MESSAGE': {
|
||||||
'format': '[\033[38;2;201;222;56m{main_time}\033[0m] {level} | {file_name}:{code_line} | {message}'
|
'format': '[{main_time}] [{logger_name}] {level} | {file_name}:{code_line} | {message}'
|
||||||
},
|
},
|
||||||
'file_time': {'strftime': '%Y-%m-%d %H-%M'},
|
'file_time': {'strftime': '%Y-%m-%d %H-%M'},
|
||||||
'main_time': {'strftime': '%Y-%m-%d %H-%M-%S:%%S'}, # %%S 三位毫秒
|
'main_time': {'strftime': '%Y-%m-%d %H-%M-%S:%%S'}, # %%S 三位毫秒
|
||||||
'encoding': 'utf-8',
|
|
||||||
...: ...
|
...: ...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,14 +141,13 @@ class ListCache:
|
|||||||
self.with_thread_lock = lock
|
self.with_thread_lock = lock
|
||||||
|
|
||||||
def append(self, value: Union[str, Iterable]):
|
def append(self, value: Union[str, Iterable]):
|
||||||
if isinstance(value, str):
|
with self.with_thread_lock:
|
||||||
with self.with_thread_lock:
|
if isinstance(value, str):
|
||||||
self._cache.append(value)
|
self._cache.append(value)
|
||||||
elif isinstance(value, Iterable):
|
elif isinstance(value, Iterable):
|
||||||
with self.with_thread_lock:
|
|
||||||
self._cache.append(*value)
|
self._cache.append(*value)
|
||||||
else:
|
else:
|
||||||
raise TypeError(f"cache must be string or Iterable. not a {type(value)}")
|
raise TypeError(f"cache must be string or Iterable. not a {type(value)}")
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
assert isinstance(item, int)
|
assert isinstance(item, int)
|
||||||
@ -145,23 +156,24 @@ class ListCache:
|
|||||||
return self._cache[item]
|
return self._cache[item]
|
||||||
except IndexError as exp:
|
except IndexError as exp:
|
||||||
print(f'cache:{self.cache}')
|
print(f'cache:{self.cache}')
|
||||||
raise IndexError(f'there is no cache at {item}!\ncache:{self.cache}')
|
raise IndexError(f'there is no cache at {item}!\ncache:{self.cache}\n{exp}')
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
return self.cache
|
return self.cache
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
with self.with_thread_lock:
|
# with self.with_thread_lock:
|
||||||
self._iter_cache = self._cache.copy()
|
# self._iter_cache = self._cache.copy()
|
||||||
self._iter_len = len(self.cache)
|
# self._iter_len = len(self.cache)
|
||||||
|
self._iter_len = len(self.cache)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __next__(self):
|
def __next__(self):
|
||||||
if self._iter_cache == -1:
|
if self._iter_len == -1:
|
||||||
del self._iter_cache
|
del self._iter_len
|
||||||
raise StopIteration('there is no more cache')
|
raise StopIteration('there is no more cache')
|
||||||
returns = self._iter_cache[-self._iter_len]
|
returns = self.cache[-self._iter_len]
|
||||||
self._iter_cache -= 1
|
self._iter_len -= 1
|
||||||
return returns
|
return returns
|
||||||
|
|
||||||
def __bool__(self):
|
def __bool__(self):
|
||||||
@ -172,29 +184,43 @@ class ListCache:
|
|||||||
def cache(self):
|
def cache(self):
|
||||||
return self._cache
|
return self._cache
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
with self.with_thread_lock:
|
||||||
|
self.cache.clear()
|
||||||
|
|
||||||
|
|
||||||
class LogFileCache:
|
class LogFileCache:
|
||||||
"""日志文件缓存"""
|
"""日志文件缓存"""
|
||||||
|
|
||||||
def __init__(self, file_name: PathLike = 'logs//log.log', flush_time: Optional[Union[int, float]] = 1, log_cache_lens_max: int = 10):
|
def __init__(self, file_conf: Dict, flush_time: Optional[Union[int, float]] = 1, log_cache_lens_max: int = 10):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@param file_name: 日志文件名称
|
@param file_conf: 日志文件配置
|
||||||
@param flush_time: 刷新日志缓存,写入文件的时长间隔
|
@param flush_time: 刷新日志缓存,写入文件的时长间隔
|
||||||
@param log_cache_lens_max: 日志缓存在自动写入前的最大缓存长度
|
@param log_cache_lens_max: 日志缓存在自动写入前的最大缓存长度
|
||||||
"""
|
"""
|
||||||
# 配置相关
|
# 配置相关
|
||||||
self._logfile_name = file_name # log 文件名称
|
self._logfile_name = os.path.abspath(format_str(file_conf['file_name'])) # log 文件名称
|
||||||
|
self.level = get_key_from_dict(file_conf, 'level', DEBUG)
|
||||||
|
self.file_conf = file_conf
|
||||||
self.flush_time = flush_time # 缓存刷新时长
|
self.flush_time = flush_time # 缓存刷新时长
|
||||||
self.cache_entries_num = log_cache_lens_max
|
self.cache_entries_num = log_cache_lens_max
|
||||||
self.started = False
|
self.started = False
|
||||||
# self.log
|
|
||||||
# 同步锁
|
# 同步锁
|
||||||
self.cache_lock = threading.Lock() # 主锁
|
self.cache_lock = threading.Lock() # 主锁
|
||||||
self.time_limit_lock = ThreadLock(self.cache_lock, time_out=1 / 60) # 直接用于 with 的主锁
|
self.time_limit_lock = ThreadLock(self.cache_lock, time_out=1 / 60) # 直接用于 with 的主锁
|
||||||
self.threaded_write = threading.Timer(1, self._log_file_time_write) # 基于 timer 的多线程
|
self.threaded_write = threading.Timer(1, self._log_file_time_write) # 基于 timer 的多线程
|
||||||
# 日志缓存表
|
# 日志缓存表
|
||||||
self.log_cache = ListCache(self.time_limit_lock)
|
self.log_cache = ListCache(self.time_limit_lock)
|
||||||
|
self.file_setup()
|
||||||
|
|
||||||
|
def file_setup(self):
|
||||||
|
cache_time = 0
|
||||||
|
file_type = self.logfile_name[self.logfile_name.rfind('.'):]
|
||||||
|
file_pure_name = self.logfile_name[:self.logfile_name.rfind('.')]
|
||||||
|
while os.path.isfile(self.logfile_name):
|
||||||
|
cache_time += 1
|
||||||
|
self.logfile_name = f'{file_pure_name}-{cache_time}{file_type}'
|
||||||
|
|
||||||
def end_thread(self) -> None:
|
def end_thread(self) -> None:
|
||||||
"""结束日志写入进程,顺手把目前的缓存写入"""
|
"""结束日志写入进程,顺手把目前的缓存写入"""
|
||||||
@ -211,6 +237,7 @@ class LogFileCache:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def logfile_name(self) -> PathLike:
|
def logfile_name(self) -> PathLike:
|
||||||
|
self._logfile_name: PathLike
|
||||||
return self._logfile_name
|
return self._logfile_name
|
||||||
|
|
||||||
@logfile_name.setter
|
@logfile_name.setter
|
||||||
@ -223,23 +250,26 @@ class LogFileCache:
|
|||||||
if self.log_cache:
|
if self.log_cache:
|
||||||
with self.time_limit_lock:
|
with self.time_limit_lock:
|
||||||
if self.log_cache:
|
if self.log_cache:
|
||||||
with open(file=self.logfile_name, encoding='utf-8', mode='a') as log_file:
|
with open(file=self.logfile_name,
|
||||||
log_file.writelines(self.log_cache)
|
encoding=get_key_from_dict(self.file_conf, 'encoding', 'utf-8'),
|
||||||
|
mode=get_key_from_dict(self.file_conf, 'mode', 'a')) as log_file:
|
||||||
|
log_file.writelines(self.log_cache.cache.copy())
|
||||||
|
self.log_cache.clear()
|
||||||
|
|
||||||
def write_logs(self, string: str, wait_for_cache: bool = True) -> None:
|
def write_logs(self, string: str, flush: bool = False) -> None:
|
||||||
if not wait_for_cache:
|
self.log_cache.append(string)
|
||||||
with self.time_limit_lock and open(file=self.logfile_name, encoding='utf-8', mode='a') as log_file:
|
if len(self.log_cache.cache) >= 10:
|
||||||
if self.log_cache:
|
self._log_file_time_write()
|
||||||
log_file.writelines(self.log_cache)
|
return None
|
||||||
log_file.write(string)
|
if flush:
|
||||||
else:
|
self._log_file_time_write()
|
||||||
self.log_cache.append(string)
|
print(self.log_cache.cache)
|
||||||
|
|
||||||
|
|
||||||
class Logger:
|
class Logger:
|
||||||
"""shenjack logger"""
|
"""shenjack logger"""
|
||||||
|
|
||||||
def __init__(self, name: str = None, level: int = None, file_name: PathLike = None, colors: Dict[int, Dict[str, str]] = None, formats=None, **kwargs) -> None:
|
def __init__(self, name: str = None, level: int = None, file_conf: Dict = None, colors: Dict[Union[int, str], Dict[str, str]] = None, formats=None, **kwargs) -> None:
|
||||||
"""
|
"""
|
||||||
配置模式: 使用 kwargs 配置
|
配置模式: 使用 kwargs 配置
|
||||||
@param name: logger 名称 默认为 root
|
@param name: logger 名称 默认为 root
|
||||||
@ -250,8 +280,9 @@ class Logger:
|
|||||||
self.level = level if level is not None else DEBUG
|
self.level = level if level is not None else DEBUG
|
||||||
self.colors = colors or logger_configs['Color']['main_color']
|
self.colors = colors or logger_configs['Color']['main_color']
|
||||||
self.formats = formats or logger_configs['Formatter']
|
self.formats = formats or logger_configs['Formatter']
|
||||||
if file_name:
|
if file_conf:
|
||||||
self.file_cache = LogFileCache(file_name=file_name)
|
self.file_cache = LogFileCache(file_conf=file_conf)
|
||||||
|
self.file_cache.start_thread()
|
||||||
else:
|
else:
|
||||||
self.file_cache = False
|
self.file_cache = False
|
||||||
self.warn = self.warning
|
self.warn = self.warning
|
||||||
@ -262,7 +293,11 @@ class Logger:
|
|||||||
end: Optional[str] = '\n',
|
end: Optional[str] = '\n',
|
||||||
flush: Optional[bool] = False,
|
flush: Optional[bool] = False,
|
||||||
frame: Optional[FrameType] = None) -> None:
|
frame: Optional[FrameType] = None) -> None:
|
||||||
if level < self.level:
|
# print(level, self.level,
|
||||||
|
# self.file_cache.level if self.file_cache else False,
|
||||||
|
# level >= self.level,
|
||||||
|
# self.file_cache and (level >= self.file_cache.level))
|
||||||
|
if level < self.level and self.file_cache and (level < self.file_cache.level):
|
||||||
return None
|
return None
|
||||||
if not frame:
|
if not frame:
|
||||||
frame = inspect.currentframe()
|
frame = inspect.currentframe()
|
||||||
@ -272,10 +307,11 @@ class Logger:
|
|||||||
text = sep.join(i if type(i) is str else str(i) for i in values)
|
text = sep.join(i if type(i) is str else str(i) for i in values)
|
||||||
text = f"{self.colors[level]['message']}{text}{color_reset_suffix}"
|
text = f"{self.colors[level]['message']}{text}{color_reset_suffix}"
|
||||||
print_text = self.format_text(level=level, text=text, frame=frame)
|
print_text = self.format_text(level=level, text=text, frame=frame)
|
||||||
print(print_text, end=end)
|
if level >= self.level:
|
||||||
if self.file_cache:
|
print(print_text, end=end)
|
||||||
|
if self.file_cache and (level >= self.file_cache.level):
|
||||||
self.file_cache: LogFileCache
|
self.file_cache: LogFileCache
|
||||||
self.file_cache.write_logs(re.sub(re_find_color_code, '', print_text), wait_for_cache=flush)
|
self.file_cache.write_logs(f"{re.sub(re_find_color_code, '', print_text)}{end}", flush=flush)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def format_text(self, level: int, text: str, frame: Optional[FrameType]) -> str:
|
def format_text(self, level: int, text: str, frame: Optional[FrameType]) -> str:
|
||||||
@ -288,17 +324,18 @@ class Logger:
|
|||||||
formats['file_name'] = 'no frame'
|
formats['file_name'] = 'no frame'
|
||||||
formats['code_line'] = 'no frame'
|
formats['code_line'] = 'no frame'
|
||||||
else:
|
else:
|
||||||
formats['file_name'] = os.path.split(frame.f_code.co_filename)[-1]
|
formats['file_name'] = f"{self.colors['file']}{os.path.split(frame.f_code.co_filename)[-1]}{color_reset_suffix}"
|
||||||
formats['code_line'] = frame.f_lineno
|
formats['code_line'] = f"{self.colors['line']}{frame.f_lineno}{color_reset_suffix}"
|
||||||
now_time = str(time.time())
|
now_time = str(time.time())
|
||||||
for key, value in formats.items():
|
for key, value in formats.items():
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
if 'strftime' in value:
|
if 'strftime' in value:
|
||||||
value['strftime']: str
|
value['strftime']: str
|
||||||
formats[key] = strftime(value['strftime'].replace('%%S', now_time[now_time.find('.') + 1:now_time.find('.') + 4]))
|
formats[key] = f"{self.colors['date']}{strftime(value['strftime'].replace('%%S', now_time[now_time.find('.') + 1:now_time.find('.') + 4]))}{color_reset_suffix}"
|
||||||
elif value == 'game.version':
|
print_text = self.formats['MESSAGE']['format'].format(level_with_color=level_with_color,
|
||||||
formats[key] = DR_runtime.DR_version
|
level=level_with_color, message=text,
|
||||||
print_text = self.formats['MESSAGE']['format'].format(level_with_color=level_with_color, level=level_with_color, message=text, **formats)
|
logger_name=self.name,
|
||||||
|
**formats)
|
||||||
return print_text
|
return print_text
|
||||||
|
|
||||||
def trace(self, *values: object,
|
def trace(self, *values: object,
|
||||||
@ -349,6 +386,24 @@ class Logger:
|
|||||||
return self.make_log(*values, level=FATAL, sep=sep, end=end, flush=flush)
|
return self.make_log(*values, level=FATAL, sep=sep, end=end, flush=flush)
|
||||||
|
|
||||||
|
|
||||||
|
def get_key_from_dict(a_dict: Dict, key: Any, default: Any = None) -> Optional[Any]:
|
||||||
|
try:
|
||||||
|
return a_dict[key]
|
||||||
|
except KeyError:
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def format_str(text: str) -> str:
|
||||||
|
formats = logger_configs['Formatter'].copy()
|
||||||
|
now_time = str(time.time())
|
||||||
|
for key, value in formats.items():
|
||||||
|
if isinstance(value, dict):
|
||||||
|
if 'strftime' in value:
|
||||||
|
value['strftime']: str
|
||||||
|
formats[key] = strftime(value['strftime'].replace('%%S', now_time[now_time.find('.') + 1:now_time.find('.') + 4]))
|
||||||
|
return text.format(**formats)
|
||||||
|
|
||||||
|
|
||||||
def color_in_033(*args) -> str:
|
def color_in_033(*args) -> str:
|
||||||
color_text = ';'.join(args)
|
color_text = ';'.join(args)
|
||||||
color_text = f'\033[{color_text}m'
|
color_text = f'\033[{color_text}m'
|
||||||
@ -395,17 +450,26 @@ def add_kwargs_to_global(**kwargs) -> dict:
|
|||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
def get_logger(name: str = 'name') -> Logger:
|
def get_logger(name: str = 'root') -> Logger:
|
||||||
"""
|
"""
|
||||||
此函数用于从 global_config 中取出对应的配置建立一个相应的 logger
|
此函数用于从 global_config 中取出对应的配置建立一个相应的 logger
|
||||||
@param name: logger的名称
|
@param name: logger的名称
|
||||||
@return: 创建好的 logger
|
@return: 创建好的 logger
|
||||||
"""
|
"""
|
||||||
...
|
if name in logger_configs['Logger']:
|
||||||
|
the_config = logger_configs['Logger'][name]
|
||||||
|
else:
|
||||||
|
the_config = logger_configs['Logger']['root']
|
||||||
|
a_logger = Logger(name=name,
|
||||||
|
level=the_config['level'],
|
||||||
|
file_conf=logger_configs['File'][the_config['file']] if 'file' in the_config else None,
|
||||||
|
colors=logger_configs['Color'][get_key_from_dict(the_config, 'color', 'main_color')],
|
||||||
|
formats=logger_configs['Formatter'])
|
||||||
|
return a_logger
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
os.chdir('githubs/DR')
|
||||||
# 在这里可以使用 add_kwargs_to_global
|
# 在这里可以使用 add_kwargs_to_global
|
||||||
logger = Logger(name="Main", level=NOTSET)
|
logger = Logger(name="Main", level=NOTSET)
|
||||||
for x in range(5):
|
for x in range(5):
|
||||||
@ -416,3 +480,21 @@ if __name__ == "__main__":
|
|||||||
logger.warn('warning')
|
logger.warn('warning')
|
||||||
logger.error('error haaaa')
|
logger.error('error haaaa')
|
||||||
logger.fatal('oh no')
|
logger.fatal('oh no')
|
||||||
|
a_logger = get_logger('client')
|
||||||
|
a_logger.trace('tracing')
|
||||||
|
# time.sleep(5)
|
||||||
|
a_logger.fine('some fine!')
|
||||||
|
a_logger.debug('debugging')
|
||||||
|
# time.sleep(5)
|
||||||
|
a_logger.info("Hello World!!")
|
||||||
|
a_logger.warn('warning')
|
||||||
|
a_logger.error('error haaaa')
|
||||||
|
a_logger.fatal('oh no')
|
||||||
|
for x in range(5):
|
||||||
|
a_logger.trace('tracing')
|
||||||
|
a_logger.fine('some fine!')
|
||||||
|
a_logger.debug('debugging')
|
||||||
|
a_logger.info("Hello World!!")
|
||||||
|
a_logger.warn('warning')
|
||||||
|
a_logger.error('error haaaa')
|
||||||
|
a_logger.fatal('oh no')
|
||||||
|
@ -41,7 +41,8 @@ class ThreadLock:
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
self.lock.release()
|
if self.lock.locked():
|
||||||
|
self.lock.release()
|
||||||
if (exc_type is None) and (exc_val is None) and (exc_tb is None):
|
if (exc_type is None) and (exc_val is None) and (exc_tb is None):
|
||||||
# 没有出 bug
|
# 没有出 bug
|
||||||
# print('exit with no error')
|
# print('exit with no error')
|
||||||
|
Loading…
Reference in New Issue
Block a user