🎨 改进了对占位符和正则表达式的处理

This commit is contained in:
Nanaloveyuki
2025-07-29 14:17:22 +08:00
parent 89bc381235
commit 796a8a82f6
8 changed files with 331 additions and 206 deletions

View File

@ -7,7 +7,7 @@
"enable_console": true, "enable_console": true,
"enable_file": true, "enable_file": true,
"console_color": true, "console_color": true,
"console_level": "INFO", "console_level": "DEBUG",
"console_format": "{time} {levelname} | {prefix}{message}", "console_format": "{time} {levelname} | {prefix}{message}",
"console_prefix": "Auto", "console_prefix": "Auto",
"console_encoding": "utf-8", "console_encoding": "utf-8",

View File

@ -11,96 +11,116 @@ from ..utils.fmt import fmt_file, fmt_message, fmt_console
from ..utils.configs import get_config, set_config from ..utils.configs import get_config, set_config
from ..utils.time import get_asctime from ..utils.time import get_asctime
import pathlib import pathlib
from pathlib import Path
def _get_full_path(file_path, file_name): def _get_full_path(file_path, file_name):
file_path.mkdir(parents=True, exist_ok=True) file_path.mkdir(parents=True, exist_ok=True)
return file_path / file_name 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: class Logger:
def __init__(self): def __init__(self):
if pathlib.Path(file_path).exists(): project_root = Path(__file__).parent.parent.parent.parent
if not pathlib.Path(file_path).is_dir(): file_path = project_root / get_config("file_path")
self.warn("日志文件路径不是目录,已自动自动使用默认目录") 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") set_config("file_path", "./logs")
pathlib.Path("./logs").mkdir(parents=True, exist_ok=True) 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 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("日志文件已存在,已自动重命名") self.debug("日志文件已存在,已自动重命名")
def _log(self, msg, pf, lvn): def _log(self, msg, pf, lvn, no_file: bool = False, no_console: bool = False):
if is_enable_file: project_root = Path(__file__).parent.parent.parent.parent
with open(_get_full_path(file_path, file_name), "a", encoding=file_encoding) as f: file_path = project_root / get_config("file_path")
f.write(fmt_file(lvn, fmt_message(msg, no_placeholder=True), pf)) file_path.mkdir(parents=True, exist_ok=True)
if is_enable_console: 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) console_output = fmt_console(lvn, fmt_message(msg, no_placeholder=True), pf)
if console_output is not None: if console_output is not None:
print(console_output) print(console_output)
return fmt_console(lvn, fmt_message(msg, no_placeholder=True), pf) 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 Debug log
:param message: 消息内容 Message content :param message: 消息内容 Message content
:param prefix: 前缀 Prefix :param prefix: 前缀 Prefix
:param level: 日志级别 Log level(0~9) :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 Info log
:param message: 消息内容 Message content :param message: 消息内容 Message content
:param prefix: 前缀 Prefix :param prefix: 前缀 Prefix
:param level: 日志级别 Log level(10~19) :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 Warn log
:param message: 消息内容 Message content :param message: 消息内容 Message content
:param prefix: 前缀 Prefix :param prefix: 前缀 Prefix
:param level: 日志级别 Log level(20~29) :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 Error log
:param message: 消息内容 Message content :param message: 消息内容 Message content
:param prefix: 前缀 Prefix :param prefix: 前缀 Prefix
:param level: 日志级别 Log level(30~39) :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 Critical error log
:param message: 消息内容 Message content :param message: 消息内容 Message content
:param prefix: 前缀 Prefix :param prefix: 前缀 Prefix
:param level: 日志级别 Log level(40~49) :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 Custom log
:param message: 消息内容 Message content :param message: 消息内容 Message content
:param prefix: 前缀 Prefix :param prefix: 前缀 Prefix
:param level: 日志级别 Log level(50~59...) :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) return self._log(message, prefix, level, no_file, no_console)

View File

