aury-boot 0.0.29__py3-none-any.whl → 0.0.30__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.
- aury/boot/_version.py +2 -2
- aury/boot/application/app/base.py +126 -2
- aury/boot/application/app/components.py +224 -1
- aury/boot/application/config/settings.py +195 -3
- aury/boot/application/constants/components.py +3 -0
- aury/boot/application/middleware/logging.py +45 -6
- aury/boot/commands/docs.py +40 -0
- aury/boot/commands/init.py +2 -0
- aury/boot/commands/templates/project/AGENTS.md.tpl +5 -0
- aury/boot/commands/templates/project/alert_rules.example.yaml.tpl +85 -0
- aury/boot/commands/templates/project/aury_docs/00-overview.md.tpl +3 -0
- aury/boot/commands/templates/project/aury_docs/17-alerting.md.tpl +210 -0
- aury/boot/commands/templates/project/env_templates/monitoring.tpl +61 -0
- aury/boot/common/logging/context.py +17 -1
- aury/boot/common/logging/format.py +4 -0
- aury/boot/infrastructure/channel/base.py +6 -2
- aury/boot/infrastructure/database/query_tools/__init__.py +3 -5
- aury/boot/infrastructure/monitoring/__init__.py +210 -6
- aury/boot/infrastructure/monitoring/alerting/__init__.py +50 -0
- aury/boot/infrastructure/monitoring/alerting/aggregator.py +193 -0
- aury/boot/infrastructure/monitoring/alerting/events.py +141 -0
- aury/boot/infrastructure/monitoring/alerting/manager.py +428 -0
- aury/boot/infrastructure/monitoring/alerting/notifiers/__init__.py +16 -0
- aury/boot/infrastructure/monitoring/alerting/notifiers/base.py +60 -0
- aury/boot/infrastructure/monitoring/alerting/notifiers/feishu.py +209 -0
- aury/boot/infrastructure/monitoring/alerting/notifiers/webhook.py +110 -0
- aury/boot/infrastructure/monitoring/alerting/rules.py +163 -0
- aury/boot/infrastructure/monitoring/health/__init__.py +231 -0
- aury/boot/infrastructure/monitoring/tracing/__init__.py +55 -0
- aury/boot/infrastructure/monitoring/tracing/context.py +43 -0
- aury/boot/infrastructure/monitoring/tracing/logging.py +73 -0
- aury/boot/infrastructure/monitoring/tracing/processor.py +327 -0
- aury/boot/infrastructure/monitoring/tracing/provider.py +320 -0
- aury/boot/infrastructure/monitoring/tracing/tracing.py +235 -0
- {aury_boot-0.0.29.dist-info → aury_boot-0.0.30.dist-info}/METADATA +14 -1
- {aury_boot-0.0.29.dist-info → aury_boot-0.0.30.dist-info}/RECORD +38 -19
- {aury_boot-0.0.29.dist-info → aury_boot-0.0.30.dist-info}/WHEEL +0 -0
- {aury_boot-0.0.29.dist-info → aury_boot-0.0.30.dist-info}/entry_points.txt +0 -0
aury/boot/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 0,
|
|
31
|
+
__version__ = version = '0.0.30'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 0, 30)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -83,6 +83,78 @@ class Middleware(ABC):
|
|
|
83
83
|
pass
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
# TODO: 未来考虑合并 Middleware/Plugin/Component 为统一的 Extension 概念:
|
|
87
|
+
# class Extension:
|
|
88
|
+
# def build_middleware(self, config) -> StarletteMiddleware | None # 可选:app 创建前
|
|
89
|
+
# def install(self, app, config) -> None # 可选:app 创建后
|
|
90
|
+
# async def setup(self, app, config) -> None # 可选:lifespan 启动
|
|
91
|
+
# async def teardown(self, app) -> None # 可选:lifespan 关闭
|
|
92
|
+
# 这样一个扩展可以同时实现多个 hook,适用于 OTEL、认证等复杂场景。
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class Plugin(ABC):
|
|
96
|
+
"""应用插件基类。
|
|
97
|
+
|
|
98
|
+
用于需要 app 实例的扩展,如 OpenTelemetry instrument、Admin Console 挂载等。
|
|
99
|
+
插件在 app 创建后、lifespan 之前同步执行。
|
|
100
|
+
|
|
101
|
+
生命周期:
|
|
102
|
+
1. ``install()`` - 在 app 创建后同步调用,可以访问 app 实例。
|
|
103
|
+
|
|
104
|
+
与 Middleware 的区别:
|
|
105
|
+
- Middleware: 在 app 创建前构建,返回 Starlette Middleware
|
|
106
|
+
- Plugin: 在 app 创建后执行,可以操作 app 实例
|
|
107
|
+
|
|
108
|
+
与 Component 的区别:
|
|
109
|
+
- Component: 在 lifespan 里异步执行,用于基础设施初始化
|
|
110
|
+
- Plugin: 在 lifespan 前同步执行,用于 app 扩展
|
|
111
|
+
|
|
112
|
+
使用示例:
|
|
113
|
+
class TelemetryPlugin(Plugin):
|
|
114
|
+
name = "telemetry"
|
|
115
|
+
enabled = True
|
|
116
|
+
|
|
117
|
+
def install(self, app: FoundationApp, config: BaseConfig) -> None:
|
|
118
|
+
FastAPIInstrumentor.instrument_app(app)
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
name: str = "plugin"
|
|
122
|
+
enabled: bool = True
|
|
123
|
+
|
|
124
|
+
def can_enable(self, config: BaseConfig) -> bool:
|
|
125
|
+
"""是否可以启用此插件。
|
|
126
|
+
|
|
127
|
+
子类可以重写此方法以实现条件化启用。
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
config: 应用配置
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
是否启用
|
|
134
|
+
"""
|
|
135
|
+
return self.enabled
|
|
136
|
+
|
|
137
|
+
@abstractmethod
|
|
138
|
+
def install(self, app: FoundationApp, config: BaseConfig) -> None:
|
|
139
|
+
"""安装插件(同步,在 app 创建后、lifespan 之前调用)。
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
app: 应用实例
|
|
143
|
+
config: 应用配置
|
|
144
|
+
"""
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
def uninstall(self, app: FoundationApp) -> None:
|
|
148
|
+
"""卸载插件(可选,在 lifespan 关闭时调用)。
|
|
149
|
+
|
|
150
|
+
用于清理资源。默认不做任何操作。
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
app: 应用实例
|
|
154
|
+
"""
|
|
155
|
+
pass
|
|
156
|
+
|
|
157
|
+
|
|
86
158
|
class Component(ABC):
|
|
87
159
|
"""基础设施组件基类。
|
|
88
160
|
|
|
@@ -195,6 +267,9 @@ class FoundationApp(FastAPI):
|
|
|
195
267
|
# 默认中间件列表(子类可以覆盖)
|
|
196
268
|
middlewares: ClassVar[list[type[Middleware] | Middleware]] = []
|
|
197
269
|
|
|
270
|
+
# 默认插件列表(子类可以覆盖)
|
|
271
|
+
plugins: ClassVar[list[type[Plugin] | Plugin]] = []
|
|
272
|
+
|
|
198
273
|
# 默认组件列表(子类可以覆盖)
|
|
199
274
|
components: ClassVar[list[type[Component] | Component]] = []
|
|
200
275
|
|
|
@@ -249,8 +324,9 @@ class FoundationApp(FastAPI):
|
|
|
249
324
|
# 注册 access 日志(HTTP 请求日志)
|
|
250
325
|
register_log_sink("access", filter_key="access")
|
|
251
326
|
|
|
252
|
-
#
|
|
327
|
+
# 初始化中间件、插件和组件管理
|
|
253
328
|
self._middlewares: dict[str, Middleware] = {}
|
|
329
|
+
self._plugins: dict[str, Plugin] = {}
|
|
254
330
|
self._components: dict[str, Component] = {}
|
|
255
331
|
self._lifecycle_listeners: dict[str, list[Callable]] = {}
|
|
256
332
|
|
|
@@ -263,9 +339,12 @@ class FoundationApp(FastAPI):
|
|
|
263
339
|
yield
|
|
264
340
|
# 关闭
|
|
265
341
|
await self._on_shutdown()
|
|
342
|
+
# 卸载插件
|
|
343
|
+
self._uninstall_plugins()
|
|
266
344
|
|
|
267
|
-
#
|
|
345
|
+
# 收集中间件、插件和组件实例并过滤
|
|
268
346
|
self._collect_middlewares()
|
|
347
|
+
self._collect_plugins()
|
|
269
348
|
self._collect_components()
|
|
270
349
|
|
|
271
350
|
# 构建中间件实例列表,传给 FastAPI
|
|
@@ -281,6 +360,9 @@ class FoundationApp(FastAPI):
|
|
|
281
360
|
**kwargs,
|
|
282
361
|
)
|
|
283
362
|
|
|
363
|
+
# 安装插件(app 创建后、lifespan 之前)
|
|
364
|
+
self._install_plugins()
|
|
365
|
+
|
|
284
366
|
# 异常处理:显式注册以覆盖 FastAPI/Starlette 默认处理器,确保统一响应格式
|
|
285
367
|
self.add_exception_handler(RequestValidationError, global_exception_handler) # 422 参数校验
|
|
286
368
|
self.add_exception_handler(HTTPException, global_exception_handler) # 4xx/5xx HTTP 异常
|
|
@@ -306,6 +388,22 @@ class FoundationApp(FastAPI):
|
|
|
306
388
|
self._middlewares[middleware.name] = middleware
|
|
307
389
|
logger.debug(f"中间件已收集: {middleware.name}")
|
|
308
390
|
|
|
391
|
+
def _collect_plugins(self) -> None:
|
|
392
|
+
"""收集并实例化所有插件。
|
|
393
|
+
|
|
394
|
+
这一步在 super().__init__() 之前执行,只做实例化和过滤。
|
|
395
|
+
"""
|
|
396
|
+
for item in self.plugins:
|
|
397
|
+
# 支持类或实例
|
|
398
|
+
if isinstance(item, type):
|
|
399
|
+
plugin = item()
|
|
400
|
+
else:
|
|
401
|
+
plugin = item
|
|
402
|
+
|
|
403
|
+
if plugin.can_enable(self._config):
|
|
404
|
+
self._plugins[plugin.name] = plugin
|
|
405
|
+
logger.debug(f"插件已收集: {plugin.name}")
|
|
406
|
+
|
|
309
407
|
def _collect_components(self) -> None:
|
|
310
408
|
"""收集并实例化所有组件。
|
|
311
409
|
|
|
@@ -323,6 +421,32 @@ class FoundationApp(FastAPI):
|
|
|
323
421
|
self._components[component.name] = component
|
|
324
422
|
logger.debug(f"组件已收集: {component.name}")
|
|
325
423
|
|
|
424
|
+
def _install_plugins(self) -> None:
|
|
425
|
+
"""安装所有插件。
|
|
426
|
+
|
|
427
|
+
在 super().__init__() 之后、lifespan 之前同步调用。
|
|
428
|
+
此时 app 已创建,插件可以访问 app 实例。
|
|
429
|
+
"""
|
|
430
|
+
for plugin in self._plugins.values():
|
|
431
|
+
try:
|
|
432
|
+
plugin.install(self, self._config)
|
|
433
|
+
logger.info(f"插件已安装: {plugin.name}")
|
|
434
|
+
except Exception as e:
|
|
435
|
+
logger.warning(f"插件安装失败 ({plugin.name}): {e}")
|
|
436
|
+
|
|
437
|
+
def _uninstall_plugins(self) -> None:
|
|
438
|
+
"""卸载所有插件。
|
|
439
|
+
|
|
440
|
+
在 lifespan 关闭后同步调用。
|
|
441
|
+
"""
|
|
442
|
+
# 反序卸载
|
|
443
|
+
for plugin in reversed(list(self._plugins.values())):
|
|
444
|
+
try:
|
|
445
|
+
plugin.uninstall(self)
|
|
446
|
+
logger.debug(f"插件已卸载: {plugin.name}")
|
|
447
|
+
except Exception as e:
|
|
448
|
+
logger.warning(f"插件卸载失败 ({plugin.name}): {e}")
|
|
449
|
+
|
|
326
450
|
def _build_middlewares(self) -> list[StarletteMiddleware]:
|
|
327
451
|
"""构建所有中间件实例。
|
|
328
452
|
|
|
@@ -9,7 +9,7 @@ import importlib
|
|
|
9
9
|
import pkgutil
|
|
10
10
|
from typing import ClassVar
|
|
11
11
|
|
|
12
|
-
from aury.boot.application.app.base import Component, FoundationApp
|
|
12
|
+
from aury.boot.application.app.base import Component, FoundationApp, Plugin
|
|
13
13
|
from aury.boot.application.config import BaseConfig
|
|
14
14
|
from aury.boot.application.constants import ComponentName, ServiceType
|
|
15
15
|
from aury.boot.application.migrations import MigrationManager
|
|
@@ -18,6 +18,17 @@ from aury.boot.infrastructure.cache import CacheManager
|
|
|
18
18
|
from aury.boot.infrastructure.channel import ChannelManager
|
|
19
19
|
from aury.boot.infrastructure.database import DatabaseManager
|
|
20
20
|
from aury.boot.infrastructure.events import EventBusManager
|
|
21
|
+
from aury.boot.infrastructure.monitoring.alerting import (
|
|
22
|
+
AlertEventType,
|
|
23
|
+
AlertManager,
|
|
24
|
+
AlertSeverity,
|
|
25
|
+
emit_alert,
|
|
26
|
+
)
|
|
27
|
+
from aury.boot.infrastructure.monitoring.tracing import (
|
|
28
|
+
TelemetryConfig,
|
|
29
|
+
TelemetryProvider,
|
|
30
|
+
setup_otel_logging,
|
|
31
|
+
)
|
|
21
32
|
from aury.boot.infrastructure.mq import MQManager
|
|
22
33
|
from aury.boot.infrastructure.scheduler import SchedulerManager
|
|
23
34
|
from aury.boot.infrastructure.storage import StorageManager
|
|
@@ -48,9 +59,26 @@ class DatabaseComponent(Component):
|
|
|
48
59
|
pool_timeout=config.database.pool_timeout,
|
|
49
60
|
pool_recycle=config.database.pool_recycle,
|
|
50
61
|
)
|
|
62
|
+
# 在 engine 创建后进行 OTEL instrumentation
|
|
63
|
+
self._instrument_engine(db_manager, config)
|
|
51
64
|
except Exception as e:
|
|
52
65
|
logger.error(f"数据库初始化失败: {e}")
|
|
53
66
|
raise
|
|
67
|
+
|
|
68
|
+
def _instrument_engine(self, db_manager: DatabaseManager, config: BaseConfig) -> None:
|
|
69
|
+
"""对数据库引擎进行 OTEL instrumentation。"""
|
|
70
|
+
if not config.telemetry.enabled:
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
|
|
75
|
+
# async engine 需要使用 sync_engine
|
|
76
|
+
SQLAlchemyInstrumentor().instrument(engine=db_manager.engine.sync_engine)
|
|
77
|
+
logger.debug("SQLAlchemy engine instrumentation 已启用")
|
|
78
|
+
except ImportError:
|
|
79
|
+
logger.debug("SQLAlchemy instrumentation 未安装,跳过")
|
|
80
|
+
except Exception as e:
|
|
81
|
+
logger.warning(f"SQLAlchemy engine instrumentation 失败: {e}")
|
|
54
82
|
|
|
55
83
|
async def teardown(self, app: FoundationApp) -> None:
|
|
56
84
|
"""关闭数据库。"""
|
|
@@ -549,6 +577,193 @@ class ChannelComponent(Component):
|
|
|
549
577
|
logger.warning(f"通道 [{name}] 关闭失败: {e}")
|
|
550
578
|
|
|
551
579
|
|
|
580
|
+
class TelemetryPlugin(Plugin):
|
|
581
|
+
"""OpenTelemetry 插件。
|
|
582
|
+
|
|
583
|
+
在 app 创建后同步执行,负责:
|
|
584
|
+
- 初始化 TracerProvider
|
|
585
|
+
- 对 FastAPI app 进行 instrumentation
|
|
586
|
+
- 配置 AlertingSpanProcessor 检测慢请求/异常(告警配置从 ALERT__ 读取)
|
|
587
|
+
|
|
588
|
+
配置方式(环境变量):
|
|
589
|
+
TELEMETRY__ENABLED=true
|
|
590
|
+
TELEMETRY__TRACES_ENDPOINT=http://jaeger:4317 # 可选
|
|
591
|
+
TELEMETRY__LOGS_ENDPOINT=http://loki:3100 # 可选
|
|
592
|
+
TELEMETRY__METRICS_ENDPOINT=http://prometheus:9090 # 可选
|
|
593
|
+
|
|
594
|
+
# 告警相关配置从 ALERT__ 读取
|
|
595
|
+
ALERT__ENABLED=true
|
|
596
|
+
ALERT__SLOW_REQUEST_THRESHOLD=1.0
|
|
597
|
+
ALERT__SLOW_SQL_THRESHOLD=0.5
|
|
598
|
+
ALERT__ALERT_ON_SLOW_REQUEST=true
|
|
599
|
+
ALERT__ALERT_ON_SLOW_SQL=true
|
|
600
|
+
ALERT__ALERT_ON_ERROR=true
|
|
601
|
+
"""
|
|
602
|
+
|
|
603
|
+
name = "telemetry"
|
|
604
|
+
enabled = True
|
|
605
|
+
|
|
606
|
+
def can_enable(self, config: BaseConfig) -> bool:
|
|
607
|
+
"""仅当 TELEMETRY__ENABLED=true 时启用。"""
|
|
608
|
+
return self.enabled and config.telemetry.enabled
|
|
609
|
+
|
|
610
|
+
def install(self, app: FoundationApp, config: BaseConfig) -> None:
|
|
611
|
+
"""初始化 OpenTelemetry 并对 app 进行 instrumentation。"""
|
|
612
|
+
try:
|
|
613
|
+
# 创建告警回调(调用 alerting 模块,自动应用定制化规则)
|
|
614
|
+
alert_callback = self._create_alert_callback() if config.alert.enabled else None
|
|
615
|
+
|
|
616
|
+
telemetry_config = TelemetryConfig(
|
|
617
|
+
service_name=config.service.name,
|
|
618
|
+
service_version=config.service.version,
|
|
619
|
+
environment=config.service.environment,
|
|
620
|
+
instrument_fastapi=config.telemetry.instrument_fastapi,
|
|
621
|
+
instrument_sqlalchemy=config.telemetry.instrument_sqlalchemy,
|
|
622
|
+
instrument_httpx=config.telemetry.instrument_httpx,
|
|
623
|
+
# 告警配置全部从 AlertSettings 读取
|
|
624
|
+
alert_enabled=config.alert.enabled,
|
|
625
|
+
slow_request_threshold=config.alert.slow_request_threshold,
|
|
626
|
+
slow_sql_threshold=config.alert.slow_sql_threshold,
|
|
627
|
+
alert_on_slow_request=config.alert.alert_on_slow_request,
|
|
628
|
+
alert_on_slow_sql=config.alert.alert_on_slow_sql,
|
|
629
|
+
alert_on_error=config.alert.alert_on_error,
|
|
630
|
+
alert_callback=alert_callback,
|
|
631
|
+
traces_endpoint=config.telemetry.traces_endpoint,
|
|
632
|
+
traces_headers=config.telemetry.traces_headers,
|
|
633
|
+
logs_endpoint=config.telemetry.logs_endpoint,
|
|
634
|
+
logs_headers=config.telemetry.logs_headers,
|
|
635
|
+
metrics_endpoint=config.telemetry.metrics_endpoint,
|
|
636
|
+
metrics_headers=config.telemetry.metrics_headers,
|
|
637
|
+
sampling_rate=config.telemetry.sampling_rate,
|
|
638
|
+
)
|
|
639
|
+
|
|
640
|
+
# 创建并初始化 TelemetryProvider
|
|
641
|
+
provider = TelemetryProvider(telemetry_config)
|
|
642
|
+
success = provider.initialize()
|
|
643
|
+
|
|
644
|
+
if success:
|
|
645
|
+
# 保存到 app.state 以便 shutdown 时使用
|
|
646
|
+
app.state.telemetry_provider = provider
|
|
647
|
+
|
|
648
|
+
# 对已创建的 FastAPI app 进行 instrumentation
|
|
649
|
+
provider.instrument_fastapi_app(app)
|
|
650
|
+
|
|
651
|
+
# 配置 OTLP 日志导出(可选)
|
|
652
|
+
if config.telemetry.logs_endpoint:
|
|
653
|
+
self._setup_otlp_logging(
|
|
654
|
+
config.telemetry.logs_endpoint,
|
|
655
|
+
config.telemetry.logs_headers,
|
|
656
|
+
)
|
|
657
|
+
else:
|
|
658
|
+
logger.warning("OpenTelemetry 初始化失败(非关键)")
|
|
659
|
+
|
|
660
|
+
except ImportError as e:
|
|
661
|
+
logger.warning(f"OpenTelemetry 未安装,跳过初始化: {e}")
|
|
662
|
+
except Exception as e:
|
|
663
|
+
logger.warning(f"OpenTelemetry 初始化失败(非关键): {e}")
|
|
664
|
+
|
|
665
|
+
def _create_alert_callback(self):
|
|
666
|
+
"""创建告警回调函数。
|
|
667
|
+
|
|
668
|
+
回调会调用 alerting 模块的 emit_alert,自动应用 YAML 定制化规则。
|
|
669
|
+
"""
|
|
670
|
+
# 事件类型映射
|
|
671
|
+
type_mapping = {
|
|
672
|
+
"slow_request": AlertEventType.SLOW_REQUEST,
|
|
673
|
+
"slow_sql": AlertEventType.SLOW_SQL,
|
|
674
|
+
"exception": AlertEventType.EXCEPTION,
|
|
675
|
+
"custom": AlertEventType.CUSTOM,
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
# 严重级别映射
|
|
679
|
+
severity_mapping = {
|
|
680
|
+
"info": AlertSeverity.INFO,
|
|
681
|
+
"warning": AlertSeverity.WARNING,
|
|
682
|
+
"error": AlertSeverity.ERROR,
|
|
683
|
+
"critical": AlertSeverity.CRITICAL,
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
async def callback(event_type: str, message: str, **metadata):
|
|
687
|
+
try:
|
|
688
|
+
alert_event_type = type_mapping.get(event_type, AlertEventType.CUSTOM)
|
|
689
|
+
severity_str = metadata.pop("severity", "warning")
|
|
690
|
+
severity = severity_mapping.get(severity_str, AlertSeverity.WARNING)
|
|
691
|
+
|
|
692
|
+
await emit_alert(
|
|
693
|
+
alert_event_type,
|
|
694
|
+
message,
|
|
695
|
+
severity=severity,
|
|
696
|
+
**metadata,
|
|
697
|
+
)
|
|
698
|
+
except Exception as e:
|
|
699
|
+
logger.debug(f"发送告警失败: {e}")
|
|
700
|
+
|
|
701
|
+
return callback
|
|
702
|
+
|
|
703
|
+
def _setup_otlp_logging(
|
|
704
|
+
self,
|
|
705
|
+
endpoint: str,
|
|
706
|
+
headers: dict[str, str] | None = None,
|
|
707
|
+
) -> None:
|
|
708
|
+
"""配置 OTLP 日志导出。"""
|
|
709
|
+
try:
|
|
710
|
+
setup_otel_logging(endpoint=endpoint, headers=headers)
|
|
711
|
+
except Exception as e:
|
|
712
|
+
logger.warning(f"OTLP 日志导出配置失败: {e}")
|
|
713
|
+
|
|
714
|
+
def uninstall(self, app: FoundationApp) -> None:
|
|
715
|
+
"""关闭 OpenTelemetry。"""
|
|
716
|
+
try:
|
|
717
|
+
provider = getattr(app.state, "telemetry_provider", None)
|
|
718
|
+
if provider:
|
|
719
|
+
provider.shutdown()
|
|
720
|
+
logger.info("OpenTelemetry 已关闭")
|
|
721
|
+
except Exception as e:
|
|
722
|
+
logger.warning(f"OpenTelemetry 关闭失败: {e}")
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
class AlertComponent(Component):
|
|
726
|
+
"""告警系统组件。
|
|
727
|
+
|
|
728
|
+
在 lifespan 启动时异步初始化 AlertManager。
|
|
729
|
+
"""
|
|
730
|
+
|
|
731
|
+
name = "alert"
|
|
732
|
+
enabled = True
|
|
733
|
+
depends_on: ClassVar[list[str]] = []
|
|
734
|
+
|
|
735
|
+
def can_enable(self, config: BaseConfig) -> bool:
|
|
736
|
+
"""仅当 ALERT__ENABLED=true 时启用。"""
|
|
737
|
+
return self.enabled and config.alert.enabled
|
|
738
|
+
|
|
739
|
+
async def setup(self, app: FoundationApp, config: BaseConfig) -> None:
|
|
740
|
+
"""初始化告警管理器。"""
|
|
741
|
+
try:
|
|
742
|
+
alert_manager = AlertManager.get_instance()
|
|
743
|
+
if not alert_manager.is_initialized:
|
|
744
|
+
await alert_manager.initialize(
|
|
745
|
+
enabled=config.alert.enabled,
|
|
746
|
+
service_name=config.service.name,
|
|
747
|
+
rules_file=config.alert.rules_file,
|
|
748
|
+
defaults={
|
|
749
|
+
"slow_request_threshold": config.alert.slow_request_threshold,
|
|
750
|
+
"slow_sql_threshold": config.alert.slow_sql_threshold,
|
|
751
|
+
"aggregate_window": config.alert.aggregate_window,
|
|
752
|
+
"slow_request_aggregate": config.alert.slow_request_aggregate,
|
|
753
|
+
"slow_sql_aggregate": config.alert.slow_sql_aggregate,
|
|
754
|
+
"exception_aggregate": config.alert.exception_aggregate,
|
|
755
|
+
"suppress_seconds": config.alert.suppress_seconds,
|
|
756
|
+
},
|
|
757
|
+
notifiers=config.alert.get_notifiers(),
|
|
758
|
+
)
|
|
759
|
+
except Exception as e:
|
|
760
|
+
logger.warning(f"告警管理器初始化失败(非关键): {e}")
|
|
761
|
+
|
|
762
|
+
async def teardown(self, app: FoundationApp) -> None:
|
|
763
|
+
"""无需清理。"""
|
|
764
|
+
pass
|
|
765
|
+
|
|
766
|
+
|
|
552
767
|
class EventBusComponent(Component):
|
|
553
768
|
"""事件总线组件。
|
|
554
769
|
|
|
@@ -590,8 +805,14 @@ class EventBusComponent(Component):
|
|
|
590
805
|
logger.warning(f"事件总线 [{name}] 关闭失败: {e}")
|
|
591
806
|
|
|
592
807
|
|
|
808
|
+
# 设置默认插件
|
|
809
|
+
FoundationApp.plugins = [
|
|
810
|
+
TelemetryPlugin, # app 创建后立即 instrument
|
|
811
|
+
]
|
|
812
|
+
|
|
593
813
|
# 设置默认组件
|
|
594
814
|
FoundationApp.components = [
|
|
815
|
+
AlertComponent, # 最先初始化告警管理器
|
|
595
816
|
DatabaseComponent,
|
|
596
817
|
MigrationComponent,
|
|
597
818
|
AdminConsoleComponent,
|
|
@@ -607,6 +828,7 @@ FoundationApp.components = [
|
|
|
607
828
|
|
|
608
829
|
__all__ = [
|
|
609
830
|
"AdminConsoleComponent",
|
|
831
|
+
"AlertComponent",
|
|
610
832
|
"CacheComponent",
|
|
611
833
|
"ChannelComponent",
|
|
612
834
|
"DatabaseComponent",
|
|
@@ -616,5 +838,6 @@ __all__ = [
|
|
|
616
838
|
"SchedulerComponent",
|
|
617
839
|
"StorageComponent",
|
|
618
840
|
"TaskComponent",
|
|
841
|
+
"TelemetryPlugin",
|
|
619
842
|
]
|
|
620
843
|
|