mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-08-03 13:27:01 +03:00
feat: Add plugins version in logs (#871)
This commit is contained in:
@@ -152,11 +152,12 @@ class PluginBrowser:
|
||||
# logger.debug("current plugins: %s", snapshot_string)
|
||||
|
||||
if name in self.plugins:
|
||||
logger.debug("Plugin %s was found", name)
|
||||
plugin_display = self.plugins[name].get_display_name()
|
||||
logger.debug(f"Plugin {plugin_display} was found")
|
||||
await self.plugins[name].stop(uninstall=True)
|
||||
logger.debug("Plugin %s was stopped", name)
|
||||
logger.debug(f"Plugin {plugin_display} was stopped")
|
||||
del self.plugins[name]
|
||||
logger.debug("Plugin %s was removed from the dictionary", name)
|
||||
logger.debug(f"Plugin {plugin_display} was removed from the dictionary")
|
||||
self.cleanup_plugin_settings(name)
|
||||
logger.debug("removing files %s" % str(name))
|
||||
rmtree(plugin_dir)
|
||||
|
||||
@@ -171,16 +171,16 @@ class Loader:
|
||||
return
|
||||
if plugin.name in self.plugins:
|
||||
if not "debug" in plugin.flags and refresh:
|
||||
self.logger.info(f"Plugin {plugin.name} is already loaded and has requested to not be re-loaded")
|
||||
self.logger.info(f"Plugin {plugin.get_display_name()} is already loaded and has requested to not be re-loaded")
|
||||
return
|
||||
else:
|
||||
await self.plugins[plugin.name].stop()
|
||||
self.plugins.pop(plugin.name, None)
|
||||
if plugin.passive:
|
||||
self.logger.info(f"Plugin {plugin.name} is passive")
|
||||
self.logger.info(f"Plugin {plugin.get_display_name()} is passive")
|
||||
|
||||
self.plugins[plugin.name] = plugin.start()
|
||||
self.logger.info(f"Loaded {plugin.name}")
|
||||
self.logger.info(f"Loaded {plugin.get_display_name()}")
|
||||
if not batch:
|
||||
self.loop.create_task(self.dispatch_plugin(plugin.name, plugin.version, plugin.load_type))
|
||||
except Exception as e:
|
||||
@@ -208,7 +208,7 @@ class Loader:
|
||||
plugin = self.plugins[plugin_name]
|
||||
try:
|
||||
if method_name.startswith("_"):
|
||||
raise RuntimeError(f"Plugin {plugin.name} tried to call private method {method_name}")
|
||||
raise RuntimeError(f"Plugin {plugin.get_display_name()} tried to call private method {method_name}")
|
||||
res["result"] = await plugin.execute_legacy_method(method_name, kwargs)
|
||||
res["success"] = True
|
||||
except Exception as e:
|
||||
@@ -220,10 +220,10 @@ class Loader:
|
||||
plugin = self.plugins[plugin_name]
|
||||
try:
|
||||
if method_name.startswith("_"):
|
||||
raise RuntimeError(f"Plugin {plugin.name} tried to call private method {method_name}")
|
||||
raise RuntimeError(f"Plugin {plugin.get_display_name()} tried to call private method {method_name}")
|
||||
result = await plugin.execute_method(method_name, *args)
|
||||
except Exception as e:
|
||||
self.logger.error(f"Method {method_name} of plugin {plugin.name} failed with the following exception:\n{format_exc()}")
|
||||
self.logger.error(f"Method {method_name} of plugin {plugin.get_display_name()} failed with the following exception:\n{format_exc()}")
|
||||
raise e # throw again to pass the error to the frontend
|
||||
return result
|
||||
|
||||
|
||||
@@ -81,6 +81,13 @@ class PluginWrapper:
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def get_display_name(self) -> str:
|
||||
"""Returns plugin name with version if available, formatted for logging."""
|
||||
if self.version:
|
||||
return f"{self.name} (v{self.version})"
|
||||
|
||||
return self.name
|
||||
|
||||
async def _response_listener(self):
|
||||
while self._socket.active:
|
||||
try:
|
||||
@@ -92,7 +99,7 @@ class PluginWrapper:
|
||||
elif res["type"] == SocketMessageType.RESPONSE.value:
|
||||
self._method_call_requests.pop(res["id"]).set_result(res)
|
||||
except CancelledError:
|
||||
self.log.info(f"Stopping response listener for {self.name}")
|
||||
self.log.info(f"Stopping response listener for {self.get_display_name()}")
|
||||
await self._socket.close_socket_connection()
|
||||
|
||||
# Cancel the request so that WSRouter can perform cleanup
|
||||
@@ -107,7 +114,7 @@ class PluginWrapper:
|
||||
async def execute_legacy_method(self, method_name: str, kwargs: Dict[Any, Any]):
|
||||
if not self.legacy_method_warning:
|
||||
self.legacy_method_warning = True
|
||||
self.log.warning(f"Plugin {self.name} is using legacy method calls. This will be removed in a future release.")
|
||||
self.log.warning(f"Plugin {self.get_display_name()} is using legacy method calls. This will be removed in a future release.")
|
||||
if self.passive:
|
||||
raise RuntimeError("This plugin is passive (aka does not implement main.py)")
|
||||
|
||||
@@ -142,7 +149,7 @@ class PluginWrapper:
|
||||
start_time = time()
|
||||
if self.passive:
|
||||
return
|
||||
self.log.info(f"Shutting down {self.name}")
|
||||
self.log.info(f"Shutting down {self.get_display_name()}")
|
||||
|
||||
pending: set[Task[None]] | None = None;
|
||||
|
||||
@@ -162,16 +169,16 @@ class PluginWrapper:
|
||||
for pending_task in pending:
|
||||
pending_task.cancel()
|
||||
|
||||
self.log.info(f"Plugin {self.name} has been stopped in {time() - start_time:.1f}s")
|
||||
self.log.info(f"Plugin {self.get_display_name()} has been stopped in {time() - start_time:.1f}s")
|
||||
except Exception as e:
|
||||
self.log.error(f"Error during shutdown for plugin {self.name}: {str(e)}\n{format_exc()}")
|
||||
self.log.error(f"Error during shutdown for plugin {self.get_display_name()}: {str(e)}\n{format_exc()}")
|
||||
|
||||
async def kill_if_still_running(self):
|
||||
start_time = time()
|
||||
while self.proc and self.proc.is_alive():
|
||||
elapsed_time = time() - start_time
|
||||
if elapsed_time >= 5:
|
||||
self.log.warning(f"Plugin {self.name} still alive 5 seconds after stop request! Sending SIGKILL!")
|
||||
self.log.warning(f"Plugin {self.get_display_name()} still alive 5 seconds after stop request! Sending SIGKILL!")
|
||||
self.terminate(True)
|
||||
await sleep(0.1)
|
||||
|
||||
|
||||
@@ -45,6 +45,13 @@ class SandboxedPlugin:
|
||||
|
||||
self.log = getLogger("sandboxed_plugin")
|
||||
|
||||
def get_display_name(self) -> str:
|
||||
"""Returns plugin name with version if available, formatted for logging."""
|
||||
if self.version:
|
||||
return f"{self.name} (v{self.version})"
|
||||
|
||||
return self.name
|
||||
|
||||
def initialize(self, socket: LocalSocket):
|
||||
self._socket = socket
|
||||
|
||||
@@ -125,51 +132,51 @@ class SandboxedPlugin:
|
||||
get_event_loop().create_task(self.Plugin._main(self.Plugin))
|
||||
get_event_loop().create_task(socket.setup_server(self.on_new_message))
|
||||
except:
|
||||
self.log.error("Failed to start " + self.name + "!\n" + format_exc())
|
||||
self.log.error(f"Failed to start {self.get_display_name()}!\n{format_exc()}")
|
||||
sys.exit(0)
|
||||
try:
|
||||
get_event_loop().run_forever()
|
||||
except SystemExit:
|
||||
pass
|
||||
except:
|
||||
self.log.error("Loop exited for " + self.name + "!\n" + format_exc())
|
||||
self.log.error(f"Loop exited for {self.get_display_name()}!\n{format_exc()}")
|
||||
finally:
|
||||
get_event_loop().close()
|
||||
|
||||
async def _unload(self):
|
||||
try:
|
||||
self.log.info("Attempting to unload with plugin " + self.name + "'s \"_unload\" function.\n")
|
||||
self.log.info(f"Attempting to unload with plugin {self.get_display_name()}'s \"_unload\" function.\n")
|
||||
if hasattr(self.Plugin, "_unload"):
|
||||
if self.api_version > 0:
|
||||
await self.Plugin._unload()
|
||||
else:
|
||||
await self.Plugin._unload(self.Plugin)
|
||||
self.log.info("Unloaded " + self.name + "\n")
|
||||
self.log.info(f"Unloaded {self.get_display_name()}\n")
|
||||
else:
|
||||
self.log.info("Could not find \"_unload\" in " + self.name + "'s main.py" + "\n")
|
||||
self.log.info(f"Could not find \"_unload\" in {self.get_display_name()}'s main.py\n")
|
||||
except:
|
||||
self.log.error("Failed to unload " + self.name + "!\n" + format_exc())
|
||||
self.log.error(f"Failed to unload {self.get_display_name()}!\n{format_exc()}")
|
||||
pass
|
||||
|
||||
async def _uninstall(self):
|
||||
try:
|
||||
self.log.info("Attempting to uninstall with plugin " + self.name + "'s \"_uninstall\" function.\n")
|
||||
self.log.info(f"Attempting to uninstall with plugin {self.get_display_name()}'s \"_uninstall\" function.\n")
|
||||
if hasattr(self.Plugin, "_uninstall"):
|
||||
if self.api_version > 0:
|
||||
await self.Plugin._uninstall()
|
||||
else:
|
||||
await self.Plugin._uninstall(self.Plugin)
|
||||
self.log.info("Uninstalled " + self.name + "\n")
|
||||
self.log.info(f"Uninstalled {self.get_display_name()}\n")
|
||||
else:
|
||||
self.log.info("Could not find \"_uninstall\" in " + self.name + "'s main.py" + "\n")
|
||||
self.log.info(f"Could not find \"_uninstall\" in {self.get_display_name()}'s main.py\n")
|
||||
except:
|
||||
self.log.error("Failed to uninstall " + self.name + "!\n" + format_exc())
|
||||
self.log.error(f"Failed to uninstall {self.get_display_name()}!\n{format_exc()}")
|
||||
pass
|
||||
|
||||
async def shutdown(self):
|
||||
if not self.shutdown_running:
|
||||
self.shutdown_running = True
|
||||
self.log.info(f"Calling Loader unload function for {self.name}.")
|
||||
self.log.info(f"Calling Loader unload function for {self.get_display_name()}.")
|
||||
await self._unload()
|
||||
|
||||
if self.uninstalling:
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
requestPluginInstall,
|
||||
} from '../../../../store';
|
||||
import { useSetting } from '../../../../utils/hooks/useSetting';
|
||||
import { getPluginDisplayName } from '../../../../utils/pluginHelpers';
|
||||
import { useDeckyState } from '../../../DeckyState';
|
||||
import PluginListLabel from './PluginListLabel';
|
||||
|
||||
@@ -69,7 +70,7 @@ function PluginInteractables(props: { entry: ReorderableEntry<PluginTableData> }
|
||||
try {
|
||||
await reloadPluginBackend(name);
|
||||
} catch (err) {
|
||||
console.error('Error Reloading Plugin Backend', err);
|
||||
console.error(`Error Reloading Plugin Backend for ${getPluginDisplayName(name, version)}`, err);
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -38,6 +38,7 @@ import { checkForPluginUpdates } from './store';
|
||||
import TabsHook from './tabs-hook';
|
||||
import Toaster from './toaster';
|
||||
import { getVersionInfo } from './updater';
|
||||
import { getPluginDisplayName } from './utils/pluginHelpers';
|
||||
import { getSetting, setSetting } from './utils/settings';
|
||||
import TranslationHelper, { TranslationClass } from './utils/TranslationHelper';
|
||||
|
||||
@@ -343,7 +344,7 @@ class PluginLoader extends Logger {
|
||||
|
||||
public dismountAll() {
|
||||
for (const plugin of this.plugins) {
|
||||
this.log(`Dismounting ${plugin.name}`);
|
||||
this.log(`Dismounting ${getPluginDisplayName(plugin.name, plugin.version)}`);
|
||||
plugin.onDismount?.();
|
||||
}
|
||||
}
|
||||
@@ -403,14 +404,14 @@ class PluginLoader extends Logger {
|
||||
timeoutMS?: number,
|
||||
) {
|
||||
if (useQueue && this.reloadLock) {
|
||||
this.log('Reload currently in progress, adding to queue', name);
|
||||
this.log(`Reload currently in progress, adding ${getPluginDisplayName(name, version)} to queue`);
|
||||
this.pluginReloadQueue.push({ name, version: version, loadType });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (useQueue) this.reloadLock = true;
|
||||
this.log(`Trying to load ${name}`);
|
||||
this.log(`Trying to load ${getPluginDisplayName(name, version)}`);
|
||||
|
||||
this.unloadPlugin(name, true);
|
||||
const startTime = performance.now();
|
||||
@@ -420,7 +421,7 @@ class PluginLoader extends Logger {
|
||||
|
||||
this.deckyState.setDisabledPlugins(this.deckyState.publicState().disabledPlugins.filter((d) => d.name !== name));
|
||||
this.deckyState.setPlugins(this.plugins);
|
||||
this.log(`Loaded ${name} in ${endTime - startTime}ms`);
|
||||
this.log(`Loaded ${getPluginDisplayName(name, version)} in ${endTime - startTime}ms`);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
} finally {
|
||||
@@ -502,16 +503,17 @@ class PluginLoader extends Logger {
|
||||
version: version,
|
||||
loadType,
|
||||
});
|
||||
} else throw new Error(`${name} frontend_bundle not OK`);
|
||||
} else throw new Error(`${getPluginDisplayName(name, version)} frontend_bundle not OK`);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`${name} has no defined loadType.`);
|
||||
throw new Error(`${getPluginDisplayName(name, version)} has no defined loadType.`);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e === timeoutException) throw timeoutException;
|
||||
|
||||
this.error('Error loading plugin ' + name, e);
|
||||
this.error(`Error loading plugin ${getPluginDisplayName(name, version)}`, e);
|
||||
|
||||
const TheError: FC<{}> = () => (
|
||||
<PanelSection>
|
||||
<PanelSectionRow>
|
||||
@@ -731,7 +733,8 @@ class PluginLoader extends Logger {
|
||||
backendAPI.useQuickAccessVisible = useQuickAccessVisible;
|
||||
}
|
||||
|
||||
this.debug(`${pluginName} connected to loader API.`);
|
||||
// Note: version info not available at connection time
|
||||
this.debug(`Plugin ${pluginName} connected to loader API.`);
|
||||
return backendAPI;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Returns a formatted display name for a plugin with version if available
|
||||
* @param name - The plugin name
|
||||
* @param version - The optional plugin version
|
||||
*
|
||||
* @returns Formatted string like "PluginName (v1.2.3)" or just "PluginName" if version is undefined
|
||||
*/
|
||||
export function getPluginDisplayName(name: string, version?: string): string {
|
||||
if (version) {
|
||||
return `${name} (v${version})`;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
Reference in New Issue
Block a user