From 9e7c7ac16355fa60d6521b4fca7aa6612a16584a Mon Sep 17 00:00:00 2001 From: GyDi Date: Mon, 5 Sep 2022 20:30:39 +0800 Subject: [PATCH] feat: log info --- src-tauri/src/cmds.rs | 8 +- src-tauri/src/core/mod.rs | 1 + src-tauri/src/core/service.rs | 108 +++++++++++++++++-------- src-tauri/src/main.rs | 1 + src/components/layout/use-log-setup.ts | 3 + src/services/cmds.ts | 23 ++++++ 6 files changed, 110 insertions(+), 34 deletions(-) diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs index 19fb739..e7a3d2b 100644 --- a/src-tauri/src/cmds.rs +++ b/src-tauri/src/cmds.rs @@ -5,7 +5,7 @@ use crate::{ use crate::{log_if_err, ret_err, wrap_err}; use anyhow::Result; use serde_yaml::Mapping; -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use tauri::{api, State}; type CmdResult = Result; @@ -256,6 +256,12 @@ pub fn get_cur_proxy(core: State<'_, Core>) -> CmdResult> wrap_err!(sysopt.get_sysproxy()) } +#[tauri::command] +pub fn get_clash_logs(core: State<'_, Core>) -> CmdResult> { + let service = core.service.lock(); + Ok(service.get_logs()) +} + /// open app config dir #[tauri::command] pub fn open_app_dir() -> Result<(), String> { diff --git a/src-tauri/src/core/mod.rs b/src-tauri/src/core/mod.rs index 70b0e5c..f3bd3f9 100644 --- a/src-tauri/src/core/mod.rs +++ b/src-tauri/src/core/mod.rs @@ -129,6 +129,7 @@ impl Core { let mut service = self.service.lock(); service.stop()?; service.set_core(Some(clash_core)); + service.clear_logs(); service.start()?; drop(service); diff --git a/src-tauri/src/core/service.rs b/src-tauri/src/core/service.rs index 8557dcd..7c29e6f 100644 --- a/src-tauri/src/core/service.rs +++ b/src-tauri/src/core/service.rs @@ -2,28 +2,38 @@ use super::{notice::Notice, ClashInfo}; use crate::log_if_err; use crate::utils::{config, dirs}; use anyhow::{bail, Result}; +use parking_lot::RwLock; use reqwest::header::HeaderMap; use serde_yaml::Mapping; use std::fs; use std::io::Write; -use std::{collections::HashMap, time::Duration}; +use std::sync::Arc; +use std::{ + collections::{HashMap, VecDeque}, + time::Duration, +}; use tauri::api::process::{Command, CommandChild, CommandEvent}; use tokio::time::sleep; static mut CLASH_CORE: &str = "clash"; +const LOGS_QUEUE_LEN: usize = 100; #[derive(Debug)] pub struct Service { sidecar: Option, + logs: Arc>>, + #[allow(unused)] service_mode: bool, } impl Service { pub fn new() -> Service { + let queue = VecDeque::with_capacity(LOGS_QUEUE_LEN + 10); Service { sidecar: None, + logs: Arc::new(RwLock::new(queue)), service_mode: false, } } @@ -39,47 +49,47 @@ impl Service { self.service_mode = enable; } - #[cfg(not(windows))] pub fn start(&mut self) -> Result<()> { - self.start_clash_by_sidecar() - } + #[cfg(not(windows))] + self.start_clash_by_sidecar()?; - #[cfg(windows)] - pub fn start(&mut self) -> Result<()> { - if !self.service_mode { - return self.start_clash_by_sidecar(); - } - - tauri::async_runtime::spawn(async move { - match Self::check_service().await { - Ok(status) => { - // 未启动clash - if status.code != 0 { - log_if_err!(Self::start_clash_by_service().await); - } - } - Err(err) => log::error!(target: "app", "{err}"), + #[cfg(windows)] + { + if !self.service_mode { + return self.start_clash_by_sidecar(); } - }); + + tauri::async_runtime::spawn(async move { + match Self::check_service().await { + Ok(status) => { + // 未启动clash + if status.code != 0 { + log_if_err!(Self::start_clash_by_service().await); + } + } + Err(err) => log::error!(target: "app", "{err}"), + } + }); + } Ok(()) } - #[cfg(not(windows))] pub fn stop(&mut self) -> Result<()> { - self.stop_clash_by_sidecar() - } + #[cfg(not(windows))] + self.stop_clash_by_sidecar()?; - #[cfg(windows)] - pub fn stop(&mut self) -> Result<()> { - if !self.service_mode { - return self.stop_clash_by_sidecar(); + #[cfg(windows)] + { + if !self.service_mode { + return self.stop_clash_by_sidecar(); + } + + tauri::async_runtime::spawn(async move { + log_if_err!(Self::stop_clash_by_service().await); + }); } - tauri::async_runtime::spawn(async move { - log_if_err!(Self::stop_clash_by_service().await); - }); - Ok(()) } @@ -88,6 +98,24 @@ impl Service { self.start() } + pub fn get_logs(&self) -> VecDeque { + self.logs.read().clone() + } + + #[allow(unused)] + pub fn set_logs(&self, text: String) { + let mut logs = self.logs.write(); + if logs.len() > LOGS_QUEUE_LEN { + (*logs).pop_front(); + } + (*logs).push_back(text); + } + + pub fn clear_logs(&self) { + let mut logs = self.logs.write(); + (*logs).clear(); + } + /// start the clash sidecar fn start_clash_by_sidecar(&mut self) -> Result<()> { if self.sidecar.is_some() { @@ -112,14 +140,28 @@ impl Service { self.sidecar = Some(cmd_child); // clash log + let logs = self.logs.clone(); tauri::async_runtime::spawn(async move { + let write_log = |text: String| { + let mut logs = logs.write(); + if logs.len() >= LOGS_QUEUE_LEN { + (*logs).pop_front(); + } + (*logs).push_back(text); + }; + while let Some(event) = rx.recv().await { match event { CommandEvent::Stdout(line) => { - let stdout = if line.len() > 33 { &line[33..] } else { &line }; + let can_short = line.starts_with("time=") && line.len() > 33; + let stdout = if can_short { &line[33..] } else { &line }; log::info!(target: "app" ,"[clash]: {}", stdout); + write_log(line); + } + CommandEvent::Stderr(err) => { + log::error!(target: "app" ,"[clash error]: {}", err); + write_log(err); } - CommandEvent::Stderr(err) => log::error!(target: "app" ,"[clash error]: {}", err), _ => {} } } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index fff8cb1..542eb7a 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -118,6 +118,7 @@ fn main() -> std::io::Result<()> { cmds::restart_sidecar, // clash cmds::get_clash_info, + cmds::get_clash_logs, cmds::patch_clash_config, cmds::change_clash_core, cmds::get_runtime_config, diff --git a/src/components/layout/use-log-setup.ts b/src/components/layout/use-log-setup.ts index 9c5f0d5..2d05477 100644 --- a/src/components/layout/use-log-setup.ts +++ b/src/components/layout/use-log-setup.ts @@ -3,6 +3,7 @@ import { useEffect, useState } from "react"; import { useSetRecoilState } from "recoil"; import { listen } from "@tauri-apps/api/event"; import { getInformation } from "@/services/api"; +import { getClashLogs } from "@/services/cmds"; import { atomLogData } from "@/services/states"; const MAX_LOG_NUM = 1000; @@ -13,6 +14,8 @@ export default function useLogSetup() { const setLogData = useSetRecoilState(atomLogData); useEffect(() => { + getClashLogs().then(setLogData); + let ws: WebSocket = null!; const handler = (event: MessageEvent) => { diff --git a/src/services/cmds.ts b/src/services/cmds.ts index a5844f4..e4f9ebf 100644 --- a/src/services/cmds.ts +++ b/src/services/cmds.ts @@ -1,6 +1,29 @@ import { invoke } from "@tauri-apps/api/tauri"; import Notice from "@/components/base/base-notice"; +export async function getClashLogs() { + const regex = /time="(.+?)"\s+level=(.+?)\s+msg="(.+?)"/; + const newRegex = /(.+?)\s+(.+?)\s+(.+)/; + const logs = await invoke("get_clash_logs"); + + return logs + .map((log) => { + const result = log.match(regex); + if (result) { + const [_, time, type, payload] = result; + return { time, type, payload }; + } + + const result2 = log.match(newRegex); + if (result2) { + const [_, time, type, payload] = result2; + return { time, type, payload }; + } + return null; + }) + .filter(Boolean) as ApiType.LogItem[]; +} + export async function getProfiles() { return invoke("get_profiles"); }