Title: Implement minimal core framework with PL injection and update build config
Key features implemented: - Updated package metadata and dependencies in PKG-INFO, setup files - Added main.py entry point for backward compatibility with README launch method - Enhanced CLI with config options, system info command, and proper signal handling - Implemented minimal PluginManager loading only plugin-loader core plugin - Refactored PluginLoader to follow minimal core design, removed sandbox/isolation complexity - Updated auto-dependency plugin with safer PL injection mechanism and disabled pl_injection - Removed legacy plugin files (firewall, frp_proxy, ftp_server, multi_lang_deploy, ops_toolbox, security_gateway) as functionality moved to core plugin system - Improved gitignore with comprehensive ignore patterns The changes implement a minimal core framework design where only the plugin-loader is directly loaded by the core, with all other plugins managed through the PL injection mechanism, significantly simplifying the architecture.
This commit is contained in:
8
store/@{FutureOSS}/plugin-loader-pro/SIGNATURE
Normal file
8
store/@{FutureOSS}/plugin-loader-pro/SIGNATURE
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"signature": "j3U1ZFmpc+pOBC8auYyj84O9DMaAmhOx7F0yGIdrpnclTvteuuXDa7qdBduF+cTu7JStUxN9Yx4oA8dZkorvCZgShQ26jWgLxTAUpa74Pqv6b1q1KQVGcgmiIcF5spIu3zNH4R2tfAWidm7Jncmd2BDDrjVMg16d6Bk73fvMN8GajAaNt3PELIr55LFEER3mOMB9ooeuvUmr7EIoDvZap5bLO4iP88kZaKd6xArNhYi5sCgm4HOxKxUFBOLRAnmJFcOKTqGLL0kYwsoqiN1UPLEawndQKNyX47ZQRfKCut8qQZEPpXl4rYpI6j++Lw7NNrj/jX+IEWFpqMaXiumJAG3tDWKWd5I/7/CAOpttERooJEjG2tVyM2ka9HjIyrc4TrWD9DZTamwkRlrbWm0Q7soTn3O6ZkolQ2n/WUxWKu1o84OHkeeoXDg9AS/uiKsOf7ufTpL7doXUm4bj4xTNkPk63D5PlAoF/kLBgcLHo2UkdxYhv9Y/moig2ogqr//nU5ucIZLmGIIX2Bag8RKgwnhRnKZ+KIGJntIuOoAuoH1H3G/EV42/siqU/AsRSOBtCxhAoqBxaHzZMnyios8kguE/6BfIEs7yS4DzN2ANNcA6tXfbvWGq7oeEB2DBAdamPbyVB76rSsdi0/4zGugvXmBJO4yZuxcuu/HeBH7ES+0=",
|
||||
"signer": "FutureOSS",
|
||||
"algorithm": "RSA-SHA256",
|
||||
"timestamp": 1775964226.5213168,
|
||||
"plugin_hash": "bed620b64c10798828613a45e3227a7849a9a450e471dfd009135354fb650a1e",
|
||||
"author": "FutureOSS"
|
||||
}
|
||||
64
store/@{FutureOSS}/plugin-loader-pro/circuit/breaker.py
Normal file
64
store/@{FutureOSS}/plugin-loader-pro/circuit/breaker.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""熔断器实现"""
|
||||
import time
|
||||
from typing import Callable, Any
|
||||
from .state import CircuitState
|
||||
|
||||
|
||||
class CircuitBreaker:
|
||||
"""熔断器"""
|
||||
|
||||
def __init__(self, failure_threshold: int = 3, recovery_timeout: int = 60, half_open_requests: int = 1):
|
||||
self.failure_threshold = failure_threshold
|
||||
self.recovery_timeout = recovery_timeout
|
||||
self.half_open_requests = half_open_requests
|
||||
|
||||
self.state = CircuitState.CLOSED
|
||||
self.failure_count = 0
|
||||
self.success_count = 0
|
||||
self.last_failure_time = 0
|
||||
self.half_open_calls = 0
|
||||
|
||||
def call(self, func: Callable, *args, **kwargs) -> Any:
|
||||
"""执行调用"""
|
||||
if self.state == CircuitState.OPEN:
|
||||
if time.time() - self.last_failure_time >= self.recovery_timeout:
|
||||
self.state = CircuitState.HALF_OPEN
|
||||
self.half_open_calls = 0
|
||||
else:
|
||||
raise Exception("熔断器已打开,调用被拒绝")
|
||||
|
||||
try:
|
||||
result = func(*args, **kwargs)
|
||||
self._on_success()
|
||||
return result
|
||||
except Exception as e:
|
||||
self._on_failure()
|
||||
raise
|
||||
|
||||
def _on_success(self):
|
||||
"""成功回调"""
|
||||
self.failure_count = 0
|
||||
if self.state == CircuitState.HALF_OPEN:
|
||||
self.half_open_calls += 1
|
||||
if self.half_open_calls >= self.half_open_requests:
|
||||
self.state = CircuitState.CLOSED
|
||||
self.half_open_calls = 0
|
||||
|
||||
def _on_failure(self):
|
||||
"""失败回调"""
|
||||
self.failure_count += 1
|
||||
self.last_failure_time = time.time()
|
||||
|
||||
if self.state == CircuitState.HALF_OPEN:
|
||||
self.state = CircuitState.OPEN
|
||||
elif self.failure_count >= self.failure_threshold:
|
||||
self.state = CircuitState.OPEN
|
||||
|
||||
def reset(self):
|
||||
"""重置熔断器"""
|
||||
self.state = CircuitState.CLOSED
|
||||
self.failure_count = 0
|
||||
self.half_open_calls = 0
|
||||
|
||||
def get_state(self) -> str:
|
||||
return self.state
|
||||
8
store/@{FutureOSS}/plugin-loader-pro/circuit/state.py
Normal file
8
store/@{FutureOSS}/plugin-loader-pro/circuit/state.py
Normal file
@@ -0,0 +1,8 @@
|
||||
"""熔断器状态枚举"""
|
||||
|
||||
|
||||
class CircuitState:
|
||||
"""熔断器状态"""
|
||||
CLOSED = "closed" # 正常状态
|
||||
OPEN = "open" # 熔断状态
|
||||
HALF_OPEN = "half_open" # 半开状态
|
||||
56
store/@{FutureOSS}/plugin-loader-pro/core/config.py
Normal file
56
store/@{FutureOSS}/plugin-loader-pro/core/config.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""Pro 配置模型"""
|
||||
|
||||
|
||||
class CircuitBreakerConfig:
|
||||
"""熔断器配置"""
|
||||
def __init__(self, config: dict = None):
|
||||
config = config or {}
|
||||
self.failure_threshold = config.get("failure_threshold", 3)
|
||||
self.recovery_timeout = config.get("recovery_timeout", 60)
|
||||
self.half_open_requests = config.get("half_open_requests", 1)
|
||||
|
||||
|
||||
class RetryConfig:
|
||||
"""重试配置"""
|
||||
def __init__(self, config: dict = None):
|
||||
config = config or {}
|
||||
self.max_retries = config.get("max_retries", 3)
|
||||
self.backoff_factor = config.get("backoff_factor", 2)
|
||||
self.initial_delay = config.get("initial_delay", 1)
|
||||
|
||||
|
||||
class HealthCheckConfig:
|
||||
"""健康检查配置"""
|
||||
def __init__(self, config: dict = None):
|
||||
config = config or {}
|
||||
self.interval = config.get("interval", 30)
|
||||
self.timeout = config.get("timeout", 5)
|
||||
self.max_failures = config.get("max_failures", 5)
|
||||
|
||||
|
||||
class AutoRecoveryConfig:
|
||||
"""自动恢复配置"""
|
||||
def __init__(self, config: dict = None):
|
||||
config = config or {}
|
||||
self.enabled = config.get("enabled", True)
|
||||
self.max_attempts = config.get("max_attempts", 3)
|
||||
self.delay = config.get("delay", 10)
|
||||
|
||||
|
||||
class IsolationConfig:
|
||||
"""隔离配置"""
|
||||
def __init__(self, config: dict = None):
|
||||
config = config or {}
|
||||
self.enabled = config.get("enabled", True)
|
||||
self.timeout_per_plugin = config.get("timeout_per_plugin", 30)
|
||||
|
||||
|
||||
class ProConfig:
|
||||
"""Pro 总配置"""
|
||||
def __init__(self, config: dict = None):
|
||||
config = config or {}
|
||||
self.circuit_breaker = CircuitBreakerConfig(config.get("circuit_breaker"))
|
||||
self.retry = RetryConfig(config.get("retry"))
|
||||
self.health_check = HealthCheckConfig(config.get("health_check"))
|
||||
self.auto_recovery = AutoRecoveryConfig(config.get("auto_recovery"))
|
||||
self.isolation = IsolationConfig(config.get("isolation"))
|
||||
209
store/@{FutureOSS}/plugin-loader-pro/core/enhancer.py
Normal file
209
store/@{FutureOSS}/plugin-loader-pro/core/enhancer.py
Normal file
@@ -0,0 +1,209 @@
|
||||
"""插件加载增强器"""
|
||||
from ..circuit.breaker import CircuitBreaker
|
||||
from ..recovery.health import HealthChecker
|
||||
from ..recovery.auto_fix import AutoRecovery
|
||||
from ..utils.logger import ProLogger
|
||||
from .config import ProConfig
|
||||
|
||||
|
||||
class PluginLoaderEnhancer:
|
||||
"""插件加载增强器 - 为现有 plugin-loader 提供高级机制"""
|
||||
|
||||
def __init__(self, plugin_manager, config: ProConfig):
|
||||
self.pm = plugin_manager
|
||||
self.config = config
|
||||
self._breakers = {}
|
||||
self._health_checker = None
|
||||
self._auto_recovery = AutoRecovery(
|
||||
config.auto_recovery.max_attempts,
|
||||
config.auto_recovery.delay
|
||||
)
|
||||
self._enhanced = False
|
||||
|
||||
def enhance(self):
|
||||
"""增强 plugin-loader"""
|
||||
if self._enhanced:
|
||||
return
|
||||
|
||||
ProLogger.info("enhancer", "开始增强 plugin-loader...")
|
||||
|
||||
# 1. 为所有插件创建熔断器
|
||||
self._setup_circuit_breakers()
|
||||
|
||||
# 2. 包装启动方法(带重试和容错)
|
||||
self._wrap_start_methods()
|
||||
|
||||
# 3. 启动健康检查
|
||||
self._start_health_check()
|
||||
|
||||
self._enhanced = True
|
||||
ProLogger.info("enhancer", "增强完成,共增强 {} 个插件".format(
|
||||
len(self.pm.plugins)
|
||||
))
|
||||
|
||||
def _setup_circuit_breakers(self):
|
||||
"""为所有插件创建熔断器"""
|
||||
for name, info in self.pm.plugins.items():
|
||||
self._breakers[name] = CircuitBreaker(
|
||||
self.config.circuit_breaker.failure_threshold,
|
||||
self.config.circuit_breaker.recovery_timeout,
|
||||
self.config.circuit_breaker.half_open_requests
|
||||
)
|
||||
ProLogger.debug("enhancer", f"为 {name} 创建熔断器")
|
||||
|
||||
def _wrap_start_methods(self):
|
||||
"""包装启动方法"""
|
||||
original_start_all = getattr(self.pm, 'start_all', None)
|
||||
if original_start_all:
|
||||
def wrapped_start_all():
|
||||
self._safe_start_all()
|
||||
|
||||
self.pm.start_all = wrapped_start_all
|
||||
ProLogger.info("enhancer", "已包装 start_all 方法")
|
||||
|
||||
original_init_and_start = getattr(
|
||||
self.pm, 'init_and_start_all', None
|
||||
)
|
||||
if original_init_and_start:
|
||||
def wrapped_init_and_start():
|
||||
self._safe_init_and_start_all()
|
||||
|
||||
self.pm.init_and_start_all = wrapped_init_and_start
|
||||
ProLogger.info("enhancer", "已包装 init_and_start_all 方法")
|
||||
|
||||
def _safe_init_and_start_all(self):
|
||||
"""安全的初始化并启动"""
|
||||
ordered = self._get_ordered_plugins()
|
||||
|
||||
# 安全初始化
|
||||
for name in ordered:
|
||||
self._safe_call(name, 'init', '初始化')
|
||||
|
||||
# 安全启动
|
||||
for name in ordered:
|
||||
self._safe_call(name, 'start', '启动')
|
||||
|
||||
def _safe_start_all(self):
|
||||
"""安全启动所有"""
|
||||
for name in self.pm.plugins:
|
||||
self._safe_call(name, 'start', '启动')
|
||||
|
||||
def _safe_call(self, name: str, method: str, action: str):
|
||||
"""安全调用插件方法(带熔断和重试)"""
|
||||
info = self.pm.plugins.get(name)
|
||||
if not info:
|
||||
return
|
||||
|
||||
instance = info.get("instance")
|
||||
if not instance or not hasattr(instance, method):
|
||||
return
|
||||
|
||||
breaker = self._breakers.get(name)
|
||||
if not breaker:
|
||||
# 没有熔断器,直接调用
|
||||
try:
|
||||
getattr(instance, method)()
|
||||
except Exception as e:
|
||||
ProLogger.error("safe", f"{name} {action}失败: {e}")
|
||||
self._on_plugin_error(name, info, str(e))
|
||||
return
|
||||
|
||||
# 有熔断器,包装调用
|
||||
def do_call():
|
||||
return getattr(instance, method)()
|
||||
|
||||
try:
|
||||
breaker.call(do_call)
|
||||
info["info"].error_count = 0
|
||||
ProLogger.info("safe", f"{name} {action}成功")
|
||||
except Exception as e:
|
||||
ProLogger.error("safe", f"{name} {action}失败: {e}")
|
||||
self._on_plugin_error(name, info, str(e))
|
||||
|
||||
def _on_plugin_error(self, name: str, info: dict, error: str):
|
||||
"""插件错误处理"""
|
||||
info["info"].error_count += 1
|
||||
info["info"].last_error = error
|
||||
|
||||
# 自动恢复
|
||||
if self.config.auto_recovery.enabled:
|
||||
plugin_dir = info.get("dir")
|
||||
module = info.get("module")
|
||||
|
||||
if plugin_dir:
|
||||
result = self._auto_recovery.attempt_recovery(
|
||||
name, plugin_dir, module, info.get("instance")
|
||||
)
|
||||
if result:
|
||||
info["instance"] = result
|
||||
info["info"].error_count = 0
|
||||
ProLogger.info("recovery", f"{name} 自动恢复成功")
|
||||
|
||||
def _start_health_check(self):
|
||||
"""启动健康检查"""
|
||||
self._health_checker = HealthChecker(
|
||||
self.config.health_check.interval,
|
||||
self.config.health_check.timeout,
|
||||
self.config.health_check.max_failures
|
||||
)
|
||||
|
||||
for name, info in self.pm.plugins.items():
|
||||
self._health_checker.add_plugin(name, info["instance"])
|
||||
|
||||
self._health_checker.start(
|
||||
on_failure_callback=self._on_health_check_failure
|
||||
)
|
||||
ProLogger.info("enhancer", "健康检查已启动")
|
||||
|
||||
def _on_health_check_failure(self, name: str):
|
||||
"""健康检查失败回调"""
|
||||
ProLogger.error("health", f"插件 {name} 健康检查失败")
|
||||
|
||||
info = self.pm.plugins.get(name)
|
||||
if not info:
|
||||
return
|
||||
|
||||
plugin_dir = info.get("dir")
|
||||
module = info.get("module")
|
||||
|
||||
if plugin_dir:
|
||||
result = self._auto_recovery.attempt_recovery(
|
||||
name, plugin_dir, module, info.get("instance")
|
||||
)
|
||||
if result:
|
||||
info["instance"] = result
|
||||
self._health_checker.reset_failure_count(name)
|
||||
ProLogger.info("recovery", f"{name} 健康恢复成功")
|
||||
|
||||
def _get_ordered_plugins(self) -> list[str]:
|
||||
"""获取按依赖排序的插件列表"""
|
||||
ordered = []
|
||||
visited = set()
|
||||
|
||||
def visit(name):
|
||||
if name in visited:
|
||||
return
|
||||
visited.add(name)
|
||||
|
||||
info = self.pm.plugins.get(name)
|
||||
if not info:
|
||||
return
|
||||
|
||||
for dep in info["info"].dependencies:
|
||||
clean_dep = dep.rstrip("}")
|
||||
if clean_dep in self.pm.plugins:
|
||||
visit(clean_dep)
|
||||
|
||||
ordered.append(name)
|
||||
|
||||
for name in self.pm.plugins:
|
||||
visit(name)
|
||||
|
||||
return ordered
|
||||
|
||||
def disable(self):
|
||||
"""禁用增强器"""
|
||||
if self._health_checker:
|
||||
self._health_checker.stop()
|
||||
self._enhanced = False
|
||||
ProLogger.info("enhancer", "增强器已禁用")
|
||||
278
store/@{FutureOSS}/plugin-loader-pro/core/manager.py
Normal file
278
store/@{FutureOSS}/plugin-loader-pro/core/manager.py
Normal file
@@ -0,0 +1,278 @@
|
||||
"""插件加载 Pro - 核心管理器"""
|
||||
import sys
|
||||
import json
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional
|
||||
|
||||
from oss.plugin.types import Plugin
|
||||
from .config import ProConfig
|
||||
from .registry import CapabilityRegistry
|
||||
from .proxy import PluginProxy, PermissionError
|
||||
from ..models.plugin_info import PluginInfo
|
||||
from ..circuit.breaker import CircuitBreaker
|
||||
from ..retry.handler import RetryHandler
|
||||
from ..fallback.handler import FallbackHandler
|
||||
from ..recovery.health import HealthChecker
|
||||
from ..recovery.auto_fix import AutoRecovery
|
||||
from ..isolation.timeout import TimeoutController, TimeoutError
|
||||
from ..utils.logger import ProLogger
|
||||
from oss.plugin.capabilities import scan_capabilities
|
||||
|
||||
|
||||
class ProPluginManager:
|
||||
"""Pro 插件管理器"""
|
||||
|
||||
def __init__(self, config: ProConfig):
|
||||
self.config = config
|
||||
self.plugins: dict[str, dict[str, Any]] = {}
|
||||
self.capability_registry = CapabilityRegistry()
|
||||
self._breakers: dict[str, CircuitBreaker] = {}
|
||||
self._health_checker = HealthChecker(
|
||||
config.health_check.interval,
|
||||
config.health_check.timeout,
|
||||
config.health_check.max_failures
|
||||
)
|
||||
self._auto_recovery = AutoRecovery(
|
||||
config.auto_recovery.max_attempts,
|
||||
config.auto_recovery.delay
|
||||
)
|
||||
|
||||
def load_all(self, store_dir: str = "store"):
|
||||
"""加载所有插件"""
|
||||
ProLogger.info("loader", "开始扫描插件...")
|
||||
|
||||
self._load_from_dir(Path(store_dir))
|
||||
|
||||
ProLogger.info("loader", f"共加载 {len(self.plugins)} 个插件")
|
||||
|
||||
def _load_from_dir(self, store_dir: Path):
|
||||
"""从目录加载插件"""
|
||||
if not store_dir.exists():
|
||||
return
|
||||
|
||||
for author_dir in store_dir.iterdir():
|
||||
if not author_dir.is_dir():
|
||||
continue
|
||||
|
||||
for plugin_dir in author_dir.iterdir():
|
||||
if not plugin_dir.is_dir():
|
||||
continue
|
||||
|
||||
main_file = plugin_dir / "main.py"
|
||||
if not main_file.exists():
|
||||
continue
|
||||
|
||||
self._load_single_plugin(plugin_dir)
|
||||
|
||||
def _load_single_plugin(self, plugin_dir: Path) -> Optional[Any]:
|
||||
"""加载单个插件"""
|
||||
main_file = plugin_dir / "main.py"
|
||||
manifest_file = plugin_dir / "manifest.json"
|
||||
|
||||
try:
|
||||
manifest = {}
|
||||
if manifest_file.exists():
|
||||
with open(manifest_file, "r", encoding="utf-8") as f:
|
||||
manifest = json.load(f)
|
||||
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
f"pro_plugin.{plugin_dir.name}", str(main_file)
|
||||
)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[spec.name] = module
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
if not hasattr(module, "New"):
|
||||
return None
|
||||
|
||||
instance = module.New()
|
||||
|
||||
plugin_name = plugin_dir.name.rstrip("}")
|
||||
permissions = manifest.get("permissions", [])
|
||||
|
||||
if permissions:
|
||||
instance = PluginProxy(
|
||||
plugin_name, instance, permissions, self.plugins
|
||||
)
|
||||
|
||||
info = PluginInfo()
|
||||
meta = manifest.get("metadata", {})
|
||||
info.name = meta.get("name", plugin_name)
|
||||
info.version = meta.get("version", "1.0.0")
|
||||
info.author = meta.get("author", "")
|
||||
info.description = meta.get("description", "")
|
||||
info.dependencies = manifest.get("dependencies", [])
|
||||
info.capabilities = scan_capabilities(plugin_dir)
|
||||
|
||||
for cap in info.capabilities:
|
||||
self.capability_registry.register_provider(
|
||||
cap, plugin_name, instance
|
||||
)
|
||||
|
||||
self._breakers[plugin_name] = CircuitBreaker(
|
||||
self.config.circuit_breaker.failure_threshold,
|
||||
self.config.circuit_breaker.recovery_timeout,
|
||||
self.config.circuit_breaker.half_open_requests
|
||||
)
|
||||
|
||||
self.plugins[plugin_name] = {
|
||||
"instance": instance,
|
||||
"module": module,
|
||||
"info": info,
|
||||
"permissions": permissions,
|
||||
"dir": plugin_dir
|
||||
}
|
||||
|
||||
ProLogger.info("loader", f"已加载: {plugin_name} v{info.version}")
|
||||
return instance
|
||||
|
||||
except Exception as e:
|
||||
ProLogger.error("loader", f"加载失败 {plugin_dir.name}: {e}")
|
||||
return None
|
||||
|
||||
def init_and_start_all(self):
|
||||
"""初始化并启动所有插件"""
|
||||
ProLogger.info("manager", "开始初始化所有插件...")
|
||||
|
||||
self._inject_dependencies()
|
||||
ordered = self._get_ordered_plugins()
|
||||
|
||||
for name in ordered:
|
||||
self._safe_init(name)
|
||||
|
||||
ProLogger.info("manager", "开始启动所有插件...")
|
||||
for name in ordered:
|
||||
self._safe_start(name)
|
||||
|
||||
self._health_checker.start(
|
||||
on_failure_callback=self._on_plugin_failure
|
||||
)
|
||||
|
||||
def _safe_init(self, name: str):
|
||||
"""安全初始化插件"""
|
||||
info = self.plugins[name]
|
||||
instance = info["instance"]
|
||||
breaker = self._breakers[name]
|
||||
|
||||
try:
|
||||
breaker.call(instance.init)
|
||||
info["info"].status = "initialized"
|
||||
ProLogger.info("manager", f"已初始化: {name}")
|
||||
except Exception as e:
|
||||
ProLogger.error("manager", f"初始化失败 {name}: {e}")
|
||||
info["info"].status = "error"
|
||||
info["info"].error_count += 1
|
||||
info["info"].last_error = str(e)
|
||||
|
||||
def _safe_start(self, name: str):
|
||||
"""安全启动插件"""
|
||||
info = self.plugins[name]
|
||||
instance = info["instance"]
|
||||
breaker = self._breakers[name]
|
||||
|
||||
try:
|
||||
breaker.call(instance.start)
|
||||
info["info"].status = "running"
|
||||
self._health_checker.add_plugin(name, instance)
|
||||
ProLogger.info("manager", f"已启动: {name}")
|
||||
except Exception as e:
|
||||
ProLogger.error("manager", f"启动失败 {name}: {e}")
|
||||
info["info"].status = "error"
|
||||
info["info"].error_count += 1
|
||||
info["info"].last_error = str(e)
|
||||
|
||||
def stop_all(self):
|
||||
"""停止所有插件"""
|
||||
self._health_checker.stop()
|
||||
|
||||
for name in reversed(list(self.plugins.keys())):
|
||||
self._safe_stop(name)
|
||||
|
||||
def _safe_stop(self, name: str):
|
||||
"""安全停止插件"""
|
||||
info = self.plugins[name]
|
||||
instance = info["instance"]
|
||||
|
||||
try:
|
||||
instance.stop()
|
||||
info["info"].status = "stopped"
|
||||
ProLogger.info("manager", f"已停止: {name}")
|
||||
except Exception as e:
|
||||
ProLogger.warn("manager", f"停止异常 {name}: {e}")
|
||||
|
||||
def _on_plugin_failure(self, name: str):
|
||||
"""插件失败回调"""
|
||||
ProLogger.error("recovery", f"插件 {name} 健康检查失败")
|
||||
|
||||
if not self.config.auto_recovery.enabled:
|
||||
return
|
||||
|
||||
info = self.plugins.get(name)
|
||||
if not info:
|
||||
return
|
||||
|
||||
plugin_dir = info.get("dir")
|
||||
module = info.get("module")
|
||||
instance = info.get("instance")
|
||||
|
||||
if plugin_dir:
|
||||
result = self._auto_recovery.attempt_recovery(
|
||||
name, plugin_dir, module, instance
|
||||
)
|
||||
if result:
|
||||
info["instance"] = result
|
||||
info["info"].status = "running"
|
||||
self._health_checker.reset_failure_count(name)
|
||||
|
||||
def _inject_dependencies(self):
|
||||
"""注入依赖"""
|
||||
name_map = {}
|
||||
for name in self.plugins:
|
||||
clean = name.rstrip("}")
|
||||
name_map[clean] = name
|
||||
name_map[clean + "}"] = name
|
||||
|
||||
for name, info in self.plugins.items():
|
||||
deps = info["info"].dependencies
|
||||
if not deps:
|
||||
continue
|
||||
|
||||
for dep_name in deps:
|
||||
actual_dep = name_map.get(dep_name) or name_map.get(dep_name + "}")
|
||||
if actual_dep and actual_dep in self.plugins:
|
||||
dep_instance = self.plugins[actual_dep]["instance"]
|
||||
setter = f"set_{dep_name.replace('-', '_')}"
|
||||
|
||||
if hasattr(info["instance"], setter):
|
||||
try:
|
||||
getattr(info["instance"], setter)(dep_instance)
|
||||
ProLogger.info("inject", f"{name} <- {actual_dep}")
|
||||
except Exception as e:
|
||||
ProLogger.error("inject", f"注入失败 {name}.{setter}: {e}")
|
||||
|
||||
def _get_ordered_plugins(self) -> list[str]:
|
||||
"""获取插件顺序"""
|
||||
ordered = []
|
||||
visited = set()
|
||||
|
||||
def visit(name):
|
||||
if name in visited:
|
||||
return
|
||||
visited.add(name)
|
||||
|
||||
info = self.plugins.get(name)
|
||||
if not info:
|
||||
return
|
||||
|
||||
for dep in info["info"].dependencies:
|
||||
clean_dep = dep.rstrip("}")
|
||||
if clean_dep in self.plugins:
|
||||
visit(clean_dep)
|
||||
|
||||
ordered.append(name)
|
||||
|
||||
for name in self.plugins:
|
||||
visit(name)
|
||||
|
||||
return ordered
|
||||
36
store/@{FutureOSS}/plugin-loader-pro/core/proxy.py
Normal file
36
store/@{FutureOSS}/plugin-loader-pro/core/proxy.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""插件代理 - 防越级访问"""
|
||||
|
||||
|
||||
class PermissionError(Exception):
|
||||
"""权限错误"""
|
||||
pass
|
||||
|
||||
|
||||
class PluginProxy:
|
||||
"""插件代理"""
|
||||
|
||||
def __init__(self, plugin_name: str, plugin_instance: any,
|
||||
allowed_plugins: list[str], all_plugins: dict[str, dict]):
|
||||
self._plugin_name = plugin_name
|
||||
self._plugin_instance = plugin_instance
|
||||
self._allowed_plugins = set(allowed_plugins)
|
||||
self._all_plugins = all_plugins
|
||||
|
||||
def get_plugin(self, name: str) -> any:
|
||||
"""获取其他插件实例(带权限检查)"""
|
||||
if name not in self._allowed_plugins and "*" not in self._allowed_plugins:
|
||||
raise PermissionError(
|
||||
f"插件 '{self._plugin_name}' 无权访问插件 '{name}'"
|
||||
)
|
||||
if name not in self._all_plugins:
|
||||
return None
|
||||
return self._all_plugins[name]["instance"]
|
||||
|
||||
def list_plugins(self) -> list[str]:
|
||||
"""列出有权限访问的插件"""
|
||||
if "*" in self._allowed_plugins:
|
||||
return list(self._all_plugins.keys())
|
||||
return [n for n in self._allowed_plugins if n in self._all_plugins]
|
||||
|
||||
def __getattr__(self, name: str):
|
||||
return getattr(self._plugin_instance, name)
|
||||
51
store/@{FutureOSS}/plugin-loader-pro/core/registry.py
Normal file
51
store/@{FutureOSS}/plugin-loader-pro/core/registry.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""能力注册表"""
|
||||
from typing import Any, Optional
|
||||
from .proxy import PermissionError
|
||||
|
||||
|
||||
class CapabilityRegistry:
|
||||
"""能力注册表"""
|
||||
|
||||
def __init__(self, permission_check: bool = True):
|
||||
self.providers: dict[str, dict[str, Any]] = {}
|
||||
self.consumers: dict[str, list[str]] = {}
|
||||
self.permission_check = permission_check
|
||||
|
||||
def register_provider(self, capability: str, plugin_name: str, instance: Any):
|
||||
"""注册能力提供者"""
|
||||
self.providers[capability] = {
|
||||
"plugin": plugin_name,
|
||||
"instance": instance,
|
||||
}
|
||||
if capability not in self.consumers:
|
||||
self.consumers[capability] = []
|
||||
|
||||
def register_consumer(self, capability: str, plugin_name: str):
|
||||
"""注册能力消费者"""
|
||||
if capability not in self.consumers:
|
||||
self.consumers[capability] = []
|
||||
if plugin_name not in self.consumers[capability]:
|
||||
self.consumers[capability].append(plugin_name)
|
||||
|
||||
def get_provider(self, capability: str, requester: str = "",
|
||||
allowed_plugins: list[str] = None) -> Optional[Any]:
|
||||
"""获取能力提供者实例(带权限检查)"""
|
||||
if capability not in self.providers:
|
||||
return None
|
||||
|
||||
if self.permission_check and allowed_plugins is not None:
|
||||
provider_name = self.providers[capability]["plugin"]
|
||||
if (provider_name != requester and
|
||||
provider_name not in allowed_plugins and
|
||||
"*" not in allowed_plugins):
|
||||
raise PermissionError(
|
||||
f"插件 '{requester}' 无权使用能力 '{capability}'"
|
||||
)
|
||||
|
||||
return self.providers[capability]["instance"]
|
||||
|
||||
def has_capability(self, capability: str) -> bool:
|
||||
return capability in self.providers
|
||||
|
||||
def get_consumers(self, capability: str) -> list[str]:
|
||||
return self.consumers.get(capability, [])
|
||||
49
store/@{FutureOSS}/plugin-loader-pro/fallback/handler.py
Normal file
49
store/@{FutureOSS}/plugin-loader-pro/fallback/handler.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""降级处理器"""
|
||||
from typing import Callable, Any, Optional
|
||||
from ..utils.logger import ProLogger
|
||||
|
||||
|
||||
class FallbackStrategy:
|
||||
"""降级策略枚举"""
|
||||
RETURN_DEFAULT = "return_default"
|
||||
RETURN_CACHE = "return_cache"
|
||||
RETURN_NULL = "return_null"
|
||||
CALL_ALTERNATIVE = "call_alternative"
|
||||
|
||||
|
||||
class FallbackHandler:
|
||||
"""降级处理器"""
|
||||
|
||||
def __init__(self, strategy: str = FallbackStrategy.RETURN_NULL,
|
||||
default_value: Any = None,
|
||||
alternative_func: Callable = None):
|
||||
self.strategy = strategy
|
||||
self.default_value = default_value
|
||||
self.alternative_func = alternative_func
|
||||
self._cache = {}
|
||||
|
||||
def execute(self, func: Callable, plugin_name: str, *args, **kwargs) -> Any:
|
||||
"""执行降级逻辑"""
|
||||
try:
|
||||
result = func(*args, **kwargs)
|
||||
self._cache[plugin_name] = result
|
||||
return result
|
||||
except Exception as e:
|
||||
ProLogger.warn("fallback", f"插件 {plugin_name} 执行失败,触发降级: {e}")
|
||||
return self._apply_fallback(plugin_name)
|
||||
|
||||
def _apply_fallback(self, plugin_name: str) -> Any:
|
||||
"""应用降级策略"""
|
||||
if self.strategy == FallbackStrategy.RETURN_DEFAULT:
|
||||
return self.default_value
|
||||
elif self.strategy == FallbackStrategy.RETURN_CACHE:
|
||||
return self._cache.get(plugin_name)
|
||||
elif self.strategy == FallbackStrategy.RETURN_NULL:
|
||||
return None
|
||||
elif self.strategy == FallbackStrategy.CALL_ALTERNATIVE:
|
||||
if self.alternative_func:
|
||||
try:
|
||||
return self.alternative_func()
|
||||
except Exception as e:
|
||||
ProLogger.error("fallback", f"备选方案也失败了: {e}")
|
||||
return None
|
||||
29
store/@{FutureOSS}/plugin-loader-pro/isolation/timeout.py
Normal file
29
store/@{FutureOSS}/plugin-loader-pro/isolation/timeout.py
Normal file
@@ -0,0 +1,29 @@
|
||||
"""超时控制"""
|
||||
import signal
|
||||
|
||||
|
||||
class TimeoutError(Exception):
|
||||
"""超时错误"""
|
||||
pass
|
||||
|
||||
|
||||
class TimeoutController:
|
||||
"""超时控制器"""
|
||||
|
||||
def __init__(self, timeout: int = 30):
|
||||
self.timeout = timeout
|
||||
|
||||
def execute_with_timeout(self, func, *args, **kwargs) -> any:
|
||||
"""在超时限制内执行函数"""
|
||||
def handler(signum, frame):
|
||||
raise TimeoutError(f"执行超时 (>{self.timeout}s)")
|
||||
|
||||
old_handler = signal.signal(signal.SIGALRM, handler)
|
||||
signal.alarm(self.timeout)
|
||||
|
||||
try:
|
||||
result = func(*args, **kwargs)
|
||||
signal.alarm(0)
|
||||
return result
|
||||
finally:
|
||||
signal.signal(signal.SIGALRM, old_handler)
|
||||
76
store/@{FutureOSS}/plugin-loader-pro/main.py
Normal file
76
store/@{FutureOSS}/plugin-loader-pro/main.py
Normal file
@@ -0,0 +1,76 @@
|
||||
"""插件加载 Pro - 为 plugin-loader 提供高级机制"""
|
||||
from oss.plugin.types import Plugin, register_plugin_type
|
||||
from .core.config import ProConfig
|
||||
from .core.enhancer import PluginLoaderEnhancer
|
||||
from .utils.logger import ProLogger
|
||||
|
||||
|
||||
class PluginLoaderPro(Plugin):
|
||||
"""插件加载 Pro - 增强器"""
|
||||
|
||||
def __init__(self):
|
||||
self.plugin_loader = None
|
||||
self.enhancer = None
|
||||
self.config = None
|
||||
self._started = False
|
||||
|
||||
def meta(self):
|
||||
from oss.plugin.types import Metadata, PluginConfig, Manifest
|
||||
return Manifest(
|
||||
metadata=Metadata(
|
||||
name="plugin-loader-pro",
|
||||
version="1.0.0",
|
||||
author="FutureOSS",
|
||||
description="为 plugin-loader 提供熔断、降级、容错、自动修复等高级机制"
|
||||
),
|
||||
config=PluginConfig(
|
||||
enabled=True,
|
||||
args={}
|
||||
),
|
||||
dependencies=["plugin-loader"]
|
||||
)
|
||||
|
||||
def set_plugin_loader(self, plugin_loader):
|
||||
self.plugin_loader = plugin_loader
|
||||
ProLogger.info("main", "已注入 plugin-loader")
|
||||
|
||||
def init(self, deps: dict = None):
|
||||
if not self.plugin_loader:
|
||||
ProLogger.warn("main", "未找到 plugin-loader 依赖")
|
||||
return
|
||||
|
||||
config = {}
|
||||
if deps:
|
||||
config = deps.get("config", {})
|
||||
|
||||
self.config = ProConfig(config)
|
||||
self.enhancer = PluginLoaderEnhancer(
|
||||
self.plugin_loader.manager,
|
||||
self.config
|
||||
)
|
||||
|
||||
ProLogger.info("main", "增强器已初始化")
|
||||
|
||||
def start(self):
|
||||
if self._started:
|
||||
return
|
||||
self._started = True
|
||||
|
||||
if not self.enhancer:
|
||||
ProLogger.warn("main", "增强器未初始化,跳过启动")
|
||||
return
|
||||
|
||||
ProLogger.info("main", "开始增强 plugin-loader...")
|
||||
self.enhancer.enhance()
|
||||
|
||||
def stop(self):
|
||||
ProLogger.info("main", "停止增强器...")
|
||||
if self.enhancer:
|
||||
self.enhancer.disable()
|
||||
|
||||
|
||||
register_plugin_type("PluginLoaderPro", PluginLoaderPro)
|
||||
|
||||
|
||||
def New():
|
||||
return PluginLoaderPro()
|
||||
40
store/@{FutureOSS}/plugin-loader-pro/manifest.json
Normal file
40
store/@{FutureOSS}/plugin-loader-pro/manifest.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"metadata": {
|
||||
"name": "plugin-loader-pro",
|
||||
"version": "1.0.0",
|
||||
"author": "FutureOSS",
|
||||
"description": "插件加载 Pro - 为 plugin-loader 提供熔断、降级、容错、自动修复等高级机制",
|
||||
"type": "enhancer"
|
||||
},
|
||||
"config": {
|
||||
"enabled": true,
|
||||
"args": {
|
||||
"circuit_breaker": {
|
||||
"failure_threshold": 3,
|
||||
"recovery_timeout": 60,
|
||||
"half_open_requests": 1
|
||||
},
|
||||
"retry": {
|
||||
"max_retries": 3,
|
||||
"backoff_factor": 2,
|
||||
"initial_delay": 1
|
||||
},
|
||||
"health_check": {
|
||||
"interval": 30,
|
||||
"timeout": 5,
|
||||
"max_failures": 5
|
||||
},
|
||||
"auto_recovery": {
|
||||
"enabled": true,
|
||||
"max_attempts": 3,
|
||||
"delay": 10
|
||||
},
|
||||
"isolation": {
|
||||
"enabled": true,
|
||||
"timeout_per_plugin": 30
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": ["plugin-loader"],
|
||||
"permissions": ["*"]
|
||||
}
|
||||
30
store/@{FutureOSS}/plugin-loader-pro/models/plugin_info.py
Normal file
30
store/@{FutureOSS}/plugin-loader-pro/models/plugin_info.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""插件信息模型"""
|
||||
from typing import Any
|
||||
|
||||
|
||||
class PluginInfo:
|
||||
"""插件信息"""
|
||||
def __init__(self):
|
||||
self.name: str = ""
|
||||
self.version: str = ""
|
||||
self.author: str = ""
|
||||
self.description: str = ""
|
||||
self.readme: str = ""
|
||||
self.config: dict[str, Any] = {}
|
||||
self.extensions: dict[str, Any] = {}
|
||||
self.lifecycle: Any = None
|
||||
self.capabilities: set[str] = set()
|
||||
self.dependencies: list[str] = []
|
||||
self.status: str = "idle" # idle, running, stopped, error
|
||||
self.error_count: int = 0
|
||||
self.last_error: str = ""
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"name": self.name,
|
||||
"version": self.version,
|
||||
"author": self.author,
|
||||
"description": self.description,
|
||||
"status": self.status,
|
||||
"error_count": self.error_count
|
||||
}
|
||||
60
store/@{FutureOSS}/plugin-loader-pro/recovery/auto_fix.py
Normal file
60
store/@{FutureOSS}/plugin-loader-pro/recovery/auto_fix.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""自动修复器"""
|
||||
import time
|
||||
import importlib
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from ..utils.logger import ProLogger
|
||||
|
||||
|
||||
class AutoRecovery:
|
||||
"""自动修复器"""
|
||||
|
||||
def __init__(self, max_attempts: int = 3, delay: int = 10):
|
||||
self.max_attempts = max_attempts
|
||||
self.delay = delay
|
||||
self._recovery_attempts: dict[str, int] = {}
|
||||
|
||||
def attempt_recovery(self, name: str, plugin_dir: Path,
|
||||
module: any, instance: any) -> bool:
|
||||
"""尝试恢复插件"""
|
||||
attempts = self._recovery_attempts.get(name, 0)
|
||||
|
||||
if attempts >= self.max_attempts:
|
||||
ProLogger.error("recovery", f"插件 {name} 已达到最大恢复次数,放弃恢复")
|
||||
return False
|
||||
|
||||
ProLogger.warn("recovery", f"尝试恢复插件 {name} (第 {attempts + 1} 次)")
|
||||
|
||||
try:
|
||||
time.sleep(self.delay)
|
||||
|
||||
# 重新加载模块
|
||||
if module and hasattr(module, '__file__'):
|
||||
module_path = Path(module.__file__)
|
||||
if module_path.exists():
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
module.__name__, str(module_path)
|
||||
)
|
||||
new_module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[spec.name] = new_module
|
||||
spec.loader.exec_module(new_module)
|
||||
|
||||
if hasattr(new_module, 'New'):
|
||||
new_instance = new_module.New()
|
||||
ProLogger.info("recovery", f"插件 {name} 恢复成功")
|
||||
self._recovery_attempts[name] = 0
|
||||
return new_instance
|
||||
|
||||
except Exception as e:
|
||||
ProLogger.error("recovery", f"恢复插件 {name} 失败: {e}")
|
||||
|
||||
self._recovery_attempts[name] = attempts + 1
|
||||
return False
|
||||
|
||||
def reset_attempts(self, name: str):
|
||||
"""重置恢复尝试次数"""
|
||||
self._recovery_attempts[name] = 0
|
||||
|
||||
def get_attempts(self, name: str) -> int:
|
||||
"""获取恢复尝试次数"""
|
||||
return self._recovery_attempts.get(name, 0)
|
||||
76
store/@{FutureOSS}/plugin-loader-pro/recovery/health.py
Normal file
76
store/@{FutureOSS}/plugin-loader-pro/recovery/health.py
Normal file
@@ -0,0 +1,76 @@
|
||||
"""健康检查器"""
|
||||
import time
|
||||
import threading
|
||||
from typing import Any
|
||||
from ..utils.logger import ProLogger
|
||||
|
||||
|
||||
class HealthChecker:
|
||||
"""健康检查器"""
|
||||
|
||||
def __init__(self, interval: int = 30, timeout: int = 5, max_failures: int = 5):
|
||||
self.interval = interval
|
||||
self.timeout = timeout
|
||||
self.max_failures = max_failures
|
||||
|
||||
self._running = False
|
||||
self._thread = None
|
||||
self._plugins: dict[str, Any] = {}
|
||||
self._failure_counts: dict[str, int] = {}
|
||||
self._on_failure_callback = None
|
||||
|
||||
def add_plugin(self, name: str, instance: Any):
|
||||
"""添加要监控的插件"""
|
||||
self._plugins[name] = instance
|
||||
self._failure_counts[name] = 0
|
||||
|
||||
def start(self, on_failure_callback=None):
|
||||
"""启动健康检查"""
|
||||
self._on_failure_callback = on_failure_callback
|
||||
self._running = True
|
||||
self._thread = threading.Thread(target=self._check_loop, daemon=True)
|
||||
self._thread.start()
|
||||
ProLogger.info("health", "健康检查已启动")
|
||||
|
||||
def stop(self):
|
||||
"""停止健康检查"""
|
||||
self._running = False
|
||||
if self._thread:
|
||||
self._thread.join(timeout=5)
|
||||
|
||||
def _check_loop(self):
|
||||
"""检查循环"""
|
||||
while self._running:
|
||||
for name, instance in self._plugins.items():
|
||||
self._check_plugin(name, instance)
|
||||
time.sleep(self.interval)
|
||||
|
||||
def _check_plugin(self, name: str, instance: Any):
|
||||
"""检查单个插件"""
|
||||
try:
|
||||
if hasattr(instance, 'health'):
|
||||
healthy = instance.health()
|
||||
if not healthy:
|
||||
self._on_failure(name)
|
||||
else:
|
||||
self._failure_counts[name] = 0
|
||||
except Exception as e:
|
||||
ProLogger.error("health", f"插件 {name} 健康检查失败: {e}")
|
||||
self._on_failure(name)
|
||||
|
||||
def _on_failure(self, name: str):
|
||||
"""失败处理"""
|
||||
self._failure_counts[name] = self._failure_counts.get(name, 0) + 1
|
||||
|
||||
if self._failure_counts[name] >= self.max_failures:
|
||||
ProLogger.warn("health", f"插件 {name} 连续失败 {self._failure_counts[name]} 次")
|
||||
if self._on_failure_callback:
|
||||
self._on_failure_callback(name)
|
||||
|
||||
def reset_failure_count(self, name: str):
|
||||
"""重置失败计数"""
|
||||
self._failure_counts[name] = 0
|
||||
|
||||
def get_failure_count(self, name: str) -> int:
|
||||
"""获取失败计数"""
|
||||
return self._failure_counts.get(name, 0)
|
||||
39
store/@{FutureOSS}/plugin-loader-pro/retry/handler.py
Normal file
39
store/@{FutureOSS}/plugin-loader-pro/retry/handler.py
Normal file
@@ -0,0 +1,39 @@
|
||||
"""重试处理器"""
|
||||
import time
|
||||
import random
|
||||
from typing import Callable, Any
|
||||
from ..core.config import RetryConfig
|
||||
from ..utils.logger import ProLogger
|
||||
|
||||
|
||||
class RetryHandler:
|
||||
"""重试处理器"""
|
||||
|
||||
def __init__(self, config: RetryConfig = None):
|
||||
config = config or RetryConfig()
|
||||
self.max_retries = config.max_retries
|
||||
self.backoff_factor = config.backoff_factor
|
||||
self.initial_delay = config.initial_delay
|
||||
|
||||
def execute(self, func: Callable, *args, **kwargs) -> Any:
|
||||
"""执行带重试的调用"""
|
||||
last_exception = None
|
||||
|
||||
for attempt in range(self.max_retries + 1):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except Exception as e:
|
||||
last_exception = e
|
||||
|
||||
if attempt < self.max_retries:
|
||||
delay = self._calculate_delay(attempt)
|
||||
ProLogger.warn("retry", f"第 {attempt + 1} 次重试,等待 {delay:.1f}s: {e}")
|
||||
time.sleep(delay)
|
||||
|
||||
raise last_exception
|
||||
|
||||
def _calculate_delay(self, attempt: int) -> float:
|
||||
"""计算退避延迟"""
|
||||
delay = self.initial_delay * (self.backoff_factor ** attempt)
|
||||
jitter = random.uniform(0, delay * 0.1)
|
||||
return delay + jitter
|
||||
60
store/@{FutureOSS}/plugin-loader-pro/utils/logger.py
Normal file
60
store/@{FutureOSS}/plugin-loader-pro/utils/logger.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""插件加载 Pro - 日志工具"""
|
||||
import sys
|
||||
|
||||
|
||||
class ProLogger:
|
||||
"""Pro 日志记录器 - 智能颜色识别"""
|
||||
|
||||
_COLORS = {
|
||||
"reset": "\033[0m",
|
||||
"white": "\033[0;37m",
|
||||
"yellow": "\033[1;33m",
|
||||
"blue": "\033[1;34m",
|
||||
"red": "\033[1;31m",
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _colorize(text: str, color: str) -> str:
|
||||
"""添加颜色(终端支持时)"""
|
||||
if not sys.stdout.isatty():
|
||||
return text
|
||||
return f"{ProLogger._COLORS.get(color, '')}{text}{ProLogger._COLORS['reset']}"
|
||||
|
||||
@staticmethod
|
||||
def info(component: str, message: str):
|
||||
"""正常日志 - 白色"""
|
||||
tag = ProLogger._colorize(f"[pro:{component}]", "white")
|
||||
msg = ProLogger._colorize(message, "white")
|
||||
print(f"{tag} {msg}")
|
||||
|
||||
@staticmethod
|
||||
def warn(component: str, message: str):
|
||||
"""警告日志 - 黄色"""
|
||||
tag = ProLogger._colorize(f"[pro:{component}]", "yellow")
|
||||
icon = ProLogger._colorize("⚠", "yellow")
|
||||
msg = ProLogger._colorize(message, "yellow")
|
||||
print(f"{tag} {icon} {msg}")
|
||||
|
||||
@staticmethod
|
||||
def error(component: str, message: str):
|
||||
"""错误日志 - 红色"""
|
||||
tag = ProLogger._colorize(f"[pro:{component}]", "red")
|
||||
icon = ProLogger._colorize("✗", "red")
|
||||
msg = ProLogger._colorize(message, "red")
|
||||
print(f"{tag} {icon} {msg}")
|
||||
|
||||
@staticmethod
|
||||
def debug(component: str, message: str):
|
||||
"""调试日志 - 蓝色"""
|
||||
tag = ProLogger._colorize(f"[pro:{component}]", "blue")
|
||||
icon = ProLogger._colorize("ℹ", "blue")
|
||||
msg = ProLogger._colorize(message, "blue")
|
||||
print(f"{tag} {icon} {msg}")
|
||||
|
||||
@staticmethod
|
||||
def tip(component: str, message: str):
|
||||
"""提示日志 - 蓝色(用于小提示/额外信息)"""
|
||||
tag = ProLogger._colorize(f"[pro:{component}]", "blue")
|
||||
icon = ProLogger._colorize("→", "blue")
|
||||
msg = ProLogger._colorize(message, "blue")
|
||||
print(f"{tag} {icon} {msg}")
|
||||
Reference in New Issue
Block a user