xmi-logger 0.0.9__py3-none-any.whl → 0.0.10__py3-none-any.whl
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.
- xmi_logger/__version__.py +4 -4
- xmi_logger/xmi_logger.py +72 -52
- {xmi_logger-0.0.9.dist-info → xmi_logger-0.0.10.dist-info}/METADATA +7 -7
- xmi_logger-0.0.10.dist-info/RECORD +8 -0
- xmi_logger-0.0.9.dist-info/RECORD +0 -8
- {xmi_logger-0.0.9.dist-info → xmi_logger-0.0.10.dist-info}/WHEEL +0 -0
- {xmi_logger-0.0.9.dist-info → xmi_logger-0.0.10.dist-info}/top_level.txt +0 -0
xmi_logger/__version__.py
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
__title__ = "xmi_logger"
|
|
5
5
|
__description__ = "An enhanced logger based on Loguru"
|
|
6
|
-
__version__ = "0.0.
|
|
7
|
-
__author__ = "gm.
|
|
8
|
-
__author_email__ = "gm.
|
|
9
|
-
__url__ = "https://github.com/
|
|
6
|
+
__version__ = "0.0.10"
|
|
7
|
+
__author__ = "gm.xmi.one"
|
|
8
|
+
__author_email__ = "gm.xmi.one@gmail.com"
|
|
9
|
+
__url__ = "https://github.com/xmi-one/xmi_logger"
|
|
10
10
|
__license__ = "MIT"
|
xmi_logger/xmi_logger.py
CHANGED
|
@@ -20,7 +20,7 @@ from functools import wraps
|
|
|
20
20
|
from time import perf_counter
|
|
21
21
|
from contextvars import ContextVar
|
|
22
22
|
from concurrent.futures import ThreadPoolExecutor
|
|
23
|
-
from collections import defaultdict
|
|
23
|
+
from collections import defaultdict, deque
|
|
24
24
|
from datetime import datetime, timedelta
|
|
25
25
|
import threading
|
|
26
26
|
|
|
@@ -159,6 +159,8 @@ class XmiLogger:
|
|
|
159
159
|
request_id=self.request_id_var.get() or "no-request-id"
|
|
160
160
|
)
|
|
161
161
|
)
|
|
162
|
+
# 缓存常用的 opt(depth=1),减少热路径对象创建开销
|
|
163
|
+
self._logger_d1 = self.logger.opt(depth=1)
|
|
162
164
|
if work_type:
|
|
163
165
|
self.enqueue = False
|
|
164
166
|
self.diagnose = False
|
|
@@ -179,6 +181,7 @@ class XmiLogger:
|
|
|
179
181
|
self.configure_logger()
|
|
180
182
|
|
|
181
183
|
self._stats_lock = threading.Lock()
|
|
184
|
+
self._error_history_size = 200
|
|
182
185
|
self._stats = {
|
|
183
186
|
'total': 0,
|
|
184
187
|
'error': 0,
|
|
@@ -187,7 +190,7 @@ class XmiLogger:
|
|
|
187
190
|
'debug': 0,
|
|
188
191
|
'by_category': defaultdict(int),
|
|
189
192
|
'by_hour': defaultdict(int),
|
|
190
|
-
'errors':
|
|
193
|
+
'errors': deque(maxlen=self._error_history_size),
|
|
191
194
|
'last_error_time': None,
|
|
192
195
|
'error_rate': 0.0
|
|
193
196
|
}
|
|
@@ -293,6 +296,9 @@ class XmiLogger:
|
|
|
293
296
|
# 设置异常处理器
|
|
294
297
|
if self._exception_hook_enabled:
|
|
295
298
|
self.setup_exception_handler()
|
|
299
|
+
|
|
300
|
+
# 重新缓存 opt(depth=1),确保使用最新的 logger 配置
|
|
301
|
+
self._logger_d1 = self.logger.opt(depth=1)
|
|
296
302
|
|
|
297
303
|
except Exception as e:
|
|
298
304
|
# 如果配置失败,使用基本配置
|
|
@@ -418,7 +424,7 @@ class XmiLogger:
|
|
|
418
424
|
tag: 标签名称
|
|
419
425
|
"""
|
|
420
426
|
self._update_stats(level)
|
|
421
|
-
logger_opt = self.
|
|
427
|
+
logger_opt = self._logger_d1
|
|
422
428
|
log_method = getattr(logger_opt, level.lower(), logger_opt.info)
|
|
423
429
|
tagged_message = self._msg('LOG_TAGGED', tag=tag, message=message)
|
|
424
430
|
log_method(tagged_message)
|
|
@@ -433,7 +439,7 @@ class XmiLogger:
|
|
|
433
439
|
category: 分类名称
|
|
434
440
|
"""
|
|
435
441
|
self._update_stats(level, category=category)
|
|
436
|
-
logger_opt = self.
|
|
442
|
+
logger_opt = self._logger_d1
|
|
437
443
|
log_method = getattr(logger_opt, level.lower(), logger_opt.info)
|
|
438
444
|
categorized_message = self._msg('LOG_CATEGORY', category=category, message=message)
|
|
439
445
|
log_method(categorized_message)
|
|
@@ -666,36 +672,36 @@ class XmiLogger:
|
|
|
666
672
|
Args:
|
|
667
673
|
level (str): 日志级别方法名称。
|
|
668
674
|
"""
|
|
669
|
-
logger_method = getattr(self.
|
|
675
|
+
logger_method = getattr(self._logger_d1, level)
|
|
670
676
|
return logger_method
|
|
671
677
|
|
|
672
678
|
def log(self, level: str, message: str, *args, **kwargs):
|
|
673
679
|
self._update_stats(level)
|
|
674
|
-
return self.
|
|
680
|
+
return self._logger_d1.log(level, message, *args, **kwargs)
|
|
675
681
|
|
|
676
682
|
def debug(self, message: str, *args, **kwargs):
|
|
677
683
|
self._update_stats("DEBUG")
|
|
678
|
-
return self.
|
|
684
|
+
return self._logger_d1.debug(message, *args, **kwargs)
|
|
679
685
|
|
|
680
686
|
def info(self, message: str, *args, **kwargs):
|
|
681
687
|
self._update_stats("INFO")
|
|
682
|
-
return self.
|
|
688
|
+
return self._logger_d1.info(message, *args, **kwargs)
|
|
683
689
|
|
|
684
690
|
def warning(self, message: str, *args, **kwargs):
|
|
685
691
|
self._update_stats("WARNING")
|
|
686
|
-
return self.
|
|
692
|
+
return self._logger_d1.warning(message, *args, **kwargs)
|
|
687
693
|
|
|
688
694
|
def error(self, message: str, *args, **kwargs):
|
|
689
695
|
self._update_stats("ERROR")
|
|
690
|
-
return self.
|
|
696
|
+
return self._logger_d1.error(message, *args, **kwargs)
|
|
691
697
|
|
|
692
698
|
def critical(self, message: str, *args, **kwargs):
|
|
693
699
|
self._update_stats("CRITICAL")
|
|
694
|
-
return self.
|
|
700
|
+
return self._logger_d1.critical(message, *args, **kwargs)
|
|
695
701
|
|
|
696
702
|
def exception(self, message: str, *args, **kwargs):
|
|
697
703
|
self._update_stats("ERROR")
|
|
698
|
-
return self.
|
|
704
|
+
return self._logger_d1.exception(message, *args, **kwargs)
|
|
699
705
|
|
|
700
706
|
def log_decorator(self, msg: Optional[str] = None, level: str = "ERROR", trace: bool = True):
|
|
701
707
|
"""
|
|
@@ -887,25 +893,25 @@ class XmiLogger:
|
|
|
887
893
|
return
|
|
888
894
|
|
|
889
895
|
with self._stats_lock:
|
|
896
|
+
now_dt = datetime.now()
|
|
890
897
|
self._stats['total'] += 1
|
|
891
|
-
|
|
898
|
+
level_key = level.lower()
|
|
899
|
+
if level_key not in self._stats:
|
|
900
|
+
self._stats[level_key] = 0
|
|
901
|
+
self._stats[level_key] += 1
|
|
892
902
|
|
|
893
903
|
if category:
|
|
894
904
|
self._stats['by_category'][category] += 1
|
|
895
905
|
|
|
896
|
-
current_hour =
|
|
906
|
+
current_hour = now_dt.strftime('%Y-%m-%d %H:00')
|
|
897
907
|
self._stats['by_hour'][current_hour] += 1
|
|
898
908
|
|
|
899
909
|
if level.upper() == 'ERROR':
|
|
900
910
|
self._stats['errors'].append({
|
|
901
|
-
'time':
|
|
911
|
+
'time': now_dt,
|
|
902
912
|
'message': f"Error occurred at {current_hour}"
|
|
903
913
|
})
|
|
904
|
-
self._stats['last_error_time'] =
|
|
905
|
-
|
|
906
|
-
# 计算错误率
|
|
907
|
-
if self._stats['total'] > 0:
|
|
908
|
-
self._stats['error_rate'] = self._stats['error'] / self._stats['total']
|
|
914
|
+
self._stats['last_error_time'] = now_dt
|
|
909
915
|
|
|
910
916
|
def get_stats(self) -> Dict[str, Any]:
|
|
911
917
|
"""获取详细的日志统计信息,优化性能"""
|
|
@@ -916,16 +922,19 @@ class XmiLogger:
|
|
|
916
922
|
return self._stats_cache.copy()
|
|
917
923
|
|
|
918
924
|
with self._stats_lock:
|
|
925
|
+
total = int(self._stats.get('total', 0))
|
|
926
|
+
error = int(self._stats.get('error', 0))
|
|
927
|
+
error_rate = (error / total) if total else 0.0
|
|
919
928
|
stats = {
|
|
920
|
-
'total':
|
|
921
|
-
'error':
|
|
922
|
-
'warning': self._stats
|
|
923
|
-
'info': self._stats
|
|
924
|
-
'debug': self._stats
|
|
929
|
+
'total': total,
|
|
930
|
+
'error': error,
|
|
931
|
+
'warning': int(self._stats.get('warning', 0)),
|
|
932
|
+
'info': int(self._stats.get('info', 0)),
|
|
933
|
+
'debug': int(self._stats.get('debug', 0)),
|
|
925
934
|
'duration': str(current_time - self._stats_start_time),
|
|
926
935
|
'by_category': dict(self._stats['by_category']),
|
|
927
936
|
'by_hour': dict(self._stats['by_hour']),
|
|
928
|
-
'error_rate': float(
|
|
937
|
+
'error_rate': float(error_rate),
|
|
929
938
|
'time_since_last_error': str(current_time - self._stats['last_error_time']) if self._stats['last_error_time'] else None
|
|
930
939
|
}
|
|
931
940
|
|
|
@@ -935,12 +944,13 @@ class XmiLogger:
|
|
|
935
944
|
|
|
936
945
|
# 获取最近的错误
|
|
937
946
|
if self._stats['errors']:
|
|
947
|
+
recent_errors = list(self._stats['errors'])[-10:]
|
|
938
948
|
stats['recent_errors'] = [
|
|
939
949
|
{
|
|
940
950
|
'time': str(error['time']),
|
|
941
951
|
'message': str(error['message'])
|
|
942
952
|
}
|
|
943
|
-
for error in
|
|
953
|
+
for error in recent_errors
|
|
944
954
|
]
|
|
945
955
|
|
|
946
956
|
# 更新缓存
|
|
@@ -983,7 +993,7 @@ class XmiLogger:
|
|
|
983
993
|
'debug': 0,
|
|
984
994
|
'by_category': defaultdict(int),
|
|
985
995
|
'by_hour': defaultdict(int),
|
|
986
|
-
'errors':
|
|
996
|
+
'errors': deque(maxlen=self._error_history_size),
|
|
987
997
|
'last_error_time': None,
|
|
988
998
|
'error_rate': 0.0
|
|
989
999
|
}
|
|
@@ -1021,7 +1031,7 @@ class XmiLogger:
|
|
|
1021
1031
|
else:
|
|
1022
1032
|
full_message = message
|
|
1023
1033
|
|
|
1024
|
-
logger_opt = self.
|
|
1034
|
+
logger_opt = self._logger_d1
|
|
1025
1035
|
log_method = getattr(logger_opt, level.lower(), logger_opt.info)
|
|
1026
1036
|
log_method(full_message)
|
|
1027
1037
|
|
|
@@ -1076,9 +1086,15 @@ class XmiLogger:
|
|
|
1076
1086
|
else:
|
|
1077
1087
|
self.log(level, message)
|
|
1078
1088
|
|
|
1079
|
-
async def async_batch_log(self, logs: List[Dict[str, Any]]) -> None:
|
|
1080
|
-
"""异步批量记录日志
|
|
1081
|
-
|
|
1089
|
+
async def async_batch_log(self, logs: List[Dict[str, Any]], yield_every: int = 0, sleep_s: float = 0.0) -> None:
|
|
1090
|
+
"""异步批量记录日志
|
|
1091
|
+
|
|
1092
|
+
Args:
|
|
1093
|
+
logs: 日志条目列表
|
|
1094
|
+
yield_every: 每处理 N 条日志让出一次事件循环(0 表示不主动让出)
|
|
1095
|
+
sleep_s: 让出时 sleep 的秒数;为 0 时使用 await asyncio.sleep(0)
|
|
1096
|
+
"""
|
|
1097
|
+
for i, log_entry in enumerate(logs, 1):
|
|
1082
1098
|
level = log_entry.get('level', 'INFO')
|
|
1083
1099
|
message = log_entry.get('message', '')
|
|
1084
1100
|
tag = log_entry.get('tag')
|
|
@@ -1090,9 +1106,9 @@ class XmiLogger:
|
|
|
1090
1106
|
self.log_with_category(level, message, category)
|
|
1091
1107
|
else:
|
|
1092
1108
|
self.log(level, message)
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1109
|
+
|
|
1110
|
+
if yield_every and (i % yield_every == 0):
|
|
1111
|
+
await asyncio.sleep(sleep_s if sleep_s > 0 else 0)
|
|
1096
1112
|
|
|
1097
1113
|
def log_with_context(self, level: str, message: str, context: Dict[str, Any] = None):
|
|
1098
1114
|
"""带上下文的日志记录"""
|
|
@@ -1103,7 +1119,7 @@ class XmiLogger:
|
|
|
1103
1119
|
else:
|
|
1104
1120
|
full_message = message
|
|
1105
1121
|
|
|
1106
|
-
logger_opt = self.
|
|
1122
|
+
logger_opt = self._logger_d1
|
|
1107
1123
|
log_method = getattr(logger_opt, level.lower(), logger_opt.info)
|
|
1108
1124
|
log_method(full_message)
|
|
1109
1125
|
|
|
@@ -1113,7 +1129,7 @@ class XmiLogger:
|
|
|
1113
1129
|
timing_str = " | ".join([f"{k}={v:.3f}s" for k, v in timing_data.items()])
|
|
1114
1130
|
full_message = f"{message} | {timing_str}"
|
|
1115
1131
|
|
|
1116
|
-
logger_opt = self.
|
|
1132
|
+
logger_opt = self._logger_d1
|
|
1117
1133
|
log_method = getattr(logger_opt, level.lower(), logger_opt.info)
|
|
1118
1134
|
log_method(full_message)
|
|
1119
1135
|
|
|
@@ -1158,24 +1174,28 @@ class XmiLogger:
|
|
|
1158
1174
|
def enable_performance_mode(self) -> None:
|
|
1159
1175
|
"""启用性能模式"""
|
|
1160
1176
|
if self.performance_mode:
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1177
|
+
return
|
|
1178
|
+
self.performance_mode = True
|
|
1179
|
+
# 减少日志输出
|
|
1180
|
+
self.filter_level = "WARNING"
|
|
1181
|
+
self._update_logger_level()
|
|
1182
|
+
# 增加缓存大小
|
|
1183
|
+
self._cache_size = min(self._cache_size * 2, 2048)
|
|
1184
|
+
# 禁用详细统计
|
|
1185
|
+
self.enable_stats = False
|
|
1168
1186
|
|
|
1169
1187
|
def disable_performance_mode(self) -> None:
|
|
1170
1188
|
"""禁用性能模式"""
|
|
1171
|
-
if self.performance_mode:
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1189
|
+
if not self.performance_mode:
|
|
1190
|
+
return
|
|
1191
|
+
self.performance_mode = False
|
|
1192
|
+
# 恢复日志级别
|
|
1193
|
+
self.filter_level = "INFO"
|
|
1194
|
+
self._update_logger_level()
|
|
1195
|
+
# 恢复缓存大小
|
|
1196
|
+
self._cache_size = max(self._cache_size // 2, 128)
|
|
1197
|
+
# 恢复统计功能
|
|
1198
|
+
self.enable_stats = True
|
|
1179
1199
|
|
|
1180
1200
|
def compress_logs(self, days_old: int = 7) -> None:
|
|
1181
1201
|
"""压缩指定天数之前的日志文件"""
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xmi_logger
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.10
|
|
4
4
|
Summary: An enhanced logger based on Loguru
|
|
5
|
-
Home-page: https://github.com/
|
|
6
|
-
Author: gm.
|
|
7
|
-
Author-email: gm.
|
|
5
|
+
Home-page: https://github.com/xmi-one/xmi_logger
|
|
6
|
+
Author: gm.xmi.one
|
|
7
|
+
Author-email: gm.xmi.one@gmail.com
|
|
8
8
|
License: MIT
|
|
9
|
-
Project-URL: Bug Reports, https://github.com/
|
|
10
|
-
Project-URL: Source, https://github.com/
|
|
9
|
+
Project-URL: Bug Reports, https://github.com/xmi-one/xmi_logger/issues
|
|
10
|
+
Project-URL: Source, https://github.com/xmi-one/xmi_logger
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.8
|
|
@@ -54,7 +54,7 @@ Dynamic: summary
|
|
|
54
54
|
## 安装
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
pip install xmi_logger
|
|
57
|
+
pip install -U xmi_logger
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
## 快速开始
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
xmi_logger/__init__.py,sha256=VOhz14eFSphmKeNv-r57mAvkITz5H5U1C3EaMSDjSjc,151
|
|
2
|
+
xmi_logger/__version__.py,sha256=jqvp23U8lw0e3YcWnyYAmV7tRSmU88CpSayVpk1StEM,258
|
|
3
|
+
xmi_logger/advanced_features.py,sha256=hRWdpPvHgx2WBsB94ydDEBu5Q-c61Z6fzFPOBiNG2jk,28738
|
|
4
|
+
xmi_logger/xmi_logger.py,sha256=6B61gc--1fx-BPL52QsGcrEsn5jQJujoIxdsm04e23c,56346
|
|
5
|
+
xmi_logger-0.0.10.dist-info/METADATA,sha256=MoH4yoMM7TyRCgMGqbm34ETMso2bCAbGPv7TNbggWWQ,15346
|
|
6
|
+
xmi_logger-0.0.10.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
7
|
+
xmi_logger-0.0.10.dist-info/top_level.txt,sha256=utvT64x2C9UI6eB5aJTFpFRldWEvCk2OcZtB8NIMIeU,11
|
|
8
|
+
xmi_logger-0.0.10.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
xmi_logger/__init__.py,sha256=VOhz14eFSphmKeNv-r57mAvkITz5H5U1C3EaMSDjSjc,151
|
|
2
|
-
xmi_logger/__version__.py,sha256=1etiFmErQL3onC4ySvBh4IyMCIXJf8uMl2kFSaHV3Uw,266
|
|
3
|
-
xmi_logger/advanced_features.py,sha256=hRWdpPvHgx2WBsB94ydDEBu5Q-c61Z6fzFPOBiNG2jk,28738
|
|
4
|
-
xmi_logger/xmi_logger.py,sha256=5XzTF03amEbPLvEBymJs_p-P0A2tdBtaHs-ugmxWD84,55529
|
|
5
|
-
xmi_logger-0.0.9.dist-info/METADATA,sha256=tf6yTLojZEo7KRENoSNML0gT_5tB-KuAJz1Hvq1GnpA,15356
|
|
6
|
-
xmi_logger-0.0.9.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
7
|
-
xmi_logger-0.0.9.dist-info/top_level.txt,sha256=utvT64x2C9UI6eB5aJTFpFRldWEvCk2OcZtB8NIMIeU,11
|
|
8
|
-
xmi_logger-0.0.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|