springbootAI 1.8.0__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.
- spring/__init__.py +66 -0
- spring/ai/__init__.py +78 -0
- spring/ai/advisors.py +139 -0
- spring/ai/annotations.py +74 -0
- spring/ai/autoconfig.py +481 -0
- spring/ai/core.py +391 -0
- spring/ai/etl.py +188 -0
- spring/ai/memory.py +109 -0
- spring/ai/observability.py +129 -0
- spring/ai/providers.py +789 -0
- spring/ai/resilience.py +258 -0
- spring/ai/tools.py +106 -0
- spring/ai/vectorstore.py +303 -0
- spring/annotations/__init__.py +188 -0
- spring/annotations/cache.py +126 -0
- spring/annotations/cloud.py +207 -0
- spring/annotations/conditional.py +272 -0
- spring/annotations/core.py +864 -0
- spring/annotations/messaging.py +107 -0
- spring/aop/__init__.py +4 -0
- spring/aop/cloud_aop.py +404 -0
- spring/aop/comprehensive_aop.py +1015 -0
- spring/aop/method_interceptor.py +19 -0
- spring/aop/proxy_factory.py +55 -0
- spring/cloud/__init__.py +76 -0
- spring/cloud/discovery.py +364 -0
- spring/cloud/feign.py +469 -0
- spring/cloud/gateway.py +452 -0
- spring/cloud/load_balancer.py +149 -0
- spring/cloud/seata.py +557 -0
- spring/cloud/sentinel.py +525 -0
- spring/cloud/tracer.py +337 -0
- spring/config/__init__.py +21 -0
- spring/config/binding.py +206 -0
- spring/config/config_loader.py +405 -0
- spring/context/__init__.py +13 -0
- spring/context/application_context.py +589 -0
- spring/context/bean_definition.py +70 -0
- spring/context/bean_factory.py +1052 -0
- spring/context/registry.py +58 -0
- spring/context/scanner.py +106 -0
- spring/core/__init__.py +3 -0
- spring/core/graceful_shutdown.py +196 -0
- spring/core/typing_utils.py +50 -0
- spring/csv/__init__.py +52 -0
- spring/csv/annotations.py +402 -0
- spring/csv/converters.py +69 -0
- spring/csv/easy_csv.py +95 -0
- spring/csv/exceptions.py +27 -0
- spring/csv/reader.py +195 -0
- spring/csv/writer.py +155 -0
- spring/data/__init__.py +54 -0
- spring/data/page.py +181 -0
- spring/data/repository.py +274 -0
- spring/data/specification.py +228 -0
- spring/datasource/__init__.py +66 -0
- spring/datasource/annotations.py +133 -0
- spring/datasource/context.py +69 -0
- spring/datasource/dynamic.py +148 -0
- spring/event/__init__.py +7 -0
- spring/event/publisher.py +69 -0
- spring/excel/__init__.py +51 -0
- spring/excel/annotations.py +405 -0
- spring/excel/converters.py +231 -0
- spring/excel/easy_excel.py +94 -0
- spring/excel/exceptions.py +31 -0
- spring/excel/reader.py +254 -0
- spring/excel/style.py +95 -0
- spring/excel/writer.py +197 -0
- spring/i18n/__init__.py +97 -0
- spring/i18n/accessor.py +94 -0
- spring/i18n/auto_config.py +177 -0
- spring/i18n/holder.py +106 -0
- spring/i18n/locale.py +152 -0
- spring/i18n/locale_resolver.py +367 -0
- spring/i18n/message_source.py +250 -0
- spring/i18n/middleware.py +79 -0
- spring/i18n/properties.py +168 -0
- spring/i18n/sources.py +255 -0
- spring/logging/__init__.py +1 -0
- spring/logging/loguru_logger.py +228 -0
- spring/main.py +378 -0
- spring/messaging/__init__.py +1 -0
- spring/messaging/rabbitmq.py +302 -0
- spring/monitoring/__init__.py +1 -0
- spring/monitoring/prometheus.py +199 -0
- spring/orm/__init__.py +258 -0
- spring/orm/database.py +222 -0
- spring/orm/ddl_auto.py +1217 -0
- spring/orm/migration.py +419 -0
- spring/orm/mybatis_integration.py +400 -0
- spring/orm/pymybatis/__init__.py +86 -0
- spring/orm/pymybatis/annotations/__init__.py +30 -0
- spring/orm/pymybatis/annotations/annotations.py +332 -0
- spring/orm/pymybatis/cache/__init__.py +47 -0
- spring/orm/pymybatis/cache/cache.py +371 -0
- spring/orm/pymybatis/cache/redis_cache.py +434 -0
- spring/orm/pymybatis/circuit_breaker/__init__.py +21 -0
- spring/orm/pymybatis/circuit_breaker/circuit_breaker.py +424 -0
- spring/orm/pymybatis/configuration.py +525 -0
- spring/orm/pymybatis/core/__init__.py +10 -0
- spring/orm/pymybatis/core/sql_session.py +1382 -0
- spring/orm/pymybatis/core/sql_session_factory.py +76 -0
- spring/orm/pymybatis/dialect/__init__.py +9 -0
- spring/orm/pymybatis/dialect/dialect.py +445 -0
- spring/orm/pymybatis/dynamic_sql/__init__.py +9 -0
- spring/orm/pymybatis/dynamic_sql/dynamic_sql.py +900 -0
- spring/orm/pymybatis/interceptor/__init__.py +31 -0
- spring/orm/pymybatis/interceptor/interceptor.py +427 -0
- spring/orm/pymybatis/mapper/__init__.py +9 -0
- spring/orm/pymybatis/mapper/mapper.py +540 -0
- spring/orm/pymybatis/metrics/__init__.py +41 -0
- spring/orm/pymybatis/metrics/metrics.py +595 -0
- spring/orm/pymybatis/pool/__init__.py +9 -0
- spring/orm/pymybatis/pool/connection_pool.py +711 -0
- spring/orm/pymybatis/security/__init__.py +19 -0
- spring/orm/pymybatis/security/access_control.py +415 -0
- spring/orm/pymybatis/security/password_encoder.py +293 -0
- spring/orm/pymybatis/security/sensitive_data_masker.py +326 -0
- spring/orm/pymybatis/security/sql_injection_detector.py +675 -0
- spring/orm/pymybatis/transaction/__init__.py +9 -0
- spring/orm/pymybatis/transaction/transaction.py +288 -0
- spring/orm/pymybatis/type_handler/__init__.py +37 -0
- spring/orm/pymybatis/type_handler/type_handler.py +473 -0
- spring/orm/pymybatis/version.py +9 -0
- spring/orm/pymybatis/xml_parser/__init__.py +9 -0
- spring/orm/pymybatis/xml_parser/xml_parser.py +761 -0
- spring/retry/__init__.py +12 -0
- spring/retry/retry_annotations.py +71 -0
- spring/retry/retry_decorator.py +155 -0
- spring/scheduling/__init__.py +3 -0
- spring/scheduling/scheduler.py +389 -0
- spring/security/__init__.py +39 -0
- spring/security/jwt_utils.py +281 -0
- spring/security/replay_protection.py +206 -0
- spring/security/secret_manager.py +226 -0
- spring/security/security_aop.py +248 -0
- spring/security/security_context.py +172 -0
- spring/test/__init__.py +45 -0
- spring/test/slicing.py +341 -0
- spring/tracing/__init__.py +11 -0
- spring/tracing/skywalking.py +229 -0
- spring/tx/__init__.py +52 -0
- spring/tx/events.py +172 -0
- spring/tx/synchronization.py +143 -0
- spring/utils/__init__.py +5 -0
- spring/utils/banner.py +32 -0
- spring/utils/logger.py +73 -0
- spring/utils/redis_client.py +526 -0
- spring/validation/__init__.py +55 -0
- spring/validation/aop.py +141 -0
- spring/validation/constraints.py +357 -0
- spring/validation/exceptions.py +55 -0
- spring/validation/validator.py +139 -0
- spring/web/__init__.py +12 -0
- spring/web/actuator.py +319 -0
- spring/web/exception_handler.py +61 -0
- spring/web/health.py +399 -0
- spring/web/interceptor.py +91 -0
- spring/web/result.py +44 -0
- spring/web/swagger.py +601 -0
- spring/web/web_context.py +755 -0
- spring/websocket/__init__.py +86 -0
- spring/websocket/annotations.py +169 -0
- spring/websocket/broker.py +238 -0
- spring/websocket/exceptions.py +26 -0
- spring/websocket/handler.py +243 -0
- spring/websocket/router.py +526 -0
- spring/websocket/session.py +216 -0
- springbootai-1.8.0.dist-info/METADATA +2796 -0
- springbootai-1.8.0.dist-info/RECORD +175 -0
- springbootai-1.8.0.dist-info/WHEEL +5 -0
- springbootai-1.8.0.dist-info/entry_points.txt +2 -0
- springbootai-1.8.0.dist-info/licenses/LICENSE +7 -0
- springbootai-1.8.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyMyBatis监控指标模块
|
|
3
|
+
|
|
4
|
+
提供Prometheus兼容的指标收集和导出功能
|
|
5
|
+
|
|
6
|
+
核心指标类型:
|
|
7
|
+
- Counter: 计数器,只能递增
|
|
8
|
+
- Gauge: 仪表盘,可以增减
|
|
9
|
+
- Histogram: 直方图,统计分布
|
|
10
|
+
- Timer: 计时器,基于直方图
|
|
11
|
+
|
|
12
|
+
导出格式:
|
|
13
|
+
- Prometheus文本格式
|
|
14
|
+
- JSON格式
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import time
|
|
18
|
+
import threading
|
|
19
|
+
import logging
|
|
20
|
+
from typing import Dict, Any, List, Optional, Callable
|
|
21
|
+
from collections import defaultdict
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Counter:
|
|
27
|
+
"""计数器指标"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, name: str, help_text: str, labels: Optional[List[str]] = None):
|
|
30
|
+
"""
|
|
31
|
+
初始化计数器
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
name: 指标名称
|
|
35
|
+
help_text: 帮助文本
|
|
36
|
+
labels: 标签列表
|
|
37
|
+
"""
|
|
38
|
+
self.name = name
|
|
39
|
+
self.help_text = help_text
|
|
40
|
+
self.labels = labels or []
|
|
41
|
+
self._values: Dict[str, float] = defaultdict(float)
|
|
42
|
+
self._lock = threading.RLock()
|
|
43
|
+
|
|
44
|
+
def inc(self, value: float = 1.0, **labels):
|
|
45
|
+
"""
|
|
46
|
+
增加计数
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
value: 增加的值
|
|
50
|
+
**labels: 标签值
|
|
51
|
+
"""
|
|
52
|
+
key = self._generate_key(labels)
|
|
53
|
+
with self._lock:
|
|
54
|
+
self._values[key] += value
|
|
55
|
+
|
|
56
|
+
def reset(self, **labels):
|
|
57
|
+
"""重置计数"""
|
|
58
|
+
key = self._generate_key(labels)
|
|
59
|
+
with self._lock:
|
|
60
|
+
self._values[key] = 0.0
|
|
61
|
+
|
|
62
|
+
def get(self, **labels) -> float:
|
|
63
|
+
"""获取当前值"""
|
|
64
|
+
key = self._generate_key(labels)
|
|
65
|
+
return self._values[key]
|
|
66
|
+
|
|
67
|
+
def _generate_key(self, labels: Dict[str, Any]) -> str:
|
|
68
|
+
"""生成标签key"""
|
|
69
|
+
if not self.labels:
|
|
70
|
+
return ''
|
|
71
|
+
return ','.join([f"{k}={labels.get(k, '')}" for k in self.labels])
|
|
72
|
+
|
|
73
|
+
def to_prometheus(self) -> str:
|
|
74
|
+
"""转换为Prometheus格式"""
|
|
75
|
+
lines = []
|
|
76
|
+
lines.append(f"# HELP {self.name} {self.help_text}")
|
|
77
|
+
lines.append(f"# TYPE {self.name} counter")
|
|
78
|
+
|
|
79
|
+
for key, value in self._values.items():
|
|
80
|
+
if key:
|
|
81
|
+
label_parts = []
|
|
82
|
+
for part in key.split(','):
|
|
83
|
+
k, v = part.split('=', 1)
|
|
84
|
+
label_parts.append(f'{k}="{v}"')
|
|
85
|
+
label_str = '{' + ','.join(label_parts) + '}'
|
|
86
|
+
lines.append(f"{self.name}{label_str} {value}")
|
|
87
|
+
else:
|
|
88
|
+
lines.append(f"{self.name} {value}")
|
|
89
|
+
|
|
90
|
+
return '\n'.join(lines)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class Gauge:
|
|
94
|
+
"""仪表盘指标"""
|
|
95
|
+
|
|
96
|
+
def __init__(self, name: str, help_text: str, labels: Optional[List[str]] = None):
|
|
97
|
+
"""
|
|
98
|
+
初始化仪表盘
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
name: 指标名称
|
|
102
|
+
help_text: 帮助文本
|
|
103
|
+
labels: 标签列表
|
|
104
|
+
"""
|
|
105
|
+
self.name = name
|
|
106
|
+
self.help_text = help_text
|
|
107
|
+
self.labels = labels or []
|
|
108
|
+
self._values: Dict[str, float] = defaultdict(float)
|
|
109
|
+
self._lock = threading.RLock()
|
|
110
|
+
|
|
111
|
+
def set(self, value: float, **labels):
|
|
112
|
+
"""设置值"""
|
|
113
|
+
key = self._generate_key(labels)
|
|
114
|
+
with self._lock:
|
|
115
|
+
self._values[key] = value
|
|
116
|
+
|
|
117
|
+
def inc(self, value: float = 1.0, **labels):
|
|
118
|
+
"""增加"""
|
|
119
|
+
key = self._generate_key(labels)
|
|
120
|
+
with self._lock:
|
|
121
|
+
self._values[key] += value
|
|
122
|
+
|
|
123
|
+
def dec(self, value: float = 1.0, **labels):
|
|
124
|
+
"""减少"""
|
|
125
|
+
key = self._generate_key(labels)
|
|
126
|
+
with self._lock:
|
|
127
|
+
self._values[key] -= value
|
|
128
|
+
|
|
129
|
+
def get(self, **labels) -> float:
|
|
130
|
+
"""获取当前值"""
|
|
131
|
+
key = self._generate_key(labels)
|
|
132
|
+
return self._values[key]
|
|
133
|
+
|
|
134
|
+
def _generate_key(self, labels: Dict[str, Any]) -> str:
|
|
135
|
+
"""生成标签key"""
|
|
136
|
+
if not self.labels:
|
|
137
|
+
return ''
|
|
138
|
+
return ','.join([f"{k}={labels.get(k, '')}" for k in self.labels])
|
|
139
|
+
|
|
140
|
+
def to_prometheus(self) -> str:
|
|
141
|
+
"""转换为Prometheus格式"""
|
|
142
|
+
lines = []
|
|
143
|
+
lines.append(f"# HELP {self.name} {self.help_text}")
|
|
144
|
+
lines.append(f"# TYPE {self.name} gauge")
|
|
145
|
+
|
|
146
|
+
for key, value in self._values.items():
|
|
147
|
+
if key:
|
|
148
|
+
label_parts = []
|
|
149
|
+
for part in key.split(','):
|
|
150
|
+
k, v = part.split('=', 1)
|
|
151
|
+
label_parts.append(f'{k}="{v}"')
|
|
152
|
+
label_str = '{' + ','.join(label_parts) + '}'
|
|
153
|
+
lines.append(f"{self.name}{label_str} {value}")
|
|
154
|
+
else:
|
|
155
|
+
lines.append(f"{self.name} {value}")
|
|
156
|
+
|
|
157
|
+
return '\n'.join(lines)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class Histogram:
|
|
161
|
+
"""直方图指标"""
|
|
162
|
+
|
|
163
|
+
def __init__(self, name: str, help_text: str,
|
|
164
|
+
buckets: Optional[List[float]] = None,
|
|
165
|
+
labels: Optional[List[str]] = None):
|
|
166
|
+
"""
|
|
167
|
+
初始化直方图
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
name: 指标名称
|
|
171
|
+
help_text: 帮助文本
|
|
172
|
+
buckets: 桶边界
|
|
173
|
+
labels: 标签列表
|
|
174
|
+
"""
|
|
175
|
+
self.name = name
|
|
176
|
+
self.help_text = help_text
|
|
177
|
+
self.buckets = buckets or [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0]
|
|
178
|
+
self.labels = labels or []
|
|
179
|
+
|
|
180
|
+
# 每个标签组合对应一组桶
|
|
181
|
+
self._buckets: Dict[str, List[int]] = defaultdict(lambda: [0] * (len(self.buckets) + 1))
|
|
182
|
+
self._sums: Dict[str, float] = defaultdict(float)
|
|
183
|
+
self._counts: Dict[str, int] = defaultdict(int)
|
|
184
|
+
self._lock = threading.RLock()
|
|
185
|
+
|
|
186
|
+
def observe(self, value: float, **labels):
|
|
187
|
+
"""
|
|
188
|
+
观察值
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
value: 观察的值
|
|
192
|
+
**labels: 标签值
|
|
193
|
+
"""
|
|
194
|
+
key = self._generate_key(labels)
|
|
195
|
+
|
|
196
|
+
with self._lock:
|
|
197
|
+
self._counts[key] += 1
|
|
198
|
+
self._sums[key] += value
|
|
199
|
+
|
|
200
|
+
# 更新桶计数
|
|
201
|
+
buckets = self._buckets[key]
|
|
202
|
+
for i, bucket in enumerate(self.buckets):
|
|
203
|
+
if value <= bucket:
|
|
204
|
+
buckets[i] += 1
|
|
205
|
+
buckets[-1] += 1 # +Inf桶
|
|
206
|
+
|
|
207
|
+
def get_counts(self, **labels) -> List[int]:
|
|
208
|
+
"""获取桶计数"""
|
|
209
|
+
key = self._generate_key(labels)
|
|
210
|
+
return list(self._buckets[key])
|
|
211
|
+
|
|
212
|
+
def get_sum(self, **labels) -> float:
|
|
213
|
+
"""获取总和"""
|
|
214
|
+
key = self._generate_key(labels)
|
|
215
|
+
return self._sums[key]
|
|
216
|
+
|
|
217
|
+
def get_count(self, **labels) -> int:
|
|
218
|
+
"""获取计数"""
|
|
219
|
+
key = self._generate_key(labels)
|
|
220
|
+
return self._counts[key]
|
|
221
|
+
|
|
222
|
+
def _generate_key(self, labels: Dict[str, Any]) -> str:
|
|
223
|
+
"""生成标签key"""
|
|
224
|
+
if not self.labels:
|
|
225
|
+
return ''
|
|
226
|
+
return ','.join([f"{k}={labels.get(k, '')}" for k in self.labels])
|
|
227
|
+
|
|
228
|
+
def to_prometheus(self) -> str:
|
|
229
|
+
"""转换为Prometheus格式"""
|
|
230
|
+
lines = []
|
|
231
|
+
lines.append(f"# HELP {self.name} {self.help_text}")
|
|
232
|
+
lines.append(f"# TYPE {self.name} histogram")
|
|
233
|
+
|
|
234
|
+
for key, buckets in self._buckets.items():
|
|
235
|
+
# 解析标签
|
|
236
|
+
label_items = []
|
|
237
|
+
if key:
|
|
238
|
+
for part in key.split(','):
|
|
239
|
+
k, v = part.split('=', 1)
|
|
240
|
+
label_items.append(f'{k}="{v}"')
|
|
241
|
+
|
|
242
|
+
# 输出桶
|
|
243
|
+
for i, bucket in enumerate(self.buckets):
|
|
244
|
+
bucket_labels = label_items + [f'le="{bucket}"']
|
|
245
|
+
bucket_label_str = '{' + ','.join(bucket_labels) + '}'
|
|
246
|
+
lines.append(f"{self.name}_bucket{bucket_label_str} {buckets[i]}")
|
|
247
|
+
|
|
248
|
+
# +Inf桶
|
|
249
|
+
inf_labels = label_items + ['le="+Inf"']
|
|
250
|
+
inf_label_str = '{' + ','.join(inf_labels) + '}'
|
|
251
|
+
lines.append(f"{self.name}_bucket{inf_label_str} {buckets[-1]}")
|
|
252
|
+
|
|
253
|
+
# sum和count
|
|
254
|
+
base_label_str = '{' + ','.join(label_items) + '}' if label_items else ''
|
|
255
|
+
lines.append(f"{self.name}_sum{base_label_str} {self._sums[key]}")
|
|
256
|
+
lines.append(f"{self.name}_count{base_label_str} {self._counts[key]}")
|
|
257
|
+
|
|
258
|
+
return '\n'.join(lines)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class Timer:
|
|
262
|
+
"""计时器指标"""
|
|
263
|
+
|
|
264
|
+
def __init__(self, name: str, help_text: str,
|
|
265
|
+
buckets: Optional[List[float]] = None,
|
|
266
|
+
labels: Optional[List[str]] = None):
|
|
267
|
+
"""
|
|
268
|
+
初始化计时器
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
name: 指标名称
|
|
272
|
+
help_text: 帮助文本
|
|
273
|
+
buckets: 桶边界(秒)
|
|
274
|
+
labels: 标签列表
|
|
275
|
+
"""
|
|
276
|
+
self._histogram = Histogram(name, help_text, buckets, labels)
|
|
277
|
+
|
|
278
|
+
def observe(self, duration: float, **labels):
|
|
279
|
+
"""观察耗时(秒)"""
|
|
280
|
+
self._histogram.observe(duration, **labels)
|
|
281
|
+
|
|
282
|
+
def time(self, **labels) -> 'TimerContext':
|
|
283
|
+
"""
|
|
284
|
+
上下文管理器,自动计时
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
计时器上下文
|
|
288
|
+
"""
|
|
289
|
+
return TimerContext(self, labels)
|
|
290
|
+
|
|
291
|
+
def to_prometheus(self) -> str:
|
|
292
|
+
"""转换为Prometheus格式"""
|
|
293
|
+
return self._histogram.to_prometheus()
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class TimerContext:
|
|
297
|
+
"""计时器上下文管理器"""
|
|
298
|
+
|
|
299
|
+
def __init__(self, timer: Timer, labels: Dict[str, Any]):
|
|
300
|
+
self._timer = timer
|
|
301
|
+
self._labels = labels
|
|
302
|
+
self._start_time = None
|
|
303
|
+
|
|
304
|
+
def __enter__(self):
|
|
305
|
+
"""进入上下文,开始计时"""
|
|
306
|
+
self._start_time = time.time()
|
|
307
|
+
return self
|
|
308
|
+
|
|
309
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
310
|
+
"""退出上下文,记录耗时"""
|
|
311
|
+
if self._start_time is not None:
|
|
312
|
+
duration = time.time() - self._start_time
|
|
313
|
+
self._timer.observe(duration, **self._labels)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
class MetricsCollector:
|
|
317
|
+
"""
|
|
318
|
+
指标收集器
|
|
319
|
+
|
|
320
|
+
收集和管理所有指标,支持Prometheus格式导出
|
|
321
|
+
"""
|
|
322
|
+
|
|
323
|
+
def __init__(self):
|
|
324
|
+
"""初始化指标收集器"""
|
|
325
|
+
self._counters: Dict[str, Counter] = {}
|
|
326
|
+
self._gauges: Dict[str, Gauge] = {}
|
|
327
|
+
self._histograms: Dict[str, Histogram] = {}
|
|
328
|
+
self._timers: Dict[str, Timer] = {}
|
|
329
|
+
self._lock = threading.RLock()
|
|
330
|
+
|
|
331
|
+
def counter(self, name: str, help_text: str, labels: Optional[List[str]] = None) -> Counter:
|
|
332
|
+
"""
|
|
333
|
+
获取或创建计数器
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
name: 指标名称
|
|
337
|
+
help_text: 帮助文本
|
|
338
|
+
labels: 标签列表
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
计数器实例
|
|
342
|
+
"""
|
|
343
|
+
with self._lock:
|
|
344
|
+
if name not in self._counters:
|
|
345
|
+
self._counters[name] = Counter(name, help_text, labels)
|
|
346
|
+
return self._counters[name]
|
|
347
|
+
|
|
348
|
+
def gauge(self, name: str, help_text: str, labels: Optional[List[str]] = None) -> Gauge:
|
|
349
|
+
"""
|
|
350
|
+
获取或创建仪表盘
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
name: 指标名称
|
|
354
|
+
help_text: 帮助文本
|
|
355
|
+
labels: 标签列表
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
仪表盘实例
|
|
359
|
+
"""
|
|
360
|
+
with self._lock:
|
|
361
|
+
if name not in self._gauges:
|
|
362
|
+
self._gauges[name] = Gauge(name, help_text, labels)
|
|
363
|
+
return self._gauges[name]
|
|
364
|
+
|
|
365
|
+
def histogram(self, name: str, help_text: str,
|
|
366
|
+
buckets: Optional[List[float]] = None,
|
|
367
|
+
labels: Optional[List[str]] = None) -> Histogram:
|
|
368
|
+
"""
|
|
369
|
+
获取或创建直方图
|
|
370
|
+
|
|
371
|
+
Args:
|
|
372
|
+
name: 指标名称
|
|
373
|
+
help_text: 帮助文本
|
|
374
|
+
buckets: 桶边界
|
|
375
|
+
labels: 标签列表
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
直方图实例
|
|
379
|
+
"""
|
|
380
|
+
with self._lock:
|
|
381
|
+
if name not in self._histograms:
|
|
382
|
+
self._histograms[name] = Histogram(name, help_text, buckets, labels)
|
|
383
|
+
return self._histograms[name]
|
|
384
|
+
|
|
385
|
+
def timer(self, name: str, help_text: str,
|
|
386
|
+
buckets: Optional[List[float]] = None,
|
|
387
|
+
labels: Optional[List[str]] = None) -> Timer:
|
|
388
|
+
"""
|
|
389
|
+
获取或创建计时器
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
name: 指标名称
|
|
393
|
+
help_text: 帮助文本
|
|
394
|
+
buckets: 桶边界(秒)
|
|
395
|
+
labels: 标签列表
|
|
396
|
+
|
|
397
|
+
Returns:
|
|
398
|
+
计时器实例
|
|
399
|
+
"""
|
|
400
|
+
with self._lock:
|
|
401
|
+
if name not in self._timers:
|
|
402
|
+
self._timers[name] = Timer(name, help_text, buckets, labels)
|
|
403
|
+
return self._timers[name]
|
|
404
|
+
|
|
405
|
+
def collect(self) -> List[str]:
|
|
406
|
+
"""
|
|
407
|
+
收集所有指标
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
指标字符串列表
|
|
411
|
+
"""
|
|
412
|
+
lines = []
|
|
413
|
+
|
|
414
|
+
for counter in self._counters.values():
|
|
415
|
+
lines.append(counter.to_prometheus())
|
|
416
|
+
|
|
417
|
+
for gauge in self._gauges.values():
|
|
418
|
+
lines.append(gauge.to_prometheus())
|
|
419
|
+
|
|
420
|
+
for histogram in self._histograms.values():
|
|
421
|
+
lines.append(histogram.to_prometheus())
|
|
422
|
+
|
|
423
|
+
for timer in self._timers.values():
|
|
424
|
+
lines.append(timer.to_prometheus())
|
|
425
|
+
|
|
426
|
+
return lines
|
|
427
|
+
|
|
428
|
+
def to_prometheus(self) -> str:
|
|
429
|
+
"""
|
|
430
|
+
转换为Prometheus文本格式
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
Prometheus格式字符串
|
|
434
|
+
"""
|
|
435
|
+
return '\n'.join(self.collect()) + '\n'
|
|
436
|
+
|
|
437
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
438
|
+
"""
|
|
439
|
+
转换为字典格式
|
|
440
|
+
|
|
441
|
+
Returns:
|
|
442
|
+
指标字典
|
|
443
|
+
"""
|
|
444
|
+
result = {}
|
|
445
|
+
|
|
446
|
+
# 计数器
|
|
447
|
+
result['counters'] = {}
|
|
448
|
+
for name, counter in self._counters.items():
|
|
449
|
+
result['counters'][name] = {
|
|
450
|
+
'help': counter.help_text,
|
|
451
|
+
'labels': counter.labels,
|
|
452
|
+
'values': dict(counter._values)
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
# 仪表盘
|
|
456
|
+
result['gauges'] = {}
|
|
457
|
+
for name, gauge in self._gauges.items():
|
|
458
|
+
result['gauges'][name] = {
|
|
459
|
+
'help': gauge.help_text,
|
|
460
|
+
'labels': gauge.labels,
|
|
461
|
+
'values': dict(gauge._values)
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
# 直方图
|
|
465
|
+
result['histograms'] = {}
|
|
466
|
+
for name, histogram in self._histograms.items():
|
|
467
|
+
result['histograms'][name] = {
|
|
468
|
+
'help': histogram.help_text,
|
|
469
|
+
'labels': histogram.labels,
|
|
470
|
+
'buckets': histogram.buckets,
|
|
471
|
+
'data': {}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
# 计时器
|
|
475
|
+
result['timers'] = {}
|
|
476
|
+
for name, timer in self._timers.items():
|
|
477
|
+
result['timers'][name] = {
|
|
478
|
+
'help': timer._histogram.help_text,
|
|
479
|
+
'labels': timer._histogram.labels,
|
|
480
|
+
'buckets': timer._histogram.buckets
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return result
|
|
484
|
+
|
|
485
|
+
def reset(self):
|
|
486
|
+
"""重置所有指标"""
|
|
487
|
+
with self._lock:
|
|
488
|
+
self._counters.clear()
|
|
489
|
+
self._gauges.clear()
|
|
490
|
+
self._histograms.clear()
|
|
491
|
+
self._timers.clear()
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
# 全局默认指标收集器
|
|
495
|
+
_global_collector = MetricsCollector()
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
def get_default_collector() -> MetricsCollector:
|
|
499
|
+
"""
|
|
500
|
+
获取全局默认指标收集器
|
|
501
|
+
|
|
502
|
+
Returns:
|
|
503
|
+
指标收集器实例
|
|
504
|
+
"""
|
|
505
|
+
return _global_collector
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
# 便捷函数
|
|
509
|
+
def counter(name: str, help_text: str, labels: Optional[List[str]] = None) -> Counter:
|
|
510
|
+
"""便捷函数:获取或创建计数器"""
|
|
511
|
+
return _global_collector.counter(name, help_text, labels)
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def gauge(name: str, help_text: str, labels: Optional[List[str]] = None) -> Gauge:
|
|
515
|
+
"""便捷函数:获取或创建仪表盘"""
|
|
516
|
+
return _global_collector.gauge(name, help_text, labels)
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def histogram(name: str, help_text: str,
|
|
520
|
+
buckets: Optional[List[float]] = None,
|
|
521
|
+
labels: Optional[List[str]] = None) -> Histogram:
|
|
522
|
+
"""便捷函数:获取或创建直方图"""
|
|
523
|
+
return _global_collector.histogram(name, help_text, buckets, labels)
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def timer(name: str, help_text: str,
|
|
527
|
+
buckets: Optional[List[float]] = None,
|
|
528
|
+
labels: Optional[List[str]] = None) -> Timer:
|
|
529
|
+
"""便捷函数:获取或创建计时器"""
|
|
530
|
+
return _global_collector.timer(name, help_text, buckets, labels)
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
# 预定义的PyMyBatis核心指标
|
|
534
|
+
# 查询计数
|
|
535
|
+
QUERY_COUNTER = counter(
|
|
536
|
+
'pymybatis_query_total',
|
|
537
|
+
'Total number of SQL queries executed',
|
|
538
|
+
labels=['operation', 'table', 'status']
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
# 查询耗时
|
|
542
|
+
QUERY_TIMER = timer(
|
|
543
|
+
'pymybatis_query_duration_seconds',
|
|
544
|
+
'Duration of SQL queries in seconds',
|
|
545
|
+
buckets=[0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0],
|
|
546
|
+
labels=['operation', 'table']
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
# 连接池活跃连接数
|
|
550
|
+
ACTIVE_CONNECTIONS = gauge(
|
|
551
|
+
'pymybatis_connection_pool_active_connections',
|
|
552
|
+
'Number of active connections in the pool',
|
|
553
|
+
labels=['pool']
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
# 连接池空闲连接数
|
|
557
|
+
IDLE_CONNECTIONS = gauge(
|
|
558
|
+
'pymybatis_connection_pool_idle_connections',
|
|
559
|
+
'Number of idle connections in the pool',
|
|
560
|
+
labels=['pool']
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
# 缓存命中率
|
|
564
|
+
CACHE_HIT_COUNTER = counter(
|
|
565
|
+
'pymybatis_cache_hits_total',
|
|
566
|
+
'Total number of cache hits',
|
|
567
|
+
labels=['cache_type']
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
CACHE_MISS_COUNTER = counter(
|
|
571
|
+
'pymybatis_cache_misses_total',
|
|
572
|
+
'Total number of cache misses',
|
|
573
|
+
labels=['cache_type']
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
# 事务计数
|
|
577
|
+
TRANSACTION_COUNTER = counter(
|
|
578
|
+
'pymybatis_transactions_total',
|
|
579
|
+
'Total number of transactions',
|
|
580
|
+
labels=['status']
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
# 熔断器状态
|
|
584
|
+
CIRCUIT_BREAKER_STATE = gauge(
|
|
585
|
+
'pymybatis_circuit_breaker_state',
|
|
586
|
+
'Circuit breaker state (0=closed, 1=open, 2=half-open)',
|
|
587
|
+
labels=['name']
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
# 熔断器失败率
|
|
591
|
+
CIRCUIT_BREAKER_FAILURE_RATE = gauge(
|
|
592
|
+
'pymybatis_circuit_breaker_failure_rate',
|
|
593
|
+
'Circuit breaker failure rate percentage',
|
|
594
|
+
labels=['name']
|
|
595
|
+
)
|