mirror of
https://github.com/Nanaloveyuki/py-logiliteal.git
synced 2025-10-19 16:46:23 +00:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b719e08d0f | ||
|
57d77615a9 | ||
|
02d3f8a5ba | ||
|
71d5dcaff3 | ||
|
635fc846f8 | ||
|
b1918d1948 | ||
|
da2231875f | ||
|
d1422513e9 | ||
|
80e4a4d02f | ||
|
eed6144970 | ||
|
796a8a82f6 | ||
|
89bc381235 | ||
|
89cb26b9b3 | ||
|
9b5d7cdfad | ||
|
d5f157bdf3 | ||
|
5a7756b793 | ||
|
b83984d58a | ||
|
652b6d6d2a | ||
|
9bc021d991 | ||
|
d3d9613acc | ||
|
388b1c2b75 | ||
|
563cecac97 | ||
|
827658beab | ||
|
9ebd08e5e2 | ||
|
3291f19a25 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,5 +1,7 @@
|
||||
# Custom
|
||||
*.backup.json
|
||||
*_backup.json
|
||||
auto_upload.bat
|
||||
auto_upload.sh
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
|
45
README.md
45
README.md
@@ -8,14 +8,44 @@
|
||||
py-logiliteal 允许嵌入到其他项目中, 并根据需要自定义日志记录器
|
||||
同时也支持pip安装
|
||||
```bash
|
||||
pip install py-logiliteal
|
||||
pip install logiliteal
|
||||
```
|
||||
|
||||
**支持高可扩展的样式**
|
||||
- 支持使用HEX十六进制颜色代码`<#ffffff>text</>`渲染颜色
|
||||
- 支持使用占位符`{placeholder}`渲染变量(可手动扩展)
|
||||
- 支持部分Html或Markdown语法(如`<b>text</b>`)
|
||||
- 支持自定义日志格式和日志颜色
|
||||
|
||||
Html语法支持:
|
||||
- `<b>text</b>` 加粗
|
||||
- `<i>text</i>` 斜体
|
||||
- `<u>text</u>` 下划线
|
||||
- `<s>text</s>` 删除线
|
||||
- `<c>` 清除颜色
|
||||
- `<br>` 换行
|
||||
- `</>` Html万用闭合
|
||||
|
||||
> **注意!Html嵌套可能会有问题, 不建议过多嵌套**
|
||||
|
||||
Markdown语法支持:
|
||||
- `**text**` 加粗
|
||||
- `*text*` 斜体
|
||||
- `__text__` 下划线
|
||||
- `~~text~~` 删除线
|
||||
- `[text](url)` 链接
|
||||
|
||||
> **注意!Html和Markdown语法虽然可以同时使用,但是不保证所有语法都能正常工作, 建议只使用其中一种**
|
||||
|
||||
> **目前语法解析属于测试阶段,欢迎反馈或者提出Pr**
|
||||
- 目前支持的Html标签: `<b>`, `<i>`, `<u>`, `<s>`, `<c>`, `<br>`, `</>`
|
||||
- 目前支持的Markdown语法: `**`, `*`, `__`, `~~`, `[text](url)`
|
||||
- 目前支持的变量:
|
||||
- `{asctime}` 对应日志完整时间(`config.asctime`)
|
||||
- `{time}` 对应日志简略时间(`config.time`)
|
||||
- `{weekday}` 对应日志星期(`config.weekday`)
|
||||
- `{date}` 对应日志日期(`config.date`)
|
||||
|
||||
**支持的Python版本**
|
||||
- Python 3.13.5
|
||||
- Python 3.13.4
|
||||
@@ -28,7 +58,7 @@ pip install py-logiliteal
|
||||
## 安装
|
||||
暂无安装包, 请使用release发布版或直接clone代码到本地/使用pip安装
|
||||
```bash
|
||||
pip install py-logiliteal
|
||||
pip install logiliteal
|
||||
```
|
||||
|
||||
## 文档
|
||||
@@ -37,11 +67,12 @@ pip install py-logiliteal
|
||||
## 示例
|
||||
```python
|
||||
# 导入
|
||||
from py_logiliteal import Logger
|
||||
# 或 import py_logiliteal(不推荐)
|
||||
from logiliteal import logger, Logger
|
||||
# 或 import logiliteal(不推荐)
|
||||
|
||||
# 实例化
|
||||
logger = Logger()
|
||||
# 实例化(不推荐: 会造成多实例问题)
|
||||
# 推荐直接使用 logger 而不是 Logger()
|
||||
# logger = Logger()
|
||||
|
||||
#使用功能
|
||||
logger.info("这是一条信息日志")
|
||||
@@ -51,7 +82,7 @@ logger.warn("这是一条带有前缀的警告日志", prefix="114514")
|
||||
logger.critical("这是一条带有前缀并且日志等级不同的严重错误日志", prefix="114514", level=55)
|
||||
|
||||
# 自定义配置
|
||||
from py_logiliteal import set_config, get_config
|
||||
from logiliteal import set_config, get_config
|
||||
# 读取配置
|
||||
print(get_config("console_format"))
|
||||
# 默认会输出时间、日志等级、日志前缀、日志消息
|
||||
|
178
log.txt
Normal file
178
log.txt
Normal file
@@ -0,0 +1,178 @@
|
||||
这是一条调试信息这是**粗体**这是*斜体*这是__下划线__这是~~删除线~~这是**粗斜体**这是__粗下划线__这是~~删除线~~这是<#ff0000>红色</>这是<#00ff00>绿色</>这是<#0000ff>蓝色</>这是<#ff0000>嵌<#00ff00>套</></>这是<#ff0000>不完全闭合这是<#ff0000>样式<c>清理这是<b>粗体</b>这是<i>斜体</i>这是<u>下划线</u>这是<s>删除线</s>这是<b><i>粗斜体</i></b>这是<b><u>粗下划线</u></b>这是<b><s>粗删除线</s></b><#ff0000>111<#ff0000>111这是前缀这是后缀</></>这是一条调试信息
|
||||
这是**粗体**
|
||||
这是*斜体*
|
||||
这是__下划线__
|
||||
这是~~删除线~~
|
||||
这是**粗斜体**
|
||||
这是__粗下划线__
|
||||
这是~~删除线~~
|
||||
这是<#ff0000>红色</>
|
||||
这是<#00ff00>绿色</>
|
||||
这是<#0000ff>蓝色</>
|
||||
这是<#ff0000>嵌<#00ff00>套</></>
|
||||
这是<#ff0000>不完全闭合
|
||||
这是<#ff0000>样式<c>清理
|
||||
这是<b>粗体</b>
|
||||
这是<i>斜体</i>
|
||||
这是<u>下划线</u>
|
||||
这是<s>删除线</s>
|
||||
这是<b><i>粗斜体</i></b>
|
||||
这是<b><u>粗下划线</u></b>
|
||||
这是<b><s>粗删除线</s></b>
|
||||
<#ff0000>111<#ff0000>111这是前缀
|
||||
这是后缀</></>
|
||||
这是一条调试信息
|
||||
这是**粗体**
|
||||
这是*斜体*
|
||||
这是__下划线__
|
||||
这是~~删除线~~
|
||||
这是**粗斜体**
|
||||
这是__粗下划线__
|
||||
这是~~删除线~~
|
||||
这是<#ff0000>红色</>
|
||||
这是<#00ff00>绿色</>
|
||||
这是<#0000ff>蓝色</>
|
||||
这是<#ff0000>嵌<#00ff00>套</></>
|
||||
这是<#ff0000>不完全闭合
|
||||
这是<#ff0000>样式<c>清理
|
||||
这是<b>粗体</b>
|
||||
这是<i>斜体</i>
|
||||
这是<u>下划线</u>
|
||||
这是<s>删除线</s>
|
||||
这是<b><i>粗斜体</i></b>
|
||||
这是<b><u>粗下划线</u></b>
|
||||
这是<b><s>粗删除线</s></b>
|
||||
这是一条调试信息
|
||||
|
||||
这是**粗体**
|
||||
|
||||
这是*斜体*
|
||||
|
||||
这是__下划线__
|
||||
|
||||
这是~~删除线~~
|
||||
|
||||
这是**粗斜体**
|
||||
|
||||
这是__粗下划线__
|
||||
|
||||
这是~~删除线~~
|
||||
|
||||
这是<#ff0000>红色</>
|
||||
|
||||
这是<#00ff00>绿色</>
|
||||
|
||||
这是<#0000ff>蓝色</>
|
||||
|
||||
这是<#ff0000>嵌<#00ff00>套</></>
|
||||
|
||||
这是<#ff0000>不完全闭合
|
||||
|
||||
这是<#ff0000>样式<c>清理
|
||||
|
||||
这是<b>粗体</b>
|
||||
|
||||
这是<i>斜体</i>
|
||||
|
||||
这是<u>下划线</u>
|
||||
|
||||
这是<s>删除线</s>
|
||||
|
||||
这是<b><i>粗斜体</i></b>
|
||||
|
||||
这是<b><u>粗下划线</u></b>
|
||||
|
||||
这是<b><s>粗删除线</s></b>
|
||||
|
||||
这是一条调试信息
|
||||
|
||||
这是**粗体**
|
||||
|
||||
这是*斜体*
|
||||
|
||||
这是__下划线__
|
||||
|
||||
这是~~删除线~~
|
||||
|
||||
这是**粗斜体**
|
||||
|
||||
这是__粗下划线__
|
||||
|
||||
这是~~删除线~~
|
||||
|
||||
这是<#ff0000>红色</>
|
||||
|
||||
这是<#00ff00>绿色</>
|
||||
|
||||
这是<#0000ff>蓝色</>
|
||||
|
||||
这是<#ff0000>嵌<#00ff00>套</></>
|
||||
|
||||
这是<#ff0000>不完全闭合
|
||||
|
||||
这是<#ff0000>样式<c>清理
|
||||
|
||||
这是<b>粗体</b>
|
||||
|
||||
这是<i>斜体</i>
|
||||
|
||||
这是<u>下划线</u>
|
||||
|
||||
这是<s>删除线</s>
|
||||
|
||||
这是<b><i>粗斜体</i></b>
|
||||
|
||||
这是<b><u>粗下划线</u></b>
|
||||
|
||||
这是<b><s>粗删除线</s></b>
|
||||
|
||||
<#ff0000>111<#ff0000>111这是前缀
|
||||
|
||||
这是后缀</>
|
||||
</>
|
||||
这是一条调试信息
|
||||
|
||||
这是**粗体**
|
||||
|
||||
这是*斜体*
|
||||
|
||||
这是__下划线__
|
||||
|
||||
这是~~删除线~~
|
||||
|
||||
这是**粗斜体**
|
||||
|
||||
这是__粗下划线__
|
||||
|
||||
这是~~删除线~~
|
||||
|
||||
这是<#ff0000>红色</>
|
||||
|
||||
这是<#00ff00>绿色</>
|
||||
|
||||
这是<#0000ff>蓝色</>
|
||||
|
||||
这是<#ff0000>嵌<#00ff00>套</></>
|
||||
|
||||
这是<#ff0000>不完全闭合
|
||||
|
||||
这是<#ff0000>样式<c>清理
|
||||
|
||||
这是<b>粗体</b>
|
||||
|
||||
这是<i>斜体</i>
|
||||
|
||||
这是<u>下划线</u>
|
||||
|
||||
这是<s>删除线</s>
|
||||
|
||||
这是<b><i>粗斜体</i></b>
|
||||
|
||||
这是<b><u>粗下划线</u></b>
|
||||
|
||||
这是<b><s>粗删除线</s></b>
|
||||
|
||||
<#ff0000>111<#ff0000>111这是前缀
|
||||
|
||||
这是后缀</>
|
||||
</>
|
@@ -2,17 +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": "{asctime} {levelname} | {prefix}{message}",
|
||||
"console_prefix": "Auto",
|
||||
"console_format": "{time} {levelname} | {prefix}{message}{suffix}",
|
||||
"console_encoding": "utf-8",
|
||||
"date_format": "%Y-%m-%d %H:%M:%S",
|
||||
"level_name": {
|
||||
"asctime_format": "%Y-%m-%d %H:%M:%S",
|
||||
"time_format": "%H:%M:%S",
|
||||
"date_format": "%Y-%m-%d",
|
||||
"weekday_format": "%A",
|
||||
"level_nickname": {
|
||||
"DEBUG": "DEBUG",
|
||||
"INFO": "INFO",
|
||||
"WARN": "WARN",
|
||||
@@ -21,9 +23,10 @@
|
||||
},
|
||||
"level_color": {
|
||||
"DEBUG": "#c1d5ff",
|
||||
"INFO": "#c1f8ff",
|
||||
"INFO": "#c1ffff",
|
||||
"WARN": "#fff600",
|
||||
"ERRO": "#ffa000",
|
||||
"CRIT": "#ff8181"
|
||||
}
|
||||
},
|
||||
"time_color": "#28ffb6"
|
||||
}
|
2
setup.py
2
setup.py
@@ -6,7 +6,7 @@ long_description = (here / "README.md").read_text(encoding="utf-8")
|
||||
|
||||
setup(
|
||||
name="logiliteal",
|
||||
version="0.1.0",
|
||||
version="0.1.4",
|
||||
description="简洁,高扩展性,可自定义的日志库 / Simple, high extensibility, and customizable logging library",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
|
@@ -5,23 +5,43 @@ 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, get_asctime, get_date, get_time, get_weekday, fmt_console, fmt_placeholder, fmt_message, fmt_level_name, set_style
|
||||
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
|
||||
|
||||
logger = Logger()
|
||||
|
||||
__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",
|
||||
"Logger" # 日志记录器非实例化
|
||||
"set_style",
|
||||
"_placeholder",
|
||||
"_level_name_map",
|
||||
"DEFAULT_CONFIG",
|
||||
"logger",
|
||||
"Logger",
|
||||
]
|
139
src/logiliteal/levels.py
Normal file
139
src/logiliteal/levels.py
Normal file
@@ -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,
|
||||
)
|
@@ -1,11 +0,0 @@
|
||||
"""
|
||||
日志级别模块
|
||||
Log level module
|
||||
|
||||
"""
|
||||
# encoding: utf-8
|
||||
# python 3.13.5
|
||||
|
||||
from .levels import Logger
|
||||
|
||||
__all__ = ["Logger"]
|
@@ -1,128 +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
|
||||
|
||||
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("日志文件路径不是目录,已自动自动使用默认目录")
|
||||
set_config("file_path", "./logs")
|
||||
pathlib.Path("./logs").mkdir(parents=True, exist_ok=True)
|
||||
if _get_full_path(file_path, file_name).exists():
|
||||
from os import rename
|
||||
rename(_get_full_path(file_path, file_name), _get_full_path(file_path, f"{get_asctime().replace(':', '-')}.log"))
|
||||
self.debug("日志文件已存在,已自动重命名")
|
||||
|
||||
@staticmethod
|
||||
def info(message: Any, prefix: str | None = None, level: int = 20) -> Optional[str]:
|
||||
"""
|
||||
信息日志
|
||||
Info log
|
||||
:param message: 消息内容 Message content
|
||||
:param prefix: 前缀 Prefix
|
||||
:param level: 日志级别 Log level(11~20)
|
||||
"""
|
||||
if level < 11 or level > 20:
|
||||
return None
|
||||
if is_enable_file:
|
||||
with open(_get_full_path(file_path, file_name), "a", encoding=file_encoding) as f:
|
||||
f.write(fmt_file(level, fmt_message(message, no_placeholder=True), prefix))
|
||||
if is_enable_console:
|
||||
print(fmt_console(level, fmt_message(message, no_placeholder=True), prefix))
|
||||
return fmt_console(level, fmt_message(message, no_placeholder=True), prefix)
|
||||
|
||||
@staticmethod
|
||||
def debug(message: Any, prefix: str | None = None, level: int = 10) -> Optional[str]:
|
||||
"""
|
||||
调试日志
|
||||
Debug log
|
||||
:param message: 消息内容 Message content
|
||||
:param prefix: 前缀 Prefix
|
||||
:param level: 日志级别 Log level(0~10)
|
||||
"""
|
||||
if level < 0 or level > 10:
|
||||
return None
|
||||
if is_enable_file:
|
||||
with open(_get_full_path(file_path, file_name), "a", encoding=file_encoding) as f:
|
||||
f.write(fmt_file(level, fmt_message(message, no_placeholder=True), prefix))
|
||||
if is_enable_console:
|
||||
print(fmt_console(level, fmt_message(message, no_placeholder=True), prefix))
|
||||
return fmt_console(level, fmt_message(message, no_placeholder=True), prefix)
|
||||
|
||||
@staticmethod
|
||||
def warn(message: Any, prefix: str | None = None, level: int = 31) -> Optional[str]:
|
||||
"""
|
||||
警告日志
|
||||
Warn log
|
||||
:param message: 消息内容 Message content
|
||||
:param prefix: 前缀 Prefix
|
||||
:param level: 日志级别 Log level(31~40)
|
||||
"""
|
||||
if level < 30 or level > 40:
|
||||
return None
|
||||
if is_enable_file:
|
||||
with open(_get_full_path(file_path, file_name), "a", encoding=file_encoding) as f:
|
||||
f.write(fmt_file(level, fmt_message(message, no_placeholder=True), prefix))
|
||||
if is_enable_console:
|
||||
print(fmt_console(level, fmt_message(message, no_placeholder=True), prefix))
|
||||
return fmt_console(level, fmt_message(message, no_placeholder=True), prefix)
|
||||
|
||||
@staticmethod
|
||||
def error(message: Any, prefix: str | None = None, level: int = 41) -> Optional[str]:
|
||||
"""
|
||||
错误日志
|
||||
Error log
|
||||
:param message: 消息内容 Message content
|
||||
:param prefix: 前缀 Prefix
|
||||
:param level: 日志级别 Log level(41~50)
|
||||
"""
|
||||
if level < 40 or level > 50:
|
||||
return None
|
||||
if is_enable_file:
|
||||
with open(_get_full_path(file_path, file_name), "a", encoding=file_encoding) as f:
|
||||
f.write(fmt_file(level, fmt_message(message, no_placeholder=True), prefix))
|
||||
if is_enable_console:
|
||||
print(fmt_console(level, fmt_message(message, no_placeholder=True), prefix))
|
||||
return fmt_console(level, fmt_message(message, no_placeholder=True), prefix)
|
||||
|
||||
@staticmethod
|
||||
def critical(message: Any, prefix: str | None = None, level: int = 51) -> Optional[str]:
|
||||
"""
|
||||
严重错误日志
|
||||
Critical error log
|
||||
:param message: 消息内容 Message content
|
||||
:param prefix: 前缀 Prefix
|
||||
:param level: 日志级别 Log level(51~60)
|
||||
"""
|
||||
if level < 50 or level > 60:
|
||||
return None
|
||||
if is_enable_file:
|
||||
with open(_get_full_path(file_path, file_name), "a", encoding=file_encoding) as f:
|
||||
f.write(fmt_file(level, fmt_message(message, no_placeholder=True), prefix))
|
||||
if is_enable_console:
|
||||
print(fmt_console(level, fmt_message(message, no_placeholder=True), prefix))
|
||||
return fmt_console(level, fmt_message(message, no_placeholder=True), prefix)
|
||||
|
||||
|
@@ -5,24 +5,39 @@ Utility functions
|
||||
# encoding = utf-8
|
||||
# python 3.13.5
|
||||
|
||||
from .configs import get_config, set_config, reset_config
|
||||
from .time import get_asctime, get_date, get_time, get_weekday
|
||||
from .fmt import fmt_console, fmt_placeholder, fmt_message, fmt_level_name
|
||||
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
|
||||
from .env import _placeholder, _level_name_map, DEFAULT_CONFIG
|
||||
|
||||
__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",
|
||||
"fmt_regex",
|
||||
"add_placeholder",
|
||||
"remove_placeholder",
|
||||
"get_placeholder",
|
||||
"set_color",
|
||||
"set_bg_color",
|
||||
"set_style"
|
||||
"set_style",
|
||||
"_placeholder",
|
||||
"_level_name_map",
|
||||
"DEFAULT_CONFIG",
|
||||
]
|
||||
|
@@ -6,133 +6,108 @@ py-logiliteal's config settings, used to set py-logiliteal's global config
|
||||
# encoding = utf-8
|
||||
# python 3.13.5
|
||||
|
||||
DEFAULT_CONFIG_PATH = "config.json"
|
||||
DEFAULT_CONFIG = {
|
||||
"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": "{asctime} {levelname} | {prefix}{message}",
|
||||
"console_prefix": "Auto",
|
||||
"console_encoding": "utf-8",
|
||||
"date_format": "%Y-%m-%d %H:%M:%S",
|
||||
"level_name": {"DEBUG": "DEBUG", "INFO": "INFO", "WARN": "WARN", "ERRO": "ERRO", "CRIT": "CRIT"},
|
||||
"level_color": {"DEBUG": "#c1d5ff", "INFO": "#c1ffff", "WARN": "#fff600", "ERRO": "#ffa000", "CRIT": "#ff8181"},
|
||||
}
|
||||
|
||||
from typing import Union, Optional
|
||||
from logging import error, info
|
||||
import json
|
||||
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:
|
||||
"""
|
||||
初始化配置
|
||||
Init config
|
||||
Args:
|
||||
config (dict, optional): 配置字典. Defaults to DEFAULT_CONFIG.
|
||||
config_path (str, optional): 配置文件路径. Defaults to None.
|
||||
Returns:
|
||||
bool: 是否成功初始化配置
|
||||
"""
|
||||
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
|
||||
|
||||
def get_config(select: str = None) -> Union[dict, str, bool, int, None]:
|
||||
def get_config(key: str | None = None) -> Any:
|
||||
"""
|
||||
获取配置信息 Get config info
|
||||
:param select: 配置项名称 Config item name
|
||||
:return: 配置项值 Config item value
|
||||
获取配置
|
||||
Get config
|
||||
Args:
|
||||
key (str | None, optional): 配置键. Defaults to None.
|
||||
Returns:
|
||||
Any: 配置值
|
||||
"""
|
||||
try:
|
||||
with open(DEFAULT_CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
if select:
|
||||
return config[select]
|
||||
if not pathlib.Path(str(get_config_path())).exists():
|
||||
init_config()
|
||||
with open(str(get_config_path()), "r") as f:
|
||||
config = json.load(f)
|
||||
if key is None:
|
||||
return config
|
||||
except (FileNotFoundError, json.JSONDecodeError):
|
||||
with open(DEFAULT_CONFIG_PATH, "w", encoding="utf-8") as f:
|
||||
json.dump(DEFAULT_CONFIG, f, indent=4)
|
||||
if select:
|
||||
return DEFAULT_CONFIG[select]
|
||||
return DEFAULT_CONFIG
|
||||
except KeyError as e:
|
||||
error(f"配置项 '{select}' 不存在")
|
||||
return None
|
||||
except Exception as e:
|
||||
error(f"读取配置文件失败: {e}")
|
||||
return None
|
||||
return config.get(key, None)
|
||||
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
def set_config(select: str, value: Union[dict, str, bool, int, None]) -> tuple[bool, Optional[str]]:
|
||||
def set_config(key: str, value: Any) -> bool:
|
||||
"""
|
||||
设置配置信息 Set config info
|
||||
:param select: 配置项名称 Config item name
|
||||
:param value: 配置项值 Config item value
|
||||
:return: (设置是否成功, 消息) (Set success or not, message)
|
||||
设置配置
|
||||
Set config
|
||||
Args:
|
||||
key (str): 配置键
|
||||
value (Any): 配置值
|
||||
Returns:
|
||||
bool: 是否成功设置配置
|
||||
"""
|
||||
try:
|
||||
config_path = Path(DEFAULT_CONFIG_PATH)
|
||||
backup_path = Path(__file__).parent / "configs.backup.json"
|
||||
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)
|
||||
return config.get(key, None) == value
|
||||
|
||||
if not config_path.exists():
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
json.dump(DEFAULT_CONFIG, f, indent=4)
|
||||
|
||||
shutil.copy2(config_path, backup_path)
|
||||
|
||||
with open(DEFAULT_CONFIG_PATH, "r+", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
config[select] = value
|
||||
f.seek(0)
|
||||
json.dump(config, f, indent=4)
|
||||
f.truncate()
|
||||
|
||||
with open(DEFAULT_CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
verify_config = json.load(f)
|
||||
if verify_config.get(select) != value:
|
||||
error(f"配置项 '{select}' 设置失败,值为 {value}")
|
||||
shutil.move(backup_path, DEFAULT_CONFIG_PATH)
|
||||
return False, f"配置项 '{select}' 设置失败,已恢复备份"
|
||||
|
||||
Path(backup_path).unlink(missing_ok=True)
|
||||
return True, f"配置项 '{select}' 设置成功"
|
||||
|
||||
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}")
|
||||
|
||||
if Path(backup_path).exists():
|
||||
shutil.move(backup_path, DEFAULT_CONFIG_PATH)
|
||||
return False, f"设置失败,已恢复备份: {str(e)}"
|
||||
return False, f"设置配置文件失败: {str(e)}"
|
||||
|
||||
@staticmethod
|
||||
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: 是否成功备份配置
|
||||
"""
|
||||
try:
|
||||
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 not config_path.exists():
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
json.dump(DEFAULT_CONFIG, f, indent=4)
|
||||
return True, "配置文件不存在,已创建默认配置"
|
||||
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
|
||||
|
||||
from .time import get_asctime
|
||||
timestamp = get_asctime()
|
||||
backup_path = f"{DEFAULT_CONFIG_PATH}_{timestamp}.backup.json"
|
||||
backup_path = backup_path.replace(":", "-")
|
||||
shutil.copy2(DEFAULT_CONFIG_PATH, backup_path)
|
||||
with open(DEFAULT_CONFIG_PATH, "w", encoding="utf-8") as f:
|
||||
json.dump(DEFAULT_CONFIG, f, indent=4)
|
||||
return True
|
||||
except PermissionError:
|
||||
error(f"权限不足,无法操作配置文件: {DEFAULT_CONFIG_PATH}")
|
||||
return False, "权限不足,无法重置配置"
|
||||
except json.JSONDecodeError:
|
||||
error("配置文件格式错误,无法解析")
|
||||
return False, "配置文件格式错误,无法重置"
|
||||
except Exception as e:
|
||||
error(f"重置配置文件失败: {e}")
|
||||
return False, f"重置配置失败: {str(e)}"
|
||||
def reset_config() -> bool:
|
||||
"""
|
||||
重置配置
|
||||
Reset config
|
||||
Returns:
|
||||
bool: 是否成功重置配置
|
||||
"""
|
||||
init_config(config=DEFAULT_CONFIG)
|
||||
return True
|
||||
|
89
src/logiliteal/utils/env.py
Normal file
89
src/logiliteal/utils/env.py
Normal file
@@ -0,0 +1,89 @@
|
||||
"""
|
||||
环境变量或常量与配置文件
|
||||
Environment variables, constants and configuration files
|
||||
|
||||
"""
|
||||
# encoding = utf-8
|
||||
# python 3.13.5
|
||||
|
||||
# 默认配置
|
||||
DEFAULT_CONFIG = {
|
||||
"file_level": "DEBUG",
|
||||
"file_name": "latest.log",
|
||||
"file_path": "./logs",
|
||||
"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}{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_nickname": {"DEBUG": "DEBUG", "INFO": "INFO", "WARN": "WARN", "ERRO": "ERRO", "CRIT": "CRIT"},
|
||||
"level_color": {"DEBUG": "#c1d5ff", "INFO": "#c1ffff", "WARN": "#fff600", "ERRO": "#ffa000", "CRIT": "#ff8181"},
|
||||
"time_color": "#28ffb6",
|
||||
}
|
||||
|
||||
# 占位符
|
||||
_placeholder: dict[str, str | bytes] = {
|
||||
"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": "{fmt_level_number_to_name(level_number)}",
|
||||
"level_number": "{level_number}",
|
||||
"message": "{message}",
|
||||
"prefix": "{prefix}",
|
||||
"suffix": "{suffix}",
|
||||
}
|
||||
|
||||
# 日志级别映射
|
||||
_level_name_map: dict[str, int | float] = {
|
||||
"DEBUG": 0,
|
||||
"INFO": 10,
|
||||
"WARN": 20,
|
||||
"ERRO": 30,
|
||||
"CRIT": 40,
|
||||
"UNKN": -1,
|
||||
}
|
||||
|
||||
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"))
|
||||
|
@@ -1,196 +0,0 @@
|
||||
"""
|
||||
py-logiliteal的格式化工具,用于格式化日志输出
|
||||
py-logiliteal's formatter, used to format log output
|
||||
|
||||
"""
|
||||
# encoding = utf-8
|
||||
# python 3.13.5
|
||||
|
||||
from .configs import get_config
|
||||
from typing import Any, Optional, Union
|
||||
from .time import get_asctime, get_time, get_weekday, get_date
|
||||
from .styles import set_color, set_bg_color
|
||||
import re
|
||||
|
||||
def fmt_level(level: str) -> int:
|
||||
"""
|
||||
格式化日志级别
|
||||
Format log level
|
||||
:param level: 日志级别 Log level
|
||||
:return: 格式化后的日志级别 Formatted log level
|
||||
"""
|
||||
level_map = {
|
||||
"DEBUG": 10,
|
||||
"INFO": 20,
|
||||
"WARN": 30,
|
||||
"ERRO": 40,
|
||||
"CRIT": 50,
|
||||
}
|
||||
if level == "UNKN":
|
||||
return -1
|
||||
return level_map.get(level.upper(), 0)
|
||||
|
||||
def fmt_level_number(level: int) -> str:
|
||||
"""
|
||||
格式化日志级别数字
|
||||
Format log level number
|
||||
:param level: 日志级别数字 Log level number
|
||||
:return: 格式化后的日志级别 Formatted log level
|
||||
"""
|
||||
if level <= 10 and level >= 0:
|
||||
return "DEBUG"
|
||||
elif level <= 20 and level > 10:
|
||||
return "INFO"
|
||||
elif level <= 30 and level > 20:
|
||||
return "WARN"
|
||||
elif level <= 40 and level > 30:
|
||||
return "ERRO"
|
||||
elif level <= 50 and level > 40:
|
||||
return "CRIT"
|
||||
else:
|
||||
return "UNKN"
|
||||
|
||||
def fmt_placeholder(message: Any, use_date_color: bool = True) -> str:
|
||||
"""
|
||||
格式化占位符
|
||||
Format placeholder
|
||||
:param message: 消息内容 Message content
|
||||
:return: 格式化后的消息 Formatted message
|
||||
"""
|
||||
class SafeDict(dict):
|
||||
def __missing__(self, key):
|
||||
return f'{{{key}}}'
|
||||
|
||||
if not isinstance(message, str):
|
||||
message = str(message)
|
||||
if use_date_color:
|
||||
message = message.format_map(SafeDict(
|
||||
asctime = set_color(get_asctime(),"#28ffb6"),
|
||||
time = set_color(get_time(),"#28ffb6"),
|
||||
weekday = set_color(get_weekday(),"#28ffb6"),
|
||||
date = set_color(get_date(),"#28ffb6")
|
||||
))
|
||||
else:
|
||||
message = message.format_map(SafeDict(
|
||||
asctime = get_asctime(),
|
||||
time = get_time(),
|
||||
weekday = get_weekday(),
|
||||
date = get_date(),
|
||||
))
|
||||
return message
|
||||
|
||||
def fmt_message(message: Any, no_placeholder: bool = False, no_color: bool = False) -> str:
|
||||
"""
|
||||
格式化消息内容
|
||||
Format message content
|
||||
:param message: 消息内容 Message content
|
||||
:return: 格式化后的消息 Formatted message
|
||||
"""
|
||||
|
||||
def process_color_tags(msg: str) -> str:
|
||||
stack = []
|
||||
result = []
|
||||
current_content = []
|
||||
last_end = 0
|
||||
pattern = re.compile(r'(<#([0-9a-fA-F]{6})>|</>)')
|
||||
|
||||
for match in pattern.finditer(msg):
|
||||
text_between = msg[last_end:match.start()]
|
||||
if stack:
|
||||
current_content.append(text_between)
|
||||
else:
|
||||
result.append(text_between)
|
||||
|
||||
last_end = match.end()
|
||||
tag = match.group(1)
|
||||
|
||||
if tag.startswith('<#'):
|
||||
color_code = match.group(2)
|
||||
stack.append(color_code)
|
||||
else:
|
||||
if stack:
|
||||
color = stack.pop()
|
||||
colored_content = set_color(''.join(current_content), f'#{color}')
|
||||
result.append(colored_content)
|
||||
current_content = []
|
||||
else:
|
||||
result.append(tag)
|
||||
|
||||
remaining_text = msg[last_end:]
|
||||
if stack:
|
||||
current_content.append(remaining_text)
|
||||
else:
|
||||
result.append(remaining_text)
|
||||
|
||||
for color in reversed(stack):
|
||||
result.append(f'<#{color}>')
|
||||
result.append(''.join(current_content))
|
||||
current_content = []
|
||||
|
||||
return ''.join(result)
|
||||
if no_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)
|
||||
|
||||
def fmt_level_name(level_name: str) -> str:
|
||||
if get_config("console_color") != True:
|
||||
return level_name
|
||||
level_name_nick_map = get_config("level_name")
|
||||
|
||||
if level_name in level_name_nick_map:
|
||||
_lnn = level_name_nick_map[level_name]
|
||||
level_color_map = get_config("level_color")
|
||||
|
||||
if level_name in level_color_map:
|
||||
if level_name == "DEBUG":
|
||||
return set_bg_color(set_color(_lnn, level_color_map[level_name]), "#34495e")
|
||||
return set_color(_lnn, level_color_map[level_name])
|
||||
return set_color(_lnn)
|
||||
return "UNKN"
|
||||
|
||||
def fmt_console(level: int, message: Any, prefix: str | None = None) -> Optional[str]:
|
||||
"""
|
||||
格式化控制台输出
|
||||
Format console output
|
||||
:param level: 日志级别 Log level
|
||||
:param message: 消息内容 Message content
|
||||
:return: 格式化后的消息 Formatted message
|
||||
"""
|
||||
cl = get_config("console_level")
|
||||
fmt = get_config("console_format")
|
||||
if fmt_level(cl) > level:
|
||||
return None
|
||||
if prefix is None:
|
||||
prefix = ""
|
||||
fmt = fmt_placeholder(fmt)
|
||||
return fmt.format(
|
||||
levelname = fmt_level_name(fmt_level_number(level)),
|
||||
prefix = fmt_message(prefix, no_placeholder=True),
|
||||
message = fmt_message(message, no_placeholder=True)
|
||||
)
|
||||
|
||||
def fmt_file(level: int, message: Any, prefix: str | None = None) -> Optional[str]:
|
||||
"""
|
||||
格式化文件输出
|
||||
Format file output
|
||||
:param level: 日志级别 Log level
|
||||
:param message: 消息内容 Message content
|
||||
:return: 格式化后的消息 Formatted message
|
||||
"""
|
||||
fl = get_config("file_level")
|
||||
fmt = get_config("file_format")
|
||||
if fmt_level(fl) > level:
|
||||
return None
|
||||
if prefix is None:
|
||||
prefix = ""
|
||||
fmt = fmt_placeholder(fmt, 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)
|
||||
)}\n"
|
395
src/logiliteal/utils/formats.py
Normal file
395
src/logiliteal/utils/formats.py
Normal file
@@ -0,0 +1,395 @@
|
||||
"""
|
||||
格式化工具
|
||||
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, DEFAULT_CONFIG
|
||||
from typing import Any
|
||||
import re
|
||||
|
||||
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
|
||||
Args:
|
||||
level_number (int | float): 日志级别数字
|
||||
Returns:
|
||||
str: 日志级别名称
|
||||
"""
|
||||
if not level_number in range(0, 50):
|
||||
return _level_name_map.get(-1, "UNKNOWN")
|
||||
if 0 <= level_number <= 9:
|
||||
return _level_name_map.get(0, "DEBUG")
|
||||
elif 10 <= level_number <= 19:
|
||||
return _level_name_map.get(10, "INFO")
|
||||
elif 20 <= level_number <= 29:
|
||||
return _level_name_map.get(20, "WARNING")
|
||||
elif 30 <= level_number <= 39:
|
||||
return _level_name_map.get(30, "ERROR")
|
||||
elif 40 <= level_number <= 49:
|
||||
return _level_name_map.get(40, "CRITICAL")
|
||||
return _level_name_map.get(-1, "UNKNOWN")
|
||||
|
||||
def fmt_level_name_to_number(level_name: str) -> int:
|
||||
"""
|
||||
格式化日志级别名称为数字
|
||||
Format log level name to number
|
||||
Args:
|
||||
level_name (str): 日志级别名称
|
||||
Returns:
|
||||
int: 日志级别数字
|
||||
"""
|
||||
return _level_name_map.get(level_name.upper(), -1)
|
||||
|
||||
def fmt_level_list(level_number: int | float) -> dict:
|
||||
"""
|
||||
格式化日志级别数据字典
|
||||
Format log level data dict
|
||||
Args:
|
||||
level_number (int | float): 日志级别数字
|
||||
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": level_nickname_config.get(level_name.upper(), level_name),
|
||||
"level_name": level_name.upper(),
|
||||
}
|
||||
|
||||
def get_level_number(level_list: dict) -> int | float:
|
||||
"""
|
||||
获取日志级别数字
|
||||
Get log level number
|
||||
Args:
|
||||
level_list (dict[str, int | float]): 日志级别数据字典
|
||||
Returns:
|
||||
int | float: 日志级别数字
|
||||
"""
|
||||
return level_list.get("level_number")
|
||||
|
||||
def get_level_name(level_list: dict) -> str:
|
||||
"""
|
||||
获取日志级别名称
|
||||
Get log level name
|
||||
Args:
|
||||
level_list (dict[str, int | float]): 日志级别数据字典
|
||||
Returns:
|
||||
str: 日志级别名称
|
||||
"""
|
||||
return level_list.get("level_name", "UNKN")
|
||||
|
||||
def get_level_nickname(level_list: dict) -> str:
|
||||
"""
|
||||
获取日志级别昵称
|
||||
Get log level nickname
|
||||
Args:
|
||||
level_list (dict[str, int | float]): 日志级别数据字典
|
||||
Returns:
|
||||
str: 日志级别昵称
|
||||
"""
|
||||
return level_list.get("level_nickname", "UNKN")
|
||||
|
||||
def fmt_placeholder(
|
||||
message: str = "",
|
||||
level_number: int = 0,
|
||||
) -> str:
|
||||
"""
|
||||
格式化占位符
|
||||
Format placeholder
|
||||
Args:
|
||||
message (str, optional): 日志消息. Defaults to "".
|
||||
Returns:
|
||||
str: 格式化后的日志消息或原始消息
|
||||
"""
|
||||
# 创建上下文字典,包含所有可能用到的函数和变量
|
||||
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)))
|
||||
|
||||
def add_placeholder(key: str, value: str | bytes) -> None:
|
||||
"""
|
||||
添加新的占位符
|
||||
Add new placeholder
|
||||
Args:
|
||||
key (str): 占位符键
|
||||
value (str | bytes): 占位符值
|
||||
"""
|
||||
global _placeholder
|
||||
_placeholder[key] = value
|
||||
|
||||
def get_placeholder(key: str | None = None) -> str | dict[str, str | bytes]:
|
||||
"""
|
||||
获取占位符
|
||||
Get placeholder
|
||||
Args:
|
||||
key (str | None, optional): 占位符键. Defaults to None.
|
||||
Returns:
|
||||
str | dict[str, str | bytes]: 占位符值或占位符字典
|
||||
"""
|
||||
if key is None:
|
||||
return _placeholder
|
||||
return _placeholder.get(key, "None") if key in _placeholder else "None"
|
||||
|
||||
def remove_placeholder(key: str) -> bool:
|
||||
"""
|
||||
删除占位符
|
||||
Remove placeholder
|
||||
Args:
|
||||
key (str): 占位符键
|
||||
Returns:
|
||||
bool: 删除成功返回True, 删除失败返回False
|
||||
"""
|
||||
global _placeholder
|
||||
try:
|
||||
if key in _placeholder:
|
||||
del _placeholder[key]
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
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
|
||||
Args:
|
||||
message (str): 日志消息
|
||||
Returns:
|
||||
str: 格式化后的日志消息
|
||||
"""
|
||||
if no_regex | (no_color and no_html and no_link and no_markdown and no_special_tags):
|
||||
return message
|
||||
|
||||
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
|
||||
),
|
||||
no_process=no_html
|
||||
),
|
||||
no_process=no_color
|
||||
)
|
||||
|
||||
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
|
||||
Args:
|
||||
message (str): 日志消息
|
||||
Returns:
|
||||
str: 格式化后的日志消息
|
||||
"""
|
||||
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"
|
||||
|
||||
|
||||
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_markdown: bool = False,
|
||||
no_link: bool = False,
|
||||
no_special_tags: bool = False
|
||||
) -> str:
|
||||
"""
|
||||
格式化控制台日志
|
||||
Format console log
|
||||
Args:
|
||||
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: 格式化后的控制台日志
|
||||
"""
|
||||
# 获取基础配置
|
||||
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']
|
||||
}
|
||||
|
||||
# 处理时间相关占位符的颜色
|
||||
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
|
||||
Args:
|
||||
message (str): 日志消息
|
||||
Returns:
|
||||
str: 格式化后的日志消息
|
||||
"""
|
||||
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,
|
||||
)
|
128
src/logiliteal/utils/regex.py
Normal file
128
src/logiliteal/utils/regex.py
Normal file
@@ -0,0 +1,128 @@
|
||||
"""
|
||||
正则表达式处理工具
|
||||
"""
|
||||
import re
|
||||
from collections import deque
|
||||
from .styles import set_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
|
@@ -8,19 +8,21 @@ py-logiliteal's style tools, used to format log output
|
||||
|
||||
from typing import Union, Optional
|
||||
|
||||
def _get_hex_to_ansi(hex_color: str) -> 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
|
||||
:param hex_color: 16进制颜色值 Hex color value
|
||||
:return: ANSI转义序列 ANSI escape sequence
|
||||
"""
|
||||
if not hex_color:
|
||||
return ""
|
||||
if not hex_color.startswith("#"):
|
||||
return None
|
||||
r, g, b = int(hex_color[1:3], 16), int(hex_color[3:5], 16), int(hex_color[5:7], 16)
|
||||
return f"\033[38;2;{r};{g};{b}m"
|
||||
|
||||
def set_color(text: str, color: str) -> str:
|
||||
def set_color(text: str | None, color: str | None) -> str:
|
||||
"""
|
||||
设置文本颜色
|
||||
Set text color
|
||||
@@ -28,12 +30,16 @@ def set_color(text: str, color: str) -> str:
|
||||
:param color: 颜色值 Color value
|
||||
:return: 格式化后的文本 Formatted text
|
||||
"""
|
||||
ansi = _get_hex_to_ansi(color)
|
||||
if not text:
|
||||
return ""
|
||||
if not color:
|
||||
return text
|
||||
ansi = str(_get_hex_to_ansi(color))
|
||||
if not ansi:
|
||||
return text
|
||||
return f"{ansi}{text}\033[0m"
|
||||
|
||||
def set_bg_color(text: str, color: str) -> str:
|
||||
def set_bg_color(text: str | None, color: str | None) -> str:
|
||||
"""
|
||||
设置文本背景颜色
|
||||
Set text background color
|
||||
@@ -41,28 +47,46 @@ def set_bg_color(text: str, color: str) -> str:
|
||||
:param color: 颜色值 Color value
|
||||
:return: 格式化后的文本 Formatted text
|
||||
"""
|
||||
ansi = _get_hex_to_ansi(color)
|
||||
if not text:
|
||||
return ""
|
||||
if not color:
|
||||
return text
|
||||
ansi = str(_get_hex_to_ansi(color))
|
||||
if not ansi:
|
||||
return text
|
||||
# 将前景色ANSI代码转换为背景色代码 (38→48)
|
||||
ansi = ansi.replace("38;", "48;")
|
||||
return f"{ansi}{text}\033[0m"
|
||||
|
||||
def set_style(text: str, bold: bool = False, underline: bool = False, reverse: bool = False) -> str:
|
||||
def set_style(
|
||||
text: str | None,
|
||||
bold: bool = False,
|
||||
italic: bool = False,
|
||||
underline: bool = False,
|
||||
strikethrough: bool = False,
|
||||
reverse: bool = False
|
||||
) -> str:
|
||||
"""
|
||||
设置文本样式
|
||||
Set text style
|
||||
:param text: 文本内容 Text content
|
||||
:param bold: 是否加粗 Is bold
|
||||
:param italic: 是否斜体 Is italic
|
||||
:param underline: 是否下划线 Is underline
|
||||
:param strikethrough: 是否划线 Is strikethrough
|
||||
:param reverse: 是否反相 Is reverse
|
||||
:return: 格式化后的文本 Formatted text
|
||||
"""
|
||||
if not text:
|
||||
return ""
|
||||
ansi = ""
|
||||
if bold:
|
||||
ansi += "\033[1m"
|
||||
if italic:
|
||||
ansi += "\033[3m"
|
||||
if underline:
|
||||
ansi += "\033[4m"
|
||||
if strikethrough:
|
||||
ansi += "\033[9m"
|
||||
if reverse:
|
||||
ansi += "\033[7m"
|
||||
return f"{ansi}{text}\033[0m"
|
||||
|
@@ -9,22 +9,13 @@ Time utility module, used for time formatting and output, caching time formattin
|
||||
from datetime import datetime
|
||||
from .configs import get_config
|
||||
|
||||
cache_time: str = ""
|
||||
cache_asctime: str = ""
|
||||
cache_date: str = ""
|
||||
cache_weekday: str = ""
|
||||
|
||||
def get_asctime() -> str:
|
||||
"""
|
||||
获取当前时间(YYYY-MM-DD HH:MM:SS),并缓存格式化结果
|
||||
Get current time(YYYY-MM-DD HH:MM:SS) and cache formatted result
|
||||
:return: 格式化后的时间 Formatted time
|
||||
"""
|
||||
global cache_asctime
|
||||
if cache_asctime:
|
||||
return cache_asctime
|
||||
cache_asctime = datetime.now().strftime(get_config("date_format"))
|
||||
return cache_asctime
|
||||
return str(_get_time(get_config("asctime_format")))
|
||||
|
||||
def get_time() -> str:
|
||||
"""
|
||||
@@ -32,24 +23,15 @@ def get_time() -> str:
|
||||
Get current time(HH:MM:SS) and cache formatted result
|
||||
:return: 格式化后的时间 Formatted time
|
||||
"""
|
||||
global cache_time
|
||||
if cache_time:
|
||||
return cache_time
|
||||
cache_time = datetime.now().strftime("%H:%M:%S")
|
||||
return cache_time
|
||||
return str(_get_time(get_config("time_format")))
|
||||
|
||||
def get_date() -> str:
|
||||
"""
|
||||
获取当前日期(YYYY-MM-DD),并缓存格式化结果
|
||||
获取当前日期(星期几),并缓存格式化结果
|
||||
Get current date(YYYY-MM-DD) and cache formatted result
|
||||
:return: 格式化后的日期 Formatted date
|
||||
"""
|
||||
global cache_date
|
||||
if cache_date:
|
||||
return cache_date
|
||||
cache_date = datetime.now().strftime("%Y-%m-%d")
|
||||
return cache_date
|
||||
return str(_get_time(get_config("date_format")))
|
||||
|
||||
def get_weekday() -> str:
|
||||
"""
|
||||
@@ -57,8 +39,13 @@ def get_weekday() -> str:
|
||||
Get current date(weekday) and cache formatted result
|
||||
:return: 格式化后的星期几 Formatted weekday
|
||||
"""
|
||||
global cache_weekday
|
||||
if cache_weekday:
|
||||
return cache_weekday
|
||||
cache_weekday = datetime.now().strftime("%A")
|
||||
return cache_weekday
|
||||
return str(_get_time(get_config("weekday_format")))
|
||||
|
||||
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 str(datetime.now().strftime(fmt))
|
||||
|
31
test/t-color.py
Normal file
31
test/t-color.py
Normal file
@@ -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>样式<c>清理")
|
||||
logger.debug("这是<b>粗体</b>")
|
||||
logger.debug("这是<i>斜体</i>")
|
||||
logger.debug("这是<u>下划线</u>")
|
||||
logger.debug("这是<s>删除线</s>")
|
||||
logger.debug("这是<b><i>粗斜体</i></b>")
|
||||
logger.debug("这是<b><u>粗下划线</u></b>")
|
||||
logger.debug("这是<b><s>粗删除线</s></b>")
|
||||
logger.debug("这是前缀", prefix="<#ff0000>111")
|
||||
logger.debug("这是后缀", suffix="</>")
|
@@ -1,26 +0,0 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.append(str(project_root))
|
||||
|
||||
from src.utils.configs import get_config, set_config, reset_config
|
||||
|
||||
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())
|
@@ -1,50 +0,0 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.append(str(project_root))
|
||||
|
||||
from src.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)))
|
@@ -1,12 +0,0 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.append(str(project_root))
|
||||
|
||||
from src import Logger
|
||||
|
||||
log = Logger()
|
||||
|
||||
log.info("测试信息日志")
|
||||
log.debug("测试调试日志")
|
@@ -1,4 +0,0 @@
|
||||
from logiliteal import Logger
|
||||
log = Logger()
|
||||
|
||||
log.info("hello world")
|
Reference in New Issue
Block a user