logger config!

This commit is contained in:
shenjack 2022-11-01 23:22:39 +08:00
parent d67b35e40a
commit 5e6157a9df
3 changed files with 94 additions and 53 deletions

View File

@ -1,24 +1,44 @@
version = 1.0 version = 1.0
# [Loggers] [Loggers]
[Loggers.root] [Loggers.root]
level = 'DEBUG' level = 'DEBUG'
color = 'main_color' color = 'main_color'
file = 'main_log_file' file = 'main_log_file'
format = 'format.main_format'
[Loggers.client] [Loggers.client]
level = 'TRACE' level = 'TRACE'
color = 'main_color' color = 'main_color'
file = 'main_log_file' file = 'main_log_file'
format = 'format.main_format'
[Loggers.server] [Loggers.server]
level = 'TRACE' level = 'TRACE'
color = 'DiGua_color' color = 'DiGua_color'
file = 'main_log_file' file = 'main_log_file'
format = 'format.main_format'
[Files] [Files]
[Colors] [Files.main_log_file]
name = './logs/{file_time}_logs.md'
level = 'TRACE'
cache_len = 20
cache_time = 1
mode = 'a'
encoding = 'utf-8'
[Formatter] [Formatter]
main_format = '[{long_time}] [{logger_name}] {level} | {file_name}:{code_line} | {message}'
file_name = 'no frame'
code_line = 'no frame'
short_time = '%Y-%m-%d %H-%M-%S'
long_time = '%Y-%m-%d %H-%M-%S:%%S'
[Colors]
[Colors.main_color]
# file_time

View File

