mlog-util 0.1.3__tar.gz → 0.1.5__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.
Potentially problematic release.
This version of mlog-util might be problematic. Click here for more details.
- mlog_util-0.1.5/PKG-INFO +8 -0
- mlog_util-0.1.5/pyproject.toml +16 -0
- mlog_util-0.1.5/src/mlog-util/log_manager.py +108 -0
- mlog_util-0.1.5/src/mlog_util.egg-info/PKG-INFO +8 -0
- mlog_util-0.1.5/src/mlog_util.egg-info/SOURCES.txt +10 -0
- mlog_util-0.1.5/src/mlog_util.egg-info/requires.txt +2 -0
- mlog_util-0.1.5/src/mlog_util.egg-info/top_level.txt +1 -0
- mlog_util-0.1.5/tests/test_mlog.py +37 -0
- mlog_util-0.1.3/PKG-INFO +0 -14
- mlog_util-0.1.3/mlog_util/log_manager.py +0 -116
- mlog_util-0.1.3/mlog_util.egg-info/PKG-INFO +0 -14
- mlog_util-0.1.3/mlog_util.egg-info/SOURCES.txt +0 -9
- mlog_util-0.1.3/mlog_util.egg-info/requires.txt +0 -2
- mlog_util-0.1.3/mlog_util.egg-info/top_level.txt +0 -1
- mlog_util-0.1.3/setup.py +0 -20
- {mlog_util-0.1.3 → mlog_util-0.1.5}/setup.cfg +0 -0
- {mlog_util-0.1.3/mlog_util → mlog_util-0.1.5/src/mlog-util}/__init__.py +0 -0
- {mlog_util-0.1.3/mlog_util → mlog_util-0.1.5/src/mlog-util}/handlers.py +0 -0
- {mlog_util-0.1.3 → mlog_util-0.1.5/src}/mlog_util.egg-info/dependency_links.txt +0 -0
mlog_util-0.1.5/PKG-INFO
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "mlog-util"
|
|
3
|
+
version = "0.1.5"
|
|
4
|
+
description = "Add your description here"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"rich>=14.2.0",
|
|
9
|
+
"portalocker"
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
[dependency-groups]
|
|
13
|
+
dev = [
|
|
14
|
+
"setuptools>=80.9.0",
|
|
15
|
+
"twine>=6.2.0",
|
|
16
|
+
]
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import threading
|
|
3
|
+
from rich.logging import RichHandler
|
|
4
|
+
from typing import Type, List, Optional
|
|
5
|
+
|
|
6
|
+
# from .handlers import MultiProcessSafeSizeRotatingHandler, MultiProcessSafeTimeRotatingHandler
|
|
7
|
+
|
|
8
|
+
class LogManager:
|
|
9
|
+
"""
|
|
10
|
+
一个线程安全的日志管理器,用于获取和配置具有 Rich 控制台输出的 Logger。
|
|
11
|
+
"""
|
|
12
|
+
_logger_cache: dict[str, logging.Logger] = {}
|
|
13
|
+
_lock = threading.Lock()
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def get_logger(
|
|
17
|
+
cls,
|
|
18
|
+
name: str,
|
|
19
|
+
log_file: Optional[str] = None,
|
|
20
|
+
add_console: bool = True,
|
|
21
|
+
level: int = logging.INFO,
|
|
22
|
+
custom_handlers: Optional[List[logging.Handler]] = None,
|
|
23
|
+
) -> logging.Logger:
|
|
24
|
+
"""
|
|
25
|
+
获取或创建一个配置好的 logger。
|
|
26
|
+
|
|
27
|
+
注意:Logger 实例按 name 缓存。重复调用会返回同一个实例,
|
|
28
|
+
但会确保其配置(如 level 和 handlers)符合当前调用参数。
|
|
29
|
+
|
|
30
|
+
:param name: logger 名称。
|
|
31
|
+
:param log_file: 日志文件路径,如果为 None 则不写入文件。
|
|
32
|
+
:param add_console: 是否添加带 Rich 格式的控制台 Handler。
|
|
33
|
+
:param level: 日志级别。
|
|
34
|
+
:param custom_handlers: 自定义 Handler 列表。
|
|
35
|
+
"""
|
|
36
|
+
with cls._lock:
|
|
37
|
+
# 1. 获取或创建 Logger 实例 (利用 logging 模块自身的缓存)
|
|
38
|
+
logger = logging.getLogger(name)
|
|
39
|
+
|
|
40
|
+
# 2. 确保基本配置
|
|
41
|
+
logger.setLevel(level)
|
|
42
|
+
logger.propagate = False
|
|
43
|
+
|
|
44
|
+
# 3. 配置控制台 Handler
|
|
45
|
+
if add_console and not any(isinstance(h, RichHandler) for h in logger.handlers):
|
|
46
|
+
console_handler = RichHandler(rich_tracebacks=True, show_time=False, show_path=False)
|
|
47
|
+
# 注意:RichHandler 默认有自己的时间格式,我们可以在 Formatter 中覆盖
|
|
48
|
+
console_formatter = logging.Formatter(
|
|
49
|
+
"[%(name)s - %(asctime)s] %(message)s",
|
|
50
|
+
datefmt="%Y-%m-%d %H:%M:%S"
|
|
51
|
+
)
|
|
52
|
+
console_handler.setFormatter(console_formatter)
|
|
53
|
+
logger.addHandler(console_handler)
|
|
54
|
+
|
|
55
|
+
# 4. 配置文件 Handler
|
|
56
|
+
if log_file and not any(isinstance(h, logging.FileHandler) for h in logger.handlers):
|
|
57
|
+
file_handler = logging.FileHandler(log_file, encoding="utf-8")
|
|
58
|
+
file_formatter = logging.Formatter(
|
|
59
|
+
"%(asctime)s | %(name)s | %(levelname)-8s | %(message)s",
|
|
60
|
+
datefmt="%Y-%m-%d %H:%M:%S"
|
|
61
|
+
)
|
|
62
|
+
file_handler.setFormatter(file_formatter)
|
|
63
|
+
logger.addHandler(file_handler)
|
|
64
|
+
|
|
65
|
+
# 5. 配置自定义 Handlers (修正了原代码的 Bug)
|
|
66
|
+
if custom_handlers:
|
|
67
|
+
# 为所有自定义 handlers 设置一个统一的格式
|
|
68
|
+
custom_formatter = logging.Formatter(
|
|
69
|
+
"%(asctime)s | %(name)s | %(levelname)-8s | %(message)s",
|
|
70
|
+
datefmt="%Y-%m-%d %H:%M:%S"
|
|
71
|
+
)
|
|
72
|
+
for handler in custom_handlers:
|
|
73
|
+
if handler not in logger.handlers:
|
|
74
|
+
handler.setFormatter(custom_formatter)
|
|
75
|
+
logger.addHandler(handler)
|
|
76
|
+
|
|
77
|
+
return logger
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# 全局可用的 get_logger 函数(无需引用 LogManager)
|
|
81
|
+
def get_logger(
|
|
82
|
+
name: str=None,
|
|
83
|
+
log_file: Optional[str] = None,
|
|
84
|
+
add_console: bool = True,
|
|
85
|
+
level: int = logging.INFO,
|
|
86
|
+
custom_handlers: Optional[List[logging.Handler]] = None,
|
|
87
|
+
):
|
|
88
|
+
"""
|
|
89
|
+
便捷函数:获取日志记录器,无需关心 LogManager 实例化。
|
|
90
|
+
|
|
91
|
+
使用示例:
|
|
92
|
+
from log_manager import get_logger
|
|
93
|
+
logger = get_logger("my_module", log_file="app.log")
|
|
94
|
+
logger.info("Hello world")
|
|
95
|
+
"""
|
|
96
|
+
if name is None:
|
|
97
|
+
name = "tmp_log"
|
|
98
|
+
|
|
99
|
+
return LogManager.get_logger(
|
|
100
|
+
name=name,
|
|
101
|
+
log_file=log_file,
|
|
102
|
+
add_console=add_console,
|
|
103
|
+
level=level,
|
|
104
|
+
custom_handlers=custom_handlers
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# logger = LogManager().get_logger("tmp_log")
|
|
108
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
pyproject.toml
|
|
2
|
+
src/mlog-util/__init__.py
|
|
3
|
+
src/mlog-util/handlers.py
|
|
4
|
+
src/mlog-util/log_manager.py
|
|
5
|
+
src/mlog_util.egg-info/PKG-INFO
|
|
6
|
+
src/mlog_util.egg-info/SOURCES.txt
|
|
7
|
+
src/mlog_util.egg-info/dependency_links.txt
|
|
8
|
+
src/mlog_util.egg-info/requires.txt
|
|
9
|
+
src/mlog_util.egg-info/top_level.txt
|
|
10
|
+
tests/test_mlog.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mlog-util
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import os
|
|
3
|
+
import mlog
|
|
4
|
+
from mlog import LogManager
|
|
5
|
+
from multiprocessing import Pool
|
|
6
|
+
|
|
7
|
+
log_file = "logs/a1.log"
|
|
8
|
+
|
|
9
|
+
log_manager = LogManager()
|
|
10
|
+
logger_a1 = log_manager.get_logger("a1", log_file=log_file, add_console=False)
|
|
11
|
+
# logger_a2 = log_manager.get_logger("a2", log_file=log_file, add_console=False)
|
|
12
|
+
# logger_a3 = log_manager.get_logger("a3", log_file=log_file, add_console=False)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# 测试 num 个 日志耗时
|
|
16
|
+
def test_speed_time(num = 500):
|
|
17
|
+
import time
|
|
18
|
+
_st = time.time()
|
|
19
|
+
for i in range(num):
|
|
20
|
+
logger_a1.info(i)
|
|
21
|
+
logger_a1.info(f"{num} --- {time.time() - _st}")
|
|
22
|
+
|
|
23
|
+
# 测试多进程
|
|
24
|
+
# 1000 个日志有没有
|
|
25
|
+
def test_logger(x):
|
|
26
|
+
_pid = os.getpid()
|
|
27
|
+
logger_a1.info(f"{_pid} -- {x}")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
with open(log_file, "w") as f:
|
|
32
|
+
pass
|
|
33
|
+
with Pool(2) as pool:
|
|
34
|
+
pool.map(test_logger, range(0, 5000))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
mlog_util-0.1.3/PKG-INFO
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: mlog_util
|
|
3
|
-
Version: 0.1.3
|
|
4
|
-
Summary: 自用日志库
|
|
5
|
-
Classifier: Programming Language :: Python :: 3
|
|
6
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
7
|
-
Classifier: Operating System :: OS Independent
|
|
8
|
-
Requires-Python: >=3.6
|
|
9
|
-
Requires-Dist: rich
|
|
10
|
-
Requires-Dist: portalocker
|
|
11
|
-
Dynamic: classifier
|
|
12
|
-
Dynamic: requires-dist
|
|
13
|
-
Dynamic: requires-python
|
|
14
|
-
Dynamic: summary
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import threading
|
|
3
|
-
from rich.logging import RichHandler
|
|
4
|
-
from typing import Type, List, Optional
|
|
5
|
-
|
|
6
|
-
# from .handlers import MultiProcessSafeSizeRotatingHandler, MultiProcessSafeTimeRotatingHandler
|
|
7
|
-
|
|
8
|
-
class LogManager:
|
|
9
|
-
_logger_cache = {}
|
|
10
|
-
_lock = threading.Lock() # 多线程安全
|
|
11
|
-
|
|
12
|
-
@classmethod
|
|
13
|
-
def get_logger(
|
|
14
|
-
cls,
|
|
15
|
-
name: str,
|
|
16
|
-
logger_cls: Type[logging.Logger] = logging.Logger,
|
|
17
|
-
log_file: str | None = None,
|
|
18
|
-
add_console: bool = True,
|
|
19
|
-
level: int = logging.INFO,
|
|
20
|
-
custom_handlers: list[logging.Handler] | None = None,
|
|
21
|
-
) -> logging.Logger:
|
|
22
|
-
"""
|
|
23
|
-
获取或创建 logger。
|
|
24
|
-
|
|
25
|
-
:param name: logger 名称
|
|
26
|
-
:param logger_cls: logger 类
|
|
27
|
-
:param log_file: 日志文件路径
|
|
28
|
-
:param add_console: 是否添加控制台 RichHandler
|
|
29
|
-
:param level: 日志级别
|
|
30
|
-
:param custom_handlers: 自定义 Handler 列表
|
|
31
|
-
"""
|
|
32
|
-
cache_key = (name, logger_cls)
|
|
33
|
-
with cls._lock:
|
|
34
|
-
if cache_key not in cls._logger_cache:
|
|
35
|
-
# 创建 logger
|
|
36
|
-
if logger_cls == logging.Logger:
|
|
37
|
-
logger = logging.getLogger(name)
|
|
38
|
-
else:
|
|
39
|
-
logger = logger_cls(name)
|
|
40
|
-
|
|
41
|
-
logger.setLevel(level)
|
|
42
|
-
logger.propagate = False # 不向 root logger 冒泡
|
|
43
|
-
|
|
44
|
-
# 添加控制台 handler
|
|
45
|
-
if add_console:
|
|
46
|
-
if not any(isinstance(h, RichHandler) for h in logger.handlers):
|
|
47
|
-
console_handler = RichHandler(rich_tracebacks=True)
|
|
48
|
-
console_formatter = logging.Formatter(
|
|
49
|
-
"%(message)s [%(name)s - %(asctime)s]",
|
|
50
|
-
datefmt="%Y-%m-%d %H:%M:%S"
|
|
51
|
-
)
|
|
52
|
-
console_handler.setFormatter(console_formatter)
|
|
53
|
-
logger.addHandler(console_handler)
|
|
54
|
-
|
|
55
|
-
# 添加文件 handler
|
|
56
|
-
if log_file:
|
|
57
|
-
if not any(isinstance(h, logging.FileHandler) for h in logger.handlers):
|
|
58
|
-
# handler = MultiProcessSafeSizeRotatingHandler(log_file, maxBytes=10*200, backupCount=3)
|
|
59
|
-
# file_handler = logging.FileHandler(log_file, encoding="utf-8")
|
|
60
|
-
|
|
61
|
-
handler = logging.FileHandler(log_file, encoding="utf-8")
|
|
62
|
-
file_formatter = logging.Formatter(
|
|
63
|
-
"%(asctime)s | %(name)s | %(levelname)s | %(message)s",
|
|
64
|
-
datefmt="%Y-%m-%d %H:%M:%S"
|
|
65
|
-
)
|
|
66
|
-
handler.setFormatter(file_formatter)
|
|
67
|
-
logger.addHandler(handler)
|
|
68
|
-
|
|
69
|
-
# 添加自定义 handler
|
|
70
|
-
if custom_handlers:
|
|
71
|
-
h = custom_handlers
|
|
72
|
-
if h not in logger.handlers:
|
|
73
|
-
file_formatter = logging.Formatter(
|
|
74
|
-
"%(asctime)s | %(name)s | %(levelname)s | %(message)s",
|
|
75
|
-
datefmt="%Y-%m-%d %H:%M:%S"
|
|
76
|
-
)
|
|
77
|
-
h.setFormatter(file_formatter)
|
|
78
|
-
logger.addHandler(h)
|
|
79
|
-
logger.addHandler(h)
|
|
80
|
-
|
|
81
|
-
cls._logger_cache[cache_key] = logger
|
|
82
|
-
|
|
83
|
-
return cls._logger_cache[cache_key]
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
# 全局可用的 get_logger 函数(无需引用 LogManager)
|
|
87
|
-
def get_logger(
|
|
88
|
-
name: str=None,
|
|
89
|
-
logger_cls: Type[logging.Logger] = logging.Logger,
|
|
90
|
-
log_file: Optional[str] = None,
|
|
91
|
-
add_console: bool = True,
|
|
92
|
-
level: int = logging.INFO,
|
|
93
|
-
custom_handlers: Optional[List[logging.Handler]] = None,
|
|
94
|
-
):
|
|
95
|
-
"""
|
|
96
|
-
便捷函数:获取日志记录器,无需关心 LogManager 实例化。
|
|
97
|
-
|
|
98
|
-
使用示例:
|
|
99
|
-
from log_manager import get_logger
|
|
100
|
-
logger = get_logger("my_module", log_file="app.log")
|
|
101
|
-
logger.info("Hello world")
|
|
102
|
-
"""
|
|
103
|
-
if name is None:
|
|
104
|
-
name = "tmp_log"
|
|
105
|
-
|
|
106
|
-
return LogManager.get_logger(
|
|
107
|
-
name=name,
|
|
108
|
-
logger_cls=logger_cls,
|
|
109
|
-
log_file=log_file,
|
|
110
|
-
add_console=add_console,
|
|
111
|
-
level=level,
|
|
112
|
-
custom_handlers=custom_handlers
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
# logger = LogManager().get_logger("tmp_log")
|
|
116
|
-
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: mlog_util
|
|
3
|
-
Version: 0.1.3
|
|
4
|
-
Summary: 自用日志库
|
|
5
|
-
Classifier: Programming Language :: Python :: 3
|
|
6
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
7
|
-
Classifier: Operating System :: OS Independent
|
|
8
|
-
Requires-Python: >=3.6
|
|
9
|
-
Requires-Dist: rich
|
|
10
|
-
Requires-Dist: portalocker
|
|
11
|
-
Dynamic: classifier
|
|
12
|
-
Dynamic: requires-dist
|
|
13
|
-
Dynamic: requires-python
|
|
14
|
-
Dynamic: summary
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
mlog_util
|
mlog_util-0.1.3/setup.py
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
from setuptools import setup, find_packages
|
|
2
|
-
|
|
3
|
-
setup(
|
|
4
|
-
name="mlog_util",
|
|
5
|
-
version="0.1.3",
|
|
6
|
-
packages=find_packages(),
|
|
7
|
-
install_requires=["rich", "portalocker"], # 依赖库
|
|
8
|
-
# author="may",
|
|
9
|
-
# author_email="no",
|
|
10
|
-
description="自用日志库",
|
|
11
|
-
# long_description=open("README.md", encoding="utf-8").read(),
|
|
12
|
-
# long_description_content_type="text/markdown",
|
|
13
|
-
|
|
14
|
-
classifiers=[
|
|
15
|
-
"Programming Language :: Python :: 3",
|
|
16
|
-
"License :: OSI Approved :: MIT License",
|
|
17
|
-
"Operating System :: OS Independent",
|
|
18
|
-
],
|
|
19
|
-
python_requires='>=3.6',
|
|
20
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|