refactor: 使用 tracing 输出日志 (#1856)

* change all println to tracing
This commit is contained in:
Luna Yao
2026-03-04 02:52:23 +01:00
committed by GitHub
parent 1d25240d8c
commit 5f31583a84
18 changed files with 488 additions and 384 deletions
Generated
+11
View File
@@ -2207,6 +2207,7 @@ dependencies = [
"humansize", "humansize",
"humantime-serde", "humantime-serde",
"idna 1.0.3", "idna 1.0.3",
"indoc",
"kcp-sys", "kcp-sys",
"machine-uid", "machine-uid",
"maplit", "maplit",
@@ -2222,6 +2223,7 @@ dependencies = [
"openssl", "openssl",
"ordered_hash_map", "ordered_hash_map",
"parking_lot", "parking_lot",
"paste",
"percent-encoding", "percent-encoding",
"petgraph 0.8.1", "petgraph 0.8.1",
"pin-project-lite", "pin-project-lite",
@@ -4163,6 +4165,15 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "indoc"
version = "2.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
dependencies = [
"rustversion",
]
[[package]] [[package]]
name = "infer" name = "infer"
version = "0.19.0" version = "0.19.0"
+11
View File
@@ -1130,6 +1130,7 @@ dependencies = [
"humansize", "humansize",
"humantime-serde", "humantime-serde",
"idna", "idna",
"indoc",
"kcp-sys", "kcp-sys",
"machine-uid", "machine-uid",
"multimap", "multimap",
@@ -1142,6 +1143,7 @@ dependencies = [
"once_cell", "once_cell",
"ordered_hash_map", "ordered_hash_map",
"parking_lot", "parking_lot",
"paste",
"percent-encoding", "percent-encoding",
"petgraph 0.8.2", "petgraph 0.8.2",
"pin-project-lite", "pin-project-lite",
@@ -2197,6 +2199,15 @@ dependencies = [
"hashbrown 0.16.0", "hashbrown 0.16.0",
] ]
[[package]]
name = "indoc"
version = "2.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
dependencies = [
"rustversion",
]
[[package]] [[package]]
name = "inout" name = "inout"
version = "0.1.4" version = "0.1.4"
+2 -2
View File
@@ -11,7 +11,7 @@ use api::routes::create_routes;
use clap::Parser; use clap::Parser;
use config::AppConfig; use config::AppConfig;
use db::{operations::NodeOperations, Db}; use db::{operations::NodeOperations, Db};
use easytier::utils::init_logger; use easytier::common::log;
use health_checker::HealthChecker; use health_checker::HealthChecker;
use health_checker_manager::HealthCheckerManager; use health_checker_manager::HealthCheckerManager;
use std::env; use std::env;
@@ -42,7 +42,7 @@ async fn main() -> anyhow::Result<()> {
let config = AppConfig::default(); let config = AppConfig::default();
// 初始化日志 // 初始化日志
let _ = init_logger(&config.logging, false); let _ = log::init(&config.logging, false);
// 解析命令行参数 // 解析命令行参数
let args = Args::parse(); let args = Args::parse();
+5 -2
View File
@@ -14,7 +14,10 @@ use easytier::rpc_service::remote_client::{
}; };
use easytier::web_client::{self, WebClient}; use easytier::web_client::{self, WebClient};
use easytier::{ use easytier::{
common::config::{ConfigLoader, FileLoggerConfig, LoggingConfigBuilder, TomlConfigLoader}, common::{
config::{ConfigLoader, FileLoggerConfig, LoggingConfigBuilder, TomlConfigLoader},
log,
},
instance_manager::NetworkInstanceManager, instance_manager::NetworkInstanceManager,
launcher::NetworkConfig, launcher::NetworkConfig,
rpc_service::ApiRpcServer, rpc_service::ApiRpcServer,
@@ -1119,7 +1122,7 @@ pub fn run_gui() -> std::process::ExitCode {
}) })
.build() .build()
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
let Ok(_) = utils::init_logger(&config, true) else { let Ok(_) = log::init(&config, true) else {
return Ok(()); return Ok(());
}; };
+3 -2
View File
@@ -13,10 +13,11 @@ use easytier::{
config::{ConsoleLoggerConfig, FileLoggerConfig, LoggingConfigLoader}, config::{ConsoleLoggerConfig, FileLoggerConfig, LoggingConfigLoader},
constants::EASYTIER_VERSION, constants::EASYTIER_VERSION,
error::Error, error::Error,
log,
network::{local_ipv4, local_ipv6}, network::{local_ipv4, local_ipv6},
}, },
tunnel::{tcp::TcpTunnelListener, udp::UdpTunnelListener, TunnelListener}, tunnel::{tcp::TcpTunnelListener, udp::UdpTunnelListener, TunnelListener},
utils::{init_logger, setup_panic_handler}, utils::setup_panic_handler,
}; };
use mimalloc::MiMalloc; use mimalloc::MiMalloc;
@@ -205,7 +206,7 @@ async fn main() {
setup_panic_handler(); setup_panic_handler();
let cli = Cli::parse(); let cli = Cli::parse();
init_logger(&cli, false).unwrap(); log::init(&cli, false).unwrap();
// Validate OIDC configuration: check split-deploy specific requirements // Validate OIDC configuration: check split-deploy specific requirements
// Basic OIDC parameter validation is handled in OidcConfig::from_params // Basic OIDC parameter validation is handled in OidcConfig::from_params
+3
View File
@@ -39,6 +39,9 @@ tracing-subscriber = { version = "0.3", features = [
derivative = "2.2.0" derivative = "2.2.0"
derive_more = {version = "2.1.1", features = ["full"]} derive_more = {version = "2.1.1", features = ["full"]}
console-subscriber = { version = "0.4.1", optional = true } console-subscriber = { version = "0.4.1", optional = true }
indoc = "2.0.7"
regex = "1.8"
paste = "1.0"
thiserror = "1.0" thiserror = "1.0"
auto_impl = "1.1.0" auto_impl = "1.1.0"
crossbeam = "0.8.4" crossbeam = "0.8.4"
+219
View File
@@ -0,0 +1,219 @@
use crate::common::config::LoggingConfigLoader;
use crate::common::get_logger_timer_rfc3339;
use crate::common::tracing_rolling_appender::{FileAppenderWrapper, RollingFileAppenderBase};
use crate::rpc_service::logger::{CURRENT_LOG_LEVEL, LOGGER_LEVEL_SENDER};
use anyhow::Context;
use paste::paste;
use regex::Regex;
use tracing::level_filters::LevelFilter;
use tracing::{Level, Metadata};
use tracing_subscriber::filter::{filter_fn, FilterExt};
use tracing_subscriber::fmt::layer;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::Registry;
use tracing_subscriber::{EnvFilter, Layer};
macro_rules! __log__ {
(const $var:ident = $target:expr) => {
const $var: &'static str = $target;
__log__!(@impl $target, $);
};
(@impl $target:expr, $_:tt) => {
__log__!(@impl $_, $target, error, warn, info, debug, trace);
};
(@impl $_:tt, $target:expr, $($lvl:ident),+) => {
paste! {
$(
macro_rules! [< __ $lvl __ >] {
(category: $cat:expr, $_ ($arg:tt)+) => {
tracing::$lvl!(target: concat!($target, "::", $cat), $_ ($arg)+)
};
($_ ($arg:tt)+) => {
tracing::$lvl!(target: $target, $_ ($arg)+)
};
}
#[allow(unused_imports)]
pub(crate) use [< __ $lvl __ >] as $lvl;
)+
}
};
}
__log__!(const LOG_TARGET = "CORE");
fn parse_env_filter(default_level: LevelFilter) -> Result<EnvFilter, anyhow::Error> {
let mut filter = EnvFilter::builder()
.with_default_directive(default_level.into())
.from_env()
.with_context(|| "failed to create env filter")?;
let pattern = Regex::new(&format!(r"(^|,){}\s*=", regex::escape(LOG_TARGET)))?;
if !pattern.is_match(&filter.to_string()) {
filter = filter.add_directive(format!("{LOG_TARGET}=info").parse()?);
}
Ok(filter)
}
fn is_log(meta: &Metadata) -> bool {
meta.target() == LOG_TARGET || meta.target().starts_with(&format!("{LOG_TARGET}::"))
}
pub type NewFilterSender = std::sync::mpsc::Sender<String>;
macro_rules! tracing_layer {
($layer:expr) => {
$layer.with_filter(filter_fn(is_log).not()).boxed()
};
}
macro_rules! log_layer {
($layer:expr) => {
$layer
.with_file(false)
.with_line_number(false)
.with_ansi(true)
.with_filter(filter_fn(is_log))
.boxed()
};
}
pub fn init(
config: impl LoggingConfigLoader,
need_reload: bool,
) -> Result<Option<NewFilterSender>, anyhow::Error> {
let mut layers = Vec::new();
let file_config = config.get_file_logger_config();
let file_level = file_config
.level
.map(|s| s.parse().unwrap())
.unwrap_or(LevelFilter::OFF);
let mut ret_sender: Option<NewFilterSender> = None;
// logger to a rolling file
if file_level != LevelFilter::OFF || need_reload {
let dir = file_config.dir.as_deref().unwrap_or(".");
let file = file_config.file.as_deref().unwrap_or("easytier.log");
let path = std::path::Path::new(dir).join(file);
let path_str = path.to_string_lossy().into_owned();
let builder = RollingFileAppenderBase::builder();
let file_appender = builder
.filename(path_str)
.condition_daily()
.max_filecount(file_config.count.unwrap_or(10))
.condition_max_file_size(file_config.size_mb.unwrap_or(100) * 1024 * 1024)
.build()
.unwrap();
// Create a simple wrapper that implements MakeWriter
let wrapper = FileAppenderWrapper::new(file_appender);
let (file_filter, file_filter_reloader) =
tracing_subscriber::reload::Layer::<_, Registry>::new(parse_env_filter(file_level)?);
let layer = |wrapper| {
layer()
.with_ansi(false)
.with_writer(wrapper)
.with_timer(get_logger_timer_rfc3339())
};
layers.push(
vec![
tracing_layer!(layer(wrapper.clone())),
log_layer!(layer(wrapper.clone())),
]
.with_filter(file_filter)
.boxed(),
);
if need_reload {
let (sender, recver) = std::sync::mpsc::channel();
ret_sender = Some(sender.clone());
// 初始化全局状态
let _ = LOGGER_LEVEL_SENDER.set(std::sync::Mutex::new(sender));
let _ = CURRENT_LOG_LEVEL.set(std::sync::Mutex::new(file_level.to_string()));
std::thread::spawn(move || {
while let Ok(lf) = recver.recv() {
let e = file_filter_reloader.modify(|f| {
if let Ok(nf) = EnvFilter::builder()
.with_default_directive(lf.parse::<LevelFilter>().unwrap().into())
.from_env()
.with_context(|| "failed to create file filter")
{
info!("Reload log filter succeed, new filter level: {:?}", lf);
*f = nf;
}
});
if e.is_err() {
error!("Failed to reload log filter: {:?}", e);
}
}
info!("Stop log filter reloader");
});
}
}
// logger to console
let console_config = config.get_console_logger_config();
let console_level = console_config
.level
.map(|s| s.parse().unwrap())
.unwrap_or(LevelFilter::OFF);
let (console_filter, _) =
tracing_subscriber::reload::Layer::new(parse_env_filter(console_level)?);
let layer = || {
layer()
.pretty()
.with_timer(get_logger_timer_rfc3339())
.with_writer(std::io::stderr)
};
layers.push(
vec![
tracing_layer!(layer()),
log_layer!(layer()).with_filter(LevelFilter::WARN).boxed(),
log_layer!(layer().with_writer(std::io::stdout))
.with_filter(filter_fn(|metadata| *metadata.level() > Level::WARN))
.boxed(),
]
.with_filter(console_filter)
.boxed(),
);
#[cfg(feature = "tracing")]
{
layers.push(console_subscriber::ConsoleLayer::builder().spawn().boxed());
}
Registry::default().with(layers).init();
Ok(ret_sender)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::config::{self};
async fn test_logger_reload() {
println!("current working dir: {:?}", std::env::current_dir());
let config = config::LoggingConfigBuilder::default().build().unwrap();
let s = init(&config, true).unwrap();
tracing::debug!("test not display debug");
s.unwrap().send(LevelFilter::DEBUG.to_string()).unwrap();
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
tracing::debug!("test display debug");
}
}
+1
View File
@@ -21,6 +21,7 @@ pub mod error;
pub mod global_ctx; pub mod global_ctx;
pub mod idn; pub mod idn;
pub mod ifcfg; pub mod ifcfg;
pub mod log;
pub mod netns; pub mod netns;
pub mod network; pub mod network;
pub mod scoped_task; pub mod scoped_task;
@@ -184,6 +184,7 @@ where
} }
} }
#[derive(Debug, Clone)]
pub struct FileAppenderWrapper { pub struct FileAppenderWrapper {
appender: std::sync::Arc<parking_lot::Mutex<RollingFileAppenderBase>>, appender: std::sync::Arc<parking_lot::Mutex<RollingFileAppenderBase>>,
} }
@@ -206,6 +207,7 @@ impl FileAppenderWrapper {
} }
} }
#[derive(Debug, Clone)]
pub struct FileAppenderWriter { pub struct FileAppenderWriter {
appender: std::sync::Arc<parking_lot::Mutex<RollingFileAppenderBase>>, appender: std::sync::Arc<parking_lot::Mutex<RollingFileAppenderBase>>,
} }
+2 -1
View File
@@ -5,6 +5,7 @@ use crate::{
dns::{resolve_txt_record, RESOLVER}, dns::{resolve_txt_record, RESOLVER},
error::Error, error::Error,
global_ctx::ArcGlobalCtx, global_ctx::ArcGlobalCtx,
log,
}, },
tunnel::{IpVersion, Tunnel, TunnelConnector, TunnelError, PROTO_PORT_OFFSET}, tunnel::{IpVersion, Tunnel, TunnelConnector, TunnelError, PROTO_PORT_OFFSET},
}; };
@@ -131,7 +132,7 @@ impl DNSTunnelConnector {
let parsed_record = Self::handle_one_srv_record(record, protocol); let parsed_record = Self::handle_one_srv_record(record, protocol);
tracing::info!(?parsed_record, ?srv_domain, "parsed_record"); tracing::info!(?parsed_record, ?srv_domain, "parsed_record");
if let Err(e) = &parsed_record { if let Err(e) = &parsed_record {
eprintln!("got invalid srv record {:?}", e); log::warn!("got invalid srv record {:?}", e);
continue; continue;
} }
responses.insert(parsed_record.unwrap()); responses.insert(parsed_record.unwrap());
+36 -31
View File
@@ -15,6 +15,7 @@ use crate::{
PeerConfig, PortForwardConfig, TomlConfigLoader, VpnPortalConfig, PeerConfig, PortForwardConfig, TomlConfigLoader, VpnPortalConfig,
}, },
constants::EASYTIER_VERSION, constants::EASYTIER_VERSION,
log,
}, },
defer, defer,
instance_manager::NetworkInstanceManager, instance_manager::NetworkInstanceManager,
@@ -22,7 +23,7 @@ use crate::{
proto::common::{CompressionAlgoPb, SecureModeConfig}, proto::common::{CompressionAlgoPb, SecureModeConfig},
rpc_service::ApiRpcServer, rpc_service::ApiRpcServer,
tunnel::PROTO_PORT_OFFSET, tunnel::PROTO_PORT_OFFSET,
utils::{init_logger, setup_panic_handler}, utils::setup_panic_handler,
web_client, ShellType, web_client, ShellType,
}; };
use anyhow::Context; use anyhow::Context;
@@ -1163,7 +1164,7 @@ fn win_service_event_loop(
} }
Err(e) => { Err(e) => {
status_handle.set_service_status(error_status).unwrap(); status_handle.set_service_status(error_status).unwrap();
eprintln!("error: {}", e); log::error!("{}", e);
} }
} }
}, },
@@ -1231,7 +1232,7 @@ fn win_service_main(arg: Vec<std::ffi::OsString>) {
async fn run_main(cli: Cli) -> anyhow::Result<()> { async fn run_main(cli: Cli) -> anyhow::Result<()> {
defer!(dump_profile(0);); defer!(dump_profile(0););
init_logger(&cli.logging_options, true)?; log::init(&cli.logging_options, true)?;
let manager = Arc::new(NetworkInstanceManager::new().with_config_path(cli.config_dir.clone())); let manager = Arc::new(NetworkInstanceManager::new().with_config_path(cli.config_dir.clone()));
@@ -1253,12 +1254,12 @@ async fn run_main(cli: Cli) -> anyhow::Result<()> {
) )
.await .await
.inspect(|_| { .inspect(|_| {
println!( log::info!(
"Web client started successfully...\nserver: {}", server = config_server_url_s,
config_server_url_s, "Web client started successfully...",
); );
println!("Official config website: https://easytier.cn/web"); log::info!("Official config website: https://easytier.cn/web");
})?; })?;
Some(wc) Some(wc)
@@ -1324,13 +1325,17 @@ async fn run_main(cli: Cli) -> anyhow::Result<()> {
control.set_no_delete(true); control.set_no_delete(true);
} }
println!( log::info!(
"Starting easytier from config file {:?}({:?}) with config:", "\
config_file, control.permission Starting easytier from config file {:?}({:?}) with config:\n\
############### TOML ###############\n\
{}\n\
-----------------------------------\n\
",
config_file,
control.permission,
cfg.dump()
); );
println!("############### TOML ###############\n");
println!("{}", cfg.dump());
println!("-----------------------------------");
manager.run_network_instance(cfg, true, control)?; manager.run_network_instance(cfg, true, control)?;
} }
@@ -1339,10 +1344,15 @@ async fn run_main(cli: Cli) -> anyhow::Result<()> {
cli.network_options cli.network_options
.merge_into(&cfg) .merge_into(&cfg)
.with_context(|| "failed to create config from cli".to_string())?; .with_context(|| "failed to create config from cli".to_string())?;
println!("Starting easytier from cli with config:"); log::info!(
println!("############### TOML ###############\n"); "\
println!("{}", cfg.dump()); Starting easytier from cli with config:\n\
println!("-----------------------------------"); ############### TOML ###############\n\
{}\n\
-----------------------------------\n\
",
cfg.dump()
);
manager.run_network_instance(cfg, true, ConfigFileControl::STATIC_CONFIG)?; manager.run_network_instance(cfg, true, ConfigFileControl::STATIC_CONFIG)?;
} }
@@ -1363,11 +1373,11 @@ async fn run_main(cli: Cli) -> anyhow::Result<()> {
} }
} }
_ = tokio::signal::ctrl_c() => { _ = tokio::signal::ctrl_c() => {
println!("ctrl-c received, exiting..."); log::info!("ctrl-c received, exiting...");
} }
_ = sigterm, if cfg!(unix) => { _ = sigterm, if cfg!(unix) => {
println!("terminate signal received, exiting..."); log::warn!("terminate signal received, exiting...");
} }
} }
Ok(()) Ok(())
@@ -1384,11 +1394,7 @@ fn memory_monitor(_force_dump: Arc<AtomicBool>) {
e.advance().unwrap(); e.advance().unwrap();
let new_heap_size = allocated_stats.read().unwrap(); let new_heap_size = allocated_stats.read().unwrap();
println!( log::debug!("heap size: {} bytes", new_heap_size);
"heap size: {} bytes, time: {}",
new_heap_size,
chrono::Local::now().format("%Y-%m-%d %H:%M:%S")
);
// dump every 75MB // dump every 75MB
if (last_peak_size > 0 if (last_peak_size > 0
@@ -1396,10 +1402,9 @@ fn memory_monitor(_force_dump: Arc<AtomicBool>) {
&& new_heap_size - last_peak_size > 10 * 1024 * 1024) && new_heap_size - last_peak_size > 10 * 1024 * 1024)
|| _force_dump.load(std::sync::atomic::Ordering::Relaxed) || _force_dump.load(std::sync::atomic::Ordering::Relaxed)
{ {
println!( log::debug!(
"heap size increased: {} bytes, time: {}", "heap size increased: {} bytes",
new_heap_size - last_peak_size, new_heap_size - last_peak_size,
chrono::Local::now().format("%Y-%m-%d %H:%M:%S")
); );
dump_profile(new_heap_size); dump_profile(new_heap_size);
last_peak_size = new_heap_size; last_peak_size = new_heap_size;
@@ -1475,7 +1480,7 @@ pub async fn main() -> ExitCode {
// Verify configurations // Verify configurations
if cli.check_config { if cli.check_config {
if let Err(e) = validate_config(&cli).await { if let Err(e) = validate_config(&cli).await {
eprintln!("Config validation failed: {:?}", e); log::error!("Config validation failed: {:?}", e);
return ExitCode::FAILURE; return ExitCode::FAILURE;
} else { } else {
return ExitCode::SUCCESS; return ExitCode::SUCCESS;
@@ -1484,12 +1489,12 @@ pub async fn main() -> ExitCode {
let mut ret_code = 0; let mut ret_code = 0;
if let Err(e) = run_main(cli).await { if let Err(error) = run_main(cli).await {
eprintln!("error: {:?}", e); log::error!(%error);
ret_code = 1; ret_code = 1;
} }
println!("Stopping easytier..."); log::info!("Stopping easytier...");
set_prof_active(false); set_prof_active(false);
ExitCode::from(ret_code) ExitCode::from(ret_code)
+4 -4
View File
@@ -23,7 +23,7 @@ use tracing::Instrument;
use crate::common::error::Result; use crate::common::error::Result;
use crate::common::global_ctx::{ArcGlobalCtx, GlobalCtx}; use crate::common::global_ctx::{ArcGlobalCtx, GlobalCtx};
use crate::common::join_joinset_background; use crate::common::join_joinset_background;
use crate::common::log;
use crate::common::stats_manager::{LabelSet, LabelType, MetricName}; use crate::common::stats_manager::{LabelSet, LabelType, MetricName};
use crate::peers::peer_manager::PeerManager; use crate::peers::peer_manager::PeerManager;
use crate::peers::{NicPacketFilter, PeerPacketFilter}; use crate::peers::{NicPacketFilter, PeerPacketFilter};
@@ -66,9 +66,9 @@ impl NatDstConnector for NatDstTcpConnector {
async fn connect(&self, _src: SocketAddr, nat_dst: SocketAddr) -> Result<Self::DstStream> { async fn connect(&self, _src: SocketAddr, nat_dst: SocketAddr) -> Result<Self::DstStream> {
let socket = match TcpSocket::new_v4() { let socket = match TcpSocket::new_v4() {
Ok(s) => s, Ok(s) => s,
Err(e) => { Err(error) => {
eprintln!("create v4 socket failed: {:?}", e); log::error!(%error, "create v4 socket failed");
return Err(e.into()); return Err(error.into());
} }
}; };
+37 -53
View File
@@ -12,6 +12,7 @@ use crate::{
error::Error, error::Error,
global_ctx::{ArcGlobalCtx, GlobalCtxEvent}, global_ctx::{ArcGlobalCtx, GlobalCtxEvent},
ifcfg::{IfConfiger, IfConfiguerTrait}, ifcfg::{IfConfiger, IfConfiguerTrait},
log,
}, },
instance::proxy_cidrs_monitor::ProxyCidrsMonitor, instance::proxy_cidrs_monitor::ProxyCidrsMonitor,
peers::{peer_manager::PeerManager, recv_packet_from_chan, PacketRecvChanReceiver}, peers::{peer_manager::PeerManager, recv_packet_from_chan, PacketRecvChanReceiver},
@@ -95,7 +96,7 @@ impl Stream for TunStream {
match ret { match ret {
Ok(_) => Poll::Ready(Some(Ok(ZCPacket::new_from_buf(ret_buf, ZCPacketType::NIC)))), Ok(_) => Poll::Ready(Some(Ok(ZCPacket::new_from_buf(ret_buf, ZCPacketType::NIC)))),
Err(err) => { Err(err) => {
println!("tun stream error: {:?}", err); log::error!("tun stream error: {:?}", err);
Poll::Ready(None) Poll::Ready(None)
} }
} }
@@ -249,10 +250,11 @@ impl Drop for VirtualNic {
{ {
if let Some(ref ifname) = self.ifname { if let Some(ref ifname) = self.ifname {
// Try to clean up firewall rules, but don't panic in destructor // Try to clean up firewall rules, but don't panic in destructor
if let Err(e) = crate::arch::windows::remove_interface_firewall_rules(ifname) { if let Err(error) = crate::arch::windows::remove_interface_firewall_rules(ifname) {
eprintln!( log::warn!(
"Warning: Failed to remove firewall rules for interface {}: {}", %error,
ifname, e "failed to remove firewall rules for interface {}",
ifname
); );
} }
} }
@@ -293,25 +295,20 @@ impl VirtualNic {
.unwrap_or(false); .unwrap_or(false);
if !tun_module_available { if !tun_module_available {
tracing::warn!("TUN kernel module may not be loaded"); log::warn!("TUN kernel module may not be available.");
println!("⚠ Warning: TUN kernel module may not be available."); log::warn!("\tYou may need to load it with: sudo modprobe tun.");
println!(" You may need to load it with: sudo modprobe tun");
} }
// Try to create /dev/net directory if it doesn't exist // Try to create /dev/net directory if it doesn't exist
if tokio::fs::metadata(TUN_DIR_PATH).await.is_err() { if tokio::fs::metadata(TUN_DIR_PATH).await.is_err() {
if let Err(e) = tokio::fs::create_dir_all(TUN_DIR_PATH).await { if let Err(error) = tokio::fs::create_dir_all(TUN_DIR_PATH).await {
tracing::warn!( log::warn!(
"Failed to create directory {}: {}. Continuing anyway.", ?error,
TUN_DIR_PATH, "Failed to create directory {}. TUN device creation may fail. Continuing anyway.",
e
);
println!(
"⚠ Warning: Failed to create directory {}. TUN device creation may fail.",
TUN_DIR_PATH TUN_DIR_PATH
); );
println!( log::warn!(
" You may need to run with root privileges or manually create the TUN device." "\tYou may need to run with root privileges or manually create the TUN device."
); );
Self::print_troubleshooting_info(); Self::print_troubleshooting_info();
return; return;
@@ -330,20 +327,14 @@ impl VirtualNic {
dev_node, dev_node,
) { ) {
Ok(_) => { Ok(_) => {
tracing::info!("Successfully created TUN device node {}", TUN_DEV_PATH); log::info!("Successfully created TUN device node {}", TUN_DEV_PATH);
println!("✓ Created TUN device node {}", TUN_DEV_PATH);
} }
Err(e) => { Err(error) => {
tracing::warn!( tracing::warn!(
"Failed to create TUN device node {}: {}. Continuing anyway.", %error,
"Failed to create TUN device node {}. Continuing anyway.",
TUN_DEV_PATH, TUN_DEV_PATH,
e
); );
println!(
"⚠ Warning: Failed to create TUN device node {}.",
TUN_DEV_PATH
);
println!(" Error: {}", e);
Self::print_troubleshooting_info(); Self::print_troubleshooting_info();
} }
} }
@@ -352,13 +343,15 @@ impl VirtualNic {
/// Print troubleshooting information for TUN device issues /// Print troubleshooting information for TUN device issues
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
fn print_troubleshooting_info() { fn print_troubleshooting_info() {
println!(" Possible solutions:"); log::info!(
println!(" 1. Run with root privileges: sudo ./easytier-core [options]"); "Possible solutions:\
println!(" 2. Manually create TUN device: sudo mkdir -p /dev/net && sudo mknod /dev/net/tun c 10 200"); \n\t1. Run with root privileges: sudo ./easytier-core [options]\
println!(" 3. Load TUN kernel module: sudo modprobe tun"); \n\t2. Manually create TUN device: sudo mkdir -p /dev/net && sudo mknod /dev/net/tun c 10 200\
println!(" 4. Use --no-tun flag if TUN functionality is not needed"); \n\t3. Load TUN kernel module: sudo modprobe tun\
println!(" 5. Check if your system/container supports TUN devices"); \n\t4. Use --no-tun flag if TUN functionality is not needed\
println!(" Note: TUN functionality may still work if the kernel supports dynamic device creation."); \n\t5. Check if your system/container supports TUN devices\
\nNote: TUN functionality may still work if the kernel supports dynamic device creation."
);
} }
/// For non-Linux systems, this is a no-op /// For non-Linux systems, this is a no-op
@@ -535,10 +528,9 @@ impl VirtualNic {
match crate::arch::windows::add_self_to_firewall_allowlist() { match crate::arch::windows::add_self_to_firewall_allowlist() {
Ok(_) => tracing::info!("add_self_to_firewall_allowlist successful!"), Ok(_) => tracing::info!("add_self_to_firewall_allowlist successful!"),
Err(e) => { Err(error) => {
println!("Failed to add Easytier to firewall allowlist, Subnet proxy and KCP proxy may not work properly. error: {}", e); log::warn!(%error, "Failed to add Easytier to firewall allowlist, Subnet proxy and KCP proxy may not work properly.");
println!("You can add firewall rules manually, or use --use-smoltcp to run with user-space TCP/IP stack."); log::warn!("You can add firewall rules manually, or use --use-smoltcp to run with user-space TCP/IP stack.");
println!();
} }
} }
@@ -592,7 +584,7 @@ impl VirtualNic {
&mut self, &mut self,
tun_fd: std::os::fd::RawFd, tun_fd: std::os::fd::RawFd,
) -> Result<Box<dyn Tunnel>, Error> { ) -> Result<Box<dyn Tunnel>, Error> {
println!("tun_fd: {}", tun_fd); log::debug!(%tun_fd);
let mut config = Configuration::default(); let mut config = Configuration::default();
config.layer(Layer::L3); config.layer(Layer::L3);
@@ -711,19 +703,11 @@ impl VirtualNic {
ifname ifname
); );
} }
Err(e) => { Err(error) => {
tracing::warn!("Failed to configure Windows Firewall for {}: {}", ifname, e); log::warn!(%error, "Failed to configure Windows Firewall for interface {}\
println!( \n\tThis may cause connectivity issues with ping and other network functions.\
"⚠ Warning: Failed to configure Windows Firewall for interface {}.", \n\tPlease run as Administrator or manually configure Windows Firewall.\
ifname \n\tAlternatively, you can disable Windows Firewall for testing purposes.", ifname);
);
println!(" This may cause connectivity issues with ping and other network functions.");
println!(
" Please run as Administrator or manually configure Windows Firewall."
);
println!(
" Alternatively, you can disable Windows Firewall for testing purposes."
);
} }
} }
} }
+101 -108
View File
@@ -1,11 +1,12 @@
use std::{collections::BTreeMap, path::PathBuf, sync::Arc};
use dashmap::DashMap; use dashmap::DashMap;
use std::fmt::{Display, Formatter};
use std::{collections::BTreeMap, path::PathBuf, sync::Arc};
use crate::{ use crate::{
common::{ common::{
config::{ConfigFileControl, ConfigLoader, TomlConfigLoader}, config::{ConfigFileControl, ConfigLoader, TomlConfigLoader},
global_ctx::{EventBusSubscriber, GlobalCtxEvent}, global_ctx::{EventBusSubscriber, GlobalCtxEvent},
log,
scoped_task::ScopedTask, scoped_task::ScopedTask,
}, },
launcher::{NetworkInstance, NetworkInstanceRunningInfo}, launcher::{NetworkInstance, NetworkInstanceRunningInfo},
@@ -85,10 +86,9 @@ impl NetworkInstanceManager {
.map(|event| ScopedTask::from(handle_event(instance_id, event))); .map(|event| ScopedTask::from(handle_event(instance_id, event)));
instance_stop_notifier.notified().await; instance_stop_notifier.notified().await;
if let Some(instance) = instance_map.get(&instance_id) { if let Some(instance) = instance_map.get(&instance_id) {
if let Some(e) = instance.get_latest_error_msg() { if let Some(error) = instance.get_latest_error_msg() {
tracing::error!(?e, ?instance_id, "instance stopped with error"); log::error!(%error, "instance {} stopped", instance_id);
eprintln!("instance {} stopped with error: {}", instance_id, e); instance_error_messages.insert(instance_id, error);
instance_error_messages.insert(instance_id, e);
} }
} }
stop_check_notifier.notify_one(); stop_check_notifier.notify_one();
@@ -261,6 +261,23 @@ impl NetworkInstanceManager {
} }
} }
macro_rules! event {
($lvl:ident, category: $cat:expr, $($args:tt)+) => {
event!(@impl $lvl, concat!("INSTANCE::", $cat), $($args)+)
};
($lvl:ident, $($args:tt)+) => {
event!(@impl $lvl, "INSTANCE", $($args)+)
};
(@impl $lvl:ident, $cat:expr, $($args:tt)+) => {
log::$lvl!(
category: $cat,
$($args)+
);
};
}
#[tracing::instrument] #[tracing::instrument]
fn handle_event( fn handle_event(
instance_id: uuid::Uuid, instance_id: uuid::Uuid,
@@ -270,158 +287,140 @@ fn handle_event(
loop { loop {
if let Ok(e) = events.recv().await { if let Ok(e) = events.recv().await {
match e { match e {
GlobalCtxEvent::PeerAdded(p) => { GlobalCtxEvent::PeerAdded(peer_id) => {
print_event(instance_id, format!("new peer added. peer_id: {}", p)); event!(info, peer_id, "[{}] new peer added", instance_id);
} }
GlobalCtxEvent::PeerRemoved(p) => { GlobalCtxEvent::PeerRemoved(peer_id) => {
print_event(instance_id, format!("peer removed. peer_id: {}", p)); event!(info, peer_id, "[{}] peer removed", instance_id);
} }
GlobalCtxEvent::PeerConnAdded(p) => { GlobalCtxEvent::PeerConnAdded(conn_info) => {
print_event( event!(
info,
category: "CONNECTION",
%conn_info,
"[{}] new peer connection added",
instance_id, instance_id,
format!(
"new peer connection added. conn_info: {}",
peer_conn_info_to_string(p)
),
); );
} }
GlobalCtxEvent::PeerConnRemoved(p) => { GlobalCtxEvent::PeerConnRemoved(conn_info) => {
print_event( event!(
info,
category: "CONNECTION",
%conn_info,
"[{}] peer connection removed",
instance_id, instance_id,
format!(
"peer connection removed. conn_info: {}",
peer_conn_info_to_string(p)
),
); );
} }
GlobalCtxEvent::ListenerAddFailed(p, msg) => { GlobalCtxEvent::ListenerAddFailed(listener, msg) => {
print_event( event!(warn, %listener, msg, "[{}] listener add failed", instance_id);
instance_id,
format!("listener add failed. listener: {}, msg: {}", p, msg),
);
} }
GlobalCtxEvent::ListenerAcceptFailed(p, msg) => { GlobalCtxEvent::ListenerAcceptFailed(listener, msg) => {
print_event( event!(warn, %listener, msg, "[{}] listener accept failed", instance_id);
instance_id,
format!("listener accept failed. listener: {}, msg: {}", p, msg),
);
} }
GlobalCtxEvent::ListenerAdded(p) => { GlobalCtxEvent::ListenerAdded(listener) => {
if p.scheme() == "ring" { if listener.scheme() == "ring" {
continue; continue;
} }
print_event(instance_id, format!("new listener added. listener: {}", p)); event!(
info,
%listener,
"[{}] new listener added",
instance_id
);
} }
GlobalCtxEvent::ConnectionAccepted(local, remote) => { GlobalCtxEvent::ConnectionAccepted(local, remote) => {
print_event( event!(info, category: "CONNECTION", local, remote, "[{}] new connection accepted", instance_id);
instance_id,
format!(
"new connection accepted. local: {}, remote: {}",
local, remote
),
);
} }
GlobalCtxEvent::ConnectionError(local, remote, err) => { GlobalCtxEvent::ConnectionError(local, remote, err) => {
print_event( event!(info, category: "CONNECTION", local, remote, err, "[{}] connection error", instance_id);
instance_id,
format!(
"connection error. local: {}, remote: {}, err: {}",
local, remote, err
),
);
} }
GlobalCtxEvent::TunDeviceReady(dev) => { GlobalCtxEvent::TunDeviceReady(dev) => {
print_event(instance_id, format!("tun device ready. dev: {}", dev)); event!(info, dev, "[{}] tun device ready", instance_id);
} }
GlobalCtxEvent::TunDeviceError(err) => { GlobalCtxEvent::TunDeviceError(err) => {
print_event(instance_id, format!("tun device error. err: {}", err)); event!(error, %err, "[{}] tun device error", instance_id);
} }
GlobalCtxEvent::Connecting(dst) => { GlobalCtxEvent::Connecting(dst) => {
print_event(instance_id, format!("connecting to peer. dst: {}", dst)); event!(info, category: "CONNECTION", %dst, "[{}] connecting to peer", instance_id);
} }
GlobalCtxEvent::ConnectError(dst, ip_version, err) => { GlobalCtxEvent::ConnectError(dst, ip_version, error) => {
print_event( event!(
instance_id, info,
format!( category: "CONNECTION",
"connect to peer error. dst: {}, ip_version: {}, err: {}", dst,
dst, ip_version, err ip_version,
), %error,
"[{}] connect to peer error",
instance_id
); );
} }
GlobalCtxEvent::VpnPortalStarted(portal) => { GlobalCtxEvent::VpnPortalStarted(portal) => {
print_event( event!(info, portal, "[{}] vpn portal started", instance_id);
instance_id,
format!("vpn portal started. portal: {}", portal),
);
} }
GlobalCtxEvent::VpnPortalClientConnected(portal, client_addr) => { GlobalCtxEvent::VpnPortalClientConnected(portal, client_addr) => {
print_event( event!(
instance_id, info,
format!( portal,
"vpn portal client connected. portal: {}, client_addr: {}", client_addr,
portal, client_addr "[{}] vpn portal client connected",
), instance_id
); );
} }
GlobalCtxEvent::VpnPortalClientDisconnected(portal, client_addr) => { GlobalCtxEvent::VpnPortalClientDisconnected(portal, client_addr) => {
print_event( event!(
instance_id, info,
format!( portal,
"vpn portal client disconnected. portal: {}, client_addr: {}", client_addr,
portal, client_addr "[{}] vpn portal client disconnected",
), instance_id
); );
} }
GlobalCtxEvent::DhcpIpv4Changed(old, new) => { GlobalCtxEvent::DhcpIpv4Changed(old, new) => {
print_event( event!(info, ?old, ?new, "[{}] dhcp ip changed", instance_id);
instance_id,
format!("dhcp ip changed. old: {:?}, new: {:?}", old, new),
);
} }
GlobalCtxEvent::DhcpIpv4Conflicted(ip) => { GlobalCtxEvent::DhcpIpv4Conflicted(ip) => {
print_event(instance_id, format!("dhcp ip conflict. ip: {:?}", ip)); event!(info, ?ip, "[{}] dhcp ip conflict", instance_id);
} }
GlobalCtxEvent::PortForwardAdded(cfg) => { GlobalCtxEvent::PortForwardAdded(cfg) => {
print_event( event!(
info,
local = %cfg.bind_addr.unwrap(),
remote = %cfg.dst_addr.unwrap(),
proto = %cfg.socket_type().as_str_name(),
"[{}] port forward added",
instance_id, instance_id,
format!(
"port forward added. local: {}, remote: {}, proto: {}",
cfg.bind_addr.unwrap(),
cfg.dst_addr.unwrap(),
cfg.socket_type().as_str_name()
),
); );
} }
GlobalCtxEvent::ConfigPatched(patch) => { GlobalCtxEvent::ConfigPatched(patch) => {
print_event(instance_id, format!("config patched. patch: {:?}", patch)); event!(info, ?patch, "[{}] config patched", instance_id);
} }
GlobalCtxEvent::ProxyCidrsUpdated(added, removed) => { GlobalCtxEvent::ProxyCidrsUpdated(added, removed) => {
print_event( event!(
instance_id, info,
format!( ?added,
"proxy CIDRs updated. added: {:?}, removed: {:?}", ?removed,
added, removed "[{}] proxy CIDRs updated",
), instance_id
); );
} }
} }
@@ -432,20 +431,14 @@ fn handle_event(
}) })
} }
fn print_event(instance_id: uuid::Uuid, msg: String) { impl Display for proto::api::instance::PeerConnInfo {
println!( fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
"{}: [{}] {}", f.debug_struct("PeerConnInfo")
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"), .field("my_peer_id", &self.my_peer_id)
instance_id, .field("dst_peer_id", &self.peer_id)
msg .field("tunnel_info", &self.tunnel)
); .finish()
} }
fn peer_conn_info_to_string(p: proto::api::instance::PeerConnInfo) -> String {
format!(
"my_peer_id: {}, dst_peer_id: {}, tunnel_info: {:?}",
p.my_peer_id, p.peer_id, p.tunnel
)
} }
#[cfg(test)] #[cfg(test)]
+5 -2
View File
@@ -1,6 +1,9 @@
use std::sync::Arc; use std::sync::Arc;
use crate::{common::config::EncryptionAlgorithm, tunnel::packet_def::ZCPacket}; use crate::{
common::{config::EncryptionAlgorithm, log},
tunnel::packet_def::ZCPacket,
};
#[cfg(feature = "wireguard")] #[cfg(feature = "wireguard")]
pub mod ring_aes_gcm; pub mod ring_aes_gcm;
@@ -66,7 +69,7 @@ pub fn create_encryptor(
let algorithm = match EncryptionAlgorithm::try_from(algorithm) { let algorithm = match EncryptionAlgorithm::try_from(algorithm) {
Ok(algorithm) => algorithm, Ok(algorithm) => algorithm,
Err(_) => { Err(_) => {
eprintln!( log::warn!(
"Unknown encryption algorithm: {}, falling back to default AES-GCM", "Unknown encryption algorithm: {}, falling back to default AES-GCM",
algorithm algorithm
); );
+5 -3
View File
@@ -4,6 +4,8 @@ use std::path::PathBuf;
use service_manager::ServiceManager as _; use service_manager::ServiceManager as _;
use crate::common::log;
#[derive(Debug)] #[derive(Debug)]
pub struct ServiceInstallOptions { pub struct ServiceInstallOptions {
pub program: PathBuf, pub program: PathBuf,
@@ -72,7 +74,7 @@ impl Service {
let kind = service_manager::ServiceManagerKind::native()?; let kind = service_manager::ServiceManagerKind::native()?;
println!("service manager kind: {:?}", kind); log::info!("service manager kind: {:?}", kind);
Ok(Self { Ok(Self {
label: name.parse()?, label: name.parse()?,
@@ -98,7 +100,7 @@ impl Service {
self.service_manager self.service_manager
.update(ctx) .update(ctx)
.map_err(|e| anyhow::anyhow!("failed to update service: {:?}", e))?; .map_err(|e| anyhow::anyhow!("failed to update service: {:?}", e))?;
println!("Service updated successfully! Service Name: {}", self.label); log::info!("Service updated successfully! Service Name: {}", self.label);
return Ok(()); return Ok(());
} }
@@ -106,7 +108,7 @@ impl Service {
.install(ctx) .install(ctx)
.map_err(|e| anyhow::anyhow!("failed to install service: {:?}", e))?; .map_err(|e| anyhow::anyhow!("failed to install service: {:?}", e))?;
println!( log::info!(
"Service installed successfully! Service Name: {}", "Service installed successfully! Service Name: {}",
self.label self.label
); );
+35 -168
View File
@@ -1,15 +1,7 @@
use crate::common::log;
use indoc::formatdoc;
use std::{fs::OpenOptions, str::FromStr}; use std::{fs::OpenOptions, str::FromStr};
use anyhow::Context;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::{
layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer, Registry,
};
use crate::common::{
config::LoggingConfigLoader, get_logger_timer_rfc3339, tracing_rolling_appender::*,
};
pub type PeerRoutePair = crate::proto::api::instance::PeerRoutePair; pub type PeerRoutePair = crate::proto::api::instance::PeerRoutePair;
pub fn cost_to_str(cost: i32) -> String { pub fn cost_to_str(cost: i32) -> String {
@@ -24,124 +16,6 @@ pub fn float_to_str(f: f64, precision: usize) -> String {
format!("{:.1$}", f, precision) format!("{:.1$}", f, precision)
} }
pub type NewFilterSender = std::sync::mpsc::Sender<String>;
pub fn init_logger(
config: impl LoggingConfigLoader,
need_reload: bool,
) -> Result<Option<NewFilterSender>, anyhow::Error> {
use crate::rpc_service::logger::{CURRENT_LOG_LEVEL, LOGGER_LEVEL_SENDER};
let file_config = config.get_file_logger_config();
let file_level = file_config
.level
.map(|s| s.parse().unwrap())
.unwrap_or(LevelFilter::OFF);
let mut ret_sender: Option<NewFilterSender> = None;
// logger to rolling file
let mut file_layer = None;
if file_level != LevelFilter::OFF || need_reload {
let mut l = tracing_subscriber::fmt::layer();
l.set_ansi(false);
let file_filter = EnvFilter::builder()
.with_default_directive(file_level.into())
.from_env()
.with_context(|| "failed to create file filter")?;
let (file_filter, file_filter_reloader) =
tracing_subscriber::reload::Layer::new(file_filter);
if need_reload {
let (sender, recver) = std::sync::mpsc::channel();
ret_sender = Some(sender.clone());
// 初始化全局状态
let _ = LOGGER_LEVEL_SENDER.set(std::sync::Mutex::new(sender));
let _ = CURRENT_LOG_LEVEL.set(std::sync::Mutex::new(file_level.to_string()));
std::thread::spawn(move || {
while let Ok(lf) = recver.recv() {
let e = file_filter_reloader.modify(|f| {
if let Ok(nf) = EnvFilter::builder()
.with_default_directive(lf.parse::<LevelFilter>().unwrap().into())
.from_env()
.with_context(|| "failed to create file filter")
{
println!("Reload log filter succeed, new filter level: {:?}", lf);
*f = nf;
}
});
if e.is_err() {
println!("Failed to reload log filter: {:?}", e);
}
}
println!("Stop log filter reloader");
});
}
let dir = file_config.dir.as_deref().unwrap_or(".");
let file = file_config.file.as_deref().unwrap_or("easytier.log");
let path = std::path::Path::new(dir).join(file);
let path_str = path.to_string_lossy().into_owned();
let builder = RollingFileAppenderBase::builder();
let file_appender = builder
.filename(path_str)
.condition_daily()
.max_filecount(file_config.count.unwrap_or(10))
.condition_max_file_size(file_config.size_mb.unwrap_or(100) * 1024 * 1024)
.build()
.unwrap();
let wrapper = FileAppenderWrapper::new(file_appender);
// Create a simple wrapper that implements MakeWriter
file_layer = Some(
l.with_writer(wrapper)
.with_timer(get_logger_timer_rfc3339())
.with_filter(file_filter),
);
}
// logger to console
let console_config = config.get_console_logger_config();
let console_level = console_config
.level
.map(|s| s.parse().unwrap())
.unwrap_or(LevelFilter::OFF);
let console_filter = EnvFilter::builder()
.with_default_directive(console_level.into())
.from_env()
.unwrap();
let console_layer = tracing_subscriber::fmt::layer()
.pretty()
.with_timer(get_logger_timer_rfc3339())
.with_writer(std::io::stderr)
.with_filter(console_filter);
let registry = Registry::default();
#[cfg(not(feature = "tracing"))]
{
registry.with(console_layer).with(file_layer).init();
}
#[cfg(feature = "tracing")]
{
let console_subscriber_layer = console_subscriber::ConsoleLayer::builder().spawn();
registry
.with(console_layer)
.with(file_layer)
.with(console_subscriber_layer)
.init();
}
Ok(ret_sender)
}
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub fn utf8_or_gbk_to_string(s: &[u8]) -> String { pub fn utf8_or_gbk_to_string(s: &[u8]) -> String {
use encoding::{all::GBK, DecoderTrap, Encoding}; use encoding::{all::GBK, DecoderTrap, Encoding};
@@ -165,13 +39,17 @@ pub fn setup_panic_handler() {
use std::backtrace; use std::backtrace;
use std::io::Write; use std::io::Write;
std::panic::set_hook(Box::new(|info| { std::panic::set_hook(Box::new(|info| {
let mut stderr = std::io::stderr();
let sep = format!("{}\n", "=======".repeat(10));
let _ = stderr.write_all(format!("{sep}{}\n{sep}", "!PANIC!".repeat(10)).as_bytes());
PANIC_COUNT.with(|c| { PANIC_COUNT.with(|c| {
let mut count = c.borrow_mut(); let mut count = c.borrow_mut();
*count += 1; *count += 1;
}); });
let panic_count = PANIC_COUNT.with(|c| *c.borrow()); let panic_count = PANIC_COUNT.with(|c| *c.borrow());
if panic_count > 1 { if panic_count > 1 {
println!("panic happened more than once, exit immediately"); log::error!("panic happened more than once, exit immediately");
std::process::exit(1); std::process::exit(1);
} }
@@ -208,36 +86,42 @@ pub fn setup_panic_handler() {
} }
} }
println!("{}", rust_i18n::t!("core_app.panic_backtrace_save")); log::error!("{}", rust_i18n::t!("core_app.panic_backtrace_save"));
// write str to stderr & file // write str to stderr & file
let write_err = |s: String| { let mut write_err = |s: String| {
let mut stderr = std::io::stderr(); let _ = stderr.write_all(s.as_bytes());
let content = format!("{}: {}", chrono::Local::now(), s);
let _ = writeln!(stderr, "{}", content);
if let Some(mut f) = file.as_ref() { if let Some(mut f) = file.as_ref() {
let _ = writeln!(f, "{}", content); let _ = f.write_all(s.as_bytes());
} }
}; };
write_err("panic occurred, if this is a bug, please report this issue on github (https://github.com/easytier/easytier/issues)".to_string()); let msg = formatdoc! {"
write_err(format!("easytier version: {}", crate::VERSION)); panic occurred, if this is a bug, please report this issue on github (https://github.com/easytier/easytier/issues)
write_err(format!("os version: {}", std::env::consts::OS)); easytier version: {version}
write_err(format!("arch: {}", std::env::consts::ARCH)); os: {os}
write_err(format!( arch: {arch}
"panic is recorded in: {}", panic is recorded in: {file}
file_path thread: {thread}
time: {time}
location: {location}
panic info: {payload}
",
version = crate::VERSION,
os = std::env::consts::OS,
arch = std::env::consts::ARCH,
file = file_path
.and_then(|p| p.to_str().map(|x| x.to_string())) .and_then(|p| p.to_str().map(|x| x.to_string()))
.unwrap_or("<no file>".to_string()) .unwrap_or("<no file>".to_string()),
)); thread = thread,
write_err(format!("thread: {}", thread)); time = chrono::Local::now(),
write_err(format!("time: {}", chrono::Local::now())); location = location,
write_err(format!("location: {}", location)); payload = payload_str,
write_err(format!("panic info: {}", payload_str)); };
// backtrace is risky, so use it last write_err(msg);
let backtrace = backtrace::Backtrace::force_capture(); write_err(sep);
write_err(format!("backtrace: {:#?}", backtrace)); write_err(format!("{:#?}", backtrace::Backtrace::force_capture()));
std::process::exit(1); std::process::exit(1);
})); }));
@@ -257,20 +141,3 @@ pub fn weak_upgrade<T>(weak: &std::sync::Weak<T>) -> anyhow::Result<std::sync::A
weak.upgrade() weak.upgrade()
.ok_or_else(|| anyhow::anyhow!("{} not available", std::any::type_name::<T>())) .ok_or_else(|| anyhow::anyhow!("{} not available", std::any::type_name::<T>()))
} }
#[cfg(test)]
mod tests {
use crate::common::config::{self};
use super::*;
async fn test_logger_reload() {
println!("current working dir: {:?}", std::env::current_dir());
let config = config::LoggingConfigBuilder::default().build().unwrap();
let s = init_logger(&config, true).unwrap();
tracing::debug!("test not display debug");
s.unwrap().send(LevelFilter::DEBUG.to_string()).unwrap();
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
tracing::debug!("test display debug");
}
}
+6 -8
View File
@@ -2,7 +2,7 @@ use std::sync::Arc;
use crate::{ use crate::{
common::{ common::{
config::TomlConfigLoader, global_ctx::GlobalCtx, scoped_task::ScopedTask, config::TomlConfigLoader, global_ctx::GlobalCtx, log, scoped_task::ScopedTask,
set_default_machine_id, stun::MockStunInfoCollector, set_default_machine_id, stun::MockStunInfoCollector,
}, },
connector::create_connector_by_url, connector::create_connector_by_url,
@@ -87,18 +87,16 @@ impl WebClient {
loop { loop {
let conn = match connector.connect().await { let conn = match connector.connect().await {
Ok(conn) => conn, Ok(conn) => conn,
Err(e) => { Err(error) => {
println!( let wait = 1;
"Failed to connect to the server ({}), retrying in 5 seconds...", log::warn!(%error, "Failed to connect to the server, retrying in {} seconds...", wait);
e tokio::time::sleep(std::time::Duration::from_secs(wait)).await;
);
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
continue; continue;
} }
}; };
connected.store(true, Ordering::Release); connected.store(true, Ordering::Release);
println!("Successfully connected to {:?}", conn.info()); log::info!("Successfully connected to {:?}", conn.info());
let mut session = session::Session::new(conn, controller.clone()); let mut session = session::Session::new(conn, controller.clone());
session.wait().await; session.wait().await;