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 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.9"
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
  # 如果配置失败,使用基本配置
@@ -418,7 +424,7 @@ class XmiLogger:
418
424
  tag: 标签名称
419
425
  """
420
426
  self._update_stats(level)
421
- logger_opt = self.logger.opt(depth=1)
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.logger.opt(depth=1)
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.logger.opt(depth=1), level)
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.logger.opt(depth=1).log(level, message, *args, **kwargs)
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.logger.opt(depth=1).debug(message, *args, **kwargs)
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.logger.opt(depth=1).info(message, *args, **kwargs)
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.logger.opt(depth=1).warning(message, *args, **kwargs)
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.logger.opt(depth=1).error(message, *args, **kwargs)
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.logger.opt(depth=1).critical(message, *args, **kwargs)
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.logger.opt(depth=1).exception(message, *args, **kwargs)
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
- 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
892
902
 
893
903
  if category:
894
904
  self._stats['by_category'][category] += 1
895
905
 
896
- current_hour = datetime.now().strftime('%Y-%m-%d %H:00')
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': datetime.now(),
911
+ 'time': now_dt,
902
912
  'message': f"Error occurred at {current_hour}"
903
913
  })
904
- self._stats['last_error_time'] = datetime.now()
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': self._stats['total'],
921
- 'error': self._stats['error'],
922
- 'warning': self._stats['warning'],
923
- 'info': self._stats['info'],
924
- '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)),
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(self._stats['error_rate']),
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 self._stats['errors'][-10:]
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.logger.opt(depth=1)
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
- 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):
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
- 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)
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.logger.opt(depth=1)
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.logger.opt(depth=1)
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
- self.filter_level = "WARNING"
1163
- self._update_logger_level()
1164
- # 增加缓存大小
1165
- self._cache_size = min(self._cache_size * 2, 2048)
1166
- # 禁用详细统计
1167
- 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
1168
1186
 
1169
1187
  def disable_performance_mode(self) -> None:
1170
1188
  """禁用性能模式"""
1171
- if self.performance_mode:
1172
- # 恢复日志级别
1173
- self.filter_level = "INFO"
1174
- self._update_logger_level()
1175
- # 恢复缓存大小
1176
- self._cache_size = max(self._cache_size // 2, 128)
1177
- # 恢复统计功能
1178
- 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
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.9
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,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,,