xmi-logger 0.0.8__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 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.8"
7
- __author__ = "gm.zhibo.wang"
8
- __author_email__ = "gm.zhibo.wang@gmail.com"
9
- __url__ = "https://github.com/wang-zhibo/xmi_logger"
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
  # 如果配置失败,使用基本配置
@@ -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>:<cyan>{function}</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
- log_method = getattr(self.logger, level.lower(), self.logger.info)
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
- log_method = getattr(self.logger, level.lower(), self.logger.info)
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
- return getattr(self.logger, level)
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.logger.log(level, message, *args, **kwargs)
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.logger.debug(message, *args, **kwargs)
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.logger.info(message, *args, **kwargs)
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.logger.warning(message, *args, **kwargs)
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.logger.error(message, *args, **kwargs)
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.logger.critical(message, *args, **kwargs)
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.logger.exception(message, *args, **kwargs)
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
- self._stats[level.lower()] += 1
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 = datetime.now().strftime('%Y-%m-%d %H:00')
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': datetime.now(),
911
+ 'time': now_dt,
899
912
  'message': f"Error occurred at {current_hour}"
900
913
  })
901
- self._stats['last_error_time'] = datetime.now()
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': self._stats['total'],
918
- 'error': self._stats['error'],
919
- 'warning': self._stats['warning'],
920
- 'info': self._stats['info'],
921
- 'debug': self._stats['debug'],
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(self._stats['error_rate']),
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 self._stats['errors'][-10:]
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
- log_method = getattr(self.logger, level.lower(), self.logger.info)
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
- for log_entry in logs:
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
- await asyncio.sleep(0.001)
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
- log_method = getattr(self.logger, level.lower(), self.logger.info)
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
- log_method = getattr(self.logger, level.lower(), self.logger.info)
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
- self.filter_level = "WARNING"
1157
- self._update_logger_level()
1158
- # 增加缓存大小
1159
- self._cache_size = min(self._cache_size * 2, 2048)
1160
- # 禁用详细统计
1161
- self.enable_stats = False
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
- self.filter_level = "INFO"
1168
- self._update_logger_level()
1169
- # 恢复缓存大小
1170
- self._cache_size = max(self._cache_size // 2, 128)
1171
- # 恢复统计功能
1172
- self.enable_stats = True
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.8
3
+ Version: 0.0.10
4
4
  Summary: An enhanced logger based on Loguru
5
- Home-page: https://github.com/wang-zhibo/xmi_logger
6
- Author: gm.zhibo.wang
7
- Author-email: gm.zhibo.wang@gmail.com
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/wang-zhibo/xmi_logger/issues
10
- Project-URL: Source, https://github.com/wang-zhibo/xmi_logger
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,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,8 +0,0 @@
1
- xmi_logger/__init__.py,sha256=VOhz14eFSphmKeNv-r57mAvkITz5H5U1C3EaMSDjSjc,151
2
- xmi_logger/__version__.py,sha256=sfRayrt6GQ9GssP0CbHRJvX9JHxAPSa9yR-Hh0ybzmU,266
3
- xmi_logger/advanced_features.py,sha256=hRWdpPvHgx2WBsB94ydDEBu5Q-c61Z6fzFPOBiNG2jk,28738
4
- xmi_logger/xmi_logger.py,sha256=c78cFXk4kN5w19JbHkTJTcuAvF5KmgksExzGhcNBo-Y,55191
5
- xmi_logger-0.0.8.dist-info/METADATA,sha256=p1ziEhsqS4rS2SoGgoMKOu9rPhZ4XWXJO3kaAjJhfqs,15356
6
- xmi_logger-0.0.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
- xmi_logger-0.0.8.dist-info/top_level.txt,sha256=utvT64x2C9UI6eB5aJTFpFRldWEvCk2OcZtB8NIMIeU,11
8
- xmi_logger-0.0.8.dist-info/RECORD,,