From 0d7e01a478ba14601da9918109283d1ae4750dec Mon Sep 17 00:00:00 2001 From: FrogTheFrog Date: Thu, 2 Jul 2026 23:16:59 +0300 Subject: [PATCH 1/2] wip --- defaults/python/cli.py | 5 ++-- defaults/python/lib/constants.py | 1 + defaults/python/lib/logger.py | 25 ++++++++++--------- defaults/python/runner.py | 7 +++--- main.py | 24 +++++++++++++++--- package.json | 1 + pnpm-lock.yaml | 11 ++++++++- src/lib/executeasync.ts | 15 ++++++++---- src/lib/logger.ts | 42 ++++++++++++++++++++++++++------ src/lib/settingsmanager.ts | 1 + 10 files changed, 100 insertions(+), 32 deletions(-) diff --git a/defaults/python/cli.py b/defaults/python/cli.py index 4d02d3de..a894c52d 100644 --- a/defaults/python/cli.py +++ b/defaults/python/cli.py @@ -432,7 +432,7 @@ async def main(): verbose = False try: # ---- Setup early logging to at least print something out - set_logger_settings(None, print_to_std=True) + set_logger_settings(logger, None, print_to_std=True) # ---- General parser setup [start] parser = ArgumentParserWithRedirect( @@ -446,7 +446,8 @@ async def main(): # -------- Setup logging (as early as possible) parser_args, _ = parser.parse_known_args() verbose = parser_args.verbose - set_logger_settings(parser_args.log_file, + set_logger_settings(logger, + parser_args.log_file, print_to_std=True, log_preamble=f"Executing: {sys.argv[:]}, CWD: {Path.cwd()}", verbose=verbose) diff --git a/defaults/python/lib/constants.py b/defaults/python/lib/constants.py index 9525bf93..d99e2d56 100644 --- a/defaults/python/lib/constants.py +++ b/defaults/python/lib/constants.py @@ -1,5 +1,6 @@ BUDDY_API_VERSION = 8 LOG_FILE = "/tmp/moondeck.log" +FRONTEND_LOG_FILE = "/tmp/moondeck-frontend.log" RUNNER_LOG_FILE = "/tmp/moondeck-runner.log" MOONLIGHT_LOG_FILE = "/tmp/moondeck-runner-moonlight.log" RUNNER_RESULT_FILE = "/tmp/moondeck-runner.result" diff --git a/defaults/python/lib/logger.py b/defaults/python/lib/logger.py index 80fe1a8b..da53489e 100644 --- a/defaults/python/lib/logger.py +++ b/defaults/python/lib/logger.py @@ -1,6 +1,6 @@ import logging -def set_logger_settings(filename: str | None, rotate: bool = True, log_preamble: str | None = None, print_to_std: bool = False, verbose: bool = False): +def set_logger_settings(instance: logging.Logger, filename: str | None, rotate: bool = True, log_preamble: str | None = None, print_to_std: bool = False, verbose: bool = False): # Lazy import to improve CLI performance import sys from pathlib import Path @@ -22,8 +22,7 @@ def set_logger_settings(filename: str | None, rotate: bool = True, log_preamble: handler.setFormatter(logging.Formatter("%(message)s")) handler.emit(logging.makeLogRecord({"msg": f"{optional_separator}{log_preamble}"})) - handler.setFormatter(logging.Formatter( - "[%(asctime)s] %(levelname)s: %(message)s")) + handler.setFormatter(logging.Formatter("[%(asctime)s] %(levelname)s: %(message)s")) handlers.append(handler) if print_to_std: @@ -42,16 +41,20 @@ def set_logger_settings(filename: str | None, rotate: bool = True, log_preamble: handlers.append(stdout_handler) handlers.append(stderr_handler) - logging.basicConfig(handlers=handlers, - level=logging.INFO, - force=True) + for h in instance.handlers[:]: + instance.removeHandler(h) + h.close() - if verbose: - enable_debug_level() + for h in handlers: + instance.addHandler(h) + instance.setLevel(logging.DEBUG if verbose else logging.INFO) -def enable_debug_level(): - logging.getLogger().setLevel(logging.DEBUG) +def get_logger(name: str | None = None): + logger = logging.getLogger(name) + logger.propagate = False + return logger -logger = logging.getLogger() + +logger = get_logger() diff --git a/defaults/python/runner.py b/defaults/python/runner.py index 4c92ca83..e5aff315 100644 --- a/defaults/python/runner.py +++ b/defaults/python/runner.py @@ -13,11 +13,12 @@ def add_plugin_to_path(): # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< # autopep8: on +import logging from lib.buddyrequests import BuddyException from lib.runner.moondeckapprunner import MoonDeckAppRunner from lib.runner.moonlightonlyrunner import MoonlightOnlyRunner -from lib.logger import logger, set_logger_settings, enable_debug_level +from lib.logger import logger, set_logger_settings from lib.utils import is_moondeck_runner_ready from lib.runner.settingsparser import parse_settings, RunnerType @@ -25,7 +26,7 @@ def add_plugin_to_path(): import lib.runnerresult as runnerresult -set_logger_settings(constants.RUNNER_LOG_FILE, rotate=False) +set_logger_settings(logger, constants.RUNNER_LOG_FILE, rotate=False) async def main(): try: @@ -39,7 +40,7 @@ async def main(): settings = await parse_settings() if settings["debug_logs"]: - enable_debug_level() + logger.setLevel(logging.DEBUG) if settings["runner_type"] == RunnerType.MoonDeck: await MoonDeckAppRunner.run(settings) diff --git a/main.py b/main.py index f74c43c5..a711fc64 100644 --- a/main.py +++ b/main.py @@ -15,6 +15,7 @@ def add_plugin_to_path(): import asyncio +import logging import pathlib import lib.gamestreaminfo as gamestreaminfo import lib.constants as constants @@ -22,15 +23,18 @@ def add_plugin_to_path(): from typing import Optional from lib.plugin.settings import UserSettings, UserSettingsManager -from lib.logger import logger, set_logger_settings +from lib.logger import logger, set_logger_settings, get_logger from lib.buddyrequests import SteamUiMode, SteamUiModeResponse, CurrentUserResponse, BuddyException from lib.buddyclient import BuddyClient, AbortHostStateChangeResult from lib.utils import wake_on_lan, change_moondeck_runner_ready_state, TimedPooler from lib.runnerresult import Result, set_result, get_result from lib.moonlightproxy import MoonlightProxy +set_logger_settings(logger, constants.LOG_FILE, rotate=True) + +frontend_logger = get_logger("frontend") +set_logger_settings(frontend_logger, constants.FRONTEND_LOG_FILE, rotate=True) -set_logger_settings(constants.LOG_FILE, rotate=True) settings_manager = UserSettingsManager(constants.get_config_file_path()) @@ -47,6 +51,12 @@ async def _main(self): async def _unload(self): self.__cleanup_states() + async def frontend_log_entry(self, level: str, message: str): + try: + frontend_logger.log(logging._checkLevel(level), message) + except Exception: + logger.exception("Failed to save frontend log") + @utils.async_scope_log(logger.info) async def set_runner_ready(self): change_moondeck_runner_ready_state(True) @@ -57,11 +67,18 @@ async def get_runner_result(self): # Cleanup is needed to differentiate between moondeck launches set_result(None) return result + + def set_log_levels(self, data: UserSettings): + level = logging.DEBUG if data["runnerDebugLogs"] else logging.INFO + logger.setLevel(level) + frontend_logger.setLevel(level) @utils.async_scope_log(logger.info) async def get_user_settings(self): try: - return await settings_manager.read_or_update() + data = await settings_manager.read_or_update() + self.set_log_levels(data) + return data except Exception: logger.exception("Failed to get user settings") return None @@ -70,6 +87,7 @@ async def get_user_settings(self): async def set_user_settings(self, data: UserSettings): try: await settings_manager.write(data) + self.set_log_levels(data) except Exception: logger.exception("Failed to set user settings") diff --git a/package.json b/package.json index 1d1bb578..8fb93f16 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "async-mutex": "^0.5.0", "ip-regex": "^5.0.0", "lodash": "^4.17.21", + "loupe": "^3.2.1", "mobx": "^6.15.0", "promise-throttle-all": "^1.1.1", "react-icons": "^5.5.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ca6ff042..ef096ceb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: lodash: specifier: ^4.17.21 version: 4.17.21 + loupe: + specifier: ^3.2.1 + version: 3.2.1 mobx: specifier: ^6.15.0 version: 6.15.0 @@ -841,11 +844,12 @@ packages: glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} @@ -997,6 +1001,9 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -2308,6 +2315,8 @@ snapshots: lodash@4.17.21: {} + loupe@3.2.1: {} + lru-cache@10.4.3: {} magic-string@0.30.10: diff --git a/src/lib/executeasync.ts b/src/lib/executeasync.ts index 8391fc75..4cb1b326 100644 --- a/src/lib/executeasync.ts +++ b/src/lib/executeasync.ts @@ -1,17 +1,22 @@ import { logger } from "./logger"; -type AsyncCallback = () => Promise; +type VoidPromise = Promise; +type AsyncCallback = () => VoidPromise; -export function executeAsyncWrapper(callback: AsyncCallback) { +export function executeAsyncWrapper(input: AsyncCallback | VoidPromise) { return () => { Promise.resolve() .then(async () => { - await callback(); + if (typeof input === "function") { + await input(); + } else { + await input; + } }) .catch((e) => logger.critical(e)); }; } -export function executeAsync(callback: AsyncCallback) { - executeAsyncWrapper(callback)(); +export function executeAsync(input: AsyncCallback | VoidPromise) { + executeAsyncWrapper(input)(); } diff --git a/src/lib/logger.ts b/src/lib/logger.ts index 499b58b9..beac9898 100644 --- a/src/lib/logger.ts +++ b/src/lib/logger.ts @@ -1,6 +1,32 @@ -import { toaster } from "@decky/api"; +import { call, toaster } from "@decky/api"; +import { executeAsync } from "./executeasync"; +import { inspect } from "loupe"; + +function serialize(...args: unknown[]) { + return args.map((arg) => inspect(arg)).join(" "); +} + +function criticalConsoleLog(...args: unknown[]) { + console.error( + "%c MoonDeck %c Critical:", + "background: #16a085; color: black;", + "background: transparent;", + ...args + ); +} + +type PythonLogLevel = "DEBUG" | "INFO" | "WARNING" | "ERROR" | "CRITICAL"; +async function frontendLogEntry(level: PythonLogLevel, ...args: unknown[]): Promise { + try { + await call<[PythonLogLevel, string], unknown>("frontend_log_entry", level, serialize(...args)); + } catch (message) { + criticalConsoleLog("Error while trying to save frontend log: ", message); + } +} class Logger { + public verbose: boolean = false; + toast(entry: string, options: { duration?: number; output?: "debug" | "log" | "warn" | "error" | null } = {}): void { const { duration = 5000, output = "log" } = options; const critical = output === "error"; @@ -32,6 +58,9 @@ class Logger { "background: transparent;", ...args ); + if (this.verbose) { + executeAsync(frontendLogEntry("DEBUG", ...args)); + } } log(...args: unknown[]): void { @@ -42,6 +71,7 @@ class Logger { "background: transparent;", ...args ); + executeAsync(frontendLogEntry("INFO", ...args)); } warn(...args: unknown[]): void { @@ -52,6 +82,7 @@ class Logger { "background: transparent;", ...args ); + executeAsync(frontendLogEntry("WARNING", ...args)); } error(...args: unknown[]): void { @@ -62,15 +93,12 @@ class Logger { "background: transparent;", ...args ); + executeAsync(frontendLogEntry("ERROR", ...args)); } critical(...args: unknown[]): void { - console.error( - "%c MoonDeck %c Critical:", - "background: #16a085; color: black;", - "background: transparent;", - ...args - ); + criticalConsoleLog(...args); + executeAsync(frontendLogEntry("CRITICAL", ...args)); } } diff --git a/src/lib/settingsmanager.ts b/src/lib/settingsmanager.ts index 6f87c9c9..a866c0e8 100644 --- a/src/lib/settingsmanager.ts +++ b/src/lib/settingsmanager.ts @@ -227,6 +227,7 @@ export class SettingsManager { const settings = await getUserSettings(); if (!isEqual(this.settings.value, settings)) { + logger.verbose = settings?.runnerDebugLogs ?? false; this.settingsSubject.next(settings); } } finally { From b8ad776f767fc12944114a857c8b2ac10457cb52 Mon Sep 17 00:00:00 2001 From: FrogTheFrog Date: Fri, 3 Jul 2026 00:22:01 +0300 Subject: [PATCH 2/2] wip --- defaults/python/lib/logger.py | 2 +- main.py | 10 ++++++---- src/lib/logger.ts | 21 ++++++++++----------- src/lib/settingsmanager.ts | 1 - 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/defaults/python/lib/logger.py b/defaults/python/lib/logger.py index da53489e..c876e3fa 100644 --- a/defaults/python/lib/logger.py +++ b/defaults/python/lib/logger.py @@ -17,7 +17,7 @@ def set_logger_settings(instance: logging.Logger, filename: str | None, rotate: ) if log_preamble is not None: - optional_separator = "\n\n" if Path(filename).stat().st_size > 0 else "" + optional_separator = "\n\n" if Path(filename).stat().st_size > 0 and log_preamble != "" else "" handler.setFormatter(logging.Formatter("%(message)s")) handler.emit(logging.makeLogRecord({"msg": f"{optional_separator}{log_preamble}"})) diff --git a/main.py b/main.py index a711fc64..43677bd4 100644 --- a/main.py +++ b/main.py @@ -30,10 +30,10 @@ def add_plugin_to_path(): from lib.runnerresult import Result, set_result, get_result from lib.moonlightproxy import MoonlightProxy -set_logger_settings(logger, constants.LOG_FILE, rotate=True) +set_logger_settings(logger, constants.LOG_FILE, rotate=True, verbose=True) frontend_logger = get_logger("frontend") -set_logger_settings(frontend_logger, constants.FRONTEND_LOG_FILE, rotate=True) +set_logger_settings(frontend_logger, constants.FRONTEND_LOG_FILE, rotate=True, log_preamble="", verbose=True) settings_manager = UserSettingsManager(constants.get_config_file_path()) @@ -70,8 +70,10 @@ async def get_runner_result(self): def set_log_levels(self, data: UserSettings): level = logging.DEBUG if data["runnerDebugLogs"] else logging.INFO - logger.setLevel(level) - frontend_logger.setLevel(level) + for instance in [logger, frontend_logger]: + if instance.level != level: + instance.setLevel(level) + instance.info(f"Set logger log level to {logging.getLevelName(level)}") @utils.async_scope_log(logger.info) async def get_user_settings(self): diff --git a/src/lib/logger.ts b/src/lib/logger.ts index beac9898..60867672 100644 --- a/src/lib/logger.ts +++ b/src/lib/logger.ts @@ -1,9 +1,8 @@ import { call, toaster } from "@decky/api"; -import { executeAsync } from "./executeasync"; import { inspect } from "loupe"; function serialize(...args: unknown[]) { - return args.map((arg) => inspect(arg)).join(" "); + return args.map((arg) => typeof arg === "string" ? arg : inspect(arg)).join(" "); } function criticalConsoleLog(...args: unknown[]) { @@ -15,6 +14,10 @@ function criticalConsoleLog(...args: unknown[]) { ); } +function executeAsyncSafe(input: Promise) { + Promise.resolve(input).catch((e) => criticalConsoleLog(e)); +} + type PythonLogLevel = "DEBUG" | "INFO" | "WARNING" | "ERROR" | "CRITICAL"; async function frontendLogEntry(level: PythonLogLevel, ...args: unknown[]): Promise { try { @@ -25,8 +28,6 @@ async function frontendLogEntry(level: PythonLogLevel, ...args: unknown[]): Prom } class Logger { - public verbose: boolean = false; - toast(entry: string, options: { duration?: number; output?: "debug" | "log" | "warn" | "error" | null } = {}): void { const { duration = 5000, output = "log" } = options; const critical = output === "error"; @@ -58,9 +59,7 @@ class Logger { "background: transparent;", ...args ); - if (this.verbose) { - executeAsync(frontendLogEntry("DEBUG", ...args)); - } + executeAsyncSafe(frontendLogEntry("DEBUG", ...args)); } log(...args: unknown[]): void { @@ -71,7 +70,7 @@ class Logger { "background: transparent;", ...args ); - executeAsync(frontendLogEntry("INFO", ...args)); + executeAsyncSafe(frontendLogEntry("INFO", ...args)); } warn(...args: unknown[]): void { @@ -82,7 +81,7 @@ class Logger { "background: transparent;", ...args ); - executeAsync(frontendLogEntry("WARNING", ...args)); + executeAsyncSafe(frontendLogEntry("WARNING", ...args)); } error(...args: unknown[]): void { @@ -93,12 +92,12 @@ class Logger { "background: transparent;", ...args ); - executeAsync(frontendLogEntry("ERROR", ...args)); + executeAsyncSafe(frontendLogEntry("ERROR", ...args)); } critical(...args: unknown[]): void { criticalConsoleLog(...args); - executeAsync(frontendLogEntry("CRITICAL", ...args)); + executeAsyncSafe(frontendLogEntry("CRITICAL", ...args)); } } diff --git a/src/lib/settingsmanager.ts b/src/lib/settingsmanager.ts index a866c0e8..6f87c9c9 100644 --- a/src/lib/settingsmanager.ts +++ b/src/lib/settingsmanager.ts @@ -227,7 +227,6 @@ export class SettingsManager { const settings = await getUserSettings(); if (!isEqual(this.settings.value, settings)) { - logger.verbose = settings?.runnerDebugLogs ?? false; this.settingsSubject.next(settings); } } finally {