infomankit 0.3.23__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.
Files changed (143) hide show
  1. infoman/__init__.py +1 -0
  2. infoman/cli/README.md +378 -0
  3. infoman/cli/__init__.py +7 -0
  4. infoman/cli/commands/__init__.py +3 -0
  5. infoman/cli/commands/init.py +312 -0
  6. infoman/cli/scaffold.py +634 -0
  7. infoman/cli/templates/Makefile.template +132 -0
  8. infoman/cli/templates/app/__init__.py.template +3 -0
  9. infoman/cli/templates/app/app.py.template +4 -0
  10. infoman/cli/templates/app/models_base.py.template +18 -0
  11. infoman/cli/templates/app/models_entity_init.py.template +11 -0
  12. infoman/cli/templates/app/models_schemas_init.py.template +11 -0
  13. infoman/cli/templates/app/repository_init.py.template +11 -0
  14. infoman/cli/templates/app/routers_init.py.template +15 -0
  15. infoman/cli/templates/app/services_init.py.template +11 -0
  16. infoman/cli/templates/app/static_index.html.template +39 -0
  17. infoman/cli/templates/app/static_main.js.template +31 -0
  18. infoman/cli/templates/app/static_style.css.template +111 -0
  19. infoman/cli/templates/app/utils_init.py.template +11 -0
  20. infoman/cli/templates/config/.env.dev.template +43 -0
  21. infoman/cli/templates/config/.env.prod.template +43 -0
  22. infoman/cli/templates/config/README.md.template +28 -0
  23. infoman/cli/templates/docker/.dockerignore.template +60 -0
  24. infoman/cli/templates/docker/Dockerfile.template +47 -0
  25. infoman/cli/templates/docker/README.md.template +240 -0
  26. infoman/cli/templates/docker/docker-compose.yml.template +81 -0
  27. infoman/cli/templates/docker/mysql_custom.cnf.template +42 -0
  28. infoman/cli/templates/docker/mysql_init.sql.template +15 -0
  29. infoman/cli/templates/project/.env.example.template +1 -0
  30. infoman/cli/templates/project/.gitignore.template +60 -0
  31. infoman/cli/templates/project/Makefile.template +38 -0
  32. infoman/cli/templates/project/README.md.template +137 -0
  33. infoman/cli/templates/project/deploy.sh.template +97 -0
  34. infoman/cli/templates/project/main.py.template +10 -0
  35. infoman/cli/templates/project/manage.sh.template +97 -0
  36. infoman/cli/templates/project/pyproject.toml.template +47 -0
  37. infoman/cli/templates/project/service.sh.template +203 -0
  38. infoman/config/__init__.py +25 -0
  39. infoman/config/base.py +67 -0
  40. infoman/config/db_cache.py +237 -0
  41. infoman/config/db_relation.py +181 -0
  42. infoman/config/db_vector.py +39 -0
  43. infoman/config/jwt.py +16 -0
  44. infoman/config/llm.py +16 -0
  45. infoman/config/log.py +627 -0
  46. infoman/config/mq.py +26 -0
  47. infoman/config/settings.py +65 -0
  48. infoman/llm/__init__.py +0 -0
  49. infoman/llm/llm.py +297 -0
  50. infoman/logger/__init__.py +57 -0
  51. infoman/logger/context.py +191 -0
  52. infoman/logger/core.py +358 -0
  53. infoman/logger/filters.py +157 -0
  54. infoman/logger/formatters.py +138 -0
  55. infoman/logger/handlers.py +276 -0
  56. infoman/logger/metrics.py +160 -0
  57. infoman/performance/README.md +583 -0
  58. infoman/performance/__init__.py +19 -0
  59. infoman/performance/cli.py +215 -0
  60. infoman/performance/config.py +166 -0
  61. infoman/performance/reporter.py +519 -0
  62. infoman/performance/runner.py +303 -0
  63. infoman/performance/standards.py +222 -0
  64. infoman/service/__init__.py +8 -0
  65. infoman/service/app.py +67 -0
  66. infoman/service/core/__init__.py +0 -0
  67. infoman/service/core/auth.py +105 -0
  68. infoman/service/core/lifespan.py +132 -0
  69. infoman/service/core/monitor.py +57 -0
  70. infoman/service/core/response.py +37 -0
  71. infoman/service/exception/__init__.py +7 -0
  72. infoman/service/exception/error.py +274 -0
  73. infoman/service/exception/exception.py +25 -0
  74. infoman/service/exception/handler.py +238 -0
  75. infoman/service/infrastructure/__init__.py +8 -0
  76. infoman/service/infrastructure/base.py +212 -0
  77. infoman/service/infrastructure/db_cache/__init__.py +8 -0
  78. infoman/service/infrastructure/db_cache/manager.py +194 -0
  79. infoman/service/infrastructure/db_relation/__init__.py +41 -0
  80. infoman/service/infrastructure/db_relation/manager.py +300 -0
  81. infoman/service/infrastructure/db_relation/manager_pro.py +408 -0
  82. infoman/service/infrastructure/db_relation/mysql.py +52 -0
  83. infoman/service/infrastructure/db_relation/pgsql.py +54 -0
  84. infoman/service/infrastructure/db_relation/sqllite.py +25 -0
  85. infoman/service/infrastructure/db_vector/__init__.py +40 -0
  86. infoman/service/infrastructure/db_vector/manager.py +201 -0
  87. infoman/service/infrastructure/db_vector/qdrant.py +322 -0
  88. infoman/service/infrastructure/mq/__init__.py +15 -0
  89. infoman/service/infrastructure/mq/manager.py +178 -0
  90. infoman/service/infrastructure/mq/nats/__init__.py +0 -0
  91. infoman/service/infrastructure/mq/nats/nats_client.py +57 -0
  92. infoman/service/infrastructure/mq/nats/nats_event_router.py +25 -0
  93. infoman/service/launch.py +284 -0
  94. infoman/service/middleware/__init__.py +7 -0
  95. infoman/service/middleware/base.py +41 -0
  96. infoman/service/middleware/logging.py +51 -0
  97. infoman/service/middleware/rate_limit.py +301 -0
  98. infoman/service/middleware/request_id.py +21 -0
  99. infoman/service/middleware/white_list.py +24 -0
  100. infoman/service/models/__init__.py +8 -0
  101. infoman/service/models/base.py +441 -0
  102. infoman/service/models/type/embed.py +70 -0
  103. infoman/service/routers/__init__.py +18 -0
  104. infoman/service/routers/health_router.py +311 -0
  105. infoman/service/routers/monitor_router.py +44 -0
  106. infoman/service/utils/__init__.py +8 -0
  107. infoman/service/utils/cache/__init__.py +0 -0
  108. infoman/service/utils/cache/cache.py +192 -0
  109. infoman/service/utils/module_loader.py +10 -0
  110. infoman/service/utils/parse.py +10 -0
  111. infoman/service/utils/resolver/__init__.py +8 -0
  112. infoman/service/utils/resolver/base.py +47 -0
  113. infoman/service/utils/resolver/resp.py +102 -0
  114. infoman/service/vector/__init__.py +20 -0
  115. infoman/service/vector/base.py +56 -0
  116. infoman/service/vector/qdrant.py +125 -0
  117. infoman/service/vector/service.py +67 -0
  118. infoman/utils/__init__.py +2 -0
  119. infoman/utils/decorators/__init__.py +8 -0
  120. infoman/utils/decorators/cache.py +137 -0
  121. infoman/utils/decorators/retry.py +99 -0
  122. infoman/utils/decorators/safe_execute.py +99 -0
  123. infoman/utils/decorators/timing.py +99 -0
  124. infoman/utils/encryption/__init__.py +8 -0
  125. infoman/utils/encryption/aes.py +66 -0
  126. infoman/utils/encryption/ecc.py +108 -0
  127. infoman/utils/encryption/rsa.py +112 -0
  128. infoman/utils/file/__init__.py +0 -0
  129. infoman/utils/file/handler.py +22 -0
  130. infoman/utils/hash/__init__.py +0 -0
  131. infoman/utils/hash/hash.py +61 -0
  132. infoman/utils/http/__init__.py +8 -0
  133. infoman/utils/http/client.py +62 -0
  134. infoman/utils/http/info.py +94 -0
  135. infoman/utils/http/result.py +19 -0
  136. infoman/utils/notification/__init__.py +8 -0
  137. infoman/utils/notification/feishu.py +35 -0
  138. infoman/utils/text/__init__.py +8 -0
  139. infoman/utils/text/extractor.py +111 -0
  140. infomankit-0.3.23.dist-info/METADATA +632 -0
  141. infomankit-0.3.23.dist-info/RECORD +143 -0
  142. infomankit-0.3.23.dist-info/WHEEL +4 -0
  143. infomankit-0.3.23.dist-info/entry_points.txt +5 -0
