mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 07:27:15 +08:00
fix(plugin): isolate virtual instance logs
This commit is contained in:
@@ -384,6 +384,8 @@ class LoggerManager:
|
|||||||
"""
|
"""
|
||||||
识别日志调用文件和插件来源。
|
识别日志调用文件和插件来源。
|
||||||
|
|
||||||
|
虚拟实例共享物理源码,因此优先使用实例专属模块命名空间路由日志;
|
||||||
|
无模块身份的旧插件仍按调用文件路径识别。
|
||||||
插件调用宿主公共方法时,调用栈中仍保留插件帧,因此日志继续进入该插件
|
插件调用宿主公共方法时,调用栈中仍保留插件帧,因此日志继续进入该插件
|
||||||
的独立文件,而不是混入主程序日志。
|
的独立文件,而不是混入主程序日志。
|
||||||
"""
|
"""
|
||||||
@@ -400,6 +402,16 @@ class LoggerManager:
|
|||||||
parts = filepath.parts
|
parts = filepath.parts
|
||||||
if not caller_name:
|
if not caller_name:
|
||||||
caller_name = parts[-2] if parts[-1] == "__init__.py" and len(parts) >= 2 else parts[-1]
|
caller_name = parts[-2] if parts[-1] == "__init__.py" and len(parts) >= 2 else parts[-1]
|
||||||
|
module_name = frame.f_globals.get("__name__")
|
||||||
|
if isinstance(module_name, str):
|
||||||
|
module_parts = module_name.split(".")
|
||||||
|
if (
|
||||||
|
len(module_parts) >= 3
|
||||||
|
and module_parts[:2] == ["app", "plugins"]
|
||||||
|
and module_parts[2]
|
||||||
|
):
|
||||||
|
plugin_name = module_parts[2]
|
||||||
|
break
|
||||||
if "app" in parts:
|
if "app" in parts:
|
||||||
if not plugin_name and "plugins" in parts:
|
if not plugin_name and "plugins" in parts:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
"""插件日志文件路由测试。"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
from app.runtime.log import LoggerManager, logger
|
||||||
|
|
||||||
|
|
||||||
|
class CapturingLogWriter:
|
||||||
|
"""记录日志写入目标,避免测试访问真实文件系统。"""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.entries: list[tuple[str, str, Path]] = []
|
||||||
|
|
||||||
|
def write_log(self, level: str, message: str, file_path: Path) -> None:
|
||||||
|
"""保存单条日志的级别、内容和目标路径。"""
|
||||||
|
self.entries.append((level, message, file_path))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def shutdown() -> bool:
|
||||||
|
"""测试写入器没有待释放资源。"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def test_virtual_plugin_routes_log_by_runtime_module_identity(monkeypatch, tmp_path):
|
||||||
|
"""共享物理源码的虚拟实例应按运行实例 ID 写入独立日志文件。"""
|
||||||
|
writer = CapturingLogWriter()
|
||||||
|
monkeypatch.setattr(LoggerManager, "_writer", writer)
|
||||||
|
monkeypatch.setattr(LoggerManager, "_log_path", tmp_path)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
LoggerManager,
|
||||||
|
"_get_console_logger",
|
||||||
|
classmethod(
|
||||||
|
lambda _cls, _logfile: SimpleNamespace(info=lambda *_args, **_kwargs: None)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
namespace = {
|
||||||
|
"__name__": "app.plugins.mediawarp1",
|
||||||
|
"logger": logger,
|
||||||
|
}
|
||||||
|
source = compile(
|
||||||
|
"def emit_log():\n logger.info('virtual instance started')\n",
|
||||||
|
"/config/app/plugins/mediawarp/__init__.py",
|
||||||
|
"exec",
|
||||||
|
)
|
||||||
|
exec(source, namespace)
|
||||||
|
|
||||||
|
namespace["emit_log"]()
|
||||||
|
|
||||||
|
assert writer.entries == [
|
||||||
|
(
|
||||||
|
"INFO",
|
||||||
|
"mediawarp - virtual instance started",
|
||||||
|
tmp_path / "plugins" / "mediawarp1.log",
|
||||||
|
)
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user