mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-04 23:17:20 +08:00
feat: observe legacy facade method hits
This commit is contained in:
Vendored
+2
@@ -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):
|
||||
"""
|
||||
插件市场管理,下载安装插件到本地
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -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。"""
|
||||
|
||||
Reference in New Issue
Block a user