@@ -0,0 +1,276 @@
1
+ # !/usr/bin/env python
2
+ # -*-coding:utf-8 -*-
3
+
4
+ """
5
+ # Time :2025/12/23 18:02
6
+ # Author :Maxwell
7
+ # Description:
8
+ """
9
+ import orjson
10
+ import time
11
+ from pathlib import Path
12
+ from typing import Dict, List, Optional
13
+ from threading import Thread, Lock, Event
14
+ from queue import Queue, Empty
15
+
16
+ try:
17
+ import httpx
18
+
19
+ HTTPX_AVAILABLE = True
20
+ except ImportError:
21
+ HTTPX_AVAILABLE = False
22
+
23
+
24
+ class LokiHandler:
25
+ """
26
+ Loki 日志处理器
27
+
28
+ 功能:
29
+ 1. 批量推送(减少网络请求)
30
+ 2. 异步推送(不阻塞主线程)
31
+ 3. 自动重试(网络异常时)
32
+ 4. 降级策略(Loki 不可用时写备份文件)
33
+ """
34
+
35
+ def __init__(
36
+ self,
37
+ url: str,
38
+ labels: Dict[str, str],
39
+ batch_size: int = 100,
40
+ flush_interval: float = 5.0,
41
+ timeout: float = 10.0,
42
+ retry_times: int = 3,
43
+ retry_backoff: float = 2.0,
44
+ enable_fallback: bool = True,
45
+ fallback_file: Optional[Path] = None,
46
+ ):
47
+ """
48
+ 初始化 Loki 处理器
49
+
50
+ Args:
51
+ url: Loki 服务地址
52
+ labels: Loki Labels
53
+ batch_size: 批量推送大小
54
+ flush_interval: 刷新间隔(秒)
55
+ timeout: 推送超时(秒)
56
+ retry_times: 重试次数
57
+ retry_backoff: 重试退避系数
58
+ enable_fallback: 启用降级策略
59
+ fallback_file: 备份文件路径
60
+ """
61
+ if not HTTPX_AVAILABLE:
62
+ raise ImportError("LokiHandler 需要 httpx: pip install httpx")
63
+
64
+ self.url = f"{url}/loki/api/v1/push"
65
+ self.labels = labels
66
+ self.batch_size = batch_size
67
+ self.flush_interval = flush_interval
68
+ self.timeout = timeout
69
+ self.retry_times = retry_times
70
+ self.retry_backoff = retry_backoff
71
+ self.enable_fallback = enable_fallback
72
+ self.fallback_file = fallback_file
73
+
74
+ # 日志队列
75
+ self.queue: Queue = Queue(maxsize=10000)
76
+ self.batch: List[Dict] = []
77
+ self.lock = Lock()
78
+
79
+ # 后台线程
80
+ self.stop_event = Event()
81
+ self.worker_thread = Thread(target=self._worker, daemon=True)
82
+ self.worker_thread.start()
83
+
84
+ # HTTP 客户端
85
+ self.client = httpx.Client(timeout=timeout)
86
+
87
+ # 统计信息
88
+ self.sent_count = 0
89
+ self.failed_count = 0
90
+ self.fallback_count = 0
91
+
92
+ def write(self, message):
93
+ """
94
+ 写入日志(Loguru 调用)
95
+
96
+ Args:
97
+ message: 日志消息对象
98
+ """
99
+ try:
100
+ record = message.record
101
+
102
+ # 构造日志条目
103
+ entry = {
104
+ "timestamp": int(record["time"].timestamp() * 1e9), # 纳秒时间戳
105
+ "line": self._format_line(record),
106
+ }
107
+
108
+ # 放入队列
109
+ self.queue.put_nowait(entry)
110
+
111
+ except Exception as e:
112
+ # 避免日志处理器本身抛出异常
113
+ print(f"LokiHandler 写入失败: {e}")
114
+
115
+ def _format_line(self, record: Dict) -> str:
116
+ """格式化日志行"""
117
+ return orjson.dumps({
118
+ "level": record["level"].name,
119
+ "logger": record["name"],
120
+ "function": record["function"],
121
+ "line": record["line"],
122
+ "message": record["message"],
123
+ "extra": record.get("extra", {}),
124
+ }).decode('utf-8')
125
+
126
+ def _worker(self):
127
+ """后台工作线程"""
128
+ last_flush_time = time.time()
129
+
130
+ while not self.stop_event.is_set():
131
+ try:
132
+ # 从队列获取日志
133
+ try:
134
+ entry = self.queue.get(timeout=1.0)
135
+
136
+ with self.lock:
137
+ self.batch.append(entry)
138
+
139
+ except Empty:
140
+ pass
141
+
142
+ # 检查是否需要刷新
143
+ current_time = time.time()
144
+ should_flush = (
145
+ len(self.batch) >= self.batch_size or
146
+ (self.batch and current_time - last_flush_time >= self.flush_interval)
147
+ )
148
+
149
+ if should_flush:
150
+ self._flush()
151
+ last_flush_time = current_time
152
+
153
+ except Exception as e:
154
+ print(f"LokiHandler 工作线程异常: {e}")
155
+
156
+ # 线程退出前,刷新剩余日志
157
+ self._flush()
158
+
159
+ def _flush(self):
160
+ """刷新日志批次"""
161
+ with self.lock:
162
+ if not self.batch:
163
+ return
164
+
165
+ batch_to_send = self.batch.copy()
166
+ self.batch.clear()
167
+
168
+ # 推送到 Loki
169
+ success = self._push_to_loki(batch_to_send)
170
+
171
+ if success:
172
+ self.sent_count += len(batch_to_send)
173
+ else:
174
+ self.failed_count += len(batch_to_send)
175
+
176
+ # 降级策略:写备份文件
177
+ if self.enable_fallback:
178
+ self._write_fallback(batch_to_send)
179
+
180
+ def _push_to_loki(self, batch: List[Dict]) -> bool:
181
+ """
182
+ 推送日志到 Loki
183
+
184
+ Args:
185
+ batch: 日志批次
186
+
187
+ Returns:
188
+ True: 成功
189
+ False: 失败
190
+ """
191
+ # 构造 Loki 请求体
192
+ payload = {
193
+ "streams": [
194
+ {
195
+ "stream": self.labels,
196
+ "values": [
197
+ [str(entry["timestamp"]), entry["line"]]
198
+ for entry in batch
199
+ ]
200
+ }
201
+ ]
202
+ }
203
+
204
+ # 重试推送
205
+ for attempt in range(self.retry_times + 1):
206
+ try:
207
+ response = self.client.post(
208
+ self.url,
209
+ json=payload,
210
+ headers={"Content-Type": "application/json"},
211
+ )
212
+
213
+ if response.status_code == 204:
214
+ return True
215
+
216
+ print(
217
+ f"Loki 推送失败 [尝试 {attempt + 1}/{self.retry_times + 1}]: "
218
+ f"HTTP {response.status_code} - {response.text}"
219
+ )
220
+
221
+ except Exception as e:
222
+ print(
223
+ f"Loki 推送异常 [尝试 {attempt + 1}/{self.retry_times + 1}]: {e}"
224
+ )
225
+
226
+ # 退避重试
227
+ if attempt < self.retry_times:
228
+ time.sleep(self.retry_backoff ** attempt)
229
+
230
+ return False
231
+
232
+ def _write_fallback(self, batch: List[Dict]):
233
+ """
234
+ 写入备份文件
235
+
236
+ Args:
237
+ batch: 日志批次
238
+ """
239
+ if not self.fallback_file:
240
+ return
241
+
242
+ try:
243
+ with open(self.fallback_file, "a", encoding="utf-8") as f:
244
+ for entry in batch:
245
+ f.write(entry["line"] + "\n")
246
+
247
+ self.fallback_count += len(batch)
248
+
249
+ except Exception as e:
250
+ print(f"写入备份文件失败: {e}")
251
+
252
+ def flush(self):
253
+ """手动刷新"""
254
+ self._flush()
255
+
256
+ def close(self):
257
+ """关闭处理器"""
258
+ # 停止工作线程
259
+ self.stop_event.set()
260
+ self.worker_thread.join(timeout=5.0)
261
+
262
+ # 关闭 HTTP 客户端
263
+ self.client.close()
264
+
265
+ print(
266
+ f"LokiHandler 已关闭 "
267
+ f"[发送={self.sent_count}, 失败={self.failed_count}, "
268
+ f"备份={self.fallback_count}]"
269
+ )
270
+
271
+ def __del__(self):
272
+ """析构函数"""
273
+ try:
274
+ self.close()
275
+ except:
276
+ pass
@@ -0,0 +1,160 @@
1
+ # !/usr/bin/env python
2
+ # -*-coding:utf-8 -*-
3
+
4
+ """
5
+ # Time :2025/12/23 17:58
6
+ # Author :Maxwell
7
+ # Description:
8
+ """
9
+
10
+ """日志指标统计"""
11
+
12
+ import time
13
+ from collections import defaultdict, deque
14
+ from typing import Dict, Optional
15
+ from threading import Lock
16
+
17
+
18
+ class LogMetrics:
19
+ """日志指标统计器"""
20
+
21
+ def __init__(self, window_size: int = 60):
22
+ """
23
+ Args:
24
+ window_size: 统计窗口大小(秒)
25
+ """
26
+ self.window_size = window_size
27
+ self.lock = Lock()
28
+
29
+ # 日志计数(按级别)
30
+ self.counts: Dict[str, int] = defaultdict(int)
31
+
32
+ # 时间序列(用于速率计算)
33
+ self.timestamps: Dict[str, deque] = defaultdict(
34
+ lambda: deque(maxlen=1000)
35
+ )
36
+
37
+ # 错误统计
38
+ self.error_count = 0
39
+ self.error_timestamps = deque(maxlen=1000)
40
+
41
+ def record(self, level: str):
42
+ """
43
+ 记录日志
44
+
45
+ Args:
46
+ level: 日志级别
47
+ """
48
+ with self.lock:
49
+ current_time = time.time()
50
+
51
+ # 更新计数
52
+ self.counts[level] += 1
53
+ self.timestamps[level].append(current_time)
54
+
55
+ # 记录错误
56
+ if level in ("ERROR", "CRITICAL"):
57
+ self.error_count += 1
58
+ self.error_timestamps.append(current_time)
59
+
60
+ def get_counts(self) -> Dict[str, int]:
61
+ """获取日志计数"""
62
+ with self.lock:
63
+ return dict(self.counts)
64
+
65
+ def get_rate(self, level: str) -> float:
66
+ """
67
+ 获取日志速率(每秒)
68
+
69
+ Args:
70
+ level: 日志级别
71
+
72
+ Returns:
73
+ 每秒日志数
74
+ """
75
+ with self.lock:
76
+ timestamps = self.timestamps[level]
77
+ if not timestamps:
78
+ return 0.0
79
+
80
+ current_time = time.time()
81
+
82
+ # 过滤窗口内的时间戳
83
+ recent = [
84
+ ts for ts in timestamps
85
+ if current_time - ts <= self.window_size
86
+ ]
87
+
88
+ if not recent:
89
+ return 0.0
90
+
91
+ duration = current_time - recent[0]
92
+ return len(recent) / duration if duration > 0 else 0.0
93
+
94
+ def get_error_rate(self) -> float:
95
+ """获取错误速率(每秒)"""
96
+ with self.lock:
97
+ if not self.error_timestamps:
98
+ return 0.0
99
+
100
+ current_time = time.time()
101
+
102
+ # 过滤窗口内的时间戳
103
+ recent = [
104
+ ts for ts in self.error_timestamps
105
+ if current_time - ts <= self.window_size
106
+ ]
107
+
108
+ if not recent:
109
+ return 0.0
110
+
111
+ duration = current_time - recent[0]
112
+ return len(recent) / duration if duration > 0 else 0.0
113
+
114
+ def check_error_threshold(self, threshold: int, window: int) -> bool:
115
+ """
116
+ 检查错误阈值
117
+
118
+ Args:
119
+ threshold: 错误阈值
120
+ window: 时间窗口(秒)
121
+
122
+ Returns:
123
+ True: 超过阈值
124
+ False: 未超过阈值
125
+ """
126
+ with self.lock:
127
+ current_time = time.time()
128
+
129
+ # 统计窗口内的错误数
130
+ recent_errors = sum(
131
+ 1 for ts in self.error_timestamps
132
+ if current_time - ts <= window
133
+ )
134
+
135
+ return recent_errors >= threshold
136
+
137
+ def get_summary(self) -> Dict[str, any]:
138
+ """获取统计摘要"""
139
+ with self.lock:
140
+ return {
141
+ "total_counts": dict(self.counts),
142
+ "error_count": self.error_count,
143
+ "error_rate": round(self.get_error_rate(), 2),
144
+ "rates": {
145
+ level: round(self.get_rate(level), 2)
146
+ for level in self.counts.keys()
147
+ },
148
+ }
149
+
150
+
151
+ # 全局指标实例
152
+ _metrics_instance: Optional[LogMetrics] = None
153
+
154
+
155
+ def get_metrics() -> LogMetrics:
156
+ """获取指标实例"""
157
+ global _metrics_instance
158
+ if _metrics_instance is None:
159
+ _metrics_instance = LogMetrics()
160
+ return _metrics_instance