@ -9,11 +9,43 @@ py-logiliteal's config settings, used to set py-logiliteal's global config
import json import json
from os import remove from os import remove
import shutil import shutil
import os
from pathlib import Path from pathlib import Path
from typing import Union, Optional, Tuple from typing import Union, Optional, Tuple
from logging import error 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 = { DEFAULT_CONFIG = {
"file_level": "DEBUG", "file_level": "DEBUG",
"file_name": "latest.log", "file_name": "latest.log",
@ -23,7 +55,7 @@ DEFAULT_CONFIG = {
"enable_console": True, "enable_console": True,
"enable_file": True, "enable_file": True,
"console_color": True, "console_color": True,
"console_level": "INFO", "console_level": "DEBUG",
"console_format": "{time} {levelname} | {prefix}{message}", "console_format": "{time} {levelname} | {prefix}{message}",
"console_prefix": "Auto", "console_prefix": "Auto",
"console_encoding": "utf-8", "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_cache = json.load(f)
g_config_mtime = current_mtime g_config_mtime = current_mtime
else: else:
# 确保目录存在
config_path.parent.mkdir(parents=True, exist_ok=True)
with open(config_path, "w", encoding="utf-8") as f: with open(config_path, "w", encoding="utf-8") as f:
json.dump(DEFAULT_CONFIG, f, indent=4) json.dump(DEFAULT_CONFIG, f, indent=4)
g_config_cache = DEFAULT_CONFIG g_config_cache = DEFAULT_CONFIG

View File

@ -10,7 +10,15 @@ from .configs import get_config
from typing import Any, Optional from typing import Any, Optional
from .time import get_asctime, get_time, get_weekday, get_date from .time import get_asctime, get_time, get_weekday, get_date
from .styles import set_color, set_bg_color 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: if get_config("time_color") is None:
time_color = "#28ffb6" time_color = "#28ffb6"
@ -61,29 +69,28 @@ def fmt_placeholder(message: Any, use_date_color: bool = True) -> str:
:param message: 消息内容 Message content :param message: 消息内容 Message content
:return: 格式化后的消息 Formatted message :return: 格式化后的消息 Formatted message
""" """
class SafeDict(dict):
def __missing__(self, key):
return f'{{{key}}}'
if not isinstance(message, str): if not isinstance(message, str):
message = str(message) 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 Format message content
@ -91,26 +98,29 @@ def fmt_message(message: Any, no_placeholder: bool = False, no_color: bool = Fal
:return: 格式化后的消息 Formatted message :return: 格式化后的消息 Formatted message
""" """
def process_color_tags(msg: str) -> str: def process_color_tags(msg: str, no_process: bool = False) -> str:
# 优化处理顺序,确保链接和基础格式优先处理 processed = process_color_formatting(
processed = _process_color_formatting( process_special_tags(
_process_special_tags( process_html_styles(
_process_html_styles( process_markdown_formats(
_process_markdown_formats( process_links(msg, no_links),
_process_links(msg) no_markdown
) ),
) no_html
) ),
no_tags
),
no_process
) )
return processed return processed
if no_color: processed_message = str(message)
processed_message = str(message) if not no_placeholder:
else: processed_message = fmt_placeholder(processed_message)
processed_message = process_color_tags(str(message)) if not no_style:
if no_placeholder: processed_message = process_color_tags(processed_message)
return processed_message if no_process:
else: return message
return process_color_tags(fmt_placeholder(processed_message)) if not no_color else fmt_placeholder(processed_message) return processed_message
def fmt_level_name(level_name: str) -> str: def fmt_level_name(level_name: str) -> str:
if get_config("console_color") != True: 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 :return: 格式化后的消息 Formatted message
""" """
console_level = get_config("console_level") 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 return None
fmt = get_config("console_format") fmt = get_config("console_format")
prefix = prefix or "" 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") fl = get_config("file_level")
fmt = get_config("file_format") fmt = get_config("file_format")
if fmt_level(fl) > level: if level != -1 and level < fmt_level(fl):
return None return None
if prefix is None: if prefix is None:
prefix = "" prefix = ""
fmt = fmt_placeholder(fmt, use_date_color=False) fmt = fmt_placeholder(fmt, use_date_color=False)
return f"{fmt.format( return f"{fmt.format(
levelname = fmt_level_number(level), levelname = fmt_level_number(level),
prefix = fmt_message(prefix, no_placeholder=True, no_color=True), prefix = fmt_message(prefix, no_placeholder=True, no_style=True),
message = fmt_message(message, no_placeholder=True, no_color=True) message = fmt_message(message, no_placeholder=True, no_style=True)
)}\n" )}\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'<a\s+href="([^"]+)">(.*?)</a>', replace_link, text, flags=re.DOTALL)
text = re.sub(r'<link\s+href="([^"]+)">(.*?)</link>', 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'<i>([^<]*?)(</i>|$)',
lambda m: '\033[3m' + m.group(1) + '\033[23m', text, flags=re.DOTALL)
text = re.sub(r'<b>([^<]*?)</b>',
lambda m: '\033[1m' + m.group(1) + '\033[22m', text)
text = re.sub(r'<u>([^<]*?)</u>',
lambda m: '\033[4m' + m.group(1) + '\033[24m', text)
text = re.sub(r'<s>([^<]*?)(</s>|$)',
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'<br>', '\n', text)
text = re.sub(r'<c>', '\033[0m', text)
text = re.sub(r'<p>(.*?)</p>', r'\n\033[0m\g<1>\033[0m\n', text, flags=re.DOTALL)
text = re.sub(r'<p>(.*?)(</p>|$)', r'\n\033[0m\g<1>\033[0m\n', text, flags=re.DOTALL)
text = re.sub(r'</p>', '\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

View File

@ -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

View File

@ -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'<a\s+href="([^"]+)">(.*?)</a>', replace_link, text, flags=re.DOTALL)
text = re.sub(r'<link\s+href="([^"]+)">(.*?)</link>', 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斜体 <i></i>
text = re.sub(r'<i>([^<]*?)(</i>|$)',
lambda m: '\033[3m' + m.group(1) + '\033[23m', text, flags=re.DOTALL)
# HTML粗体 <b></b>
text = re.sub(r'<b>([^<]*?)</b>',
lambda m: '\033[1m' + m.group(1) + '\033[22m', text)
# HTML下划线 <u></u>
text = re.sub(r'<u>([^<]*?)</u>',
lambda m: '\033[4m' + m.group(1) + '\033[24m', text)
# HTML删除线 <s></s>
text = re.sub(r'<s>([^<]*?)(</s>|$)',
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'<br>', '\n', text)
text = re.sub(r'<c>', '\033[0m', text)
# 处理段落标签
text = re.sub(r'<p>(.*?)</p>', r'\n\033[0m\\g<1>\033[0m\n', text, flags=re.DOTALL)
text = re.sub(r'<p>(.*?)(</p>|$)', r'\n\033[0m\\g<1>\033[0m\n', text, flags=re.DOTALL)
text = re.sub(r'</p>', '\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

View File

@ -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"
}
}

View File

@ -8,8 +8,8 @@ from src.logiliteal import Logger
log = Logger() log = Logger()
log.info("测试信息日志")
log.debug("测试调试日志") log.debug("测试调试日志")
log.info("测试信息日志")
log.warn("测试警告日志") log.warn("测试警告日志")
log.error("测试错误日志") log.error("测试错误日志")
log.critical("测试严重错误日志") log.critical("测试严重错误日志")