feat: observe legacy facade method hits

This commit is contained in:
jxxghp
2026-08-23 00:37:03 +08:00
parent 2031f3c420
commit a001291c0b
5 changed files with 138 additions and 1 deletions
+2
View File
@@ -43,6 +43,7 @@ from app.adapters.system.plugin.manifest import (
load_dependency_manifest,
)
from app.runtime.log import logger
from app.runtime.observability import observe_compat_facade
from app.adapters.network.http import RequestUtils, AsyncRequestUtils
from app.foundation.singleton import WeakSingleton
@@ -165,6 +166,7 @@ def merge_plugin_market_repos(
return merged_repos
@observe_compat_facade("PluginHelper")
class PluginHelper(metaclass=WeakSingleton):
"""
插件市场管理,下载安装插件到本地
+2
View File
@@ -14,6 +14,7 @@ from app.foundation.crypto import RSAUtils
from app.foundation.singleton import Singleton
from app.foundation.version import compare_version
from app.runtime.log import logger
from app.runtime.observability import observe_compat_facade
from app.runtime.config import settings
from app.runtime.events import EventHandlerBinding, eventmanager
from app.runtime.reload import ConfigReloadMixin
@@ -120,6 +121,7 @@ def configure_plugin_catalog_factory(factory: PluginCatalogFactory) -> None:
_plugin_catalog_factory = factory
@observe_compat_facade("PluginManager")
class PluginManager(ConfigReloadMixin, metaclass=Singleton):
"""插件管理器"""
CONFIG_WATCH = {"DEV", "PLUGIN_AUTO_RELOAD", "PLUGIN_LOCAL_REPO_PATHS"}
+75 -1
View File
@@ -3,10 +3,12 @@
from __future__ import annotations
import time
import functools
import inspect
from contextlib import contextmanager
from dataclasses import dataclass
from enum import StrEnum
from typing import Iterator, Mapping, Protocol
from typing import Any, Callable, Iterator, Mapping, Protocol, TypeVar, cast
class MetricKind(StrEnum):
@@ -42,6 +44,11 @@ METRIC_SPECS = {
MetricKind.COUNTER,
frozenset({"method", "caller_type", "abi_source"}),
),
MetricSpec(
"compat.facade.hit",
MetricKind.COUNTER,
frozenset({"facade", "operation", "visibility", "abi_source"}),
),
MetricSpec("scheduler.job.duration", MetricKind.HISTOGRAM, frozenset({"owner", "outcome"})),
MetricSpec("scheduler.job.overlap_skip", MetricKind.COUNTER, frozenset({"owner"})),
MetricSpec("scheduler.job.retry", MetricKind.COUNTER, frozenset({"owner"})),
@@ -71,6 +78,73 @@ class NoopObservationPort:
_observation_port: ObservationPort = NoopObservationPort()
_FacadeClass = TypeVar("_FacadeClass", bound=type)
def observe_compat_facade(facade: str) -> Callable[[_FacadeClass], _FacadeClass]:
"""为旧 ABI Facade 的公开和私有方法记录低基数命中,不改变方法合同。"""
def decorate(cls: _FacadeClass) -> _FacadeClass:
for name, descriptor in tuple(vars(cls).items()):
if name.startswith("__") and name.endswith("__"):
continue
visibility = "private" if name.startswith("_") else "public"
if isinstance(descriptor, classmethod):
wrapped = _wrap_compat_method(
descriptor.__func__, facade, name, visibility
)
setattr(cls, name, classmethod(wrapped))
elif isinstance(descriptor, staticmethod):
wrapped = _wrap_compat_method(
descriptor.__func__, facade, name, visibility
)
setattr(cls, name, staticmethod(wrapped))
elif callable(descriptor):
setattr(
cls,
name,
_wrap_compat_method(descriptor, facade, name, visibility),
)
return cls
return decorate
def _wrap_compat_method(
method: Callable[..., Any],
facade: str,
operation: str,
visibility: str,
) -> Callable[..., Any]:
"""包装一个兼容方法并保持同步/异步调用形态及反射元数据。"""
if inspect.iscoroutinefunction(method):
@functools.wraps(method)
async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
record_metric(
"compat.facade.hit",
facade=facade,
operation=operation,
visibility=visibility,
abi_source="legacy_facade",
)
return await method(*args, **kwargs)
return cast(Callable[..., Any], async_wrapper)
@functools.wraps(method)
def wrapper(*args: Any, **kwargs: Any) -> Any:
record_metric(
"compat.facade.hit",
facade=facade,
operation=operation,
visibility=visibility,
abi_source="legacy_facade",
)
return method(*args, **kwargs)
return cast(Callable[..., Any], wrapper)
def configure_observation(port: ObservationPort | None) -> None:
"""由组合根替换进程级端口;None 明确恢复 no-op。"""