From b719e08d0fa8b2a5b49cd7d983b6ad4059d15ceb Mon Sep 17 00:00:00 2001 From: Nanaloveyuki Date: Wed, 30 Jul 2025 21:33:00 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=A9=20=E2=99=BB=EF=B8=8F=20=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E4=BA=86`fmt`=E5=92=8C`config`=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- log.txt | 178 ++++++++++++++++ logger_config.json | 7 +- src/logiliteal/__init__.py | 31 ++- src/logiliteal/levels.py | 139 +++++++++++++ src/logiliteal/levels/__init__.py | 11 - src/logiliteal/levels/levels.py | 150 -------------- src/logiliteal/utils/__init__.py | 6 +- src/logiliteal/utils/configs.py | 243 +++++++++------------- src/logiliteal/utils/env.py | 53 ++++- src/logiliteal/utils/formats.py | 324 +++++++++++++++++++++--------- src/logiliteal/utils/regex.py | 14 +- src/logiliteal/utils/styles.py | 12 +- src/logiliteal/utils/time.py | 20 +- test/t-color.py | 31 +++ tests/t-color-priority.py | 53 ----- tests/t-configs.py | 26 --- tests/t-fmt-text.py | 34 ---- tests/t-fmt.py | 50 ----- tests/t-level.py | 15 -- tests/t-module.py | 4 - tests/t-multithread.py | 37 ---- tests/t-repeat.py | 15 -- 22 files changed, 768 insertions(+), 685 deletions(-) create mode 100644 log.txt create mode 100644 src/logiliteal/levels.py delete mode 100644 src/logiliteal/levels/__init__.py delete mode 100644 src/logiliteal/levels/levels.py create mode 100644 test/t-color.py delete mode 100644 tests/t-color-priority.py delete mode 100644 tests/t-configs.py delete mode 100644 tests/t-fmt-text.py delete mode 100644 tests/t-fmt.py delete mode 100644 tests/t-level.py delete mode 100644 tests/t-module.py delete mode 100644 tests/t-multithread.py delete mode 100644 tests/t-repeat.py diff --git a/log.txt b/log.txt new file mode 100644 index 0000000..ff8cb09 --- /dev/null +++ b/log.txt @@ -0,0 +1,178 @@ +这是一条调试信息这是**粗体**这是*斜体*这是__下划线__这是~~删除线~~这是**粗斜体**这是__粗下划线__这是~~删除线~~这是<#ff0000>红色这是<#00ff00>绿色这是<#0000ff>蓝色这是<#ff0000>嵌<#00ff00>套这是<#ff0000>不完全闭合这是<#ff0000>样式清理这是粗体这是斜体这是下划线这是删除线这是粗斜体这是粗下划线这是粗删除线<#ff0000>111<#ff0000>111这是前缀这是后缀这是一条调试信息 +这是**粗体** +这是*斜体* +这是__下划线__ +这是~~删除线~~ +这是**粗斜体** +这是__粗下划线__ +这是~~删除线~~ +这是<#ff0000>红色 +这是<#00ff00>绿色 +这是<#0000ff>蓝色 +这是<#ff0000>嵌<#00ff00>套 +这是<#ff0000>不完全闭合 +这是<#ff0000>样式清理 +这是粗体 +这是斜体 +这是下划线 +这是删除线 +这是粗斜体 +这是粗下划线 +这是粗删除线 +<#ff0000>111<#ff0000>111这是前缀 +这是后缀 +这是一条调试信息 +这是**粗体** +这是*斜体* +这是__下划线__ +这是~~删除线~~ +这是**粗斜体** +这是__粗下划线__ +这是~~删除线~~ +这是<#ff0000>红色 +这是<#00ff00>绿色 +这是<#0000ff>蓝色 +这是<#ff0000>嵌<#00ff00>套 +这是<#ff0000>不完全闭合 +这是<#ff0000>样式清理 +这是粗体 +这是斜体 +这是下划线 +这是删除线 +这是粗斜体 +这是粗下划线 +这是粗删除线 +这是一条调试信息 + +这是**粗体** + +这是*斜体* + +这是__下划线__ + +这是~~删除线~~ + +这是**粗斜体** + +这是__粗下划线__ + +这是~~删除线~~ + +这是<#ff0000>红色 + +这是<#00ff00>绿色 + +这是<#0000ff>蓝色 + +这是<#ff0000>嵌<#00ff00>套 + +这是<#ff0000>不完全闭合 + +这是<#ff0000>样式清理 + +这是粗体 + +这是斜体 + +这是下划线 + +这是删除线 + +这是粗斜体 + +这是粗下划线 + +这是粗删除线 + +这是一条调试信息 + +这是**粗体** + +这是*斜体* + +这是__下划线__ + +这是~~删除线~~ + +这是**粗斜体** + +这是__粗下划线__ + +这是~~删除线~~ + +这是<#ff0000>红色 + +这是<#00ff00>绿色 + +这是<#0000ff>蓝色 + +这是<#ff0000>嵌<#00ff00>套 + +这是<#ff0000>不完全闭合 + +这是<#ff0000>样式清理 + +这是粗体 + +这是斜体 + +这是下划线 + +这是删除线 + +这是粗斜体 + +这是粗下划线 + +这是粗删除线 + +<#ff0000>111<#ff0000>111这是前缀 + +这是后缀 + +这是一条调试信息 + +这是**粗体** + +这是*斜体* + +这是__下划线__ + +这是~~删除线~~ + +这是**粗斜体** + +这是__粗下划线__ + +这是~~删除线~~ + +这是<#ff0000>红色 + +这是<#00ff00>绿色 + +这是<#0000ff>蓝色 + +这是<#ff0000>嵌<#00ff00>套 + +这是<#ff0000>不完全闭合 + +这是<#ff0000>样式清理 + +这是粗体 + +这是斜体 + +这是下划线 + +这是删除线 + +这是粗斜体 + +这是粗下划线 + +这是粗删除线 + +<#ff0000>111<#ff0000>111这是前缀 + +这是后缀 + diff --git a/logger_config.json b/logger_config.json index 283a3b0..3904415 100644 --- a/logger_config.json +++ b/logger_config.json @@ -2,20 +2,19 @@ "file_level": "DEBUG", "file_name": "latest.log", "file_path": "./logs", - "file_format": "{asctime} {levelname} | {prefix}{message}", + "file_format": "{asctime} {levelname} | {prefix}{message}{suffix}", "file_encoding": "utf-8", "enable_console": true, "enable_file": true, "console_color": true, "console_level": "DEBUG", - "console_format": "{time} {levelname} | {prefix}{message}", - "console_prefix": "Auto", + "console_format": "{time} {levelname} | {prefix}{message}{suffix}", "console_encoding": "utf-8", "asctime_format": "%Y-%m-%d %H:%M:%S", "time_format": "%H:%M:%S", "date_format": "%Y-%m-%d", "weekday_format": "%A", - "level_name": { + "level_nickname": { "DEBUG": "DEBUG", "INFO": "INFO", "WARN": "WARN", diff --git a/src/logiliteal/__init__.py b/src/logiliteal/__init__.py index 8daeb7f..d1a37d9 100644 --- a/src/logiliteal/__init__.py +++ b/src/logiliteal/__init__.py @@ -5,9 +5,9 @@ py-logiliteal's config settings, used to set py-logiliteal's global config # encoding = utf-8 # python 3.13.5 -from .utils import get_config, set_config, reset_config, create_backup -from .utils import get_asctime, get_date, get_time, get_weekday -from .utils import fmt_console, fmt_placeholder, fmt_message, fmt_level_name +from .utils import get_config, set_config, reset_config, backup_config, restore_config, init_config +from .utils import get_asctime, get_date, get_time, get_weekday, _get_time +from .utils import fmt_console, fmt_content, fmt_file, fmt_level_list, fmt_level_name_to_number, fmt_level_number_to_name, fmt_placeholder, fmt_regex, add_placeholder, remove_placeholder, get_placeholder from .utils import set_style, set_color, set_bg_color from .levels import Logger @@ -17,18 +17,31 @@ __all__ = [ "get_config", "set_config", "reset_config", + "backup_config", + "restore_config", + "init_config", "get_asctime", "get_date", "get_time", "get_weekday", + "_get_time", "fmt_console", + "fmt_content", + "fmt_file", + "fmt_level_list", + "fmt_level_name_to_number", + "fmt_level_number_to_name", "fmt_placeholder", - "fmt_message", - "fmt_level_name", - "set_style", + "fmt_regex", + "add_placeholder", + "remove_placeholder", + "get_placeholder", "set_color", "set_bg_color", - "create_backup", - "Logger", # 日志记录器非实例化 - "logger" # 日志记录器实例化 + "set_style", + "_placeholder", + "_level_name_map", + "DEFAULT_CONFIG", + "logger", + "Logger", ] \ No newline at end of file diff --git a/src/logiliteal/levels.py b/src/logiliteal/levels.py new file mode 100644 index 0000000..c6e607d --- /dev/null +++ b/src/logiliteal/levels.py @@ -0,0 +1,139 @@ +""" +日志级别模块 +Log level module + +""" +# encoding: utf-8 +# python 3.13.5 + +from .utils import fmt_console, fmt_file, fmt_content +from typing import Any, Optional + +class Logger: + def __init__(self): + pass + + def _log( + self, + message: Any, + prefix: str = "", + suffix: str = "", + level_number: int = 0, + no_console: bool = False, + no_file: bool = False, + no_placeholder: bool = False, + no_regex: bool = False, + no_color: bool = False, + no_html: bool = False, + no_link: bool = False, + no_markdown: bool = False, + no_special_tags: bool = False, + ) -> Optional[str]: + """ + 同步日志方法 + Synchronous logging method + Args: + message (Any): 日志消息 Log message + level_number (int, optional): 日志级别编号. Defaults to 0. + no_console (bool, optional): 是否禁用控制台输出. Defaults to False. + no_file (bool, optional): 是否禁用文件输出. Defaults to False. + no_placeholder (bool, optional): 是否禁用占位符替换. Defaults to False. + no_regex (bool, optional): 是否禁用正则表达式替换. Defaults to False. + no_color (bool, optional): 是否禁用颜色输出. Defaults to False. + no_html (bool, optional): 是否禁用HTML标签输出. Defaults to False. + no_link (bool, optional): 是否禁用链接输出. Defaults to False. + no_markdown (bool, optional): 是否禁用Markdown格式输出. Defaults to False. + no_special_tags (bool, optional): 是否禁用特殊标签输出. Defaults to False. + **kwargs: 其他参数同fmt_console和fmt_file + Returns: + Optional[Union[str, bool]]: 格式化后的日志消息或False + """ + if not no_console: + print(fmt_console( + message=message, + level_number=level_number, + prefix=prefix, + suffix=suffix, + no_placeholder=no_placeholder, + no_regex=no_regex, + no_color=no_color, + no_special_tags=no_special_tags, + )) + if not no_file: + with open("log.txt", "a", encoding="utf-8") as f: + f.write(fmt_file( + message=message, + prefix=prefix, + suffix=suffix, + level_number=level_number, + no_placeholder=no_placeholder, + no_regex=no_regex, + no_color=no_color, + no_html=no_html, + no_link=no_link, + no_markdown=no_markdown, + no_special_tags=no_special_tags, + )) + return fmt_content( + message=message, + prefix=prefix, + suffix=suffix, + level_number=level_number, + no_placeholder=no_placeholder, + no_regex=no_regex, + no_color=no_color, + no_html=no_html, + no_link=no_link, + no_markdown=no_markdown, + no_special_tags=no_special_tags, + ) + + + def debug( + self, + message: Any, + prefix: str = "", + suffix: str = "", + level_number: int = 0, + no_console: bool = False, + no_file: bool = False, + no_placeholder: bool = False, + no_regex: bool = False, + no_color: bool = False, + no_html: bool = False, + no_link: bool = False, + no_markdown: bool = False, + no_special_tags: bool = False, + ): + """ + 同步debug日志方法 + Synchronous debug logging method + Args: + message (Any): 日志消息 Log message + level_number (int, optional): 日志级别编号. Defaults to 0. + no_console (bool, optional): 是否禁用控制台输出. Defaults to False. + no_file (bool, optional): 是否禁用文件输出. Defaults to False. + no_placeholder (bool, optional): 是否禁用占位符替换. Defaults to False. + no_regex (bool, optional): 是否禁用正则表达式替换. Defaults to False. + no_color (bool, optional): 是否禁用颜色输出. Defaults to False. + no_html (bool, optional): 是否禁用HTML标签输出. Defaults to False. + no_link (bool, optional): 是否禁用链接输出. Defaults to False. + no_markdown (bool, optional): 是否禁用Markdown格式输出. Defaults to False. + no_special_tags (bool, optional): 是否禁用特殊标签输出. Defaults to False. + 其他参数同fmt_console和fmt_file + """ + self._log( + message=message, + prefix=prefix, + suffix=suffix, + level_number=level_number, + no_console=no_console, + no_file=no_file, + no_placeholder=no_placeholder, + no_regex=no_regex, + no_color=no_color, + no_html=no_html, + no_link=no_link, + no_markdown=no_markdown, + no_special_tags=no_special_tags, + ) diff --git a/src/logiliteal/levels/__init__.py b/src/logiliteal/levels/__init__.py deleted file mode 100644 index 35471cb..0000000 --- a/src/logiliteal/levels/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -""" -日志级别模块 -Log level module - -""" -# encoding: utf-8 -# python 3.13.5 - -from .levels import Logger - -__all__ = ["Logger"] \ No newline at end of file diff --git a/src/logiliteal/levels/levels.py b/src/logiliteal/levels/levels.py deleted file mode 100644 index a13d782..0000000 --- a/src/logiliteal/levels/levels.py +++ /dev/null @@ -1,150 +0,0 @@ -""" -日志级别模块 -Log level module - -""" -# encoding: utf-8 -# python 3.13.5 - -from typing import Optional, Any -from ..utils.fmt import fmt_file, fmt_message, fmt_console -from ..utils.configs import get_config, set_config -from ..utils.time import get_asctime -import pathlib -from pathlib import Path -import sys -import os - -def _get_full_path(file_path, file_name): - file_path.mkdir(parents=True, exist_ok=True) - return file_path / file_name - -class Logger: - def __init__(self): - # 检测虚拟环境并确定项目根目录 - python_path = sys.executable - project_root = Path.cwd() - - # 如果在虚拟环境中,向上查找项目根目录 - if 'venv' in python_path.lower(): - # 分割路径并查找venv目录 - path_parts = os.path.normpath(python_path).split(os.sep) - if 'venv' in path_parts: - venv_index = path_parts.index('venv') - project_root = Path(os.sep.join(path_parts[:venv_index])) - - file_path = project_root / get_config("file_path") - file_path.mkdir(parents=True, exist_ok=True) - if file_path.exists(): - if not file_path.is_dir(): - self.warn("日志文件路径不是目录,已自动使用默认目录") - set_config("file_path", "./logs") - pathlib.Path("./logs").mkdir(parents=True, exist_ok=True) - current_file = _get_full_path(file_path, get_config("file_name")) - if current_file.exists(): - from os import rename - rename(current_file, _get_full_path(file_path, f"{get_asctime().replace(':', '-')}.log")) - self.debug("日志文件已存在,已自动重命名") - - def _log(self, msg, pf, lvn, no_file: bool = False, no_console: bool = False): - # 检测虚拟环境并确定项目根目录 - python_path = sys.executable - project_root = Path.cwd() - - # 如果在虚拟环境中,向上查找项目根目录 - if 'venv' in python_path.lower(): - # 分割路径并查找venv目录 - path_parts = os.path.normpath(python_path).split(os.sep) - if 'venv' in path_parts: - venv_index = path_parts.index('venv') - project_root = Path(os.sep.join(path_parts[:venv_index])) - - file_path = project_root / get_config("file_path") - file_path.mkdir(parents=True, exist_ok=True) - file_name = get_config("file_name") - file_encoding = get_config("file_encoding") - is_enable_file = get_config("enable_file") - is_enable_console = get_config("enable_console") - if not no_file and is_enable_file: - try: - with open(_get_full_path(file_path, file_name), "a", encoding=file_encoding) as f: - f.write(fmt_file(lvn, fmt_message(msg, no_placeholder=True, no_process=True), pf)) - except Exception as e: - self.error(f"日志写入失败: {str(e)}", no_file=True) - if not no_console and is_enable_console: - console_output = fmt_console(lvn, fmt_message(msg, no_placeholder=True), pf) - if console_output is not None: - print(console_output) - return fmt_console(lvn, fmt_message(msg, no_placeholder=True), pf) - - def debug(self, message: Any, prefix: str | None = None, level: int = 0, no_file: bool = False, no_console: bool = False) -> Optional[str]: - """ - 调试日志 - Debug log - :param message: 消息内容 Message content - :param prefix: 前缀 Prefix - :param level: 日志级别 Log level(0~9) - :param no_file: 不写入文件 Do not write to file - :param no_console: 不输出到控制台 Do not output to console - """ - return self._log(message, prefix, level, no_file, no_console) - - def info(self, message: Any, prefix: str | None = None, level: int = 10, no_file: bool = False, no_console: bool = False) -> Optional[str]: - """ - 信息日志 - Info log - :param message: 消息内容 Message content - :param prefix: 前缀 Prefix - :param level: 日志级别 Log level(10~19) - :param no_file: 不写入文件 Do not write to file - :param no_console: 不输出到控制台 Do not output to console - """ - return self._log(message, prefix, level, no_file, no_console) - - def warn(self, message: Any, prefix: str | None = None, level: int = 20, no_file: bool = False, no_console: bool = False) -> Optional[str]: - """ - 警告日志 - Warn log - :param message: 消息内容 Message content - :param prefix: 前缀 Prefix - :param level: 日志级别 Log level(20~29) - :param no_file: 不写入文件 Do not write to file - :param no_console: 不输出到控制台 Do not output to console - """ - return self._log(message, prefix, level, no_file, no_console) - - def error(self, message: Any, prefix: str | None = None, level: int = 30, no_file: bool = False, no_console: bool = False) -> Optional[str]: - """ - 错误日志 - Error log - :param message: 消息内容 Message content - :param prefix: 前缀 Prefix - :param level: 日志级别 Log level(30~39) - :param no_file: 不写入文件 Do not write to file - :param no_console: 不输出到控制台 Do not output to console - """ - return self._log(message, prefix, level, no_file, no_console) - - def critical(self, message: Any, prefix: str | None = None, level: int = 40, no_file: bool = False, no_console: bool = False) -> Optional[str]: - """ - 严重错误日志 - Critical error log - :param message: 消息内容 Message content - :param prefix: 前缀 Prefix - :param level: 日志级别 Log level(40~49) - :param no_file: 不写入文件 Do not write to file - :param no_console: 不输出到控制台 Do not output to console - """ - return self._log(message, prefix, level, no_file, no_console) - - def log(self, message: Any, prefix: str | None = None, level: int = 50, no_file: bool = False, no_console: bool = False) -> Optional[str]: - """ - 自定义日志 - Custom log - :param message: 消息内容 Message content - :param prefix: 前缀 Prefix - :param level: 日志级别 Log level(50~59...) - :param no_file: 不写入文件 Do not write to file - :param no_console: 不输出到控制台 Do not output to console - """ - return self._log(message, prefix, level, no_file, no_console) diff --git a/src/logiliteal/utils/__init__.py b/src/logiliteal/utils/__init__.py index 190139a..bcc146b 100644 --- a/src/logiliteal/utils/__init__.py +++ b/src/logiliteal/utils/__init__.py @@ -5,7 +5,7 @@ Utility functions # encoding = utf-8 # python 3.13.5 -from .configs import get_config, set_config, reset_config, create_backup +from .configs import get_config, set_config, reset_config, backup_config, restore_config, init_config from .time import get_asctime, get_date, get_time, get_weekday, _get_time from .formats import fmt_console, fmt_content, fmt_file, fmt_level_list, fmt_level_name_to_number, fmt_level_number_to_name, fmt_placeholder, fmt_regex, add_placeholder, remove_placeholder, get_placeholder from .styles import set_color, set_bg_color, set_style @@ -15,7 +15,9 @@ __all__ = [ "get_config", "set_config", "reset_config", - "create_backup", + "backup_config", + "restore_config", + "init_config", "get_asctime", "get_date", "get_time", diff --git a/src/logiliteal/utils/configs.py b/src/logiliteal/utils/configs.py index 1edce89..3e2441e 100644 --- a/src/logiliteal/utils/configs.py +++ b/src/logiliteal/utils/configs.py @@ -7,166 +7,107 @@ py-logiliteal's config settings, used to set py-logiliteal's global config # python 3.13.5 import json -from os import remove -import shutil -import os -from pathlib import Path -from typing import Union, Optional, Tuple -from logging import error - -def get_config_path(): - # 检查环境变量 - env_config = os.getenv('LOGILITEAL_CONFIG') - if env_config and os.path.exists(env_config): - return env_config - - # 检查当前工作目录 - cwd_config = os.path.join(os.getcwd(), 'logger_config.json') - if os.path.exists(cwd_config): - return cwd_config - - # 检查XDG配置目录 - xdg_config_dir = os.getenv('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config')) - xdg_config_path = os.path.join(xdg_config_dir, 'logiliteal', 'logger_config.json') - - # 创建目录(如果不存在) - if not os.path.exists(os.path.dirname(xdg_config_path)): - try: - os.makedirs(os.path.dirname(xdg_config_path), exist_ok=True) - except Exception as e: - error(f"创建配置目录失败: {e}") - # 回退到项目根目录的配置文件 - project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..')) - fallback_config = os.path.join(project_root, 'logger_config.json') - if os.path.exists(fallback_config): - return fallback_config - else: - return xdg_config_path - - return xdg_config_path - -DEFAULT_CONFIG_PATH = get_config_path() - - -g_config_cache = None -g_config_mtime = 0 - -def create_backup(config_path: Path) -> Tuple[bool, str]: +from .env import DEFAULT_CONFIG, get_config_path +import pathlib +from typing import Any +def init_config(config: dict = DEFAULT_CONFIG, config_path: str = None) -> bool: """ - 创建配置文件备份 - :param config_path: 配置文件路径 - :param backup_prefix: 备份文件前缀 - :return: (是否成功, 备份路径或错误信息) + 初始化配置 + Init config + Args: + config (dict, optional): 配置字典. Defaults to DEFAULT_CONFIG. + config_path (str, optional): 配置文件路径. Defaults to None. + Returns: + bool: 是否成功初始化配置 """ - try: - if not config_path.exists(): - return False, f"配置文件不存在: {config_path}" - backup_path = config_path.parent / f"logger_config_backup.json" + if config_path is None: + config_path = str(get_config_path()) + dump_config = json.dumps(config, indent=4) + with open(config_path, "w") as f: + f.write(dump_config) + return True - if backup_path.exists(): - remove(backup_path) - shutil.copy2(config_path, backup_path) - return True, str(backup_path) - except PermissionError: - return False, f"权限不足,无法创建备份: {config_path}" - except Exception as e: - return False, f"备份失败: {str(e)}" - -def handle_config_exceptions(func): +def get_config(key: str | None = None) -> Any: """ - 配置操作异常处理装饰器 + 获取配置 + Get config + Args: + key (str | None, optional): 配置键. Defaults to None. + Returns: + Any: 配置值 """ - def wrapper(*args, **kwargs): - try: - return func(*args, **kwargs) - except json.JSONDecodeError as e: - error(f"配置文件格式错误: {e}") - return False, f"配置文件格式错误: {str(e)}" - except PermissionError: - error(f"没有权限操作配置文件: {DEFAULT_CONFIG_PATH}") - return False, "没有权限操作配置文件" - except Exception as e: - error(f"配置操作失败: {e}") - return False, f"配置操作失败: {str(e)}" - return wrapper - -def get_config(select: str = None) -> Union[dict, str, bool, int, None]: - """ - 获取配置信息 Get config info - :param select: 配置项名称 Config item name - :return: 配置项值 Config item value - """ - global g_config_cache, g_config_mtime - config_path = Path(DEFAULT_CONFIG_PATH) - - if config_path.exists(): - current_mtime = config_path.stat().st_mtime - if current_mtime != g_config_mtime or g_config_cache is None: - with open(config_path, "r", encoding="utf-8") as f: - g_config_cache = json.load(f) - g_config_mtime = current_mtime - else: - # 确保目录存在 - config_path.parent.mkdir(parents=True, exist_ok=True) - with open(config_path, "w", encoding="utf-8") as f: - json.dump(DEFAULT_CONFIG, f, indent=4) - g_config_cache = DEFAULT_CONFIG - g_config_mtime = config_path.stat().st_mtime - - if select: - return g_config_cache.get(select) - return g_config_cache - -def set_config(select: str, value: Union[dict, str, bool, int, None]) -> tuple[bool, Optional[str]]: - """ - 设置配置信息 Set config info - :param select: 配置项名称 Config item name - :param value: 配置项值 Config item value - :return: (设置是否成功, 消息) (Set success or not, message) - """ - config_path = Path(DEFAULT_CONFIG_PATH) - - if not config_path.exists(): - with open(config_path, "w", encoding="utf-8") as f: - json.dump(DEFAULT_CONFIG, f, indent=4) - - backup_success, backup_info = create_backup(config_path) - if not backup_success: - return False, f"备份失败: {backup_info}" - - with open(config_path, "r+", encoding="utf-8") as f: + if not pathlib.Path(str(get_config_path())).exists(): + init_config() + with open(str(get_config_path()), "r") as f: config = json.load(f) - config[select] = value - f.seek(0) + if key is None: + return config + return config.get(key, None) + +def set_config(key: str, value: Any) -> bool: + """ + 设置配置 + Set config + Args: + key (str): 配置键 + value (Any): 配置值 + Returns: + bool: 是否成功设置配置 + """ + if not pathlib.Path(str(get_config_path())).exists(): + init_config() + with open(str(get_config_path()), "r") as f: + config = json.load(f) + config[key] = value + with open(str(get_config_path()), "w") as f: json.dump(config, f, indent=4) - f.truncate() + return config.get(key, None) == value - with open(config_path, "r", encoding="utf-8") as f: - verify_config = json.load(f) - if verify_config.get(select) != value: - shutil.move(backup_info, config_path) - return False, f"配置项 '{select}' 设置失败,已恢复备份" - - global g_config_cache - g_config_cache = None - return True, f"配置项 '{select}' 设置成功" - -@handle_config_exceptions -def reset_config() -> tuple[bool, Optional[str]]: +def backup_config(backup_path: str = None) -> bool: """ - 重置配置信息 Reset config info - :return: (重置是否成功, 消息) (Reset success or not, message) + 备份配置 + Backup config + Args: + backup_path (str, optional): 备份文件路径. Defaults to None. + Returns: + bool: 是否成功备份配置 """ - config_path = Path(DEFAULT_CONFIG_PATH) + if not pathlib.Path(str(get_config_path())).exists(): + init_config() + if backup_path is None: + backup_path = str(get_config_path()) + ".backup" + with open(str(get_config_path()), "r") as f: + config = json.load(f) + backup_path = backup_path or str(get_config_path()) + ".backup" + with open(str(backup_path), "w") as f: + json.dump(config, f, indent=4) + return True - if config_path.exists(): - backup_success, backup_info = create_backup(config_path) - if not backup_success: - return False, f"备份失败: {backup_info}" +def restore_config(backup_path: str = None) -> bool: + """ + 恢复配置 + Restore config + Args: + backup_path (str, optional): 备份文件路径. Defaults to None. + Returns: + bool: 是否成功恢复配置 + """ + if not pathlib.Path(str(get_config_path())).exists(): + init_config() + if backup_path is None: + backup_path = str(get_config_path()) + ".backup" + with open(str(backup_path), "r") as f: + config = json.load(f) + with open(str(get_config_path()), "w") as f: + json.dump(config, f, indent=4) + return True - with open(config_path, "w", encoding="utf-8") as f: - json.dump(DEFAULT_CONFIG, f, indent=4) - - global g_config_cache - g_config_cache = None - return True, "配置已重置为默认值" +def reset_config() -> bool: + """ + 重置配置 + Reset config + Returns: + bool: 是否成功重置配置 + """ + init_config(config=DEFAULT_CONFIG) + return True diff --git a/src/logiliteal/utils/env.py b/src/logiliteal/utils/env.py index c184a96..c4a04b4 100644 --- a/src/logiliteal/utils/env.py +++ b/src/logiliteal/utils/env.py @@ -1,6 +1,10 @@ """ 环境变量或常量与配置文件 +Environment variables, constants and configuration files + """ +# encoding = utf-8 +# python 3.13.5 # 默认配置 DEFAULT_CONFIG = { @@ -13,7 +17,7 @@ DEFAULT_CONFIG = { "enable_file": True, "console_color": True, "console_level": "DEBUG", - "console_format": "{set_color(time, await get_config('time_color'))} {levelname} | {prefix}{message}{suffix}", + "console_format": "{time} {levelname} | {prefix}{message}{suffix}", "console_encoding": "utf-8", "asctime_format": "%Y-%m-%d %H:%M:%S", "time_format": "%H:%M:%S", @@ -26,12 +30,12 @@ DEFAULT_CONFIG = { # 占位符 _placeholder: dict[str, str | bytes] = { - "asctime": "{get_asctime()}", - "time": "{get_time()}", + "asctime": "{set_color(get_asctime(), get_config('asctime_color') or '#c1ffff')}", + "time": "{set_color(get_time(), get_config('time_color') or '#28ffb6')}", "weekday": "{get_weekday()}", "date": "{get_date()}", - "levelname": "{await fmt_level_number_to_name(level_number)}", - "level_number": "{await fmt_level_name_to_number(levelname)}", + "levelname": "{fmt_level_number_to_name(level_number)}", + "level_number": "{level_number}", "message": "{message}", "prefix": "{prefix}", "suffix": "{suffix}", @@ -45,4 +49,41 @@ _level_name_map: dict[str, int | float] = { "ERRO": 30, "CRIT": 40, "UNKN": -1, -} \ No newline at end of file +} + +def get_venv_path() -> str: + """ + 获取虚拟环境路径 + Get virtual environment path + """ + from os import path + from sys import prefix + return str(path.dirname(prefix)) + +def get_project_path() -> str: + """ + 获取项目路径 + Get project path + """ + from os import path + from sys import prefix + return str(path.dirname(path.dirname(prefix))) + +def get_config_path() -> str: + """ + 获取配置文件路径 + Get config file path + """ + from os import path + from sys import prefix + return str(path.join(path.dirname(path.dirname(prefix)), "config.json")) + +def get_log_path() -> str: + """ + 获取日志文件路径 + Get log file path + """ + from os import path + from sys import prefix + return str(path.join(path.dirname(path.dirname(prefix)), "logs")) + \ No newline at end of file diff --git a/src/logiliteal/utils/formats.py b/src/logiliteal/utils/formats.py index 8503660..faee7fd 100644 --- a/src/logiliteal/utils/formats.py +++ b/src/logiliteal/utils/formats.py @@ -1,41 +1,54 @@ """ 格式化工具 Formatting tools + """ +# encoding = utf-8 +# python 3.13.5 from .time import get_asctime, get_time, get_weekday, get_date from .styles import set_color from .configs import get_config from .regex import process_color_formatting, process_html_styles, process_links, process_markdown_formats, process_special_tags -from .env import _placeholder, _level_name_map +from .env import _placeholder, _level_name_map, DEFAULT_CONFIG +from typing import Any +import re -async def fmt_level_number_to_name(level_number: int | float) -> str: +class SafeDict(dict): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._placeholder = _placeholder + + def __getitem__(self, key): + return super().__getitem__(key).format(**self) + +def fmt_level_number_to_name(level_number: int | float) -> str: """ 格式化日志级别数字为名称 - Format log level number to name\n + Format log level number to name Args: level_number (int | float): 日志级别数字 Returns: str: 日志级别名称 """ if not level_number in range(0, 50): - return _level_name_map.get(-1) + return _level_name_map.get(-1, "UNKNOWN") if 0 <= level_number <= 9: - return _level_name_map.get(0) + return _level_name_map.get(0, "DEBUG") elif 10 <= level_number <= 19: - return _level_name_map.get(10) + return _level_name_map.get(10, "INFO") elif 20 <= level_number <= 29: - return _level_name_map.get(20) + return _level_name_map.get(20, "WARNING") elif 30 <= level_number <= 39: - return _level_name_map.get(30) + return _level_name_map.get(30, "ERROR") elif 40 <= level_number <= 49: - return _level_name_map.get(40) - return _level_name_map.get(-1) + return _level_name_map.get(40, "CRITICAL") + return _level_name_map.get(-1, "UNKNOWN") -async def fmt_level_name_to_number(level_name: str) -> int: +def fmt_level_name_to_number(level_name: str) -> int: """ 格式化日志级别名称为数字 - Format log level name to number\n + Format log level name to number Args: level_name (str): 日志级别名称 Returns: @@ -43,25 +56,24 @@ async def fmt_level_name_to_number(level_name: str) -> int: """ return _level_name_map.get(level_name.upper(), -1) -async def fmt_level_list(level_number: int | float, - level_name: str in _level_name_map.keys(), - ) -> dict: +def fmt_level_list(level_number: int | float) -> dict: """ 格式化日志级别数据字典 - Format log level data dict\n + Format log level data dict Args: level_number (int | float): 日志级别数字 - level_name (str): 日志级别名称 Returns: dict: 日志级别数据字典 """ + level_name = fmt_level_number_to_name(level_number) or "UNKNOWN" + level_nickname_config = get_config("level_nickname") or {} return { "level_number": level_number, - "level_nickname": get_config("level_nickname").get(level_name.upper(), level_name), + "level_nickname": level_nickname_config.get(level_name.upper(), level_name), "level_name": level_name.upper(), } -async def get_level_number(level_list: dict) -> int | float: +def get_level_number(level_list: dict) -> int | float: """ 获取日志级别数字 Get log level number @@ -72,7 +84,7 @@ async def get_level_number(level_list: dict) -> int | float: """ return level_list.get("level_number") -async def get_level_name(level_list: dict) -> str: +def get_level_name(level_list: dict) -> str: """ 获取日志级别名称 Get log level name @@ -83,7 +95,7 @@ async def get_level_name(level_list: dict) -> str: """ return level_list.get("level_name", "UNKN") -async def get_level_nickname(level_list: dict) -> str: +def get_level_nickname(level_list: dict) -> str: """ 获取日志级别昵称 Get log level nickname @@ -94,34 +106,54 @@ async def get_level_nickname(level_list: dict) -> str: """ return level_list.get("level_nickname", "UNKN") -async def fmt_placeholder( +def fmt_placeholder( message: str = "", - no_placeholder: bool = False, - **kwargs + level_number: int = 0, ) -> str: """ 格式化占位符 - Format placeholder\n + Format placeholder Args: message (str, optional): 日志消息. Defaults to "". - no_placeholder (bool, optional): 不使用占位符. Defaults to False. Returns: str: 格式化后的日志消息或原始消息 - """ - if no_placeholder: - return message - for key, value in kwargs.items(): - if key in _placeholder: - message = message.replace(_placeholder[key], str(value)) - else: - message = message.replace(f"{{{key}}}", str(value)) - return message + # 创建上下文字典,包含所有可能用到的函数和变量 + context = { + 'level_number': level_number, + 'fmt_level_list': fmt_level_list, + 'fmt_level_number_to_name': fmt_level_number_to_name, + 'get_config': get_config, + 'set_color': set_color, + 'get_asctime': get_asctime, + 'get_time': get_time, + 'get_date': get_date, + 'get_weekday': get_weekday, + } + + # 替换所有占位符 + for key, value in _placeholder.items(): + placeholder = f"{{{key}}}" + if placeholder in message: + if isinstance(value, str) and '{' in value and '}' in value: + try: + # 解析并执行占位符中的表达式 + expr = value.strip('{}') + evaluated_value = eval(expr, {}, context) + message = message.replace(placeholder, str(evaluated_value)) + except Exception as e: + # 添加详细的错误信息以帮助调试 + error_msg = f"{{ERROR: {type(e).__name__} - {str(e)} - Expr: {expr}}}" + message = message.replace(placeholder, error_msg) + else: + message = message.replace(placeholder, str(value)) + + return message.format(**SafeDict(level_list=fmt_level_list(level_number))) -async def add_placeholder(key: str, value: str | bytes) -> None: +def add_placeholder(key: str, value: str | bytes) -> None: """ 添加新的占位符 - Add new placeholder\n + Add new placeholder Args: key (str): 占位符键 value (str | bytes): 占位符值 @@ -129,10 +161,10 @@ async def add_placeholder(key: str, value: str | bytes) -> None: global _placeholder _placeholder[key] = value -async def get_placeholder(key: str | None = None) -> str | dict[str, str | bytes]: +def get_placeholder(key: str | None = None) -> str | dict[str, str | bytes]: """ 获取占位符 - Get placeholder\n + Get placeholder Args: key (str | None, optional): 占位符键. Defaults to None. Returns: @@ -142,15 +174,16 @@ async def get_placeholder(key: str | None = None) -> str | dict[str, str | bytes return _placeholder return _placeholder.get(key, "None") if key in _placeholder else "None" -async def remove_placeholder(key: str) -> bool: +def remove_placeholder(key: str) -> bool: """ 删除占位符 - Remove placeholder\n + Remove placeholder Args: key (str): 占位符键 Returns: bool: 删除成功返回True, 删除失败返回False """ + global _placeholder try: if key in _placeholder: del _placeholder[key] @@ -160,18 +193,18 @@ async def remove_placeholder(key: str) -> bool: except Exception: return False - -async def fmt_regex(message: str, - no_regex: bool = False, - no_color: bool = False, - no_html: bool = False, - no_link: bool = False, - no_markdown: bool = False, - no_special_tags: bool = False, -) -> str: +def fmt_regex( + message: str, + no_regex: bool = False, + no_color: bool = False, + no_html: bool = False, + no_link: bool = False, + no_markdown: bool = False, + no_special_tags: bool = False, + ) -> str: """ 格式化正则表达式 - Format regex\n + Format regex Args: message (str): 日志消息 Returns: @@ -180,11 +213,11 @@ async def fmt_regex(message: str, if no_regex | (no_color and no_html and no_link and no_markdown and no_special_tags): return message - return await process_color_formatting( - await process_html_styles( - await process_links( - await process_markdown_formats( - await process_special_tags(message, no_process=no_special_tags), + return process_color_formatting( + process_html_styles( + process_links( + process_markdown_formats( + process_special_tags(message, no_process=no_special_tags), no_process=no_markdown, ), no_process=no_link @@ -194,68 +227,169 @@ async def fmt_regex(message: str, no_process=no_color ) -async def fmt_content( - message: str = "", - no_placeholder: bool = False, - no_regex: bool = False, - no_color: bool = False, - no_html: bool = False, - no_link: bool = False, - no_markdown: bool = False, - no_special_tags: bool = False, -) -> str: +def fmt_content( + message: str = "", + prefix: str = "", + suffix: str = "", + level_number: int = 0, + no_placeholder: bool = False, + no_regex: bool = False, + no_color: bool = False, + no_html: bool = False, + no_link: bool = False, + no_markdown: bool = False, + no_special_tags: bool = False, + ) -> str: """ 格式化内容 - Format content\n + Format content Args: message (str): 日志消息 Returns: str: 格式化后的日志消息 """ - message = await fmt_placeholder(message, no_placeholder=no_placeholder) - message = await fmt_regex(message, no_regex=no_regex, no_color=no_color, no_html=no_html, no_link=no_link, no_markdown=no_markdown, no_special_tags=no_special_tags) - return message + if not no_placeholder: + message = fmt_placeholder(message, level_number=level_number) + if not no_regex | no_html and not no_link and not no_markdown and not no_special_tags and no_color: + message = fmt_regex(message, no_regex=no_regex, no_color=no_color, no_html=no_html, no_link=no_link, no_markdown=no_markdown, no_special_tags=no_special_tags) + return f"{prefix}{message}{suffix}\n" -async def fmt_console( - message: str = "", + +def fmt_console( + message: Any, + level_number: int | float, + prefix: str = "", + suffix: str = "", no_placeholder: bool = False, no_regex: bool = False, no_color: bool = False, no_html: bool = False, - no_link: bool = False, no_markdown: bool = False, - no_special_tags: bool = False, + no_link: bool = False, + no_special_tags: bool = False ) -> str: """ - 格式化控制台内容 - Format console content\n + 格式化控制台日志 + Format console log Args: - message (str): 日志消息 + message (Any): 日志消息 + prefix (str, optional): 前缀 + suffix (str, optional): 后缀 + level_number (int, optional): 日志级别数字 + no_placeholder (bool, optional): 是否不处理占位符 + no_regex (bool, optional): 是否不处理正则 + no_color (bool, optional): 是否不处理颜色 + no_html (bool, optional): 是否不处理HTML + no_link (bool, optional): 是否不处理链接 + no_markdown (bool, optional): 是否不处理Markdown + no_special_tags (bool, optional): 是否不处理特殊标签 Returns: - str: 格式化后的日志消息 + str: 格式化后的控制台日志 """ - message = await fmt_placeholder(message, no_placeholder=no_placeholder) - message = await fmt_regex(message, no_regex=no_regex, no_color=no_color, no_html=no_html, no_link=no_link, no_markdown=no_markdown, no_special_tags=no_special_tags) - return message + # 获取基础配置 + fmt_cfg = get_config("console_format") or DEFAULT_CONFIG["console_format"] + level_name = fmt_level_number_to_name(level_number).upper() + + # 处理所有占位符和格式化 + placeholders = { + 'asctime': get_asctime(), + 'time': get_time(), + 'date': get_date(), + 'weekday': get_weekday(), + 'levelname': level_name, + 'level_number': str(level_number), + 'prefix': prefix, + 'suffix': suffix, + 'message': str(message) + } + + # 替换格式字符串中的占位符 + # 初始化颜色配置 + color_config = { + 'time_color': get_config('time_color') or DEFAULT_CONFIG['time_color'], + 'level_colors': get_config('level_color') or DEFAULT_CONFIG['level_color'] + } -async def fmt_file( - message: str = "", - no_placeholder: bool = False, - no_regex: bool = True, - no_color: bool = False, - no_html: bool = False, - no_link: bool = False, - no_markdown: bool = False, - no_special_tags: bool = False, -) -> str: + # 处理时间相关占位符的颜色 + time_placeholders = ['asctime', 'time', 'date', 'weekday'] + for placeholder in time_placeholders: + if placeholder in placeholders: + placeholders[placeholder] = set_color(placeholders[placeholder], color_config['time_color']) + + # 处理日志级别颜色 + if 'levelname' in placeholders: + level_name = placeholders['levelname'] + level_color = color_config['level_colors'].get(level_name, DEFAULT_CONFIG['level_color']['DEBUG']) + placeholders['levelname'] = set_color(level_name, level_color) + + # 替换占位符 + for key, value in placeholders.items(): + fmt_cfg = fmt_cfg.replace(f'{{{key}}}', str(value)) + + # 应用内容格式化 + formatted_content = fmt_content( + fmt_cfg, + no_placeholder=no_placeholder, + no_regex=no_regex, + no_color=no_color, + no_html=no_html, + no_markdown=no_markdown, + no_link=no_link, + no_special_tags=no_special_tags, + ) + # 处理前缀和后缀 + if prefix: + # 清理前缀中的颜色标签 + clean_prefix = re.sub(r'<#([0-9a-fA-F]{6})>|', '', prefix) + formatted_content = f"{set_color(clean_prefix, color_config['time_color'])}{formatted_content}" + if suffix: + # 清理后缀中的颜色标签 + clean_suffix = re.sub(r'<#([0-9a-fA-F]{6})>|', '', suffix) + formatted_content = f"{formatted_content}{set_color(clean_suffix, color_config['time_color'])}" + return formatted_content + +def fmt_file( + message: str = "", + prefix: str = "", + suffix: str = "", + level_number: int = 0, + no_placeholder: bool = False, + no_regex: bool = True, + no_color: bool = False, + no_html: bool = False, + no_link: bool = False, + no_markdown: bool = False, + no_special_tags: bool = False, + ) -> str: """ 格式化文件内容 - Format file content\n + Format file content Args: message (str): 日志消息 Returns: str: 格式化后的日志消息 """ - message = await fmt_placeholder(message, no_placeholder=no_placeholder) - message = await fmt_regex(message, no_regex=no_regex, no_color=no_color, no_html=no_html, no_link=no_link, no_markdown=no_markdown, no_special_tags=no_special_tags) - return message \ No newline at end of file + fmt_cfg = get_config("file_format") if get_config("file_format") else "{asctime} {levelname} | {prefix}{message}{suffix}" + fmt_cfg = fmt_content( + message=message, + prefix=prefix, + suffix=suffix, + level_number=level_number, + no_placeholder=no_placeholder, + no_regex=no_regex, + no_color=no_color, + no_html=no_html, + ) + return fmt_content( + message=fmt_cfg, + prefix=prefix, + suffix=suffix, + level_number=level_number, + no_placeholder=no_placeholder, + no_regex=no_regex, + no_color=no_color, + no_html=no_html, + no_link=no_link, + no_markdown=no_markdown, + no_special_tags=no_special_tags, + ) diff --git a/src/logiliteal/utils/regex.py b/src/logiliteal/utils/regex.py index 886d143..4b5853f 100644 --- a/src/logiliteal/utils/regex.py +++ b/src/logiliteal/utils/regex.py @@ -3,10 +3,10 @@ """ import re from collections import deque -from .styles import set_color, set_bg_color +from .styles import set_color -async def process_links(text: str, no_process: bool = False) -> str: +def process_links(text: str, no_process: bool = False) -> str: """处理链接标签(HTML和Markdown格式)""" if no_process: return text @@ -40,7 +40,7 @@ async def process_links(text: str, no_process: bool = False) -> str: return text -async def process_markdown_formats(text: str, no_process: bool = False) -> str: +def process_markdown_formats(text: str, no_process: bool = False) -> str: """处理Markdown格式""" if no_process: return text @@ -55,7 +55,7 @@ async def process_markdown_formats(text: str, no_process: bool = False) -> str: return text -async def process_html_styles(text: str, no_process: bool = False) -> str: +def process_html_styles(text: str, no_process: bool = False) -> str: """处理HTML样式标签""" if no_process: return text @@ -74,7 +74,7 @@ async def process_html_styles(text: str, no_process: bool = False) -> str: return text -async def process_special_tags(text: str, no_process: bool = False) -> str: +def process_special_tags(text: str, no_process: bool = False) -> str: """处理特殊标签(换行、重置、段落)""" if no_process: return text @@ -87,7 +87,7 @@ async def process_special_tags(text: str, no_process: bool = False) -> str: return text -async def process_color_formatting(text: str, no_process: bool = False) -> str: +def process_color_formatting(text: str, no_process: bool = False) -> str: """处理颜色标签""" if no_process: return text @@ -125,4 +125,4 @@ async def process_color_formatting(text: str, no_process: bool = False) -> str: processed_text = re.sub(f'{color_pattern}|{close_pattern}', '', processed_text) processed_text = re.sub(r'[0-9a-fA-F]{6}', '', processed_text) - return processed_text \ No newline at end of file + return processed_text diff --git a/src/logiliteal/utils/styles.py b/src/logiliteal/utils/styles.py index 266ae8f..85fd483 100644 --- a/src/logiliteal/utils/styles.py +++ b/src/logiliteal/utils/styles.py @@ -8,7 +8,7 @@ py-logiliteal's style tools, used to format log output from typing import Union, Optional -async def _get_hex_to_ansi(hex_color: str | None) -> Union[Optional[str], None]: +def _get_hex_to_ansi(hex_color: str | None) -> Union[Optional[str], None]: """ 将16进制颜色值转换为ANSI转义序列 Convert hex color value to ANSI escape sequence @@ -22,7 +22,7 @@ async def _get_hex_to_ansi(hex_color: str | None) -> Union[Optional[str], 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" -async def set_color(text: str | None, color: str | None) -> str: +def set_color(text: str | None, color: str | None) -> str: """ 设置文本颜色 Set text color @@ -34,12 +34,12 @@ async def set_color(text: str | None, color: str | None) -> str: return "" if not color: return text - ansi = await _get_hex_to_ansi(color) + ansi = str(_get_hex_to_ansi(color)) if not ansi: return text return f"{ansi}{text}\033[0m" -async def set_bg_color(text: str | None, color: str | None) -> str: +def set_bg_color(text: str | None, color: str | None) -> str: """ 设置文本背景颜色 Set text background color @@ -51,13 +51,13 @@ async def set_bg_color(text: str | None, color: str | None) -> str: return "" if not color: return text - ansi = await _get_hex_to_ansi(color) + ansi = str(_get_hex_to_ansi(color)) if not ansi: return text ansi = ansi.replace("38;", "48;") return f"{ansi}{text}\033[0m" -async def set_style( +def set_style( text: str | None, bold: bool = False, italic: bool = False, diff --git a/src/logiliteal/utils/time.py b/src/logiliteal/utils/time.py index 47fe466..671dbdf 100644 --- a/src/logiliteal/utils/time.py +++ b/src/logiliteal/utils/time.py @@ -9,43 +9,43 @@ Time utility module, used for time formatting and output, caching time formattin from datetime import datetime from .configs import get_config -async def get_asctime() -> 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 """ - return await _get_time(get_config("asctime_format")) + return str(_get_time(get_config("asctime_format"))) -async def get_time() -> str: +def get_time() -> str: """ 获取当前时间(HH:MM:SS),并缓存格式化结果 Get current time(HH:MM:SS) and cache formatted result :return: 格式化后的时间 Formatted time """ - return await _get_time(get_config("time_format")) + return str(_get_time(get_config("time_format"))) -async def get_date() -> str: +def get_date() -> str: """ 获取当前日期(YYYY-MM-DD),并缓存格式化结果 Get current date(YYYY-MM-DD) and cache formatted result :return: 格式化后的日期 Formatted date """ - return await _get_time(get_config("date_format")) + return str(_get_time(get_config("date_format"))) -async def get_weekday() -> str: +def get_weekday() -> str: """ 获取当前日期(星期几),并缓存格式化结果 Get current date(weekday) and cache formatted result :return: 格式化后的星期几 Formatted weekday """ - return await _get_time(get_config("weekday_format")) + return str(_get_time(get_config("weekday_format"))) -async def _get_time(fmt: str) -> str: +def _get_time(fmt: str) -> str: """ 获取当前时间(根据指定格式),并缓存格式化结果 Get current time(based on specified format) and cache formatted result :param fmt: 时间格式 Time format :return: 格式化后的时间 Formatted time """ - return datetime.now().strftime(fmt) \ No newline at end of file + return str(datetime.now().strftime(fmt)) diff --git a/test/t-color.py b/test/t-color.py new file mode 100644 index 0000000..acfc15a --- /dev/null +++ b/test/t-color.py @@ -0,0 +1,31 @@ +import sys +from pathlib import Path + +project_root = Path(__file__).parent.parent +sys.path.append(str(project_root)) + +from src.logiliteal import logger + +logger.debug("这是一条调试信息") +logger.debug("这是**粗体**") +logger.debug("这是*斜体*") +logger.debug("这是__下划线__") +logger.debug("这是~~删除线~~") +logger.debug("这是**粗斜体**") +logger.debug("这是__粗下划线__") +logger.debug("这是~~删除线~~") +logger.debug("这是<#ff0000>红色") +logger.debug("这是<#00ff00>绿色") +logger.debug("这是<#0000ff>蓝色") +logger.debug("这是<#ff0000>嵌<#00ff00>套") +logger.debug("这是<#ff0000>不完全闭合") +logger.debug("这是<#ff0000>样式清理") +logger.debug("这是粗体") +logger.debug("这是斜体") +logger.debug("这是下划线") +logger.debug("这是删除线") +logger.debug("这是粗斜体") +logger.debug("这是粗下划线") +logger.debug("这是粗删除线") +logger.debug("这是前缀", prefix="<#ff0000>111") +logger.debug("这是后缀", suffix="") \ No newline at end of file diff --git a/tests/t-color-priority.py b/tests/t-color-priority.py deleted file mode 100644 index 3999aec..0000000 --- a/tests/t-color-priority.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -测试颜色优先级处理功能 -""" -import sys -import os -from pathlib import Path - -project_root = Path(__file__).parent.parent -sys.path.append(str(project_root)) - -# 添加项目根目录到Python路径 -sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) - -from src.logiliteal.utils.fmt import fmt_message - -def test_color_priority(): - """测试颜色优先级""" - print("=== 颜色优先级测试 ===") - - # 测试1: 基本颜色标签 - test1 = "<#ff0000>红色文本" - print(f"基本颜色: {test1}") - print(f"结果: {fmt_message(test1)}") - print() - - # 测试2: 嵌套颜色(内层优先级高) - test2 = "<#ff0000>外层<#00ff00>内层外层" - print(f"嵌套颜色: {test2}") - print(f"结果: {fmt_message(test2)}") - print() - - # 测试3: 颜色重叠测试(最后边颜色为主) - test3 = "<#ff0000>颜色重叠<#0000ff>测<#ff00c2>试" - print(f"颜色重叠: {test3}") - print(f"结果: {fmt_message(test3)}") - print() - - # 测试4: 多层嵌套 - test4 = "<#ff0000>1<#00ff00>2<#0000ff>3<#ffff00>4321" - print(f"多层嵌套: {test4}") - print(f"结果: {fmt_message(test4)}") - print() - - # 测试5: 未闭合标签(忽略) - test5 = "<#ff0000>未闭合标签" - print(f"未闭合标签: {test5}") - print(f"结果: {fmt_message(test5)}") - print() - -if __name__ == "__main__": - test_color_priority() \ No newline at end of file diff --git a/tests/t-configs.py b/tests/t-configs.py deleted file mode 100644 index 6f385ce..0000000 --- a/tests/t-configs.py +++ /dev/null @@ -1,26 +0,0 @@ -import sys -from pathlib import Path - -project_root = Path(__file__).parent.parent -sys.path.append(str(project_root)) - -from src.logiliteal.utils.configs import get_config, set_config, reset_config, create_backup - -print(f"\n配置更换测试") -set_config("file_level", "DEBUG") -fl = get_config("file_level") -print(fl) - -set_config("file_level", "INFO") -fl = get_config("file_level") -print(fl) - -print(f"\n配置重置测试") -print(reset_config()) - -print(f"\n配置重置后测试") -fl = get_config("file_level") -print(fl) - -print(f"\n未指定配置读取测试") -print(get_config()) \ No newline at end of file diff --git a/tests/t-fmt-text.py b/tests/t-fmt-text.py deleted file mode 100644 index 1e3ecfe..0000000 --- a/tests/t-fmt-text.py +++ /dev/null @@ -1,34 +0,0 @@ -import sys -from pathlib import Path - -project_root = Path(__file__).parent.parent -sys.path.append(str(project_root)) - -from src.logiliteal.levels import Logger - -log = Logger() - -log.info("html斜体") -log.info("html加粗") -log.info("html下划线") -log.info("html删除线") -log.info("