@ -10,7 +10,6 @@
import re import re
import os import os
import time import time
import enum
import atexit import atexit
import inspect import inspect
import threading import threading
@ -20,9 +19,6 @@ 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 NoReturn, Optional, Type, Union, Dict, Iterable, Any, List from typing import NoReturn, Optional, Type, Union, Dict, Iterable, Any, List
import ctypes
from ordered_set import T
os.system('') os.system('')
# print(os.path.abspath(os.curdir)) # print(os.path.abspath(os.curdir))
@ -152,7 +148,7 @@ logger_configs = {
}, },
'Color': { 'Color': {
'main_color': { 'main_color': {
'file_time': '\033[38;2;201;222;56m', # 'file_time': '\033[38;2;201;222;56m',
'main_time': '\033[38;2;201;222;56m', 'main_time': '\033[38;2;201;222;56m',
'file_name': '\033[38;2;0;255;180m', 'file_name': '\033[38;2;0;255;180m',
'code_line': '\033[38;2;0;255;180m', 'code_line': '\033[38;2;0;255;180m',
@ -165,10 +161,11 @@ logger_configs = {
LoggingLevel.INFO_t: {'info': '\033[0m'}, LoggingLevel.INFO_t: {'info': '\033[0m'},
LoggingLevel.WARNING_t: {'info': '\033[33m'}, LoggingLevel.WARNING_t: {'info': '\033[33m'},
LoggingLevel.ERROR_t: {'info': '\033[31m'}, LoggingLevel.ERROR_t: {'info': '\033[31m'},
LoggingLevel.FATAL_t: {'info': '\033[38;2;255;255;0;48;2;120;10;10m', 'logger': '\033[38;2;245;189;230m'} LoggingLevel.FATAL_t: {
'info': '\033[38;2;255;255;0;48;2;120;10;10m', 'logger': '\033[38;2;245;189;230m'}
}, },
'fancy_main_color': { 'fancy_main_color': {
'file_time': '\033[38;2;201;222;56m', # 'file_time': '\033[38;2;201;222;56m',
'main_time': '\033[38;2;201;222;56m', 'main_time': '\033[38;2;201;222;56m',
'file_name': '\033[38;2;0;255;180m', 'file_name': '\033[38;2;0;255;180m',
'code_line': '\033[38;2;0;255;180m', 'code_line': '\033[38;2;0;255;180m',
@ -180,11 +177,12 @@ logger_configs = {
LoggingLevel.INFO_t: {'info': '\033[0m', 'message': '\033[0m'}, LoggingLevel.INFO_t: {'info': '\033[0m', 'message': '\033[0m'},
LoggingLevel.WARNING_t: {'info': '\033[33m', 'message': '\033[33m'}, LoggingLevel.WARNING_t: {'info': '\033[33m', 'message': '\033[33m'},
LoggingLevel.ERROR_t: {'info': '\033[31m', 'message': '\033[31m'}, LoggingLevel.ERROR_t: {'info': '\033[31m', 'message': '\033[31m'},
LoggingLevel.FATAL_t: {'info': '\033[38;2;255;255;0;48;2;120;10;10m', 'message': '\033[38;2;255;255;0;48;2;120;10;10m', 'logger': '\033[38;2;245;189;230m'} LoggingLevel.FATAL_t: {'info': '\033[38;2;255;255;0;48;2;120;10;10m',
'message': '\033[38;2;255;255;0;48;2;120;10;10m', 'logger': '\033[38;2;245;189;230m'}
}, },
'DiGua_color': { 'DiGua_color': {
# catppuccin Macchiato # catppuccin Macchiato
'file_time': '\033[38;2;238;212;159m', # 'file_time': '\033[38;2;238;212;159m',
'main_time': '\033[38;2;202;211;245m', 'main_time': '\033[38;2;202;211;245m',
'file_name': '\033[38;2;139;213;202m', 'file_name': '\033[38;2;139;213;202m',
'code_line': '\033[38;2;166;218;149m', 'code_line': '\033[38;2;166;218;149m',
@ -196,7 +194,8 @@ logger_configs = {
LoggingLevel.INFO_t: {'info': '\033[0m', 'message': '\033[0m'}, LoggingLevel.INFO_t: {'info': '\033[0m', 'message': '\033[0m'},
LoggingLevel.WARNING_t: {'info': '\033[38;2;245;169;127m', 'message': '\033[38;2;245;169;127m'}, LoggingLevel.WARNING_t: {'info': '\033[38;2;245;169;127m', 'message': '\033[38;2;245;169;127m'},
LoggingLevel.ERROR_t: {'info': '\033[38;2;237;135;150m', 'message': '\033[38;2;237;135;150m'}, LoggingLevel.ERROR_t: {'info': '\033[38;2;237;135;150m', 'message': '\033[38;2;237;135;150m'},
LoggingLevel.FATAL_t: {'info': '\033[38;2;255;255;0;48;2;120;10;10m', 'message': '\033[38;2;255;255;0;48;2;120;10;10m', 'logger': '\033[38;2;245;189;230m'} LoggingLevel.FATAL_t: {'info': '\033[38;2;255;255;0;48;2;120;10;10m',
'message': '\033[38;2;255;255;0;48;2;120;10;10m', 'logger': '\033[38;2;245;189;230m'}
} }
}, },
'File': { 'File': {
@ -204,7 +203,7 @@ logger_configs = {
'mode': 'a', 'mode': 'a',
'encoding': 'utf-8', 'encoding': 'utf-8',
'level': TRACE, 'level': TRACE,
'file_name': './logs/{file_time}_logs.md', 'file_name': './logs/{long_time}_logs.md',
'cache_len': 10, 'cache_len': 10,
'cache_time': 1 'cache_time': 1
}, },
@ -273,7 +272,8 @@ class ListCache:
elif isinstance(value, Iterable): elif isinstance(value, Iterable):
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) -> str: def __getitem__(self, item) -> str:
assert isinstance(item, int) assert isinstance(item, int)
@ -282,7 +282,8 @@ 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}\n{exp}') raise IndexError(
f'there is no cache at {item}!\ncache:{self.cache}\n{exp}')
def __call__(self, *args, **kwargs) -> List[str]: def __call__(self, *args, **kwargs) -> List[str]:
return self.cache return self.cache
@ -433,7 +434,8 @@ class CachedFileHandler(StreamHandlerTemplate):
""" """
super().__init__(level=level, formatter=formatter) super().__init__(level=level, formatter=formatter)
if file_conf is not None: if file_conf is not None:
self.file_conf = file_conf if type(file_conf) is LogFileConf else LogFileConf(**file_conf) # type: ignore self.file_conf = file_conf if type(
file_conf) is LogFileConf else LogFileConf(**file_conf) # type: ignore
else: else:
... ...
self.string_queue = Queue(maxsize=self.file_conf.file_cache_len) self.string_queue = Queue(maxsize=self.file_conf.file_cache_len)
@ -456,8 +458,10 @@ class LogFileCache:
:param file_conf: 日志文件配置 :param file_conf: 日志文件配置
""" """
# 配置相关 # 配置相关
self._logfile_name = os.path.abspath(format_str(file_conf['file_name'])) # log 文件名称 self._logfile_name = os.path.abspath(
self.level: logging_level_type = get_key_from_dict(file_conf, 'level', DEBUG) format_str(file_conf['file_name'])) # log 文件名称
self.level: logging_level_type = get_key_from_dict(
file_conf, 'level', DEBUG)
self.file_conf = file_conf self.file_conf = file_conf
self.flush_time = file_conf['cache_time'] # 缓存刷新时长 self.flush_time = file_conf['cache_time'] # 缓存刷新时长
self.cache_entries_num = file_conf['cache_len'] self.cache_entries_num = file_conf['cache_len']
@ -465,8 +469,10 @@ class LogFileCache:
self.running = False self.running = False
# 同步锁 # 同步锁
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.threaded_write = threading.Timer(1, self._log_file_time_write, kwargs={'thread': True}) # 基于 timer 的多线程 self.cache_lock, time_out=1 / 60) # 直接用于 with 的主锁
self.threaded_write = threading.Timer(1, self._log_file_time_write, kwargs={
'thread': True}) # 基于 timer 的多线程
# 日志缓存表 # 日志缓存表
self.log_cache = ListCache(self.time_limit_lock) self.log_cache = ListCache(self.time_limit_lock)
self.file_setup() self.file_setup()
@ -511,7 +517,8 @@ class LogFileCache:
with self.time_limit_lock: with self.time_limit_lock:
if self.log_cache: if self.log_cache:
with open(file=self.logfile_name, with open(file=self.logfile_name,
encoding=get_key_from_dict(self.file_conf, 'encoding', 'utf-8'), encoding=get_key_from_dict(
self.file_conf, 'encoding', 'utf-8'),
mode=get_key_from_dict(self.file_conf, 'mode', 'a')) as log_file: mode=get_key_from_dict(self.file_conf, 'mode', 'a')) as log_file:
log_file.writelines(self.log_cache.cache.copy()) log_file.writelines(self.log_cache.cache.copy())
self.log_cache.clear() self.log_cache.clear()
@ -526,7 +533,8 @@ class LogFileCache:
if flush: if flush:
self._log_file_time_write() self._log_file_time_write()
if self.started and not self.running: if self.started and not self.running:
self.threaded_write = threading.Timer(1, self._log_file_time_write, kwargs={'thread': True}) # 基于 timer 的多线程 self.threaded_write = threading.Timer(1, self._log_file_time_write, kwargs={
'thread': True}) # 基于 timer 的多线程
self.threaded_write.start() self.threaded_write.start()
self.running = True self.running = True
@ -538,7 +546,8 @@ class Logger:
name: str = 'root', name: str = 'root',
level: int = DEBUG, level: int = DEBUG,
file_conf: Optional[List[LogFileCache]] = None, file_conf: Optional[List[LogFileCache]] = None,
colors: Optional[Dict[Union[int, str], Dict[str, str]]] = None, colors: Optional[Dict[Union[int, str],
Dict[str, str]]] = None,
formats=None) -> None: formats=None) -> None:
""" """
配置模式: 使用 kwargs 配置 配置模式: 使用 kwargs 配置
@ -558,13 +567,17 @@ class Logger:
self.min_level = self.level self.min_level = self.level
if file_conf: if file_conf:
self.file_cache = file_conf self.file_cache = file_conf
self.min_level = min(*[file.level for file in file_conf], self.level) self.min_level = min(
*[file.level for file in file_conf], self.level)
else: else:
self.file_cache = [] self.file_cache = []
self.warn = self.warning self.warn = self.warning
def format_formats(self) -> NoReturn: def format_formats(self) -> None:
... if 'long_time' not in self.formats:
self.formats['long_time'] = '%Y-%m-%d %H-%M-%S:%%S'
if 'short_time' not in self.formats:
self.formats['short_time'] = '%Y-%m-%d %H-%M-%S'
def add_stream(self, stream: StreamHandlerTemplate) -> bool: def add_stream(self, stream: StreamHandlerTemplate) -> bool:
""" """
@ -585,9 +598,12 @@ class Logger:
def format_time(self, input_time: Optional[float] = None) -> Dict[str, str]: def format_time(self, input_time: Optional[float] = None) -> Dict[str, str]:
# 毫秒 # 毫秒
get_time: float = input_time or time.time() get_time: float = input_time or time.time()
millisecond = (get_time - int(get_time)) * 1000 millisecond = str((get_time - int(get_time)) * 1000)
long_time = time.strftime(self.formats['long_time'] if 'long_time' in self.formats else '%Y-%m-%d %H-%M-%S:%%S', get_time) long_time = time.strftime(
return {'long_time': time.strftime(self.formats['long_time'].replace('%%S', now_time[now_time.find('.') + 1:now_time.find('.') + 5]))} self.formats['long_time'].replace('%%S', millisecond))
short_time = time.strftime(
self.formats['short_time'].replace('%%S', millisecond))
return {'long_time': long_time, 'short_time': short_time}
# def filter_and_make_log(): make_log(formatted_string) # def filter_and_make_log(): make_log(formatted_string)
def make_log(self, *values: object, def make_log(self, *values: object,
@ -601,7 +617,8 @@ class Logger:
if (frame := inspect.currentframe()) is not None: if (frame := inspect.currentframe()) is not None:
frame = frame if frame.f_back is None else frame.f_back if frame.f_back.f_back is None else frame.f_back.f_back frame = frame if frame.f_back is None else frame.f_back if frame.f_back.f_back is None else frame.f_back.f_back
# 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)
message_color = self.colors[get_name_by_level(level)]['message'] if 'message' in self.colors[get_name_by_level(level)] else self.colors['message'] message_color = self.colors[get_name_by_level(
level)]['message'] if 'message' in self.colors[get_name_by_level(level)] else self.colors['message']
text = f"{message_color}{sep.join(i if type(i) is str else str(i) for i in values)}{color_reset_suffix}" text = f"{message_color}{sep.join(i if type(i) is str else str(i) for i in values)}{color_reset_suffix}"
# print('abc', 'abc', marker='123') # print('abc', 'abc', marker='123')
print_text = self.format_text(level=level, text=text, frame=frame) print_text = self.format_text(level=level, text=text, frame=frame)
@ -611,7 +628,8 @@ class Logger:
file: LogFileCache file: LogFileCache
if level < file.level: if level < file.level:
continue continue
file.write_logs(f"{re.sub(re_find_color_code, '', print_text)}{end}", flush=flush) file.write_logs(
f"{re.sub(re_find_color_code, '', print_text)}{end}", flush=flush)
return print_text return print_text
def format_text(self, level: int, text: str, frame: Optional[FrameType]) -> str: def format_text(self, level: int, text: str, frame: Optional[FrameType]) -> str:
@ -786,7 +804,8 @@ def format_str(text: str) -> str:
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('.') + 5])) formats[key] = strftime(value['strftime'].replace(
'%%S', now_time[now_time.find('.') + 1:now_time.find('.') + 5]))
return text.format(**formats) return text.format(**formats)
@ -820,7 +839,8 @@ def gen_file_conf(file_name: str,
def gen_color_conf(color_name: Optional[str] = None, **colors) -> dict: def gen_color_conf(color_name: Optional[str] = None, **colors) -> dict:
default_color = logger_configs['Color']['main_color' if color_name is None else color_name].copy() default_color = logger_configs['Color']['main_color' if color_name is None else color_name].copy(
)
default_color.update(colors) default_color.update(colors)
return default_color return default_color
@ -875,7 +895,8 @@ def get_logger(name: str = 'root') -> Logger:
the_config = logger_configs['Logger']['root'] the_config = logger_configs['Logger']['root']
file_handler = None file_handler = None
if 'file' in the_config: if 'file' in the_config:
file_handler = [LogFileCache(logger_configs['File'][the_config['file']])] file_handler = [LogFileCache(
logger_configs['File'][the_config['file']])]
color = the_config['color'] if 'color' in the_config else 'main_color' color = the_config['color'] if 'color' in the_config else 'main_color'
return Logger(name=name, return Logger(name=name,
level=the_config['level'], level=the_config['level'],

View File

@ -10,4 +10,4 @@ with open(sys.argv[1], encoding='utf-8', mode='r') as f:
else: else:
a = json.load(f) a = json.load(f)
print(a) print(a)
pprint.pprint(a) pprint.pprint(a, width=100)