f/logs: only show the last 250 lines in the job logs

This commit is contained in:
Tyler Wilding 2023-03-05 16:17:06 -05:00
parent 93941a5cb3
commit f84a7c6403
No known key found for this signature in database
GPG key ID: 77CB07796494137E
9 changed files with 79 additions and 67 deletions

10
src-tauri/Cargo.lock generated
View file

@ -2273,6 +2273,7 @@ dependencies = [
"futures-util",
"log",
"reqwest",
"rev_buf_reader",
"serde",
"serde_json",
"sysinfo",
@ -2902,6 +2903,15 @@ dependencies = [
"winreg",
]
[[package]]
name = "rev_buf_reader"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c0f2e47e00e29920959826e2e1784728a3780d1a784247be5257258cc75f910"
dependencies = [
"memchr",
]
[[package]]
name = "rfd"
version = "0.10.0"

View file

@ -32,6 +32,7 @@ wgpu = "0.15.1"
walkdir = "2.3.2"
dir-diff = "0.3.2"
thiserror = "1.0.38"
rev_buf_reader = "0.3.0"
[features]
# by default Tauri runs in production mode

View file

@ -12,7 +12,7 @@ use tauri::Manager;
use crate::{
config::LauncherConfig,
util::file::{create_dir, overwrite_dir, read_lines_in_file},
util::file::{create_dir, overwrite_dir, read_last_lines_from_file, read_lines_in_file},
};
use super::CommandError;
@ -244,9 +244,16 @@ pub async fn update_data_directory(
})
}
#[derive(Clone, serde::Serialize)]
struct LogPayload {
stdout: String,
#[tauri::command]
pub async fn get_end_of_logs(app_handle: tauri::AppHandle) -> Result<String, CommandError> {
Ok(read_last_lines_from_file(
&app_handle
.path_resolver()
.app_log_dir()
.unwrap()
.join("extractor.log"),
250,
)?)
}
#[tauri::command]
@ -283,21 +290,6 @@ pub async fn extract_and_validate_iso(
.stdout(log_file.try_clone().unwrap())
.stderr(log_file.try_clone().unwrap())
.output()?;
// TODO - we should instead capture the logs while simulanteously streaming them to a file
// right now we aren't so I just read the file after it's done
app_handle.emit_all(
"updateJobLogs",
LogPayload {
stdout: read_lines_in_file(
&app_handle
.path_resolver()
.app_log_dir()
.unwrap()
.join("extractor.log"),
)
.expect("TODO"),
},
)?;
match output.status.code() {
Some(code) => {
if code == 0 {
@ -329,6 +321,7 @@ pub async fn run_decompiler(
app_handle: tauri::AppHandle,
path_to_iso: String,
game_name: String,
truncate_logs: bool,
) -> Result<InstallStepOutput, CommandError> {
let config_lock = config.lock().await;
let config_info = common_prelude(&config_lock)?;
@ -345,7 +338,7 @@ pub async fn run_decompiler(
.to_string();
}
let log_file = create_log_file(&app_handle, "extractor.log", true)?;
let log_file = create_log_file(&app_handle, "extractor.log", !truncate_logs)?;
let output = Command::new(&exec_info.executable_path)
.creation_flags(0x08000000)
.args([
@ -358,21 +351,6 @@ pub async fn run_decompiler(
.stderr(log_file)
.current_dir(exec_info.executable_dir)
.output()?;
// TODO - we should instead capture the logs while simulanteously streaming them to a file
// right now we aren't so I just read the file after it's done
app_handle.emit_all(
"updateJobLogs",
LogPayload {
stdout: read_lines_in_file(
&app_handle
.path_resolver()
.app_log_dir()
.unwrap()
.join("extractor.log"),
)
.expect("TODO"),
},
)?;
match output.status.code() {
Some(code) => {
if code == 0 {
@ -404,6 +382,7 @@ pub async fn run_compiler(
app_handle: tauri::AppHandle,
path_to_iso: String,
game_name: String,
truncate_logs: bool,
) -> Result<InstallStepOutput, CommandError> {
let config_lock = config.lock().await;
let config_info = common_prelude(&config_lock)?;
@ -420,7 +399,7 @@ pub async fn run_compiler(
.to_string();
}
let log_file = create_log_file(&app_handle, "extractor.log", true)?;
let log_file = create_log_file(&app_handle, "extractor.log", !truncate_logs)?;
let output = Command::new(&exec_info.executable_path)
.creation_flags(0x08000000)
.args([
@ -433,21 +412,6 @@ pub async fn run_compiler(
.stderr(log_file)
.current_dir(exec_info.executable_dir)
.output()?;
// TODO - we should instead capture the logs while simulanteously streaming them to a file
// right now we aren't so I just read the file after it's done
app_handle.emit_all(
"updateJobLogs",
LogPayload {
stdout: read_lines_in_file(
&app_handle
.path_resolver()
.app_log_dir()
.unwrap()
.join("extractor.log"),
)
.expect("TODO"),
},
)?;
match output.status.code() {
Some(code) => {
if code == 0 {

View file

@ -78,6 +78,7 @@ fn main() {
})
.invoke_handler(tauri::generate_handler![
commands::binaries::update_data_directory,
commands::binaries::get_end_of_logs,
commands::binaries::extract_and_validate_iso,
commands::binaries::launch_game,
commands::binaries::open_repl,

View file

@ -1,4 +1,4 @@
use std::path::PathBuf;
use std::{io::BufRead, path::PathBuf};
pub fn delete_dir(path: &PathBuf) -> Result<(), std::io::Error> {
if path.exists() && path.is_dir() {
@ -36,3 +36,21 @@ pub fn overwrite_dir(src: &PathBuf, dst: &PathBuf) -> Result<(), fs_extra::error
pub fn read_lines_in_file(path: &PathBuf) -> Result<String, Box<dyn std::error::Error>> {
Ok(std::fs::read_to_string(path)?)
}
pub fn read_last_lines_from_file(path: &PathBuf, lines: usize) -> Result<String, std::io::Error> {
if !path.exists() {
return Ok("".to_owned());
}
let buf = rev_buf_reader::RevBufReader::new(std::fs::File::open(path)?);
Ok(
buf
.lines()
.take(lines)
.map(|l| l.unwrap_or("".to_owned()))
.collect::<Vec<String>>()
.into_iter()
.rev()
.collect::<Vec<String>>()
.join("\n"),
)
}

View file

@ -9,13 +9,13 @@
import type { Job } from "$lib/jobs/jobs";
import { getInternalName, type SupportedGame } from "$lib/constants";
import {
getEndOfLogs,
runCompiler,
runDecompiler,
updateDataDirectory,
} from "$lib/rpc/binaries";
import { finalizeInstallation } from "$lib/rpc/config";
import { generateSupportPackage } from "$lib/rpc/support";
import { listen } from "@tauri-apps/api/event";
export let activeGame: SupportedGame;
export let jobType: Job;
@ -29,10 +29,6 @@
// It's used to provide almost the same interface as the normal installation, with logs, etc
// but for arbitrary jobs. Such as updating versions, decompiling, or compiling.
onMount(async () => {
const unlistenLogListener = await listen("updateJobLogs", async (event) => {
progressTracker.updateLogs(event.payload["stdout"]);
});
if (jobType === "decompile") {
installationError = undefined;
progressTracker.init([
@ -46,7 +42,8 @@
},
]);
progressTracker.start();
let resp = await runDecompiler("", getInternalName(activeGame));
let resp = await runDecompiler("", getInternalName(activeGame), true);
progressTracker.updateLogs(await getEndOfLogs());
if (!resp.success) {
progressTracker.halt();
installationError = resp.msg;
@ -67,7 +64,8 @@
},
]);
progressTracker.start();
let resp = await runCompiler("", getInternalName(activeGame));
let resp = await runCompiler("", getInternalName(activeGame), true);
progressTracker.updateLogs(await getEndOfLogs());
if (!resp.success) {
progressTracker.halt();
installationError = resp.msg;
@ -97,13 +95,15 @@
]);
progressTracker.start();
let resp = await updateDataDirectory(getInternalName(activeGame));
progressTracker.updateLogs(await getEndOfLogs());
if (!resp.success) {
progressTracker.halt();
installationError = resp.msg;
return;
}
progressTracker.proceed();
resp = await runDecompiler("", getInternalName(activeGame));
resp = await runDecompiler("", getInternalName(activeGame), true);
progressTracker.updateLogs(await getEndOfLogs());
if (!resp.success) {
progressTracker.halt();
installationError = resp.msg;
@ -111,6 +111,7 @@
}
progressTracker.proceed();
resp = await runCompiler("", getInternalName(activeGame));
progressTracker.updateLogs(await getEndOfLogs());
if (!resp.success) {
progressTracker.halt();
installationError = resp.msg;

View file

@ -13,6 +13,7 @@
import { Alert, Button } from "flowbite-svelte";
import {
extractAndValidateISO,
getEndOfLogs,
runCompiler,
runDecompiler,
} from "$lib/rpc/binaries";
@ -26,7 +27,6 @@
import { progressTracker } from "$lib/stores/ProgressStore";
import { generateSupportPackage } from "$lib/rpc/support";
import { isOpenGLVersionSupported } from "$lib/sidecars/glewinfo";
import { listen } from "@tauri-apps/api/event";
export let activeGame: SupportedGame;
@ -45,10 +45,6 @@
await setOpenGLRequirementMet(isOpenGLMet);
}
requirementsMet = isAvxMet && isOpenGLMet;
const unlistenLogListener = await listen("updateJobLogs", async (event) => {
progressTracker.updateLogs(event.payload["stdout"]);
});
});
async function install(viaFolder: boolean) {
@ -88,6 +84,7 @@
sourcePath,
getInternalName(activeGame)
);
progressTracker.updateLogs(await getEndOfLogs());
if (!resp.success) {
progressTracker.halt();
installationError = resp.msg;
@ -95,6 +92,7 @@
}
progressTracker.proceed();
resp = await runDecompiler(sourcePath, getInternalName(activeGame));
progressTracker.updateLogs(await getEndOfLogs());
if (!resp.success) {
progressTracker.halt();
installationError = resp.msg;
@ -102,6 +100,7 @@
}
progressTracker.proceed();
resp = await runCompiler(sourcePath, getInternalName(activeGame));
progressTracker.updateLogs(await getEndOfLogs());
if (!resp.success) {
progressTracker.halt();
installationError = resp.msg;

View file

@ -21,6 +21,8 @@
class="bg-slate-900 px-4 max-h-60 overflow-y-scroll scrollbar"
>
<p class="py-4 text-clip overflow-hidden font-mono log-output">
...Last 250 Lines:
<br />
{@html convertLogColors($progressTracker.logs)}
</p>
</div>

View file

@ -24,6 +24,18 @@ export async function updateDataDirectory(
}
}
export async function getEndOfLogs(): Promise<string> {
try {
return await invoke("get_end_of_logs", {});
} catch (e) {
console.log(
"[OG]: Unexpected error encountered when updating data directory",
e
);
return "";
}
}
export async function extractAndValidateISO(
pathToIso: string,
gameName: string
@ -47,12 +59,14 @@ export async function extractAndValidateISO(
export async function runDecompiler(
pathToIso: string,
gameName: string
gameName: string,
truncateLogs: boolean = false
): Promise<InstallationOutput> {
try {
return await invoke("run_decompiler", {
pathToIso: pathToIso,
gameName: gameName,
truncateLogs: truncateLogs,
});
} catch (e) {
console.log(
@ -68,12 +82,14 @@ export async function runDecompiler(
export async function runCompiler(
pathToIso: string,
gameName: string
gameName: string,
truncateLogs: boolean = false
): Promise<InstallationOutput> {
try {
return await invoke("run_compiler", {
pathToIso: pathToIso,
gameName: gameName,
truncateLogs: truncateLogs,
});
} catch (e) {
console.log(