unitlog 0.0.4__tar.gz → 0.0.6__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.
- {unitlog-0.0.4/unitlog.egg-info → unitlog-0.0.6}/PKG-INFO +1 -1
- unitlog-0.0.6/unitlog/__init__.py +16 -0
- {unitlog-0.0.4 → unitlog-0.0.6}/unitlog/handlers.py +7 -4
- {unitlog-0.0.4 → unitlog-0.0.6}/unitlog/unit.py +73 -9
- {unitlog-0.0.4 → unitlog-0.0.6/unitlog.egg-info}/PKG-INFO +1 -1
- unitlog-0.0.4/unitlog/__init__.py +0 -1
- {unitlog-0.0.4 → unitlog-0.0.6}/LICENSE +0 -0
- {unitlog-0.0.4 → unitlog-0.0.6}/README.md +0 -0
- {unitlog-0.0.4 → unitlog-0.0.6}/setup.cfg +0 -0
- {unitlog-0.0.4 → unitlog-0.0.6}/setup.py +0 -0
- {unitlog-0.0.4 → unitlog-0.0.6}/test/test_multi_logger.py +0 -0
- {unitlog-0.0.4 → unitlog-0.0.6}/unitlog/util_log.py +0 -0
- {unitlog-0.0.4 → unitlog-0.0.6}/unitlog.egg-info/SOURCES.txt +0 -0
- {unitlog-0.0.4 → unitlog-0.0.6}/unitlog.egg-info/dependency_links.txt +0 -0
- {unitlog-0.0.4 → unitlog-0.0.6}/unitlog.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
__version__ = "0.0.6"
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
updates = """
|
|
5
|
+
### 版本说明
|
|
6
|
+
|
|
7
|
+
**✨ 核心亮点**
|
|
8
|
+
- **多进程支持**:我们引入了更加稳健的多进程支持,现在可以通过 spawn 方式来启动和管理多个进程,提升了应用的并行处理能力。
|
|
9
|
+
- **日志系统增强**:为了确保日志记录的可靠性,我们更新了日志系统,现在会自动确保日志路径目录的存在,避免了因目录问题导致的日志记录失败。
|
|
10
|
+
|
|
11
|
+
**🚀 体验优化**
|
|
12
|
+
- **系统稳定性提升**:通过对多进程支持的改进和日志系统的优化,我们增强了应用的稳定性和可靠性,为用户提供了更加流畅的体验。
|
|
13
|
+
|
|
14
|
+
**🐛 问题修复**
|
|
15
|
+
- *(本次更新没有涉及到老功能的修复,因此此部分留空)*
|
|
16
|
+
"""
|
|
@@ -13,7 +13,10 @@ class LogBox(object):
|
|
|
13
13
|
|
|
14
14
|
class UnitHandler(logging.StreamHandler):
|
|
15
15
|
LOG_TYPE = "console"
|
|
16
|
-
|
|
16
|
+
|
|
17
|
+
def __init__(self, stream=None, bus_queue=None):
|
|
18
|
+
super().__init__(stream)
|
|
19
|
+
self.bus_queue = bus_queue
|
|
17
20
|
|
|
18
21
|
def handle(self, record):
|
|
19
22
|
""" without acquiring lock
|
|
@@ -39,7 +42,7 @@ class UnitHandler(logging.StreamHandler):
|
|
|
39
42
|
msg = self.format(record)
|
|
40
43
|
# issue 35046: merged two stream.writes into one.
|
|
41
44
|
log_msg = msg + self.terminator
|
|
42
|
-
|
|
45
|
+
self.bus_queue.put(self.wrap_msg(log_msg))
|
|
43
46
|
except RecursionError: # See issue 36272
|
|
44
47
|
raise
|
|
45
48
|
except Exception:
|
|
@@ -53,8 +56,8 @@ class UnitConsoleHandler(UnitHandler):
|
|
|
53
56
|
class UnitFileHandler(UnitHandler):
|
|
54
57
|
LOG_TYPE = "file"
|
|
55
58
|
|
|
56
|
-
def __init__(self, log_filepath, mode):
|
|
57
|
-
super().__init__()
|
|
59
|
+
def __init__(self, log_filepath, mode, bus_queue=None):
|
|
60
|
+
super().__init__(bus_queue=bus_queue)
|
|
58
61
|
self.log_filepath = log_filepath
|
|
59
62
|
self.mode = mode
|
|
60
63
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
|
+
import inspect
|
|
3
4
|
import atexit
|
|
4
5
|
import logging
|
|
5
6
|
import traceback
|
|
@@ -7,8 +8,23 @@ from queue import Empty
|
|
|
7
8
|
import multiprocessing as mp
|
|
8
9
|
from multiprocessing.synchronize import Event
|
|
9
10
|
|
|
10
|
-
from unitlog.handlers import
|
|
11
|
-
|
|
11
|
+
from unitlog.handlers import LogBox, UnitFileHandler, UnitConsoleHandler
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def is_under_testing():
|
|
16
|
+
"""
|
|
17
|
+
检查当前是否处于单元测试环境 (unittest 或 pytest)。
|
|
18
|
+
原理:向上遍历栈帧,如果发现调用链中有 unittest 或 pytest 的相关文件,则认为是测试环境。
|
|
19
|
+
"""
|
|
20
|
+
# 简单的白名单检查
|
|
21
|
+
for frame_info in inspect.stack():
|
|
22
|
+
module_name = frame_info.frame.f_globals.get('__name__', '')
|
|
23
|
+
# 如果调用栈里包含 unittest 或 pytest,说明是在测试
|
|
24
|
+
if module_name and ('unittest' in module_name or 'pytest' in module_name):
|
|
25
|
+
return True
|
|
26
|
+
return False
|
|
27
|
+
|
|
12
28
|
|
|
13
29
|
|
|
14
30
|
class PoxyConsoleLogWriter(object):
|
|
@@ -35,7 +51,8 @@ class UnitLog(object):
|
|
|
35
51
|
self.started: Event = mp.Event()
|
|
36
52
|
self.stopped: Event = mp.Event()
|
|
37
53
|
self.log_num = mp.Value('i', 0)
|
|
38
|
-
self.worker =
|
|
54
|
+
self.worker = None
|
|
55
|
+
self.bus_queue = None
|
|
39
56
|
self._proxy_handler_map = {}
|
|
40
57
|
|
|
41
58
|
def _init_proxy_handler(self, log_box: LogBox) -> PoxyConsoleLogWriter:
|
|
@@ -57,11 +74,11 @@ class UnitLog(object):
|
|
|
57
74
|
raise TypeError(f"Unsupported log type: {log_box.log_type}")
|
|
58
75
|
return self._proxy_handler_map[hkey]
|
|
59
76
|
|
|
60
|
-
def listening_log_msg(self):
|
|
77
|
+
def listening_log_msg(self, bus_queue):
|
|
61
78
|
while True:
|
|
62
79
|
self.started.set()
|
|
63
80
|
try:
|
|
64
|
-
log_box: LogBox =
|
|
81
|
+
log_box: LogBox = bus_queue.get(timeout=0.1)
|
|
65
82
|
except Empty:
|
|
66
83
|
if self.stopped.is_set():
|
|
67
84
|
break
|
|
@@ -85,7 +102,47 @@ class UnitLog(object):
|
|
|
85
102
|
log_filepath=None,
|
|
86
103
|
parent_logger_name=None,
|
|
87
104
|
force_all_console_log_to_file=False) -> logging.Logger:
|
|
105
|
+
|
|
106
|
+
caller_frame = inspect.currentframe().f_back
|
|
107
|
+
caller_module_name = caller_frame.f_globals.get('__name__')
|
|
108
|
+
caller_func_name = caller_frame.f_code.co_name
|
|
109
|
+
|
|
110
|
+
# ---------------------------------------------------------
|
|
111
|
+
# 规则 1: 严禁全局裸奔 (必须包在函数里)
|
|
112
|
+
# ---------------------------------------------------------
|
|
113
|
+
if caller_func_name == '<module>':
|
|
114
|
+
raise RuntimeError(
|
|
115
|
+
"⛔️ [禁止全局调用] 你必须把此函数放在一个 def 函数内部调用,\n"
|
|
116
|
+
"""
|
|
117
|
+
例如
|
|
118
|
+
def main():
|
|
119
|
+
register_logger()
|
|
120
|
+
|
|
121
|
+
if __name__ == '__main__':
|
|
122
|
+
main()
|
|
123
|
+
"""
|
|
124
|
+
"严禁在脚本顶层直接执行,否则会导致多进程 spawn 时的无限递归。"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# ---------------------------------------------------------
|
|
128
|
+
# 规则 2: 必须是 Main 入口,或者是 单元测试
|
|
129
|
+
# ---------------------------------------------------------
|
|
130
|
+
# 如果是直接运行脚本,name 是 __main__ -> 通过
|
|
131
|
+
# 如果是 unittest,name 是 文件名 -> 但 is_under_testing() 为真 -> 通过
|
|
132
|
+
# 如果是 spawn 子进程,name 是 文件名 -> 且不在测试栈中 -> 拦截
|
|
133
|
+
if caller_module_name != '__main__' and not is_under_testing():
|
|
134
|
+
raise RuntimeError(
|
|
135
|
+
f"⛔️ [禁止导入执行] 检测到当前模块名为 '{caller_module_name}'。\n"
|
|
136
|
+
"此函数只能在 'if __name__ == \"__main__\":' 入口下执行,\n"
|
|
137
|
+
"或者是通过单元测试(unittest/pytest)执行。\n"
|
|
138
|
+
"禁止在多进程 spawn 导入阶段或作为普通模块被 import 时执行。"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
print(f"✅ 安全检查通过 (调用者: {caller_module_name}, 环境安全)")
|
|
142
|
+
|
|
88
143
|
if not self.started.is_set():
|
|
144
|
+
self.bus_queue = mp.Queue()
|
|
145
|
+
self.worker = mp.Process(target=self.listening_log_msg, args=(self.bus_queue, ), daemon=True)
|
|
89
146
|
self.worker.start()
|
|
90
147
|
if not self.started.wait(timeout=3):
|
|
91
148
|
raise ValueError("unit log process is not started")
|
|
@@ -109,12 +166,13 @@ class UnitLog(object):
|
|
|
109
166
|
datefmt="%a, %d %b %Y %H:%M:%S"
|
|
110
167
|
)
|
|
111
168
|
if console_log:
|
|
112
|
-
console_handler = UnitConsoleHandler()
|
|
169
|
+
console_handler = UnitConsoleHandler(bus_queue=self.bus_queue)
|
|
113
170
|
console_handler.setFormatter(simple_formatter)
|
|
114
171
|
logger.handlers.append(console_handler)
|
|
115
172
|
if file_log:
|
|
116
173
|
assert log_filepath, "log_filepath must be set"
|
|
117
|
-
|
|
174
|
+
os.makedirs(os.path.dirname(log_filepath), exist_ok=True)
|
|
175
|
+
file_handler = UnitFileHandler(log_filepath, mode=file_log_mode, bus_queue=self.bus_queue)
|
|
118
176
|
file_handler.setFormatter(full_formatter)
|
|
119
177
|
logger.handlers.append(file_handler)
|
|
120
178
|
logger.info("\nLog_filename: {}".format(log_filepath))
|
|
@@ -226,10 +284,16 @@ DEFAULT_LOG = UnitLog()
|
|
|
226
284
|
register_logger: UnitLog.register_logger = DEFAULT_LOG.register_logger
|
|
227
285
|
atexit.register(lambda: DEFAULT_LOG.stopped.set())
|
|
228
286
|
|
|
229
|
-
|
|
230
|
-
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def main():
|
|
231
290
|
|
|
232
291
|
_logger = logging.getLogger("test")
|
|
233
292
|
register_logger(name="test", level=logging.DEBUG,
|
|
234
293
|
log_filepath="./temp/test.log")
|
|
235
294
|
_logger.info("lllll")
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
if __name__ == "__main__":
|
|
299
|
+
main()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.0.4"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|