From 7e53fc099e30ba0dad99acd73b5d301877881071 Mon Sep 17 00:00:00 2001 From: Nanaloveyuki Date: Sun, 27 Jul 2025 21:52:41 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E6=B7=BB=E5=8A=A0=E4=BA=86`fmt`?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E5=8F=8A=E5=85=B6=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E7=9A=84`time`=E5=92=8C`style`=E5=9F=BA=E6=9C=AC=E7=BB=84?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/fmt.py | 192 ++++++++++++++++++++++++++++++++++++++++++++ src/utils/styles.py | 50 ++++++++++++ src/utils/time.py | 64 +++++++++++++++ 3 files changed, 306 insertions(+) create mode 100644 src/utils/fmt.py create mode 100644 src/utils/styles.py create mode 100644 src/utils/time.py diff --git a/src/utils/fmt.py b/src/utils/fmt.py new file mode 100644 index 0000000..404c9f4 --- /dev/null +++ b/src/utils/fmt.py @@ -0,0 +1,192 @@ +""" +pliblog的格式化工具,用于格式化日志输出 +pliblog's formatter, used to format log output + +""" +# encoding = utf-8 +# python 3.13.5 + +from .configs import get_config +from typing import Any, Optional, Union +from .time import get_asctime, get_time, get_weekday, get_date +from .styles import set_color, set_bg_color +import re + +def fmt_level(level: str) -> int: + """ + 格式化日志级别 + Format log level + :param level: 日志级别 Log level + :return: 格式化后的日志级别 Formatted log level + """ + level_map = { + "DEBUG": 10, + "INFO": 20, + "WARN": 30, + "ERRO": 40, + "CRIT": 50, + } + if level == "UNKN": + return -1 + return level_map.get(level.upper(), 0) + +def fmt_level_number(level: int) -> str: + """ + 格式化日志级别数字 + Format log level number + :param level: 日志级别数字 Log level number + :return: 格式化后的日志级别 Formatted log level + """ + if level <= 10 and level >= 0: + return "DEBUG" + elif level <= 20 and level > 10: + return "INFO" + elif level <= 30 and level > 20: + return "WARN" + elif level <= 40 and level > 30: + return "ERRO" + elif level <= 50 and level > 40: + return "CRIT" + else: + return "UNKN" + +def fmt_placeholder(message: Any, use_date_color: bool = True) -> str: + """ + 格式化占位符 + Format placeholder + :param message: 消息内容 Message content + :return: 格式化后的消息 Formatted message + """ + class SafeDict(dict): + def __missing__(self, key): + return f'{{{key}}}' + + if not isinstance(message, str): + message = str(message) + if use_date_color: + message = message.format_map(SafeDict( + asctime = set_color(get_asctime(),"#28ffb6"), + time = set_color(get_time(),"#28ffb6"), + weekday = set_color(get_weekday(),"#28ffb6"), + date = set_color(get_date(),"#28ffb6") + )) + else: + message = message.format_map(SafeDict( + asctime = get_asctime(), + time = get_time(), + weekday = get_weekday(), + date = get_date(), + )) + return message + +def fmt_message(message: Any, no_placeholder: bool = False) -> str: + """ + 格式化消息内容 + Format message content + :param message: 消息内容 Message content + :return: 格式化后的消息 Formatted message + """ + + def process_color_tags(msg: str) -> str: + stack = [] + result = [] + current_content = [] + last_end = 0 + pattern = re.compile(r'(<#([0-9a-fA-F]{6})>|)') + + for match in pattern.finditer(msg): + text_between = msg[last_end:match.start()] + if stack: + current_content.append(text_between) + else: + result.append(text_between) + + last_end = match.end() + tag = match.group(1) + + if tag.startswith('<#'): + color_code = match.group(2) + stack.append(color_code) + else: + if stack: + color = stack.pop() + colored_content = set_color(''.join(current_content), f'#{color}') + result.append(colored_content) + current_content = [] + else: + result.append(tag) + + remaining_text = msg[last_end:] + if stack: + current_content.append(remaining_text) + else: + result.append(remaining_text) + + for color in reversed(stack): + result.append(f'<#{color}>') + result.append(''.join(current_content)) + current_content = [] + + return ''.join(result) + if no_placeholder: + return process_color_tags(str(message)) + else: + return process_color_tags(fmt_placeholder(str(message))) + +def fmt_level_name(level_name: str) -> str: + if get_config("console_color") != True: + return level_name + level_name_nick_map = get_config("level_name") + + if level_name in level_name_nick_map: + _lnn = level_name_nick_map[level_name] + level_color_map = get_config("level_color") + + if level_name in level_color_map: + if level_name == "DEBUG": + return set_bg_color(set_color(_lnn, level_color_map[level_name]), "#34495e") + return set_color(_lnn, level_color_map[level_name]) + return set_color(_lnn) + return "UNKN" + +def fmt_console(level: int, message: Any, prefix: str | None = None) -> Optional[str]: + """ + 格式化控制台输出 + Format console output + :param level: 日志级别 Log level + :param message: 消息内容 Message content + :return: 格式化后的消息 Formatted message + """ + cl = get_config("console_level") + fmt = get_config("console_format") + if fmt_level(cl) > level: + return None + if prefix is None: + prefix = "" + fmt = fmt_placeholder(fmt) + return fmt.format( + levelname = fmt_level_name(fmt_level_number(level)), + prefix = prefix, + message = fmt_message(message, no_placeholder=True) + ) + +def fmt_file(level: int, message: Any, prefix: str | None = None) -> Optional[str]: + """ + 格式化文件输出 + Format file output + :param level: 日志级别 Log level + :param message: 消息内容 Message content + :return: 格式化后的消息 Formatted message + """ + fl = get_config("file_level") + fmt = get_config("file_format") + if fmt_level(fl) > level: + return None + if prefix is None: + prefix = "" + fmt = fmt_placeholder(fmt) + return fmt.format( + levelname = fmt_level_name(fmt_level_number(level)), + prefix = prefix, + message = fmt_message(message, no_placeholder=True) + ) diff --git a/src/utils/styles.py b/src/utils/styles.py new file mode 100644 index 0000000..f81da56 --- /dev/null +++ b/src/utils/styles.py @@ -0,0 +1,50 @@ +""" +pliblog的样式工具,用于格式化日志输出 +pliblog's style tools, used to format log output + +""" +# encoding = utf-8 +# python 3.13.5 + +from .configs import get_config +from typing import Union, Optional + +def _get_hex_to_ansi(hex_color: str) -> Union[Optional[str], None]: + """ + 将16进制颜色值转换为ANSI转义序列 + Convert hex color value to ANSI escape sequence + :param hex_color: 16进制颜色值 Hex color value + :return: ANSI转义序列 ANSI escape sequence + """ + if not hex_color.startswith("#"): + return None + r, g, b = int(hex_color[1:3], 16), int(hex_color[3:5], 16), int(hex_color[5:7], 16) + return f"\033[38;2;{r};{g};{b}m" + +def set_color(text: str, color: str) -> str: + """ + 设置文本颜色 + Set text color + :param text: 文本内容 Text content + :param color: 颜色值 Color value + :return: 格式化后的文本 Formatted text + """ + ansi = _get_hex_to_ansi(color) + if not ansi: + return text + return f"{ansi}{text}\033[0m" + +def set_bg_color(text: str, color: str) -> str: + """ + 设置文本背景颜色 + Set text background color + :param text: 文本内容 Text content + :param color: 颜色值 Color value + :return: 格式化后的文本 Formatted text + """ + ansi = _get_hex_to_ansi(color) + if not ansi: + return text + # 将前景色ANSI代码转换为背景色代码 (38→48) + ansi = ansi.replace("38;", "48;") + return f"{ansi}{text}\033[0m" diff --git a/src/utils/time.py b/src/utils/time.py new file mode 100644 index 0000000..068aa9e --- /dev/null +++ b/src/utils/time.py @@ -0,0 +1,64 @@ +""" +时间工具模块,用于时间格式化和输出,缓存时间格式化结果 +Time utility module, used for time formatting and output, caching time formatting results + +""" +# encoding = utf-8 +# python 3.13.5 + +from datetime import datetime +from .configs import get_config + +cache_time: str = "" +cache_asctime: str = "" +cache_date: str = "" +cache_weekday: str = "" + +def get_asctime() -> str: + """ + 获取当前时间(YYYY-MM-DD HH:MM:SS),并缓存格式化结果 + Get current time(YYYY-MM-DD HH:MM:SS) and cache formatted result + :return: 格式化后的时间 Formatted time + """ + global cache_asctime + if cache_asctime: + return cache_asctime + cache_asctime = datetime.now().strftime(get_config("date_format")) + return cache_asctime + +def get_time() -> str: + """ + 获取当前时间(HH:MM:SS),并缓存格式化结果 + Get current time(HH:MM:SS) and cache formatted result + :return: 格式化后的时间 Formatted time + """ + global cache_time + if cache_time: + return cache_time + cache_time = datetime.now().strftime("%H:%M:%S") + return cache_time + +def get_date() -> str: + """ + 获取当前日期(YYYY-MM-DD),并缓存格式化结果 + 获取当前日期(星期几),并缓存格式化结果 + Get current date(YYYY-MM-DD) and cache formatted result + :return: 格式化后的日期 Formatted date + """ + global cache_date + if cache_date: + return cache_date + cache_date = datetime.now().strftime("%Y-%m-%d") + return cache_date + +def get_weekday() -> str: + """ + 获取当前日期(星期几),并缓存格式化结果 + Get current date(weekday) and cache formatted result + :return: 格式化后的星期几 Formatted weekday + """ + global cache_weekday + if cache_weekday: + return cache_weekday + cache_weekday = datetime.now().strftime("%A") + return cache_weekday