zy-log 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zy_log-0.1.0/LICENSE +19 -0
- zy_log-0.1.0/PKG-INFO +113 -0
- zy_log-0.1.0/README.md +96 -0
- zy_log-0.1.0/pyproject.toml +28 -0
- zy_log-0.1.0/setup.cfg +4 -0
- zy_log-0.1.0/zy_log/__init__.py +42 -0
- zy_log-0.1.0/zy_log/core.py +158 -0
- zy_log-0.1.0/zy_log/loggers.py +26 -0
- zy_log-0.1.0/zy_log.egg-info/PKG-INFO +113 -0
- zy_log-0.1.0/zy_log.egg-info/SOURCES.txt +11 -0
- zy_log-0.1.0/zy_log.egg-info/dependency_links.txt +1 -0
- zy_log-0.1.0/zy_log.egg-info/requires.txt +1 -0
- zy_log-0.1.0/zy_log.egg-info/top_level.txt +1 -0
zy_log-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) [2025] [yaoZhou]
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
zy_log-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zy_log
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An advanced, asynchronous logging system for algorithmic projects.
|
|
5
|
+
Author-email: zy <zhoumuci77@gamil.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yaosterm/zy_log
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/yaosterm/zy_log/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Topic :: System :: Logging
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: rich>=13.0.0
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# zy_log: 一个为算法项目定制的、专业的、异步的日志系统
|
|
19
|
+
|
|
20
|
+
[](https://badge.fury.io/py/zy_log) [](https://opensource.org/licenses/MIT)
|
|
21
|
+
|
|
22
|
+
一个功能强大的 Python 日志包,专为计算密集型和算法项目设计。它通过异步 I/O 确保日志记录不会阻塞您的主计算线程,并使用 `rich` 提供美观的控制台输出和异常堆栈。
|
|
23
|
+
|
|
24
|
+
## 核心功能
|
|
25
|
+
|
|
26
|
+
* **异步非阻塞**: 所有日志 I/O (文件/控制台) 都在一个单独的后台线程中处理。
|
|
27
|
+
* **Rich 集成**: 自动的控制台颜色、美观的格式化和清晰的异常堆栈追踪。
|
|
28
|
+
* **动态配置**: 可通过参数或环境变量 (`LOG_LEVEL`) 轻松覆盖配置。
|
|
29
|
+
* **致命错误处理**: 提供 `logger.error_and_raise()` 方法,在记录致命错误后立即抛出异常,实现“快速失败”(Fail-Fast)。
|
|
30
|
+
|
|
31
|
+
## 安装
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install zy_log
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 快速使用
|
|
38
|
+
|
|
39
|
+
您只需要在您算法的**主入口文件** (`main.py`) 中初始化一次,然后在任何子模块中调用 `get_logger` 即可。
|
|
40
|
+
|
|
41
|
+
### 1. 初始化 (在 `main.py` 中)
|
|
42
|
+
|
|
43
|
+
这是最关键的一步。
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
# main.py
|
|
47
|
+
import zy_log
|
|
48
|
+
import os
|
|
49
|
+
import time
|
|
50
|
+
from your_project import run_my_core_algo
|
|
51
|
+
|
|
52
|
+
# --- 日志系统初始化 (仅一次) ---
|
|
53
|
+
# 推荐:动态指定日志文件名,并设置环境变量
|
|
54
|
+
log_filename = f"run_{time.strftime('%Y%m%d_%H%M%S')}.log"
|
|
55
|
+
os.environ['LOG_LEVEL'] = 'INFO' # 生产环境
|
|
56
|
+
# os.environ['LOG_LEVEL'] = 'DEBUG' # 调试时
|
|
57
|
+
|
|
58
|
+
zy_log.setup_logging(log_file_override=log_filename)
|
|
59
|
+
# --- 初始化完成 ---
|
|
60
|
+
|
|
61
|
+
# 获取主模块的 logger
|
|
62
|
+
logger = zy_log.get_logger(__name__)
|
|
63
|
+
|
|
64
|
+
logger.info(f"算法启动,日志将记录到: {log_filename}")
|
|
65
|
+
|
|
66
|
+
# 运行您的核心算法
|
|
67
|
+
try:
|
|
68
|
+
run_my_core_algo()
|
|
69
|
+
logger.info("算法运行成功结束。")
|
|
70
|
+
except Exception as e:
|
|
71
|
+
logger.critical(f"算法主流程意外崩溃: {e}", exc_info=True)
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 2. 在任意子模块中使用
|
|
76
|
+
|
|
77
|
+
在您的任何算法子模块中:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
# your_project/core_algo.py
|
|
81
|
+
import zy_log
|
|
82
|
+
import time
|
|
83
|
+
|
|
84
|
+
# [关键] 直接获取 logger,它已经被 main.py 配置好了
|
|
85
|
+
logger = zy_log.get_logger(__name__)
|
|
86
|
+
|
|
87
|
+
def run_my_core_algo():
|
|
88
|
+
|
|
89
|
+
# 1. 记录正常流程 (Normal)
|
|
90
|
+
logger.info("开始加载数据模型...")
|
|
91
|
+
|
|
92
|
+
# 2. 记录警告 (Warning)
|
|
93
|
+
logger.warning("检测到 5% 的缺失值,将使用均值填充。")
|
|
94
|
+
|
|
95
|
+
# 3. 记录致命错误 (Error)
|
|
96
|
+
try:
|
|
97
|
+
if "关键数据" is None:
|
|
98
|
+
# 这会记录一个红色的 Error 日志,然后抛出 RuntimeError
|
|
99
|
+
logger.error_and_raise("关键数据为 None,算法无法继续!")
|
|
100
|
+
|
|
101
|
+
except RuntimeError as e:
|
|
102
|
+
logger.error(f"算法因致命错误中断: {e}")
|
|
103
|
+
raise # 必须重新抛出,让上层知道
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## 贡献
|
|
108
|
+
|
|
109
|
+
欢迎提交 PR 或 Issue。
|
|
110
|
+
|
|
111
|
+
## 许可证
|
|
112
|
+
|
|
113
|
+
本项目使用 [MIT License](LICENSE) 授权。
|
zy_log-0.1.0/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# zy_log: 一个为算法项目定制的、专业的、异步的日志系统
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/py/zy_log) [](https://opensource.org/licenses/MIT)
|
|
4
|
+
|
|
5
|
+
一个功能强大的 Python 日志包,专为计算密集型和算法项目设计。它通过异步 I/O 确保日志记录不会阻塞您的主计算线程,并使用 `rich` 提供美观的控制台输出和异常堆栈。
|
|
6
|
+
|
|
7
|
+
## 核心功能
|
|
8
|
+
|
|
9
|
+
* **异步非阻塞**: 所有日志 I/O (文件/控制台) 都在一个单独的后台线程中处理。
|
|
10
|
+
* **Rich 集成**: 自动的控制台颜色、美观的格式化和清晰的异常堆栈追踪。
|
|
11
|
+
* **动态配置**: 可通过参数或环境变量 (`LOG_LEVEL`) 轻松覆盖配置。
|
|
12
|
+
* **致命错误处理**: 提供 `logger.error_and_raise()` 方法,在记录致命错误后立即抛出异常,实现“快速失败”(Fail-Fast)。
|
|
13
|
+
|
|
14
|
+
## 安装
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install zy_log
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 快速使用
|
|
21
|
+
|
|
22
|
+
您只需要在您算法的**主入口文件** (`main.py`) 中初始化一次,然后在任何子模块中调用 `get_logger` 即可。
|
|
23
|
+
|
|
24
|
+
### 1. 初始化 (在 `main.py` 中)
|
|
25
|
+
|
|
26
|
+
这是最关键的一步。
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
# main.py
|
|
30
|
+
import zy_log
|
|
31
|
+
import os
|
|
32
|
+
import time
|
|
33
|
+
from your_project import run_my_core_algo
|
|
34
|
+
|
|
35
|
+
# --- 日志系统初始化 (仅一次) ---
|
|
36
|
+
# 推荐:动态指定日志文件名,并设置环境变量
|
|
37
|
+
log_filename = f"run_{time.strftime('%Y%m%d_%H%M%S')}.log"
|
|
38
|
+
os.environ['LOG_LEVEL'] = 'INFO' # 生产环境
|
|
39
|
+
# os.environ['LOG_LEVEL'] = 'DEBUG' # 调试时
|
|
40
|
+
|
|
41
|
+
zy_log.setup_logging(log_file_override=log_filename)
|
|
42
|
+
# --- 初始化完成 ---
|
|
43
|
+
|
|
44
|
+
# 获取主模块的 logger
|
|
45
|
+
logger = zy_log.get_logger(__name__)
|
|
46
|
+
|
|
47
|
+
logger.info(f"算法启动,日志将记录到: {log_filename}")
|
|
48
|
+
|
|
49
|
+
# 运行您的核心算法
|
|
50
|
+
try:
|
|
51
|
+
run_my_core_algo()
|
|
52
|
+
logger.info("算法运行成功结束。")
|
|
53
|
+
except Exception as e:
|
|
54
|
+
logger.critical(f"算法主流程意外崩溃: {e}", exc_info=True)
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 2. 在任意子模块中使用
|
|
59
|
+
|
|
60
|
+
在您的任何算法子模块中:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
# your_project/core_algo.py
|
|
64
|
+
import zy_log
|
|
65
|
+
import time
|
|
66
|
+
|
|
67
|
+
# [关键] 直接获取 logger,它已经被 main.py 配置好了
|
|
68
|
+
logger = zy_log.get_logger(__name__)
|
|
69
|
+
|
|
70
|
+
def run_my_core_algo():
|
|
71
|
+
|
|
72
|
+
# 1. 记录正常流程 (Normal)
|
|
73
|
+
logger.info("开始加载数据模型...")
|
|
74
|
+
|
|
75
|
+
# 2. 记录警告 (Warning)
|
|
76
|
+
logger.warning("检测到 5% 的缺失值,将使用均值填充。")
|
|
77
|
+
|
|
78
|
+
# 3. 记录致命错误 (Error)
|
|
79
|
+
try:
|
|
80
|
+
if "关键数据" is None:
|
|
81
|
+
# 这会记录一个红色的 Error 日志,然后抛出 RuntimeError
|
|
82
|
+
logger.error_and_raise("关键数据为 None,算法无法继续!")
|
|
83
|
+
|
|
84
|
+
except RuntimeError as e:
|
|
85
|
+
logger.error(f"算法因致命错误中断: {e}")
|
|
86
|
+
raise # 必须重新抛出,让上层知道
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 贡献
|
|
91
|
+
|
|
92
|
+
欢迎提交 PR 或 Issue。
|
|
93
|
+
|
|
94
|
+
## 许可证
|
|
95
|
+
|
|
96
|
+
本项目使用 [MIT License](LICENSE) 授权。
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "zy_log"
|
|
7
|
+
version = "0.1.0" # 版本升级
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="zy", email="zhoumuci77@gamil.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "An advanced, asynchronous logging system for algorithmic projects."
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.8"
|
|
14
|
+
|
|
15
|
+
license = "MIT"
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Topic :: System :: Logging",
|
|
20
|
+
]
|
|
21
|
+
# 依赖项变更:
|
|
22
|
+
dependencies = [
|
|
23
|
+
"rich>=13.0.0", # 替换 colorama
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
"Homepage" = "https://github.com/yaosterm/zy_log"
|
|
28
|
+
"Bug Tracker" = "https://github.com/yaosterm/zy_log/issues"
|
zy_log-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
zy_log: A professional, asynchronous logging system customized for algorithm projects.
|
|
3
|
+
|
|
4
|
+
This package provides an advanced logging configuration interface (setup_logging) and one
|
|
5
|
+
|
|
6
|
+
Customized Logger (get_logger), this Logger contains a special
|
|
7
|
+
|
|
8
|
+
The 'error_and_raise' method is used to immediately interrupt the algorithm execution after recording an error.
|
|
9
|
+
|
|
10
|
+
Core functions
|
|
11
|
+
|
|
12
|
+
-setup_logging: Initialize the logging system.
|
|
13
|
+
|
|
14
|
+
-get_logger: Obtain a configured Logger instance.
|
|
15
|
+
|
|
16
|
+
For complete usage, please refer to the documentation strings of 'setup_logging' and 'get_logger'.
|
|
17
|
+
|
|
18
|
+
:Author: yao zhou
|
|
19
|
+
:Version: 0.1.0
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import logging
|
|
23
|
+
from .core import setup_logging
|
|
24
|
+
from .loggers import AlgorithmicLogger
|
|
25
|
+
|
|
26
|
+
logging.setLoggerClass(AlgorithmicLogger)
|
|
27
|
+
|
|
28
|
+
def get_logger(name: str) -> logging.Logger:
|
|
29
|
+
"""
|
|
30
|
+
获取一个 Logger 实例。
|
|
31
|
+
|
|
32
|
+
因为我们已经调用了 logging.setLoggerClass(),
|
|
33
|
+
这个函数将自动返回一个 AlgorithmicLogger 实例。
|
|
34
|
+
"""
|
|
35
|
+
return logging.getLogger(name)
|
|
36
|
+
|
|
37
|
+
# 控制 'from zy_log import *' 的行为
|
|
38
|
+
__all__ = [
|
|
39
|
+
"setup_logging",
|
|
40
|
+
"get_logger",
|
|
41
|
+
"AlgorithmicLogger"
|
|
42
|
+
]
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import logging.config
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import pkgutil
|
|
6
|
+
import atexit
|
|
7
|
+
from queue import Queue
|
|
8
|
+
from logging.handlers import QueueHandler, QueueListener
|
|
9
|
+
|
|
10
|
+
# 确保我们的自定义类被注册
|
|
11
|
+
from . import loggers
|
|
12
|
+
|
|
13
|
+
# rich 是一个必需的依赖,如果它不存在,我们应该在 setup.py/pyproject.toml 中处理
|
|
14
|
+
try:
|
|
15
|
+
from rich.logging import RichHandler
|
|
16
|
+
except ImportError:
|
|
17
|
+
print("错误: 'rich' 库未安装。请运行 'pip install rich'", flush=True)
|
|
18
|
+
# 使用一个基础的 StreamHandler 作为后备
|
|
19
|
+
from logging import StreamHandler as RichHandler
|
|
20
|
+
|
|
21
|
+
# 用于存储 Listener,以便程序退出时安全停止
|
|
22
|
+
_listener = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def setup_logging(
|
|
26
|
+
config_path=None,
|
|
27
|
+
default_level=logging.INFO,
|
|
28
|
+
env_key_level='LOG_LEVEL',
|
|
29
|
+
log_file_override=None
|
|
30
|
+
):
|
|
31
|
+
"""
|
|
32
|
+
配置异步、动态的日志系统。
|
|
33
|
+
|
|
34
|
+
这是一个功能完备的日志初始化函数,它会配置异步的 QueueListener,
|
|
35
|
+
使用 rich 进行控制台美化,并允许通过环境变量和参数进行动态覆盖。
|
|
36
|
+
|
|
37
|
+
:param config_path: (可选) 自定义 JSON/YAML 配置文件的路径。
|
|
38
|
+
:type config_path: str or None
|
|
39
|
+
|
|
40
|
+
:param default_level: (可选) 如果未找到配置,使用的默认日志级别。
|
|
41
|
+
:type default_level: int
|
|
42
|
+
|
|
43
|
+
:param env_key_level: (可选) 用于覆盖日志级别的环境变量名称。
|
|
44
|
+
:type env_key_level: str
|
|
45
|
+
|
|
46
|
+
:param log_file_override: (可选) 强制指定日志文件名,覆盖所有配置。
|
|
47
|
+
:type log_file_override: str or None
|
|
48
|
+
|
|
49
|
+
:return: 无
|
|
50
|
+
:rtype: None
|
|
51
|
+
|
|
52
|
+
:raises IOError: 如果配置文件路径存在但无法读取。
|
|
53
|
+
|
|
54
|
+
**用法示例 (Usage Example):**
|
|
55
|
+
|
|
56
|
+
.. code-block:: python
|
|
57
|
+
|
|
58
|
+
import zy_log
|
|
59
|
+
import os
|
|
60
|
+
|
|
61
|
+
# 1. 简单启动 (使用默认配置)
|
|
62
|
+
zy_log.setup_logging()
|
|
63
|
+
|
|
64
|
+
# 2. 动态文件名
|
|
65
|
+
log_file = f"run_{time.strftime('%Y%m%d')}.log"
|
|
66
|
+
zy_log.setup_logging(log_file_override=log_file)
|
|
67
|
+
|
|
68
|
+
# 3. 通过环境变量设置级别
|
|
69
|
+
os.environ['LOG_LEVEL'] = 'DEBUG'
|
|
70
|
+
zy_log.setup_logging()
|
|
71
|
+
|
|
72
|
+
"""
|
|
73
|
+
global _listener
|
|
74
|
+
|
|
75
|
+
# 确保我们的 Logger 类被使用
|
|
76
|
+
logging.setLoggerClass(loggers.AlgorithmicLogger)
|
|
77
|
+
|
|
78
|
+
# 1. 加载基础配置 (从文件或默认)
|
|
79
|
+
config = None
|
|
80
|
+
if config_path and os.path.exists(config_path):
|
|
81
|
+
with open(config_path, 'r') as f:
|
|
82
|
+
config = json.load(f)
|
|
83
|
+
else:
|
|
84
|
+
try:
|
|
85
|
+
config_bytes = pkgutil.get_data(__name__, "config/default_config.json")
|
|
86
|
+
if config_bytes:
|
|
87
|
+
config = json.loads(config_bytes.decode('utf-8'))
|
|
88
|
+
else:
|
|
89
|
+
raise IOError("无法加载默认日志配置。")
|
|
90
|
+
except Exception as e:
|
|
91
|
+
logging.basicConfig(level=default_level)
|
|
92
|
+
logging.warning(f"加载日志配置失败: {e}。使用 basicConfig。")
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
# 2. 动态/环境变量覆盖
|
|
96
|
+
|
|
97
|
+
# 2.1. 覆盖日志级别
|
|
98
|
+
env_level = os.getenv(env_key_level)
|
|
99
|
+
final_console_level = env_level or config['handlers_templates']['console_rich']['level']
|
|
100
|
+
|
|
101
|
+
# 2.2. 覆盖文件路径
|
|
102
|
+
if log_file_override:
|
|
103
|
+
config['handlers_templates']['file']['filename'] = log_file_override
|
|
104
|
+
|
|
105
|
+
# 3. 实例化 Handlers
|
|
106
|
+
try:
|
|
107
|
+
# 3.1. 控制台 Handler (Rich)
|
|
108
|
+
console_handler = RichHandler(
|
|
109
|
+
level=final_console_level,
|
|
110
|
+
rich_tracebacks=config['handlers_templates']['console_rich'].get('rich_tracebacks', True),
|
|
111
|
+
tracebacks_show_locals=config['handlers_templates']['console_rich'].get('tracebacks_show_locals', True)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# 3.2. 文件 Handler (Rotating)
|
|
115
|
+
file_config = config['handlers_templates']['file']
|
|
116
|
+
file_formatter = logging.Formatter(
|
|
117
|
+
fmt=config['formatters']['standard_file']['format'],
|
|
118
|
+
datefmt=config['formatters']['standard_file']['datefmt']
|
|
119
|
+
)
|
|
120
|
+
file_handler = logging.handlers.RotatingFileHandler(
|
|
121
|
+
filename=file_config['filename'],
|
|
122
|
+
maxBytes=file_config['maxBytes'],
|
|
123
|
+
backupCount=file_config['backupCount'],
|
|
124
|
+
encoding=file_config['encoding']
|
|
125
|
+
)
|
|
126
|
+
file_handler.setLevel(file_config['level'])
|
|
127
|
+
file_handler.setFormatter(file_formatter)
|
|
128
|
+
|
|
129
|
+
except Exception as e:
|
|
130
|
+
logging.basicConfig(level=default_level)
|
|
131
|
+
logging.error(f"实例化 Handlers 失败: {e}。使用 basicConfig。")
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
# 4. [核心] 设置异步非阻塞 I/O
|
|
135
|
+
# 创建一个无限大小的队列
|
|
136
|
+
log_queue = Queue(-1)
|
|
137
|
+
|
|
138
|
+
# 创建 QueueHandler,这是 *唯一* 附加到根 Logger 的 Handler
|
|
139
|
+
# 它会立即将日志消息放入队列,主线程不会阻塞
|
|
140
|
+
queue_handler = QueueHandler(log_queue)
|
|
141
|
+
|
|
142
|
+
# 创建 QueueListener,它在 *单独的线程* 中运行
|
|
143
|
+
# 它监听 log_queue,并将消息分发给 *真正* 的 Handlers (console 和 file)
|
|
144
|
+
_listener = QueueListener(log_queue, console_handler, file_handler, respect_handler_level=True)
|
|
145
|
+
|
|
146
|
+
# 配置根 Logger
|
|
147
|
+
root_logger = logging.getLogger()
|
|
148
|
+
root_logger.setLevel(logging.DEBUG) # 根 Logger 级别必须足够低
|
|
149
|
+
root_logger.handlers.clear() # 清除任何现有的 handlers
|
|
150
|
+
root_logger.addHandler(queue_handler) # 只添加队列 Handler
|
|
151
|
+
|
|
152
|
+
# 5. 启动
|
|
153
|
+
_listener.start()
|
|
154
|
+
|
|
155
|
+
# 注册一个退出处理程序,以确保在程序关闭时停止 Listener 线程
|
|
156
|
+
atexit.register(_listener.stop)
|
|
157
|
+
|
|
158
|
+
# print("异步日志系统已初始化。") # 调试
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AlgorithmicLogger(logging.Logger):
|
|
5
|
+
"""
|
|
6
|
+
自定义 Logger 类,增加了用于算法的特定方法。
|
|
7
|
+
"""
|
|
8
|
+
def __init__(self, name, level=logging.NOTSET):
|
|
9
|
+
super().__init__(name, level)
|
|
10
|
+
|
|
11
|
+
def error_and_raise(self, msg, *args, **kwargs):
|
|
12
|
+
"""
|
|
13
|
+
记录一个 ERROR 级别的日志,然后立即抛出一个 RuntimeError。
|
|
14
|
+
这用于算法中不可恢复的致命错误。
|
|
15
|
+
"""
|
|
16
|
+
# 1. 记录日志 (现在将由 rich 美观地打印 Traceback)
|
|
17
|
+
# 注意:为了让 rich 正确捕获异常上下文,我们最好在这里传递 exc_info
|
|
18
|
+
kwargs.setdefault('exc_info', True)
|
|
19
|
+
self.error(msg, *args, **kwargs)
|
|
20
|
+
|
|
21
|
+
# 2. 抛出异常
|
|
22
|
+
raise RuntimeError(msg)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# 告诉 logging 模块,当调用 getLogger 时,使用我们的自定义类
|
|
26
|
+
logging.setLoggerClass(AlgorithmicLogger)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zy_log
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An advanced, asynchronous logging system for algorithmic projects.
|
|
5
|
+
Author-email: zy <zhoumuci77@gamil.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yaosterm/zy_log
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/yaosterm/zy_log/issues
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Topic :: System :: Logging
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: rich>=13.0.0
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# zy_log: 一个为算法项目定制的、专业的、异步的日志系统
|
|
19
|
+
|
|
20
|
+
[](https://badge.fury.io/py/zy_log) [](https://opensource.org/licenses/MIT)
|
|
21
|
+
|
|
22
|
+
一个功能强大的 Python 日志包,专为计算密集型和算法项目设计。它通过异步 I/O 确保日志记录不会阻塞您的主计算线程,并使用 `rich` 提供美观的控制台输出和异常堆栈。
|
|
23
|
+
|
|
24
|
+
## 核心功能
|
|
25
|
+
|
|
26
|
+
* **异步非阻塞**: 所有日志 I/O (文件/控制台) 都在一个单独的后台线程中处理。
|
|
27
|
+
* **Rich 集成**: 自动的控制台颜色、美观的格式化和清晰的异常堆栈追踪。
|
|
28
|
+
* **动态配置**: 可通过参数或环境变量 (`LOG_LEVEL`) 轻松覆盖配置。
|
|
29
|
+
* **致命错误处理**: 提供 `logger.error_and_raise()` 方法,在记录致命错误后立即抛出异常,实现“快速失败”(Fail-Fast)。
|
|
30
|
+
|
|
31
|
+
## 安装
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install zy_log
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 快速使用
|
|
38
|
+
|
|
39
|
+
您只需要在您算法的**主入口文件** (`main.py`) 中初始化一次,然后在任何子模块中调用 `get_logger` 即可。
|
|
40
|
+
|
|
41
|
+
### 1. 初始化 (在 `main.py` 中)
|
|
42
|
+
|
|
43
|
+
这是最关键的一步。
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
# main.py
|
|
47
|
+
import zy_log
|
|
48
|
+
import os
|
|
49
|
+
import time
|
|
50
|
+
from your_project import run_my_core_algo
|
|
51
|
+
|
|
52
|
+
# --- 日志系统初始化 (仅一次) ---
|
|
53
|
+
# 推荐:动态指定日志文件名,并设置环境变量
|
|
54
|
+
log_filename = f"run_{time.strftime('%Y%m%d_%H%M%S')}.log"
|
|
55
|
+
os.environ['LOG_LEVEL'] = 'INFO' # 生产环境
|
|
56
|
+
# os.environ['LOG_LEVEL'] = 'DEBUG' # 调试时
|
|
57
|
+
|
|
58
|
+
zy_log.setup_logging(log_file_override=log_filename)
|
|
59
|
+
# --- 初始化完成 ---
|
|
60
|
+
|
|
61
|
+
# 获取主模块的 logger
|
|
62
|
+
logger = zy_log.get_logger(__name__)
|
|
63
|
+
|
|
64
|
+
logger.info(f"算法启动,日志将记录到: {log_filename}")
|
|
65
|
+
|
|
66
|
+
# 运行您的核心算法
|
|
67
|
+
try:
|
|
68
|
+
run_my_core_algo()
|
|
69
|
+
logger.info("算法运行成功结束。")
|
|
70
|
+
except Exception as e:
|
|
71
|
+
logger.critical(f"算法主流程意外崩溃: {e}", exc_info=True)
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 2. 在任意子模块中使用
|
|
76
|
+
|
|
77
|
+
在您的任何算法子模块中:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
# your_project/core_algo.py
|
|
81
|
+
import zy_log
|
|
82
|
+
import time
|
|
83
|
+
|
|
84
|
+
# [关键] 直接获取 logger,它已经被 main.py 配置好了
|
|
85
|
+
logger = zy_log.get_logger(__name__)
|
|
86
|
+
|
|
87
|
+
def run_my_core_algo():
|
|
88
|
+
|
|
89
|
+
# 1. 记录正常流程 (Normal)
|
|
90
|
+
logger.info("开始加载数据模型...")
|
|
91
|
+
|
|
92
|
+
# 2. 记录警告 (Warning)
|
|
93
|
+
logger.warning("检测到 5% 的缺失值,将使用均值填充。")
|
|
94
|
+
|
|
95
|
+
# 3. 记录致命错误 (Error)
|
|
96
|
+
try:
|
|
97
|
+
if "关键数据" is None:
|
|
98
|
+
# 这会记录一个红色的 Error 日志,然后抛出 RuntimeError
|
|
99
|
+
logger.error_and_raise("关键数据为 None,算法无法继续!")
|
|
100
|
+
|
|
101
|
+
except RuntimeError as e:
|
|
102
|
+
logger.error(f"算法因致命错误中断: {e}")
|
|
103
|
+
raise # 必须重新抛出,让上层知道
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## 贡献
|
|
108
|
+
|
|
109
|
+
欢迎提交 PR 或 Issue。
|
|
110
|
+
|
|
111
|
+
## 许可证
|
|
112
|
+
|
|
113
|
+
本项目使用 [MIT License](LICENSE) 授权。
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rich>=13.0.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
zy_log
|