otel-declarative 0.1.2__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 (44) hide show
  1. otel_declarative/Config/__init__.py +0 -0
  2. otel_declarative/Config/extraction_config.py +79 -0
  3. otel_declarative/Engines/Strategies/__init__.py +0 -0
  4. otel_declarative/Engines/Strategies/converter_strategies.py +120 -0
  5. otel_declarative/Engines/__init__.py +0 -0
  6. otel_declarative/Engines/converter_registry.py +140 -0
  7. otel_declarative/Engines/generic_extractor.py +96 -0
  8. otel_declarative/Engines/log_processors.py +191 -0
  9. otel_declarative/Engines/model_registry.py +90 -0
  10. otel_declarative/Engines/object_hydrator.py +126 -0
  11. otel_declarative/Engines/path_resolver.py +111 -0
  12. otel_declarative/Enums/__init__.py +0 -0
  13. otel_declarative/Enums/converter_types.py +42 -0
  14. otel_declarative/Enums/extraction_source.py +19 -0
  15. otel_declarative/Factories/__init__.py +0 -0
  16. otel_declarative/Factories/extractor_factory.py +156 -0
  17. otel_declarative/Infrastructure/__init__.py +0 -0
  18. otel_declarative/Infrastructure/async_log_engine.py +164 -0
  19. otel_declarative/Infrastructure/handlers.py +39 -0
  20. otel_declarative/Interfaces/__init__.py +0 -0
  21. otel_declarative/Interfaces/extractor.py +50 -0
  22. otel_declarative/Logging/__init__.py +0 -0
  23. otel_declarative/Logging/logger_factory.py +185 -0
  24. otel_declarative/Models/Log/__init__.py +0 -0
  25. otel_declarative/Models/Log/constants.py +47 -0
  26. otel_declarative/Models/Log/context.py +105 -0
  27. otel_declarative/Models/Log/mapping.py +94 -0
  28. otel_declarative/Models/Log/state.py +59 -0
  29. otel_declarative/Models/Log/topology.py +202 -0
  30. otel_declarative/Models/__init__.py +0 -0
  31. otel_declarative/Models/engine_states.py +135 -0
  32. otel_declarative/Models/mapping_models.py +111 -0
  33. otel_declarative/Models/summary_models.py +104 -0
  34. otel_declarative/Reporters/__init__.py +0 -0
  35. otel_declarative/Reporters/structured_reporter.py +154 -0
  36. otel_declarative/__init__.py +13 -0
  37. otel_declarative/constants.py +79 -0
  38. otel_declarative/provider.py +217 -0
  39. otel_declarative/settings.py +150 -0
  40. otel_declarative-0.1.2.dist-info/METADATA +72 -0
  41. otel_declarative-0.1.2.dist-info/RECORD +44 -0
  42. otel_declarative-0.1.2.dist-info/WHEEL +5 -0
  43. otel_declarative-0.1.2.dist-info/licenses/LICENSE +21 -0
  44. otel_declarative-0.1.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,90 @@