html段落

") -log.info("html超链接") -log.info("**Markdown加粗**") -log.info("*Markdown斜体*") -log.info("`Markdown代码块`") -log.info("~~Markdown删除线~~\n") -log.info("[md超链接](https://www.baidu.com)") -log.info("--测试重复--") -log.info("<#ff0000>颜色重叠<#0000ff>测<#ff00c2>试") -log.info("链接[重叠](https://www.360.com)") - -""" -while True: - try: - log.info(get_asctime()) - time.sleep(1) - except KeyboardInterrupt: - log.info("测试结束") - break -""" \ No newline at end of file diff --git a/tests/t-fmt.py b/tests/t-fmt.py deleted file mode 100644 index 23e4e2d..0000000 --- a/tests/t-fmt.py +++ /dev/null @@ -1,50 +0,0 @@ -import sys -from pathlib import Path - -project_root = Path(__file__).parent.parent -sys.path.append(str(project_root)) - -from src.logiliteal.utils.fmt import fmt_level, fmt_level_number, fmt_console, fmt_message, fmt_placeholder - -print("日志级别测试") -print(fmt_level("DEBUG")) -print(fmt_level("INFO")) -print(fmt_level("WARN")) -print(fmt_level("ERRO")) -print(fmt_level("CRIT")) -print(fmt_level("UNKN")) - -print("日志级别数字测试") -print(fmt_level_number(10)) -print(fmt_level_number(20)) -print(fmt_level_number(30)) -print(fmt_level_number(40)) -print(fmt_level_number(50)) -print(fmt_level_number(-1)) -print(fmt_level_number(100)) - -print("消息格式化测试") -print(fmt_message("测试消息: {time},\n {asctime}, \n{unknown}")) - -print("控制台格式化测试") -print(fmt_console(10, "测试消息")) -print("前缀测试") -print(fmt_console(35, "测试消息", "可爱猫猫")) - -print("未知日志级别测试") -print(fmt_console(-1, "测试消息")) - - -print("颜色插值测试") -print("分段颜色插值") -formatted_msg = fmt_message("测试消息<#ff0000>red") -processed_msg = fmt_placeholder(formatted_msg) -print(fmt_console(15, processed_msg)) - -print("单段颜色插值") -print(fmt_console(15, fmt_placeholder("测试消息<#00ff00>green{asctime}", use_date_color=False))) - -print("不完整颜色插值") -print(fmt_console(15, fmt_placeholder("测试消息<#00ff00>green{asctime}", use_date_color=False))) -print(fmt_console(15, fmt_placeholder("测试消息green{asctime}", use_date_color=False))) -print(fmt_console(15, fmt_placeholder("测试消息<#00ff000>green{asctime}", use_date_color=False))) \ No newline at end of file diff --git a/tests/t-level.py b/tests/t-level.py deleted file mode 100644 index 1131774..0000000 --- a/tests/t-level.py +++ /dev/null @@ -1,15 +0,0 @@ -import sys -from pathlib import Path - -project_root = Path(__file__).parent.parent -sys.path.append(str(project_root)) - -from src.logiliteal import Logger - -log = Logger() - -log.debug("测试调试日志") -log.info("测试信息日志") -log.warn("测试警告日志") -log.error("测试错误日志") -log.critical("测试严重错误日志") \ No newline at end of file diff --git a/tests/t-module.py b/tests/t-module.py deleted file mode 100644 index c1722a6..0000000 --- a/tests/t-module.py +++ /dev/null @@ -1,4 +0,0 @@ -from logiliteal import Logger -log = Logger() - -log.info("hello world") \ No newline at end of file diff --git a/tests/t-multithread.py b/tests/t-multithread.py deleted file mode 100644 index 04e043e..0000000 --- a/tests/t-multithread.py +++ /dev/null @@ -1,37 +0,0 @@ -import sys -from pathlib import Path - -project_root = Path(__file__).parent.parent -sys.path.append(str(project_root)) - -from src.logiliteal import Logger - -log = Logger() - -import threading -import time -import random - -# 定义线程测试函数 -def thread_logger(thread_id: int): - """ - 线程日志测试函数 - Thread logger test function - Args: - thread_id (int): 线程ID Thread ID - """ - for i in range(10): - log.info(f"线程{thread_id}的第{i+1}条日志消息", no_file=True) - time.sleep(random.uniform(0.01, 0.05)) - -if __name__ == "__main__": - threads = [] - for i in range(10): - thread = threading.Thread(target=thread_logger, args=(i,)) - threads.append(thread) - thread.start() - - for thread in threads: - thread.join() - - log.info("所有线程日志记录完成", no_file=True) \ No newline at end of file diff --git a/tests/t-repeat.py b/tests/t-repeat.py deleted file mode 100644 index 1e559c0..0000000 --- a/tests/t-repeat.py +++ /dev/null @@ -1,15 +0,0 @@ -import sys -from pathlib import Path - -project_root = Path(__file__).parent.parent -sys.path.append(str(project_root)) - -from src.logiliteal import Logger - -log = Logger() - -i = 0 - -while i < 20000: - log.info(f"**md** | <#ff0000>测试--重--复~~日志~~: {i}") - i += 1 \ No newline at end of file