From 796a8a82f65878ad388514769d6a1957d6a3114d Mon Sep 17 00:00:00 2001
From: Nanaloveyuki
Date: Tue, 29 Jul 2025 14:17:22 +0800
Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20=E6=94=B9=E8=BF=9B=E4=BA=86?=
=?UTF-8?q?=E5=AF=B9=E5=8D=A0=E4=BD=8D=E7=AC=A6=E5=92=8C=E6=AD=A3=E5=88=99?=
=?UTF-8?q?=E8=A1=A8=E8=BE=BE=E5=BC=8F=E7=9A=84=E5=A4=84=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
logger_config.json | 2 +-
src/logiliteal/levels/levels.py | 80 +++++++-----
src/logiliteal/utils/configs.py | 38 +++++-
src/logiliteal/utils/fmt.py | 193 ++++++++--------------------
src/logiliteal/utils/placeholder.py | 62 +++++++++
src/logiliteal/utils/regex.py | 128 ++++++++++++++++++
tests/logger_config.json | 32 -----
tests/t-level.py | 2 +-
8 files changed, 331 insertions(+), 206 deletions(-)
create mode 100644 src/logiliteal/utils/placeholder.py
create mode 100644 src/logiliteal/utils/regex.py
delete mode 100644 tests/logger_config.json
diff --git a/logger_config.json b/logger_config.json
index e58ae4c..283a3b0 100644
--- a/logger_config.json
+++ b/logger_config.json
@@ -7,7 +7,7 @@
"enable_console": true,
"enable_file": true,
"console_color": true,
- "console_level": "INFO",
+ "console_level": "DEBUG",
"console_format": "{time} {levelname} | {prefix}{message}",
"console_prefix": "Auto",
"console_encoding": "utf-8",
diff --git a/src/logiliteal/levels/levels.py b/src/logiliteal/levels/levels.py
index 6271d4d..4bd9942 100644
--- a/src/logiliteal/levels/levels.py
+++ b/src/logiliteal/levels/levels.py
@@ -11,96 +11,116 @@ 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
def _get_full_path(file_path, file_name):
file_path.mkdir(parents=True, exist_ok=True)
return file_path / file_name
-file_path = pathlib.Path(get_config("file_path"))
-file_name = get_config("file_name")
-file_format = get_config("file_format")
-file_encoding = get_config("file_encoding")
-is_enable_console = get_config("enable_console")
-is_enable_file = get_config("enable_file")
-
class Logger:
def __init__(self):
- if pathlib.Path(file_path).exists():
- if not pathlib.Path(file_path).is_dir():
- self.warn("日志文件路径不是目录,已自动自动使用默认目录")
+ project_root = Path(__file__).parent.parent.parent.parent
+ 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)
- if _get_full_path(file_path, file_name).exists():
+ current_file = _get_full_path(file_path, get_config("file_name"))
+ if current_file.exists():
from os import rename
- rename(_get_full_path(file_path, file_name), _get_full_path(file_path, f"{get_asctime().replace(':', '-')}.log"))
+ rename(current_file, _get_full_path(file_path, f"{get_asctime().replace(':', '-')}.log"))
self.debug("日志文件已存在,已自动重命名")
-
- def _log(self, msg, pf, lvn):
- if is_enable_file:
- 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), pf))
- if is_enable_console:
+
+ def _log(self, msg, pf, lvn, no_file: bool = False, no_console: bool = False):
+ project_root = Path(__file__).parent.parent.parent.parent
+ 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) -> Optional[str]:
+ 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)
+ return self._log(message, prefix, level, no_file, no_console)
- def info(self, message: Any, prefix: str | None = None, level: int = 10) -> Optional[str]:
+ 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)
+ return self._log(message, prefix, level, no_file, no_console)
- def warn(self, message: Any, prefix: str | None = None, level: int = 20) -> Optional[str]:
+ 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)
+ return self._log(message, prefix, level, no_file, no_console)
- def error(self, message: Any, prefix: str | None = None, level: int = 30) -> Optional[str]:
+ 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)
+ return self._log(message, prefix, level, no_file, no_console)
- def critical(self, message: Any, prefix: str | None = None, level: int = 40) -> Optional[str]:
+ 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)
+ return self._log(message, prefix, level, no_file, no_console)
- def log(self, message: Any, prefix: str | None = None, level: int = 50) -> Optional[str]:
+ 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 newline at end of file
+ return self._log(message, prefix, level, no_file, no_console)
diff --git a/src/logiliteal/utils/configs.py b/src/logiliteal/utils/configs.py
index d236326..ed4e4b4 100644
--- a/src/logiliteal/utils/configs.py
+++ b/src/logiliteal/utils/configs.py
@@ -9,11 +9,43 @@ py-logiliteal's config settings, used to set py-logiliteal's global config
import json
from os import remove
import shutil
+import os
from pathlib import Path
from typing import Union, Optional, Tuple
from logging import error
-DEFAULT_CONFIG_PATH = "logger_config.json"
+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()
DEFAULT_CONFIG = {
"file_level": "DEBUG",
"file_name": "latest.log",
@@ -23,7 +55,7 @@ DEFAULT_CONFIG = {
"enable_console": True,
"enable_file": True,
"console_color": True,
- "console_level": "INFO",
+ "console_level": "DEBUG",
"console_format": "{time} {levelname} | {prefix}{message}",
"console_prefix": "Auto",
"console_encoding": "utf-8",
@@ -94,6 +126,8 @@ def get_config(select: str = None) -> Union[dict, str, bool, int, None]:
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
diff --git a/src/logiliteal/utils/fmt.py b/src/logiliteal/utils/fmt.py
index 83bb91c..3c895a8 100644
--- a/src/logiliteal/utils/fmt.py
+++ b/src/logiliteal/utils/fmt.py
@@ -10,7 +10,15 @@ from .configs import get_config
from typing import Any, Optional
from .time import get_asctime, get_time, get_weekday, get_date
from .styles import set_color, set_bg_color
-import re
+from .regex import (
+ process_links,
+ process_markdown_formats,
+ process_html_styles,
+ process_special_tags,
+ process_color_formatting
+)
+from .placeholder import process_placeholder, SafeDict
+
if get_config("time_color") is None:
time_color = "#28ffb6"
@@ -61,29 +69,28 @@ def fmt_placeholder(message: Any, use_date_color: bool = True) -> str:
: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(),time_color),
- time = set_color(get_time(),time_color),
- weekday = set_color(get_weekday(),time_color),
- date = set_color(get_date(),time_color)
- ))
- 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, no_color: bool = False) -> str:
+ context = {
+ "asctime": set_color(get_asctime(), time_color) if use_date_color else get_asctime(),
+ "time": set_color(get_time(), time_color) if use_date_color else get_time(),
+ "weekday": set_color(get_weekday(), time_color) if use_date_color else get_weekday(),
+ "date": set_color(get_date(), time_color) if use_date_color else get_date(),
+ }
+
+ return process_placeholder(message, context)
+
+def fmt_message(
+ message: Any,
+ no_placeholder: bool = False,
+ no_style: bool = False,
+ no_process: bool = False,
+ no_tags: bool = False,
+ no_links: bool = False,
+ no_markdown: bool = False,
+ no_html: bool = False,
+ ) -> str:
"""
格式化消息内容
Format message content
@@ -91,26 +98,29 @@ def fmt_message(message: Any, no_placeholder: bool = False, no_color: bool = Fal
:return: 格式化后的消息 Formatted message
"""
- def process_color_tags(msg: str) -> str:
- # 优化处理顺序,确保链接和基础格式优先处理
- processed = _process_color_formatting(
- _process_special_tags(
- _process_html_styles(
- _process_markdown_formats(
- _process_links(msg)
- )
- )
- )
+ def process_color_tags(msg: str, no_process: bool = False) -> str:
+ processed = process_color_formatting(
+ process_special_tags(
+ process_html_styles(
+ process_markdown_formats(
+ process_links(msg, no_links),
+ no_markdown
+ ),
+ no_html
+ ),
+ no_tags
+ ),
+ no_process
)
return processed
- if no_color:
- processed_message = str(message)
- else:
- processed_message = process_color_tags(str(message))
- if no_placeholder:
- return processed_message
- else:
- return process_color_tags(fmt_placeholder(processed_message)) if not no_color else fmt_placeholder(processed_message)
+ processed_message = str(message)
+ if not no_placeholder:
+ processed_message = fmt_placeholder(processed_message)
+ if not no_style:
+ processed_message = process_color_tags(processed_message)
+ if no_process:
+ return message
+ return processed_message
def fmt_level_name(level_name: str) -> str:
if get_config("console_color") != True:
@@ -137,7 +147,7 @@ def fmt_console(level: int, message: Any, prefix: str | None = None) -> Optional
:return: 格式化后的消息 Formatted message
"""
console_level = get_config("console_level")
- if level != -1 and fmt_level(console_level) > level:
+ if level != -1 and level < fmt_level(console_level):
return None
fmt = get_config("console_format")
prefix = prefix or ""
@@ -157,111 +167,14 @@ def fmt_file(level: int, message: Any, prefix: str | None = None) -> Optional[st
"""
fl = get_config("file_level")
fmt = get_config("file_format")
- if fmt_level(fl) > level:
+ if level != -1 and level < fmt_level(fl):
return None
if prefix is None:
prefix = ""
fmt = fmt_placeholder(fmt, use_date_color=False)
return f"{fmt.format(
levelname = fmt_level_number(level),
- prefix = fmt_message(prefix, no_placeholder=True, no_color=True),
- message = fmt_message(message, no_placeholder=True, no_color=True)
+ prefix = fmt_message(prefix, no_placeholder=True, no_style=True),
+ message = fmt_message(message, no_placeholder=True, no_style=True)
)}\n"
-def _process_links(text: str) -> str:
- from collections import deque
-
- link_stack = deque()
- placeholder_count = 0
- placeholders = {}
-
- def replace_link(m):
- nonlocal placeholder_count
- placeholder_count += 1
- if len(m.groups()) == 2 and m.group(2) and not m.group(1).startswith('http'):
- # Markdown链接 [text](url)
- url = m.group(2).strip()
- text = m.group(1)
- else:
- url = m.group(1)
- text = m.group(2)
- placeholder = f"__LINK_PLACEHOLDER_{placeholder_count}__"
- placeholders[placeholder] = (url if url else "#", text)
- link_stack.append(placeholder)
- return placeholder
-
- text = re.sub(r'(.*?)', replace_link, text, flags=re.DOTALL)
- text = re.sub(r'(.*?)', replace_link, text, flags=re.DOTALL)
- text = re.sub(r'\[(.*?)\]\((.*?)\)', replace_link, text)
-
- for placeholder, (url, text_content) in placeholders.items():
- ansi_link = f'\033]8;;{url}\033\\{set_color("\033[4m" + text_content, "#5f93ff")}\033]8;;\033\\'
- text = text.replace(placeholder, ansi_link)
-
- return text
-
-def _process_markdown_formats(text: str) -> str:
- # Markdown粗体 (**text**)
- text = re.sub(r'\*\*(.*?)\*\*', '\033[1m\\g<1>\033[22m', text)
- # Markdown斜体 (*text*)
- text = re.sub(r'\*(.*?)\*', '\033[3m\\g<1>\033[23m', text)
- # Markdown下划线 (__text__)
- text = re.sub(r'__(.*?)__', '\033[4m\\g<1>\033[24m', text)
- # Markdown删除线 (~~text~~)
- text = re.sub(r'~~(.*?)~~', '\033[9m\\g<1>\033[29m', text)
- return text
-
-def _process_html_styles(text: str) -> str:
- text = re.sub(r'([^<]*?)(|$)',
- lambda m: '\033[3m' + m.group(1) + '\033[23m', text, flags=re.DOTALL)
- text = re.sub(r'([^<]*?)',
- lambda m: '\033[1m' + m.group(1) + '\033[22m', text)
- text = re.sub(r'([^<]*?)',
- lambda m: '\033[4m' + m.group(1) + '\033[24m', text)
- text = re.sub(r'([^<]*?)(|$)',
- lambda m: '\033[9m' + m.group(1) + '\033[29m', text, flags=re.DOTALL)
- return text
-
-def _process_special_tags(text: str) -> str:
- text = re.sub(r'
', '\n', text)
- text = re.sub(r'', '\033[0m', text)
- text = re.sub(r'(.*?)
', r'\n\033[0m\g<1>\033[0m\n', text, flags=re.DOTALL)
- text = re.sub(r'(.*?)(
|$)', r'\n\033[0m\g<1>\033[0m\n', text, flags=re.DOTALL)
- text = re.sub(r'
', '\033[0m\n', text)
- return text
-
-def _process_color_formatting(text: str) -> str:
- import re
-
- color_pattern = r'<#([0-9a-fA-F]{6})>'
- close_pattern = r'>'
-
- parts = re.split(f'({color_pattern}|{close_pattern})', text)
- result = []
- color_stack = []
-
- for part in parts:
- if part and re.fullmatch(color_pattern, part):
- color = re.match(color_pattern, part).group(1)
- color_stack.append(color)
- continue
- elif part == '>':
- if color_stack:
- color_stack.pop()
- continue
- elif part:
- if color_stack:
- current_color = color_stack[-1]
- r = int(current_color[0:2], 16)
- g = int(current_color[2:4], 16)
- b = int(current_color[4:6], 16)
- ansi_code = f'\033[38;2;{r};{g};{b}m'
- reset_code = '\033[0m'
- result.append(f'{ansi_code}{part}{reset_code}')
- else:
- result.append(part)
-
- processed_text = ''.join(result)
- processed_text = re.sub(f'{color_pattern}|{close_pattern}|[0-9a-fA-F]{{6}}', '', processed_text)
-
- return processed_text
diff --git a/src/logiliteal/utils/placeholder.py b/src/logiliteal/utils/placeholder.py
new file mode 100644
index 0000000..cf085a2
--- /dev/null
+++ b/src/logiliteal/utils/placeholder.py
@@ -0,0 +1,62 @@
+"""
+占位符处理工具
+"""
+from typing import Dict, Any, Optional
+import re
+
+
+class SafeDict(Dict[str, Any]):
+ """安全的字典类,用于处理缺失键的占位符替换"""
+ def __missing__(self, key: str) -> str:
+ return f"{{{key}}}"
+
+
+def process_placeholder(text: str, context: Optional[Dict[str, Any]] = None) -> str:
+ """处理文本中的占位符替换
+
+ Args:
+ text: 包含占位符的原始文本
+ context: 用于替换占位符的上下文字典
+
+ Returns:
+ 替换后的文本
+ """
+ if not context:
+ return text
+
+ # 处理简单占位符 {key}
+ safe_context = SafeDict(**context)
+ text = text.format_map(safe_context)
+
+ # 处理条件占位符 {{if condition}}content{{endif}}
+ def replace_condition(match):
+ condition = match.group(1).strip()
+ content = match.group(2).strip()
+ # 简单条件解析(仅支持key存在性检查)
+ if condition.startswith('!'):
+ key = condition[1:].strip()
+ return '' if key in context else content
+ return content if condition in context else ''
+
+ text = re.sub(r'\{\{if\s+(.*?)\}\}([\s\S]*?)\{\{endif\}\}', replace_condition, text, flags=re.IGNORECASE)
+
+ # 处理循环占位符 {{for item in list}}content{{endfor}}
+ def replace_loop(match):
+ items = match.group(1).strip().split(' in ')
+ if len(items) != 2:
+ return match.group(0)
+ item_name, list_name = items
+ content = match.group(2).strip()
+
+ if list_name not in context or not isinstance(context[list_name], list):
+ return ''
+
+ result = []
+ for item in context[list_name]:
+ item_context = {item_name: item}
+ result.append(process_placeholder(content, item_context))
+ return ''.join(result)
+
+ text = re.sub(r'\{\{for\s+(.*?)\}\}([\s\S]*?)\{\{endfor\}\}', replace_loop, text, flags=re.IGNORECASE)
+
+ return text
\ No newline at end of file
diff --git a/src/logiliteal/utils/regex.py b/src/logiliteal/utils/regex.py
new file mode 100644
index 0000000..fce2653
--- /dev/null
+++ b/src/logiliteal/utils/regex.py
@@ -0,0 +1,128 @@
+"""
+正则表达式处理工具
+"""
+import re
+from collections import deque
+from .styles import set_color, set_bg_color
+
+
+def process_links(text: str, no_process: bool = False) -> str:
+ """处理链接标签(HTML和Markdown格式)"""
+ if no_process:
+ return text
+ link_stack = deque()
+ placeholder_count = 0
+ placeholders = {}
+
+ def replace_link(m):
+ nonlocal placeholder_count
+ placeholder_count += 1
+ if len(m.groups()) == 2 and m.group(2) and not m.group(1).startswith('http'):
+ # Markdown链接 [text](url)
+ url = m.group(2).strip()
+ text = m.group(1)
+ else:
+ url = m.group(1)
+ text = m.group(2)
+ placeholder = f"__LINK_PLACEHOLDER_{placeholder_count}__"
+ placeholders[placeholder] = (url if url else "#", text)
+ link_stack.append(placeholder)
+ return placeholder
+
+ text = re.sub(r'(.*?)', replace_link, text, flags=re.DOTALL)
+ text = re.sub(r'(.*?)', replace_link, text, flags=re.DOTALL)
+ text = re.sub(r'\[(.*?)\]\((.*?)\)', replace_link, text)
+
+ for placeholder, (url, text_content) in placeholders.items():
+ ansi_link = f'\033]8;;{url}\033\\{set_color("\033[4m" + text_content, "#5f93ff")}\033]8;;\033\\'
+ text = text.replace(placeholder, ansi_link)
+
+ return text
+
+
+def process_markdown_formats(text: str, no_process: bool = False) -> str:
+ """处理Markdown格式"""
+ if no_process:
+ return text
+ # Markdown粗体 (**text**)
+ text = re.sub(r'\*\*(.*?)\*\*', '\033[1m\\g<1>\033[22m', text)
+ # Markdown斜体 (*text*)
+ text = re.sub(r'\*(.*?)\*', '\033[3m\\g<1>\033[23m', text)
+ # Markdown下划线 (__text__)
+ text = re.sub(r'__(.*?)__', '\033[4m\\g<1>\033[24m', text)
+ # Markdown删除线 (~~text~~)
+ text = re.sub(r'~~(.*?)~~', '\033[9m\\g<1>\033[29m', text)
+ return text
+
+
+def process_html_styles(text: str, no_process: bool = False) -> str:
+ """处理HTML样式标签"""
+ if no_process:
+ return text
+ # HTML斜体
+ text = re.sub(r'([^<]*?)(|$)',
+ lambda m: '\033[3m' + m.group(1) + '\033[23m', text, flags=re.DOTALL)
+ # HTML粗体
+ text = re.sub(r'([^<]*?)',
+ lambda m: '\033[1m' + m.group(1) + '\033[22m', text)
+ # HTML下划线
+ text = re.sub(r'([^<]*?)',
+ lambda m: '\033[4m' + m.group(1) + '\033[24m', text)
+ # HTML删除线
+ text = re.sub(r'([^<]*?)(|$)',
+ lambda m: '\033[9m' + m.group(1) + '\033[29m', text, flags=re.DOTALL)
+ return text
+
+
+def process_special_tags(text: str, no_process: bool = False) -> str:
+ """处理特殊标签(换行、重置、段落)"""
+ if no_process:
+ return text
+ text = re.sub(r'
', '\n', text)
+ text = re.sub(r'', '\033[0m', text)
+ # 处理段落标签
+ text = re.sub(r'(.*?)
', r'\n\033[0m\\g<1>\033[0m\n', text, flags=re.DOTALL)
+ text = re.sub(r'(.*?)(
|$)', r'\n\033[0m\\g<1>\033[0m\n', text, flags=re.DOTALL)
+ text = re.sub(r'', '\033[0m\n', text)
+ return text
+
+
+def process_color_formatting(text: str, no_process: bool = False) -> str:
+ """处理颜色标签"""
+ if no_process:
+ return text
+ color_pattern = r'<#([0-9a-fA-F]{6})>'
+ close_pattern = r'>'
+
+ parts = re.split(f'({color_pattern}|{close_pattern})', text)
+ result = []
+ color_stack = []
+
+ for part in parts:
+ if part and re.fullmatch(color_pattern, part):
+ color = re.match(color_pattern, part).group(1)
+ color_stack.append(color)
+ continue
+ elif part == '>':
+ if color_stack:
+ color_stack.pop()
+ continue
+ elif part:
+ if color_stack:
+ current_color = color_stack[-1]
+ r = int(current_color[0:2], 16)
+ g = int(current_color[2:4], 16)
+ b = int(current_color[4:6], 16)
+ ansi_code = f'\033[38;2;{r};{g};{b}m'
+ reset_code = '\033[0m'
+ result.append(f'{ansi_code}{part}{reset_code}')
+ else:
+ processed_text = part
+ processed_text = re.sub(r'<#([0-9a-fA-F]{6})>', lambda m: f'<{set_color(f"#{m.group(1)}")}>', processed_text)
+ result.append(processed_text)
+
+ processed_text = ''.join(result)
+ 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
diff --git a/tests/logger_config.json b/tests/logger_config.json
deleted file mode 100644
index 5194963..0000000
--- a/tests/logger_config.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "file_level": "DEBUG",
- "file_name": "latest.log",
- "file_path": "./logs",
- "file_format": "{asctime} {levelname} | {prefix}{message}",
- "file_encoding": "utf-8",
- "enable_console": true,
- "enable_file": true,
- "console_color": true,
- "console_level": "INFO",
- "console_format": "{time} {levelname} | {prefix}{message}",
- "console_prefix": "Auto",
- "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": {
- "DEBUG": "DEBUG",
- "INFO": "INFO",
- "WARN": "WARN",
- "ERRO": "ERRO",
- "CRIT": "CRIT"
- },
- "level_color": {
- "DEBUG": "#c1d5ff",
- "INFO": "#c1ffff",
- "WARN": "#fff600",
- "ERRO": "#ffa000",
- "CRIT": "#ff8181"
- }
-}
\ No newline at end of file
diff --git a/tests/t-level.py b/tests/t-level.py
index 536513a..1131774 100644
--- a/tests/t-level.py
+++ b/tests/t-level.py
@@ -8,8 +8,8 @@ from src.logiliteal import Logger
log = Logger()
-log.info("测试信息日志")
log.debug("测试调试日志")
+log.info("测试信息日志")
log.warn("测试警告日志")
log.error("测试错误日志")
log.critical("测试严重错误日志")
\ No newline at end of file