1
+ import importlib
2
+ import inspect
3
+ from typing import Dict, Optional, Type, List
4
+ from pydantic import BaseModel
5
+ from otel_declarative.Logging.logger_factory import get_child_logger
6
+
7
+ logger = get_child_logger("otel_declarative.Engines", "ModelRegistry")
8
+
9
+ class ModelRegistry:
10
+ """
11
+ 业务观测模型注册中心
12
+
13
+ 职责:
14
+ 1、集中管理所有可用于声明式装配的 Pydantic 模型
15
+ 2、实现从模型名称到类定义的动态检索, 支持配置驱动的对象实例化
16
+ 3、提供自动发现机制, 减少手动注册的维护成本
17
+ """
18
+ def __init__(self):
19
+ self._models: Dict[str, Type[BaseModel]] = {}
20
+ self._manual_models: List[Type[BaseModel]] = []
21
+ self._is_initialized: bool = False
22
+
23
+ def register(self, model_class: Type[BaseModel]) -> None:
24
+ """
25
+ 手动注册一个 Pydantic 模型类
26
+
27
+ :param model_class: 继承自 pydantic.BaseModel 的类定义
28
+ """
29
+ if not issubclass(model_class, BaseModel):
30
+ logger.warning(f"类 {model_class.__name__} 不是有效的 Pydantic BaseModel, 忽略注册")
31
+ return
32
+
33
+ model_name = model_class.__name__
34
+ self._models[model_name] = model_class
35
+
36
+ if model_class not in self._manual_models:
37
+ self._manual_models.append(model_class)
38
+
39
+ logger.debug(f"已手动注册观测模型: {model_name}")
40
+
41
+ def discover_models(self, module_paths: List[str]) -> None:
42
+ """
43
+ 扫描指定模块路径下的所有 Pydantic 模型并注册
44
+
45
+ :param module_paths: 完整的 Python 模块路径列表 (例如 ['Core.Infrastructure.otel_declarative.Models'])
46
+ """
47
+ for path in module_paths:
48
+ try:
49
+ module = importlib.import_module(path)
50
+ for name, obj in inspect.getmembers(module, inspect.isclass):
51
+ if issubclass(obj, BaseModel) and obj is not BaseModel:
52
+ self._models[name] = obj
53
+ logger.info(f"自动发现并注册观测模型: {name} (来自 {path})")
54
+ except ImportError:
55
+ logger.exception(f"无法导入模块执行模型自动发现: {path}")
56
+ except Exception:
57
+ logger.exception(f"模型自动发现过程中发生未预期异常")
58
+ self._is_initialized = True
59
+
60
+ def get_model(self, model_name: str) -> Optional[Type[BaseModel]]:
61
+ """
62
+ 根据名称检索模型类定义
63
+
64
+ :param model_name: 模型类名
65
+ :return: Pydantic 类定义或 None
66
+ """
67
+ return self._models.get(model_name)
68
+
69
+ def list_registered_models(self) -> List[str]:
70
+ """
71
+ 获取当前所有已注册模型的名称列表
72
+ """
73
+ return list(self._models.keys())
74
+
75
+ def get_manual_models(self) -> List[Type[BaseModel]]:
76
+ """
77
+ 获取所有手动注册的模型类定义, 用于热重载快照
78
+
79
+ :return: 手动注册的模型类列表
80
+ """
81
+ return self._manual_models
82
+
83
+ def reset(self) -> None:
84
+ """
85
+ 重置注册中心状态
86
+ """
87
+ self._models.clear()
88
+ self._manual_models.clear()
89
+ self._is_initialized = False
90
+ logger.info("模型注册中心已重置")
@@ -0,0 +1,126 @@
1
+ import copy
2
+ from pydantic import BaseModel, ValidationError
3
+ from typing import Any, Optional, Type, TypeVar, Union, List, Set
4
+ from otel_declarative.Engines.model_registry import ModelRegistry
5
+ from otel_declarative.Models.mapping_models import FieldMapping
6
+ from otel_declarative.Logging.logger_factory import get_child_logger
7
+
8
+ logger = get_child_logger("otel_declarative.Engines", "ObjectHydrator")
9
+
10
+ T = TypeVar("T", bound=BaseModel)
11
+
12
+ class ObjectHydrator:
13
+ """
14
+ 对象装配引擎
15
+
16
+ 职责:
17
+ 1、模型检索: 根据规则从 ModelRegistry 获取类定义
18
+ 2、结构映射: 将原始字典或列表转化为强类型 Pydantic 对象
19
+ 3、熔断保护: 在装配失败时提供防御性降级, 确保不干扰主流程
20
+ """
21
+ def __init__(self, model_registry: ModelRegistry):
22
+ """
23
+ :param model_registry: 模型注册中心实例
24
+ """
25
+ self._model_registry = model_registry
26
+ self._reported_errors: Set[str] = set()
27
+
28
+ def hydrate(self, raw_data: Any, mapping: FieldMapping) -> Any:
29
+ """
30
+ 执行声明式装配流
31
+
32
+ :param raw_data: 由解析内核提取出的原始数据片段
33
+ :param mapping: 包含装配指令的映射规则模型
34
+ :return: 实例化后的对象 / 列表, 或在失败时返回 mapping.default
35
+ """
36
+ # 1、前置校验: 若未声明 model_name, 则不执行装配
37
+ if not mapping.model_name:
38
+ return raw_data
39
+
40
+ # 2、检索模型类
41
+ model_class: Optional[Type[BaseModel]] = self._model_registry.get_model(mapping.model_name)
42
+ if not model_class:
43
+ logger.warning(f"装配异常 | 找不到已注册的模型: {mapping.model_name}")
44
+ return self._safe_get_default(mapping.default)
45
+
46
+ # 3、分发装配策略
47
+ try:
48
+ if mapping.is_list:
49
+ return self._hydrate_list(data=raw_data, model_class=model_class, default=mapping.default)
50
+ return self._hydrate_single(data=raw_data, model_class=model_class, default=mapping.default)
51
+ except Exception:
52
+ logger.exception(f"装配引擎执行崩溃, 出现未预期错误")
53
+ return self._safe_get_default(mapping.default)
54
+
55
+ def _hydrate_single(self, data: Any, model_class: Type[T], default: Any) -> Union[T, Any]:
56
+ """
57
+ 执行单对象装配逻辑
58
+
59
+ :param data: 原始字典数据
60
+ :param model_class: 目标 Pydantic 类
61
+ :param default: 失败回退值
62
+ :return: 强类型实例
63
+ """
64
+ model_name: str = model_class.__name__
65
+
66
+ if not isinstance(data, dict):
67
+ logger.debug(f"装配失败 | 模型 {model_name} 需要字典, 但收到 {type(data).__name__}")
68
+ return self._safe_get_default(default)
69
+
70
+ try:
71
+ return model_class.model_validate(data)
72
+ except ValidationError as e:
73
+ fingerprint = f"{model_name}:{len(e.errors())}"
74
+ if fingerprint not in self._reported_errors:
75
+ logger.warning(f"模型校验不通过 | 模型: {model_name}", exc_info=True)
76
+ self._reported_errors.add(fingerprint)
77
+ return self._safe_get_default(default)
78
+
79
+ def _hydrate_list(self, data: Any, model_class: Type[T], default: Any) -> Union[List[T], Any]:
80
+ """
81
+ 执行对象列表的循环迭代装配, 支持部分元素解析失败的容错机制
82
+
83
+ :param data: 原始列表数据
84
+ :param model_class: 目标 Pydantic 类
85
+ :param default: 回退失败值
86
+ :return: 强类型实例列表
87
+ """
88
+ # --- 1、前置类型守卫: 确保输出必须是列表, 否则立即执行断路回退 ---
89
+ if not isinstance(data, list):
90
+ logger.debug(f"装配失败 | 列表模式需要 list, 但收到 {type(data).__name__} | 路径已回退至默认值")
91
+ return self._safe_get_default(default)
92
+
93
+ # --- 2、容错迭代装配逻辑 ---
94
+ hydrated_list: List[T] = []
95
+ model_name: str = model_class.__name__
96
+ for index, item in enumerate(data):
97
+ if not isinstance(item, dict):
98
+ logger.warning(f"列表元素装配跳过 | 索引 {index} 处元素预期为 dict, 实际为 {type(item).__name__}")
99
+ continue
100
+
101
+ try:
102
+ # 执行 Pydantic 强类型校验与转换
103
+ hydrated_instance: T = model_class.model_validate(item)
104
+ hydrated_list.append(hydrated_instance)
105
+ except ValidationError as e:
106
+ error_count: int = len(e.errors())
107
+ fingerprint: str = f"{model_name}:list_err:{error_count}"
108
+ if fingerprint not in self._reported_errors:
109
+ logger.warning(f"列表元素装配跳过 | 模型: {model_name} | 索引: {index}", exc_info=True)
110
+ self._reported_errors.add(fingerprint)
111
+
112
+ return hydrated_list
113
+
114
+ def _safe_get_default(self, default_value: Any) -> Any:
115
+ """
116
+ 安全获取默认回退值, 针对可变对象执行深度复制以防止跨 Span 污染
117
+
118
+ :param default_value: 映射规则中定义的原始默认值
119
+ :return: 深度复制后的值或原始值
120
+ """
121
+ try:
122
+ if isinstance(default_value, (dict, list)):
123
+ return copy.deepcopy(default_value)
124
+ return default_value
125
+ except Exception:
126
+ return default_value
@@ -0,0 +1,111 @@
1
+ import os
2
+ import jmespath
3
+ from jmespath import exceptions
4
+ from typing import Any, Dict
5
+ from otel_declarative.Enums.extraction_source import ExtractionSource
6
+ from otel_declarative.Models.mapping_models import FieldMapping
7
+ from otel_declarative.Engines.converter_registry import ConverterRegistry
8
+ from otel_declarative.Engines.object_hydrator import ObjectHydrator
9
+ from otel_declarative.Logging.logger_factory import get_child_logger
10
+
11
+ logger = get_child_logger("otel_declarative.Engines", "PathResolver")
12
+
13
+ class PathResolver:
14
+ """
15
+ 路径解析引擎
16
+
17
+ 职责:
18
+ 1、基于 JMESPath 标准在业务上下文中定位原始数据
19
+ 2、协调 ConverterRegistry 执行声明式的数据清洗与格式化
20
+ 3、提供全链路熔断保护, 确保解析失败时安全回退至默认值
21
+ """
22
+
23
+ def __init__(self, converter_registry: ConverterRegistry, object_hydrator: ObjectHydrator):
24
+ """
25
+ :param converter_registry: 注入的转换器注册表实例
26
+ :param object_hydrator: 对象装配引擎
27
+ """
28
+ self._converter_registry = converter_registry
29
+ self._object_hydrator = object_hydrator
30
+ self._env_snapshot = dict(os.environ)
31
+ self._compiled_expressions: Dict[str, Any] = {}
32
+
33
+ def resolve(self, context: Dict[str, Any], mapping: FieldMapping) -> Any:
34
+ """
35
+ 根据映射规则从执行上下文中解析目标值
36
+
37
+ :param context: 包含执行现场数据的字典
38
+ :param mapping: 强类型的字段映射规则模型
39
+ :return: 最终解析并转换后的值
40
+ """
41
+ try:
42
+ # 1、构造统一解析上下文
43
+ search_context: Dict[str, Any] = self._build_search_context(context)
44
+ # 2、调用 JMESPath 执行全量搜索
45
+ current_val: Any = self._apply_jmes_search(search_context, mapping.path)
46
+ if current_val is None:
47
+ return mapping.default
48
+
49
+ # 3、执行声明式转换与熔断保护
50
+ if mapping.converter:
51
+ current_val = self._converter_registry.convert(
52
+ name=mapping.converter,
53
+ value=current_val,
54
+ default=mapping.default,
55
+ )
56
+ # 4、执行递归装配逻辑
57
+ if mapping.model_name and current_val is not None:
58
+ return self._object_hydrator.hydrate(
59
+ raw_data=current_val,
60
+ mapping=mapping
61
+ )
62
+ return current_val if current_val is not None else mapping.default
63
+ except Exception:
64
+ logger.exception(f"路径解析失败 | 路径: {mapping.path}")
65
+ return mapping.default
66
+
67
+ def _build_search_context(self, raw_context: Dict[str, Any]) -> Dict[str, Any]:
68
+ """
69
+ 将零散的执行现场组装为统一的观测命名空间
70
+
71
+ :param raw_context: 装饰器捕获的原始上下文
72
+ :return: 扁平化的全量检索字典
73
+ """
74
+ # 预先提取位置参数以优化类型嗅探性能
75
+ args_raw: tuple = raw_context.get(ExtractionSource.ARGS.value, ())
76
+ args: list = list(args_raw) if args_raw is not None else []
77
+
78
+ return {
79
+ # 位置参数源
80
+ ExtractionSource.ARGS.value: args,
81
+ # 关键字参数源
82
+ ExtractionSource.KWARGS.value: raw_context.get(ExtractionSource.KWARGS.value, {}),
83
+ # 执行结果源
84
+ ExtractionSource.RESULTS.value: raw_context.get(ExtractionSource.RESULTS.value),
85
+ # 环境变量源
86
+ ExtractionSource.ENV.value: self._env_snapshot,
87
+ # 动态类型源
88
+ ExtractionSource.TYPE.value: {
89
+ "args": [type(a).__name__ for a in args] if args else [],
90
+ }
91
+ }
92
+
93
+ def _apply_jmes_search(self, root_obj: Any, expression: str) -> Any:
94
+ """
95
+ 调用 JMESPath 库执行路径检索
96
+
97
+ :param root_obj: 搜索起始对象
98
+ :param expression: JMESPath 表达式
99
+ :return: 检索到的原始数据
100
+ """
101
+ if not expression:
102
+ return root_obj
103
+
104
+ try:
105
+ if expression not in self._compiled_expressions:
106
+ self._compiled_expressions[expression] = jmespath.compile(expression)
107
+ compiled_program = self._compiled_expressions[expression]
108
+ return compiled_program.search(root_obj)
109
+ except (exceptions.JMESPathError, Exception):
110
+ logger.exception(f"JMESPath 语法错误 | 表达式: {expression}")
111
+ return None
File without changes
@@ -0,0 +1,42 @@
1
+ from enum import Enum, unique
2
+
3
+ @unique
4
+ class ConverterCategory(str, Enum):
5
+ """
6
+ 转换器逻辑分类
7
+ """
8
+ # 基础类型
9
+ PRIMITIVE = "primitive"
10
+ # 时间处理
11
+ TEMPORAL = "temporal"
12
+ # 格式化
13
+ FORMAT = "format"
14
+ # 结构重塑
15
+ STRUCTURAL = "structural"
16
+ # 语义校验
17
+ SEMANTIC = "semantic"
18
+
19
+ @unique
20
+ class StandardConverter(str, Enum):
21
+ """
22
+ 标准转换器标识符
23
+ """
24
+ # Primitive
25
+ TO_STR = "to_str"
26
+ TO_INT = "to_int"
27
+ TO_BOOL = "to_bool"
28
+
29
+ # Temporal
30
+ TO_DATETIME = "to_datetime"
31
+ TO_TIMESTAMP = "to_timestamp"
32
+
33
+ # Format
34
+ HUMAN_SIZE = "human_size"
35
+ HUMAN_DURATION = "human_duration"
36
+
37
+ # Structural
38
+ GLOM_TRANSFORM = "glom_transform"
39
+
40
+ # Semantic
41
+ TO_IP = "to_ip"
42
+ TO_UUID = "to_uuid"
@@ -0,0 +1,19 @@
1
+ from enum import Enum, unique
2
+
3
+ @unique
4
+ class ExtractionSource(str, Enum):
5
+ """
6
+ 提取来源枚举
7
+
8
+ 定义路径解析引擎 (PathResolver) 可访问的顶级命名空间
9
+ """
10
+ # 函数位置参数列表 (元组)
11
+ ARGS = "args"
12
+ # 函数关键字参数字典
13
+ KWARGS = "kwargs"
14
+ # 业务函数执行后的返回值
15
+ RESULTS = "results"
16
+ # 系统环境变量 (os.environ)
17
+ ENV = "env"
18
+ # 对象的 Python 类型信息 (用于类型嗅探)
19
+ TYPE = "type"
File without changes
@@ -0,0 +1,156 @@
1
+ from typing import Any, Dict, Optional, List, Type
2
+ from otel_declarative.Models.summary_models import BaseModel
3
+ from otel_declarative.Models.engine_states import ObservabilityEngineState
4
+ from otel_declarative.settings import ObservabilitySettings
5
+ from otel_declarative.Config.extraction_config import ObservabilityMappingConfig
6
+ from otel_declarative.Interfaces.extractor import IExtractor
7
+ from otel_declarative.Engines.generic_extractor import GenericExtractor
8
+ from otel_declarative.Engines.path_resolver import PathResolver
9
+ from otel_declarative.Engines.converter_registry import ConverterRegistry
10
+ from otel_declarative.Engines.model_registry import ModelRegistry
11
+ from otel_declarative.Engines.object_hydrator import ObjectHydrator
12
+ from otel_declarative.Logging.logger_factory import get_child_logger
13
+
14
+ logger = get_child_logger("otel_declarative.Factories", "ExtractorFactory")
15
+
16
+ class ExtractorFactory:
17
+ """
18
+ 元数据提取器策略工厂
19
+
20
+ 职责:
21
+ 1、依赖编排: 负责初始化并注入观测基础设施的所有依赖
22
+ 2、策略管理: 实现基于逻辑层级的 GenericExtractor 实例生命周期维护
23
+ 3、声明式驱动: 启动时加载 YAML 配置并预热所有层级的提取规则
24
+ 4、零开销分发: 提供毫秒级的单例策略分发能力
25
+ """
26
+ def __init__(self, settings: ObservabilitySettings):
27
+ """
28
+ :param settings: 观测性全局配置对象
29
+ """
30
+ self._settings: ObservabilitySettings = settings
31
+ self._converter_registry: ConverterRegistry = ConverterRegistry(settings)
32
+ self._state: ObservabilityEngineState = ObservabilityEngineState.create_empty()
33
+ self._bootstrap_strategies()
34
+
35
+ def _bootstrap_strategies(self) -> None:
36
+ """
37
+ 执行观测系统的启动引导流
38
+
39
+ 职责:
40
+ 1、扫描业务模型并预热注册中心
41
+ 2、加载 YAML 映射配置
42
+ 3、执行 Fail-safe 策略: 即使引导失败也不抛出异常, 确保主业务可用
43
+ """
44
+ try:
45
+ logger.info(f"正在引导观测性提取引擎策略")
46
+
47
+ # 构造影子组件
48
+ model_registry: ModelRegistry = ModelRegistry()
49
+ model_registry.discover_models(self._settings.model_scan_paths)
50
+ mapping_config: ObservabilityMappingConfig = ObservabilityMappingConfig.load_from_yaml(self._settings.mapping_config_path)
51
+ hydrator: ObjectHydrator = ObjectHydrator(model_registry)
52
+ resolver: PathResolver = PathResolver(converter_registry=self._converter_registry, object_hydrator=hydrator)
53
+
54
+ # 预热提取器实例映射
55
+ extractors: Dict[str, IExtractor] = {
56
+ layer_name: GenericExtractor(layer=layer_name, rules=rules, resolver=resolver)
57
+ for layer_name, rules in mapping_config.layer.items()
58
+ }
59
+
60
+ # 原子切换至就绪状态
61
+ self._state = ObservabilityEngineState(
62
+ model_registry=model_registry,
63
+ object_hydrator=hydrator,
64
+ path_resolver=resolver,
65
+ extractors=extractors,
66
+ is_ready=True
67
+ )
68
+ logger.info(f"观测性策略工厂引导完成,已就绪层级: {list(extractors.keys())}")
69
+ except Exception as e:
70
+ self._state = ObservabilityEngineState.create_empty()
71
+ logger.exception(
72
+ f"观测性策略工厂引导失败 | 错误原因: {str(e)} | 系统将进入 '零观测' 模式运行"
73
+ )
74
+
75
+ def get_extractor(self, layer: str, payload: Optional[Any] = None) -> Optional[IExtractor]:
76
+ """
77
+ [核心入口] 根据上下文获取匹配的提取器
78
+
79
+ :param layer: 装饰器声明的逻辑层级标识
80
+ :param payload: (可选) 运行时业务载体, 用于未来的动态启发式匹配
81
+ :return: 配置化的提取器实例, 若层级未定义则返回 None
82
+ """
83
+ current_snapshot: ObservabilityEngineState = self._state
84
+ return current_snapshot.get_extractor_for_layer(layer)
85
+
86
+ def reload(self) -> None:
87
+ """
88
+ 执行观测策略的热重载逻辑
89
+
90
+ 逻辑:
91
+ 1、构造局部影子容器, 隔离重载过程中的中间状态
92
+ 2、在影子容器中执行全量引导流 (模型发现 > 配置加载 > 实例预热)
93
+ 3、校验成功后执行原子引用替换
94
+ 4、若任何环节失败, 丢弃影子容器并保留原始引用, 确保旧策略完整
95
+ """
96
+ logger.info(f"观测性策略工厂正在尝试热重载")
97
+
98
+ old_state: ObservabilityEngineState = self._state
99
+ manual_models: List[Type[BaseModel]] = old_state.model_registry.get_manual_models()
100
+
101
+ try:
102
+ # --- 1、构造影子内核 ---
103
+ temp_model_registry: ModelRegistry = ModelRegistry()
104
+ temp_model_registry.discover_models(self._settings.model_scan_paths)
105
+ # 恢复手动注册的模型
106
+ for model_cls in manual_models:
107
+ temp_model_registry.register(model_cls)
108
+
109
+ # --- 2、加载并校验 YAML 规则 ---
110
+ mapping_config: ObservabilityMappingConfig = ObservabilityMappingConfig.load_from_yaml(
111
+ self._settings.mapping_config_path
112
+ )
113
+
114
+ # --- 3、组装影子解析链路 ---
115
+ temp_hydrator: ObjectHydrator = ObjectHydrator(temp_model_registry)
116
+ temp_resolver: PathResolver = PathResolver(converter_registry=self._converter_registry, object_hydrator=temp_hydrator)
117
+
118
+ # --- 4、预实例化所有提取器 ---
119
+ temp_extractors: Dict[str, IExtractor] = {
120
+ layer_name: GenericExtractor(layer=layer_name, rules=rules, resolver=temp_resolver)
121
+ for layer_name, rules in mapping_config.layer.items()
122
+ }
123
+
124
+ self._state = ObservabilityEngineState(
125
+ model_registry=temp_model_registry,
126
+ object_hydrator=temp_hydrator,
127
+ path_resolver=temp_resolver,
128
+ extractors=temp_extractors,
129
+ is_ready=True
130
+ )
131
+ logger.info(
132
+ f"观测性策略热重载成功,已动态更新层级: {list(temp_extractors.keys())}"
133
+ f"已注册模型数: {len(temp_model_registry.list_registered_models())}"
134
+ )
135
+ except Exception:
136
+ logger.exception(
137
+ f"观测性策略热重载失败 | "
138
+ f"操作建议: 请检查 YAML 语法或 Pydantic 模型定义 | 已执行 Fail-back,保留旧策略运行"
139
+ )
140
+
141
+ @property
142
+ def is_healthy(self) -> bool:
143
+ """
144
+ 暴露工厂的运行健康状态
145
+ """
146
+ current_snapshot: ObservabilityEngineState = self._state
147
+ return current_snapshot.is_ready and len(current_snapshot.extractors) > 0
148
+
149
+ @property
150
+ def model_registry(self) -> ModelRegistry:
151
+ """
152
+ 暴露当前活跃的模型注册中心引用
153
+
154
+ :return: 当前活跃的 ModelRegistry 实例
155
+ """
156
+ return self._state.model_registry
File without changes