feat: log info
This commit is contained in:
parent
fcee41f00d
commit
9e7c7ac163
@ -5,7 +5,7 @@ use crate::{
|
|||||||
use crate::{log_if_err, ret_err, wrap_err};
|
use crate::{log_if_err, ret_err, wrap_err};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde_yaml::Mapping;
|
use serde_yaml::Mapping;
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, VecDeque};
|
||||||
use tauri::{api, State};
|
use tauri::{api, State};
|
||||||
|
|
||||||
type CmdResult<T = ()> = Result<T, String>;
|
type CmdResult<T = ()> = Result<T, String>;
|
||||||
@ -256,6 +256,12 @@ pub fn get_cur_proxy(core: State<'_, Core>) -> CmdResult<Option<SysProxyConfig>>
|
|||||||
wrap_err!(sysopt.get_sysproxy())
|
wrap_err!(sysopt.get_sysproxy())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_clash_logs(core: State<'_, Core>) -> CmdResult<VecDeque<String>> {
|
||||||
|
let service = core.service.lock();
|
||||||
|
Ok(service.get_logs())
|
||||||
|
}
|
||||||
|
|
||||||
/// open app config dir
|
/// open app config dir
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn open_app_dir() -> Result<(), String> {
|
pub fn open_app_dir() -> Result<(), String> {
|
||||||
|
@ -129,6 +129,7 @@ impl Core {
|
|||||||
let mut service = self.service.lock();
|
let mut service = self.service.lock();
|
||||||
service.stop()?;
|
service.stop()?;
|
||||||
service.set_core(Some(clash_core));
|
service.set_core(Some(clash_core));
|
||||||
|
service.clear_logs();
|
||||||
service.start()?;
|
service.start()?;
|
||||||
drop(service);
|
drop(service);
|
||||||
|
|
||||||
|
@ -2,28 +2,38 @@ use super::{notice::Notice, ClashInfo};
|
|||||||
use crate::log_if_err;
|
use crate::log_if_err;
|
||||||
use crate::utils::{config, dirs};
|
use crate::utils::{config, dirs};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
|
use parking_lot::RwLock;
|
||||||
use reqwest::header::HeaderMap;
|
use reqwest::header::HeaderMap;
|
||||||
use serde_yaml::Mapping;
|
use serde_yaml::Mapping;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::Write;
|
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 tauri::api::process::{Command, CommandChild, CommandEvent};
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
static mut CLASH_CORE: &str = "clash";
|
static mut CLASH_CORE: &str = "clash";
|
||||||
|
const LOGS_QUEUE_LEN: usize = 100;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Service {
|
pub struct Service {
|
||||||
sidecar: Option<CommandChild>,
|
sidecar: Option<CommandChild>,
|
||||||
|
|
||||||
|
logs: Arc<RwLock<VecDeque<String>>>,
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
service_mode: bool,
|
service_mode: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Service {
|
impl Service {
|
||||||
pub fn new() -> Service {
|
pub fn new() -> Service {
|
||||||
|
let queue = VecDeque::with_capacity(LOGS_QUEUE_LEN + 10);
|
||||||
Service {
|
Service {
|
||||||
sidecar: None,
|
sidecar: None,
|
||||||
|
logs: Arc::new(RwLock::new(queue)),
|
||||||
service_mode: false,
|
service_mode: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -39,47 +49,47 @@ impl Service {
|
|||||||
self.service_mode = enable;
|
self.service_mode = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
pub fn start(&mut self) -> Result<()> {
|
pub fn start(&mut self) -> Result<()> {
|
||||||
self.start_clash_by_sidecar()
|
#[cfg(not(windows))]
|
||||||
}
|
self.start_clash_by_sidecar()?;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn start(&mut self) -> Result<()> {
|
{
|
||||||
if !self.service_mode {
|
if !self.service_mode {
|
||||||
return self.start_clash_by_sidecar();
|
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}"),
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
pub fn stop(&mut self) -> Result<()> {
|
pub fn stop(&mut self) -> Result<()> {
|
||||||
self.stop_clash_by_sidecar()
|
#[cfg(not(windows))]
|
||||||
}
|
self.stop_clash_by_sidecar()?;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn stop(&mut self) -> Result<()> {
|
{
|
||||||
if !self.service_mode {
|
if !self.service_mode {
|
||||||
return self.stop_clash_by_sidecar();
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,6 +98,24 @@ impl Service {
|
|||||||
self.start()
|
self.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_logs(&self) -> VecDeque<String> {
|
||||||
|
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
|
/// start the clash sidecar
|
||||||
fn start_clash_by_sidecar(&mut self) -> Result<()> {
|
fn start_clash_by_sidecar(&mut self) -> Result<()> {
|
||||||
if self.sidecar.is_some() {
|
if self.sidecar.is_some() {
|
||||||
@ -112,14 +140,28 @@ impl Service {
|
|||||||
self.sidecar = Some(cmd_child);
|
self.sidecar = Some(cmd_child);
|
||||||
|
|
||||||
// clash log
|
// clash log
|
||||||
|
let logs = self.logs.clone();
|
||||||
tauri::async_runtime::spawn(async move {
|
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 {
|
while let Some(event) = rx.recv().await {
|
||||||
match event {
|
match event {
|
||||||
CommandEvent::Stdout(line) => {
|
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);
|
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),
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,6 +118,7 @@ fn main() -> std::io::Result<()> {
|
|||||||
cmds::restart_sidecar,
|
cmds::restart_sidecar,
|
||||||
// clash
|
// clash
|
||||||
cmds::get_clash_info,
|
cmds::get_clash_info,
|
||||||
|
cmds::get_clash_logs,
|
||||||
cmds::patch_clash_config,
|
cmds::patch_clash_config,
|
||||||
cmds::change_clash_core,
|
cmds::change_clash_core,
|
||||||
cmds::get_runtime_config,
|
cmds::get_runtime_config,
|
||||||
|
@ -3,6 +3,7 @@ import { useEffect, useState } from "react";
|
|||||||
import { useSetRecoilState } from "recoil";
|
import { useSetRecoilState } from "recoil";
|
||||||
import { listen } from "@tauri-apps/api/event";
|
import { listen } from "@tauri-apps/api/event";
|
||||||
import { getInformation } from "@/services/api";
|
import { getInformation } from "@/services/api";
|
||||||
|
import { getClashLogs } from "@/services/cmds";
|
||||||
import { atomLogData } from "@/services/states";
|
import { atomLogData } from "@/services/states";
|
||||||
|
|
||||||
const MAX_LOG_NUM = 1000;
|
const MAX_LOG_NUM = 1000;
|
||||||
@ -13,6 +14,8 @@ export default function useLogSetup() {
|
|||||||
const setLogData = useSetRecoilState(atomLogData);
|
const setLogData = useSetRecoilState(atomLogData);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
getClashLogs().then(setLogData);
|
||||||
|
|
||||||
let ws: WebSocket = null!;
|
let ws: WebSocket = null!;
|
||||||
|
|
||||||
const handler = (event: MessageEvent<any>) => {
|
const handler = (event: MessageEvent<any>) => {
|
||||||
|
@ -1,6 +1,29 @@
|
|||||||
import { invoke } from "@tauri-apps/api/tauri";
|
import { invoke } from "@tauri-apps/api/tauri";
|
||||||
import Notice from "@/components/base/base-notice";
|
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<string[]>("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() {
|
export async function getProfiles() {
|
||||||
return invoke<CmdType.ProfilesConfig>("get_profiles");
|
return invoke<CmdType.ProfilesConfig>("get_profiles");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user