xmi-logger 0.0.8__tar.gz → 0.0.10__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.
- {xmi_logger-0.0.8 → xmi_logger-0.0.10}/PKG-INFO +7 -7
- {xmi_logger-0.0.8 → xmi_logger-0.0.10}/README.md +1 -1
- {xmi_logger-0.0.8 → xmi_logger-0.0.10}/setup.py +2 -2
- xmi_logger-0.0.10/xmi_logger/__version__.py +10 -0
- {xmi_logger-0.0.8 → xmi_logger-0.0.10}/xmi_logger/xmi_logger.py +79 -53
- {xmi_logger-0.0.8 → xmi_logger-0.0.10}/xmi_logger.egg-info/PKG-INFO +7 -7
- xmi_logger-0.0.8/xmi_logger/__version__.py +0 -10
- {xmi_logger-0.0.8 → xmi_logger-0.0.10}/setup.cfg +0 -0
- {xmi_logger-0.0.8 → xmi_logger-0.0.10}/xmi_logger/__init__.py +0 -0
- {xmi_logger-0.0.8 → xmi_logger-0.0.10}/xmi_logger/advanced_features.py +0 -0
- {xmi_logger-0.0.8 → xmi_logger-0.0.10}/xmi_logger.egg-info/SOURCES.txt +0 -0
- {xmi_logger-0.0.8 → xmi_logger-0.0.10}/xmi_logger.egg-info/dependency_links.txt +0 -0
- {xmi_logger-0.0.8 → xmi_logger-0.0.10}/xmi_logger.egg-info/requires.txt +0 -0
- {xmi_logger-0.0.8 → xmi_logger-0.0.10}/xmi_logger.egg-info/top_level.txt +0 -0
|
@@ -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
|
## 快速开始
|
|
@@ -56,8 +56,8 @@ setup(
|
|
|
56
56
|
],
|
|
57
57
|
},
|
|
58
58
|
project_urls={
|
|
59
|
-
"Bug Reports": "https://github.com/
|
|
60
|
-
"Source": "https://github.com/
|
|
59
|
+
"Bug Reports": "https://github.com/xmi-one/xmi_logger/issues",
|
|
60
|
+
"Source": "https://github.com/xmi-one/xmi_logger",
|
|
61
61
|
},
|
|
62
62
|
)
|
|
63
63
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Xmi_logger
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
__title__ = "xmi_logger"
|
|
5
|
+
__description__ = "An enhanced logger based on Loguru"
|
|
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
|
+
__license__ = "MIT"
|
|
@@ -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
|
# 如果配置失败,使用基本配置
|
|
@@ -337,7 +343,7 @@ class XmiLogger:
|
|
|
337
343
|
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
|
|
338
344
|
"<level>{level: <8}</level> | "
|
|
339
345
|
"ReqID:{extra[request_id]} | "
|
|
340
|
-
"<cyan>{file}</cyan>:<cyan>{line}</cyan
|
|
346
|
+
"<cyan>{file}</cyan>:<cyan>{line}</cyan> | "
|
|
341
347
|
"<magenta>{process}</magenta> | "
|
|
342
348
|
"<level>{message}</level>"
|
|
343
349
|
)
|
|
@@ -418,7 +424,8 @@ class XmiLogger:
|
|
|
418
424
|
tag: 标签名称
|
|
419
425
|
"""
|
|
420
426
|
self._update_stats(level)
|
|
421
|
-
|
|
427
|
+
logger_opt = self._logger_d1
|
|
428
|
+
log_method = getattr(logger_opt, level.lower(), logger_opt.info)
|
|
422
429
|
tagged_message = self._msg('LOG_TAGGED', tag=tag, message=message)
|
|
423
430
|
log_method(tagged_message)
|
|
424
431
|
|
|
@@ -432,7 +439,8 @@ class XmiLogger:
|
|
|
432
439
|
category: 分类名称
|
|
433
440
|
"""
|
|
434
441
|
self._update_stats(level, category=category)
|
|
435
|
-
|
|
442
|
+
logger_opt = self._logger_d1
|
|
443
|
+
log_method = getattr(logger_opt, level.lower(), logger_opt.info)
|
|
436
444
|
categorized_message = self._msg('LOG_CATEGORY', category=category, message=message)
|
|
437
445
|
log_method(categorized_message)
|
|
438
446
|
|
|
@@ -664,35 +672,36 @@ class XmiLogger:
|
|
|
664
672
|
Args:
|
|
665
673
|
level (str): 日志级别方法名称。
|
|
666
674
|
"""
|
|
667
|
-
|
|
675
|
+
logger_method = getattr(self._logger_d1, level)
|
|
676
|
+
return logger_method
|
|
668
677
|
|
|
669
678
|
def log(self, level: str, message: str, *args, **kwargs):
|
|
670
679
|
self._update_stats(level)
|
|
671
|
-
return self.
|
|
680
|
+
return self._logger_d1.log(level, message, *args, **kwargs)
|
|
672
681
|
|
|
673
682
|
def debug(self, message: str, *args, **kwargs):
|
|
674
683
|
self._update_stats("DEBUG")
|
|
675
|
-
return self.
|
|
684
|
+
return self._logger_d1.debug(message, *args, **kwargs)
|
|
676
685
|
|
|
677
686
|
def info(self, message: str, *args, **kwargs):
|
|
678
687
|
self._update_stats("INFO")
|
|
679
|
-
return self.
|
|
688
|
+
return self._logger_d1.info(message, *args, **kwargs)
|
|
680
689
|
|
|
681
690
|
def warning(self, message: str, *args, **kwargs):
|
|
682
691
|
self._update_stats("WARNING")
|
|
683
|
-
return self.
|
|
692
|
+
return self._logger_d1.warning(message, *args, **kwargs)
|
|
684
693
|
|
|
685
694
|
def error(self, message: str, *args, **kwargs):
|
|
686
695
|
self._update_stats("ERROR")
|
|
687
|
-
return self.
|
|
696
|
+
return self._logger_d1.error(message, *args, **kwargs)
|
|
688
697
|
|
|
689
698
|
def critical(self, message: str, *args, **kwargs):
|
|
690
699
|
self._update_stats("CRITICAL")
|
|
691
|
-
return self.
|
|
700
|
+
return self._logger_d1.critical(message, *args, **kwargs)
|
|
692
701
|
|
|
693
702
|
def exception(self, message: str, *args, **kwargs):
|
|
694
703
|
self._update_stats("ERROR")
|
|
695
|
-
return self.
|
|
704
|
+
return self._logger_d1.exception(message, *args, **kwargs)
|
|
696
705
|
|
|
697
706
|
def log_decorator(self, msg: Optional[str] = None, level: str = "ERROR", trace: bool = True):
|
|
698
707
|
"""
|
|
@@ -884,25 +893,25 @@ class XmiLogger:
|
|
|
884
893
|
return
|
|
885
894
|
|
|
886
895
|
with self._stats_lock:
|
|
896
|
+
now_dt = datetime.now()
|
|
887
897
|
self._stats['total'] += 1
|
|
888
|
-
|
|
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
|
|
889
902
|
|
|
890
903
|
if category:
|
|
891
904
|
self._stats['by_category'][category] += 1
|
|
892
905
|
|
|
893
|
-
current_hour =
|
|
906
|
+
current_hour = now_dt.strftime('%Y-%m-%d %H:00')
|
|
894
907
|
self._stats['by_hour'][current_hour] += 1
|
|
895
908
|
|
|
896
909
|
if level.upper() == 'ERROR':
|
|
897
910
|
self._stats['errors'].append({
|
|
898
|
-
'time':
|
|
911
|
+
'time': now_dt,
|
|
899
912
|
'message': f"Error occurred at {current_hour}"
|
|
900
913
|
})
|
|
901
|
-
self._stats['last_error_time'] =
|
|
902
|
-
|
|
903
|
-
# 计算错误率
|
|
904
|
-
if self._stats['total'] > 0:
|
|
905
|
-
self._stats['error_rate'] = self._stats['error'] / self._stats['total']
|
|
914
|
+
self._stats['last_error_time'] = now_dt
|
|
906
915
|
|
|
907
916
|
def get_stats(self) -> Dict[str, Any]:
|
|
908
917
|
"""获取详细的日志统计信息,优化性能"""
|
|
@@ -913,16 +922,19 @@ class XmiLogger:
|
|
|
913
922
|
return self._stats_cache.copy()
|
|
914
923
|
|
|
915
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
|
|
916
928
|
stats = {
|
|
917
|
-
'total':
|
|
918
|
-
'error':
|
|
919
|
-
'warning': self._stats
|
|
920
|
-
'info': self._stats
|
|
921
|
-
'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)),
|
|
922
934
|
'duration': str(current_time - self._stats_start_time),
|
|
923
935
|
'by_category': dict(self._stats['by_category']),
|
|
924
936
|
'by_hour': dict(self._stats['by_hour']),
|
|
925
|
-
'error_rate': float(
|
|
937
|
+
'error_rate': float(error_rate),
|
|
926
938
|
'time_since_last_error': str(current_time - self._stats['last_error_time']) if self._stats['last_error_time'] else None
|
|
927
939
|
}
|
|
928
940
|
|
|
@@ -932,12 +944,13 @@ class XmiLogger:
|
|
|
932
944
|
|
|
933
945
|
# 获取最近的错误
|
|
934
946
|
if self._stats['errors']:
|
|
947
|
+
recent_errors = list(self._stats['errors'])[-10:]
|
|
935
948
|
stats['recent_errors'] = [
|
|
936
949
|
{
|
|
937
950
|
'time': str(error['time']),
|
|
938
951
|
'message': str(error['message'])
|
|
939
952
|
}
|
|
940
|
-
for error in
|
|
953
|
+
for error in recent_errors
|
|
941
954
|
]
|
|
942
955
|
|
|
943
956
|
# 更新缓存
|
|
@@ -980,7 +993,7 @@ class XmiLogger:
|
|
|
980
993
|
'debug': 0,
|
|
981
994
|
'by_category': defaultdict(int),
|
|
982
995
|
'by_hour': defaultdict(int),
|
|
983
|
-
'errors':
|
|
996
|
+
'errors': deque(maxlen=self._error_history_size),
|
|
984
997
|
'last_error_time': None,
|
|
985
998
|
'error_rate': 0.0
|
|
986
999
|
}
|
|
@@ -1018,7 +1031,8 @@ class XmiLogger:
|
|
|
1018
1031
|
else:
|
|
1019
1032
|
full_message = message
|
|
1020
1033
|
|
|
1021
|
-
|
|
1034
|
+
logger_opt = self._logger_d1
|
|
1035
|
+
log_method = getattr(logger_opt, level.lower(), logger_opt.info)
|
|
1022
1036
|
log_method(full_message)
|
|
1023
1037
|
|
|
1024
1038
|
def get_performance_stats(self) -> Dict[str, Any]:
|
|
@@ -1072,9 +1086,15 @@ class XmiLogger:
|
|
|
1072
1086
|
else:
|
|
1073
1087
|
self.log(level, message)
|
|
1074
1088
|
|
|
1075
|
-
async def async_batch_log(self, logs: List[Dict[str, Any]]) -> None:
|
|
1076
|
-
"""异步批量记录日志
|
|
1077
|
-
|
|
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):
|
|
1078
1098
|
level = log_entry.get('level', 'INFO')
|
|
1079
1099
|
message = log_entry.get('message', '')
|
|
1080
1100
|
tag = log_entry.get('tag')
|
|
@@ -1086,9 +1106,9 @@ class XmiLogger:
|
|
|
1086
1106
|
self.log_with_category(level, message, category)
|
|
1087
1107
|
else:
|
|
1088
1108
|
self.log(level, message)
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1109
|
+
|
|
1110
|
+
if yield_every and (i % yield_every == 0):
|
|
1111
|
+
await asyncio.sleep(sleep_s if sleep_s > 0 else 0)
|
|
1092
1112
|
|
|
1093
1113
|
def log_with_context(self, level: str, message: str, context: Dict[str, Any] = None):
|
|
1094
1114
|
"""带上下文的日志记录"""
|
|
@@ -1099,7 +1119,8 @@ class XmiLogger:
|
|
|
1099
1119
|
else:
|
|
1100
1120
|
full_message = message
|
|
1101
1121
|
|
|
1102
|
-
|
|
1122
|
+
logger_opt = self._logger_d1
|
|
1123
|
+
log_method = getattr(logger_opt, level.lower(), logger_opt.info)
|
|
1103
1124
|
log_method(full_message)
|
|
1104
1125
|
|
|
1105
1126
|
def log_with_timing(self, level: str, message: str, timing_data: Dict[str, float]):
|
|
@@ -1108,7 +1129,8 @@ class XmiLogger:
|
|
|
1108
1129
|
timing_str = " | ".join([f"{k}={v:.3f}s" for k, v in timing_data.items()])
|
|
1109
1130
|
full_message = f"{message} | {timing_str}"
|
|
1110
1131
|
|
|
1111
|
-
|
|
1132
|
+
logger_opt = self._logger_d1
|
|
1133
|
+
log_method = getattr(logger_opt, level.lower(), logger_opt.info)
|
|
1112
1134
|
log_method(full_message)
|
|
1113
1135
|
|
|
1114
1136
|
def set_adaptive_level(self, error_rate_threshold: float = 0.1,
|
|
@@ -1152,24 +1174,28 @@ class XmiLogger:
|
|
|
1152
1174
|
def enable_performance_mode(self) -> None:
|
|
1153
1175
|
"""启用性能模式"""
|
|
1154
1176
|
if self.performance_mode:
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
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
|
|
1162
1186
|
|
|
1163
1187
|
def disable_performance_mode(self) -> None:
|
|
1164
1188
|
"""禁用性能模式"""
|
|
1165
|
-
if self.performance_mode:
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
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
|
|
1173
1199
|
|
|
1174
1200
|
def compress_logs(self, days_old: int = 7) -> None:
|
|
1175
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
|
## 快速开始
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# Xmi_logger
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
__title__ = "xmi_logger"
|
|
5
|
-
__description__ = "An enhanced logger based on Loguru"
|
|
6
|
-
__version__ = "0.0.8"
|
|
7
|
-
__author__ = "gm.zhibo.wang"
|
|
8
|
-
__author_email__ = "gm.zhibo.wang@gmail.com"
|
|
9
|
-
__url__ = "https://github.com/wang-zhibo/xmi_logger"
|
|
10
|
-
__license__ = "MIT"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|