Skip to content

Commit

Permalink
Merge pull request #837 from hydralauncher/feat/logs-python-process-e…
Browse files Browse the repository at this point in the history
…rrors

feat: logs python process
  • Loading branch information
zamitto authored Aug 20, 2024
2 parents aaf20b2 + 24689ca commit 7e7c4da
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 11 deletions.
28 changes: 22 additions & 6 deletions src/main/services/download/python-instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
LibtorrentPayload,
ProcessPayload,
} from "./types";
import { pythonInstanceLogger as logger } from "../logger";

export class PythonInstance {
private static pythonProcess: cp.ChildProcess | null = null;
Expand All @@ -32,11 +33,13 @@ export class PythonInstance {
});

public static spawn(args?: StartDownloadPayload) {
logger.log("spawning python process with args:", args);
this.pythonProcess = startRPCClient(args);
}

public static kill() {
if (this.pythonProcess) {
logger.log("killing python process");
this.pythonProcess.kill();
this.pythonProcess = null;
this.downloadingGameId = -1;
Expand All @@ -45,6 +48,7 @@ export class PythonInstance {

public static killTorrent() {
if (this.pythonProcess) {
logger.log("killing torrent in python process");
this.rpc.post("/action", { action: "kill-torrent" });
this.downloadingGameId = -1;
}
Expand Down Expand Up @@ -138,12 +142,14 @@ export class PythonInstance {
save_path: game.downloadPath!,
});
} else {
await this.rpc.post("/action", {
action: "start",
game_id: game.id,
magnet: game.uri,
save_path: game.downloadPath,
} as StartDownloadPayload);
await this.rpc
.post("/action", {
action: "start",
game_id: game.id,
magnet: game.uri,
save_path: game.downloadPath,
} as StartDownloadPayload)
.catch(this.handleRpcError);
}

this.downloadingGameId = game.id;
Expand All @@ -159,4 +165,14 @@ export class PythonInstance {

this.downloadingGameId = -1;
}

private static async handleRpcError(_error: unknown) {
await this.rpc.get("/healthcheck").catch(() => {
logger.error(
"RPC healthcheck failed. Killing process and starting again"
);
this.kill();
this.spawn();
});
}
}
25 changes: 21 additions & 4 deletions src/main/services/download/torrent-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import crypto from "node:crypto";
import fs from "node:fs";
import { app, dialog } from "electron";
import type { StartDownloadPayload } from "./types";
import { Readable } from "node:stream";
import { pythonInstanceLogger as logger } from "../logger";

const binaryNameByPlatform: Partial<Record<NodeJS.Platform, string>> = {
darwin: "hydra-download-manager",
Expand All @@ -15,6 +17,13 @@ export const BITTORRENT_PORT = "5881";
export const RPC_PORT = "8084";
export const RPC_PASSWORD = crypto.randomBytes(32).toString("hex");

const logStderr = (readable: Readable | null) => {
if (!readable) return;

readable.setEncoding("utf-8");
readable.on("data", logger.log);
};

export const startTorrentClient = (args?: StartDownloadPayload) => {
const commonArgs = [
BITTORRENT_PORT,
Expand All @@ -40,10 +49,14 @@ export const startTorrentClient = (args?: StartDownloadPayload) => {
app.quit();
}

return cp.spawn(binaryPath, commonArgs, {
stdio: "inherit",
const childProcess = cp.spawn(binaryPath, commonArgs, {
windowsHide: true,
stdio: ["inherit", "inherit"],
});

logStderr(childProcess.stderr);

return childProcess;
} else {
const scriptPath = path.join(
__dirname,
Expand All @@ -53,8 +66,12 @@ export const startTorrentClient = (args?: StartDownloadPayload) => {
"main.py"
);

return cp.spawn("python3", [scriptPath, ...commonArgs], {
stdio: "inherit",
const childProcess = cp.spawn("python3", [scriptPath, ...commonArgs], {
stdio: ["inherit", "inherit"],
});

logStderr(childProcess.stderr);

return childProcess;
}
};
5 changes: 5 additions & 0 deletions src/main/services/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ log.transports.file.resolvePathFn = (
_: log.PathVariables,
message?: log.LogMessage | undefined
) => {
if (message?.scope === "python-instance") {
return path.join(logsPath, "pythoninstance.txt");
}

if (message?.level === "error") {
return path.join(logsPath, "error.txt");
}
Expand All @@ -23,4 +27,5 @@ log.errorHandler.startCatching({

log.initialize();

export const pythonInstanceLogger = log.scope("python-instance");
export const logger = log.scope("main");
2 changes: 1 addition & 1 deletion src/main/services/window-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export class WindowManager {

const recentlyPlayedGames: Array<MenuItemConstructorOptions | MenuItem> =
games.map(({ title, executablePath }) => ({
label: title,
label: title.length > 15 ? `${title.slice(0, 15)}…` : title,
type: "normal",
click: async () => {
if (!executablePath) return;
Expand Down
17 changes: 17 additions & 0 deletions torrent-client/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,23 @@
class Handler(BaseHTTPRequestHandler):
rpc_password_header = 'x-hydra-rpc-password'

skip_log_routes = [
"process-list",
"status"
]

def log_error(self, format, *args):
sys.stderr.write("%s - - [%s] %s\n" %
(self.address_string(),
self.log_date_time_string(),
format%args))

def log_message(self, format, *args):
for route in self.skip_log_routes:
if route in args[0]: return

super().log_message(format, *args)

def do_GET(self):
if self.path == "/status":
if self.headers.get(self.rpc_password_header) != rpc_password:
Expand Down

0 comments on commit 7e7c4da

Please sign in to comment.