修改日志

This commit is contained in:
zqm
2026-04-09 15:15:37 +08:00
parent 64a42bebbc
commit 5be97844a3

View File

@@ -2,6 +2,77 @@ use rand::Rng;
use std::collections::{HashMap, HashSet};
use std::sync::{Arc, Mutex};
// ===================== 日志模块 =====================
/// 日志文件Mutex 确保线程安全写入)
static LOG_FILE: std::sync::Mutex<Option<std::fs::File>> = std::sync::Mutex::new(None);
/// 初始化日志文件(以追加方式打开)
fn init_log_file() {
let mut guard = LOG_FILE.lock().unwrap();
if guard.is_some() {
return;
}
let exe_path = match std::env::current_exe() {
Ok(p) => p,
Err(_) => return,
};
let log_dir = match exe_path.parent() {
Some(d) => d,
None => return,
};
let log_name = format!("Updater_{}.log", chrono::Local::now().format("%Y%m%d"));
let log_path = log_dir.join(log_name);
match std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(&log_path)
{
Ok(file) => {
eprintln!("[日志] 日志文件: {:?}", log_path);
*guard = Some(file);
}
Err(e) => {
eprintln!("[日志] 无法打开日志文件: {}", e);
}
}
}
/// 日志宏:同时输出到控制台和文件
macro_rules! log_print {
($($arg:tt)*) => {{
let msg = format!($($arg)*);
print!("{}\n", msg);
std::io::stdout().flush().ok();
if LOG_FILE.lock().unwrap().is_none() {
init_log_file();
}
if let Some(ref mut file) = *LOG_FILE.lock().unwrap() {
use std::io::Write;
let _ = writeln!(file, "{}", msg);
let _ = file.flush();
}
}};
}
/// 错误日志宏同时输出到控制台stderr和文件始终记录
macro_rules! log_eprintln {
($($arg:tt)*) => {{
let msg = format!($($arg)*);
eprintln!("{}", msg);
if LOG_FILE.lock().unwrap().is_none() {
init_log_file();
}
if let Some(ref mut file) = *LOG_FILE.lock().unwrap() {
use std::io::Write;
let _ = writeln!(file, "{}", msg);
let _ = file.flush();
}
}};
}
// ===================== 更新阶段枚举 =====================
/// 更新流程的三个阶段
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -412,7 +483,7 @@ fn schedule_bootloader_launch(
}
}
Err(e) => {
eprintln!("{} [错误] 启动 BootLoader.exe 失败: {}", ts, e);
log_eprintln!("{} [错误] 启动 BootLoader.exe 失败: {}", ts, e);
// 启动失败也发送断连信号
if let Some(tx) = shutdown_tx_arc.lock().unwrap().take() {
let _ = tx.send(());
@@ -436,7 +507,7 @@ fn schedule_bootloader_launch(
}
}
Err(e) => {
eprintln!("{} [错误] 启动 BootLoader.exe 失败: {}", ts, e);
log_eprintln!("{} [错误] 启动 BootLoader.exe 失败: {}", ts, e);
if let Some(tx) = shutdown_tx_arc.lock().unwrap().take() {
let _ = tx.send(());
}
@@ -447,28 +518,24 @@ fn schedule_bootloader_launch(
}
/// 发送 GetFileMd5 请求
fn request_file_md5(sender: &cube_lib::websocket::MessageSender, filename: &str, bytes: u64, debug: bool) {
fn request_file_md5(sender: &cube_lib::websocket::MessageSender, filename: &str, bytes: u64) {
let msg_str = format!(
r#"{{"Type":"GetFileMd5","Data":{{"filename":"{}","bytes":{}}}}}"#,
filename, bytes
);
if debug {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} 发送消息:{}", ts, msg_str);
}
log_print!("{} 发送消息:{}", ts, msg_str);
sender.send(msg_str);
}
/// 发送文件下载请求(断点续传)
fn request_download(sender: &cube_lib::websocket::MessageSender, filename: &str, offset: u64, debug: bool) {
fn request_download(sender: &cube_lib::websocket::MessageSender, filename: &str, offset: u64) {
let msg_str = format!(
r#"{{"Type":"DownloadFile","Data":{{"filename":"{}","offset":{}}}}}"#,
filename, offset
);
if debug {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} 发送消息:{}", ts, msg_str);
}
log_print!("{} 发送消息:{}", ts, msg_str);
sender.send(msg_str);
}
@@ -666,7 +733,7 @@ fn get_app_tmp_file_size(app_name: &str, relative_path: &str) -> u64 {
}
/// 获取其他应用的本地版本号(通过 PowerShell 读取 PE 版本)
fn get_local_app_version(app_name: &str, relative_path: &str, _debug: bool) -> String {
fn get_local_app_version(app_name: &str, relative_path: &str) -> String {
// 应用主 exe 一般在相对路径的顶级,如 app.exe 或 bin/app.exe
// 取第一段目录名或文件名作为 exe 查找起点
let file_path = get_updater_data_dir()
@@ -720,16 +787,14 @@ fn request_file_md5_for_app(
app_name: &str,
relative_path: &str,
offset: u64,
debug: bool,
) {
let msg_str = format!(
r#"{{"Type":"GetFileMd5","Data":{{"filename":"{}","bytes":{}}}}}"#,
relative_path, offset
);
if debug {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [应用] {} 请求 md5 (offset {}): {}", ts, app_name, offset, relative_path);
}
log_print!("{} [应用] {} 请求 md5 (offset {}): {}", ts, app_name, offset, relative_path);
log_print!("{} 发送消息:{}", ts, msg_str);
sender.send(msg_str);
}
@@ -738,16 +803,14 @@ fn request_download_for_app(
sender: &cube_lib::websocket::MessageSender,
relative_path: &str,
offset: u64,
debug: bool,
) {
let msg_str = format!(
r#"{{"Type":"DownloadFile","Data":{{"filename":"{}","offset":{}}}}}"#,
relative_path, offset
);
if debug {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [应用] 请求下载: {} (offset {})", ts, relative_path, offset);
}
log_print!("{} [应用] 请求下载: {} (offset {})", ts, relative_path, offset);
log_print!("{} 发送消息:{}", ts, msg_str);
sender.send(msg_str);
}
@@ -1072,16 +1135,14 @@ fn get_app_candidates(debug: bool) -> Vec<String> {
/// 递归扫描升级目录,返回 (relative_path, full_path) 列表
/// 跳过 .tmp 文件(如果存在同名非 tmp 文件的话)
fn scan_upgrade_dir(app_name: &str, debug: bool) -> Vec<(String, PathBuf)> {
fn scan_upgrade_dir(app_name: &str) -> Vec<(String, PathBuf)> {
let upgrade_base = get_updater_data_dir()
.join("Updater")
.join("UpGrade")
.join(app_name);
if !upgrade_base.exists() {
if debug {
println!("[应用] {} 升级目录不存在: {:?}", app_name, upgrade_base);
}
log_print!("[应用] {} 升级目录不存在: {:?}", app_name, upgrade_base);
return Vec::new();
}
@@ -1107,9 +1168,7 @@ fn scan_upgrade_dir(app_name: &str, debug: bool) -> Vec<(String, PathBuf)> {
let non_tmp = rel_str.trim_end_matches(".tmp");
let non_tmp_path = upgrade_base.join(non_tmp);
if non_tmp_path.exists() {
if debug {
println!("[应用] 跳过 tmp同名文件已存在: {} -> {}", rel_str, non_tmp);
}
log_print!("[应用] 跳过 tmp同名文件已存在: {} -> {}", rel_str, non_tmp);
continue;
}
}
@@ -1121,11 +1180,9 @@ fn scan_upgrade_dir(app_name: &str, debug: bool) -> Vec<(String, PathBuf)> {
}
}
if debug {
println!("[应用] {} 升级文件列表 ({} 个):", app_name, files.len());
log_print!("[应用] {} 升级文件列表 ({} 个):", app_name, files.len());
for (rel, _) in &files {
println!("[应用] - {}", rel);
}
log_print!("[应用] - {}", rel);
}
files
@@ -1145,7 +1202,7 @@ fn make_shutdown_channel() -> (
/// 运行 Updater使用 CubeLib 内置的自动重连)
/// 返回本次运行是否执行了更新(下载了文件
/// 返回本次运行是否更新了 Updater 自身(只有 Updater 更新了才需要退出重启
async fn run_updater(debug_mode: bool) -> bool {
// 创建本次运行的上下文
let ctx = Arc::new(UpdateContext::default());
@@ -1223,16 +1280,15 @@ async fn run_updater(debug_mode: bool) -> bool {
let update_check_done_clone2 = update_check_done_clone.clone();
client.on_message(move |msg_type, data, sender| {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
if debug_msg {
// 收到消息日志data 是完整 parsed 对象,取其中的 Data 字段打印
// 收到消息日志(始终记录)
let actual_data = data.get("Data").unwrap_or(&serde_json::Value::Null);
let data_str = serde_json::to_string(actual_data).unwrap_or_else(|_| "{}".to_string());
println!("{} 收到消息:{{\"Type\":{},\"Data\":{}}}",
log_print!("{} 收到消息:{{\"Type\":{},\"Data\":{}}}",
ts,
serde_json::to_string(&msg_type).unwrap_or_default(),
data_str
);
}
// 收到 welcome 后,根据当前阶段发送 GetFileVer
if msg_type == "welcome" {
@@ -1243,14 +1299,12 @@ async fn run_updater(debug_mode: bool) -> bool {
match current_phase {
UpdatePhase::BootLoader => {
// 阶段1只请求 BootLoader.exe
println!("{} [阶段1] 检查 BootLoader...", ts);
log_print!("{} [阶段1] 检查 BootLoader...", ts);
let msg_str = format!(
r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":["BootLoader.exe"]}}}}"#,
device_number
);
if debug_msg {
println!("{} 发送消息:{}", ts, msg_str);
}
log_print!("{} 发送消息:{}", ts, msg_str);
sender.send(msg_str);
}
_ => {
@@ -1274,40 +1328,42 @@ async fn run_updater(debug_mode: bool) -> bool {
let local_version = get_local_file_version("BootLoader.exe");
let tmp_size = get_tmp_file_size("BootLoader.exe");
println!("{} [版本] BootLoader.exe: 服务端={}, 本地={}", ts, server_version, local_version);
log_print!("{} [版本] BootLoader.exe: 服务端={}, 本地={}", ts, server_version, local_version);
let need_update = local_version == "0.0.0" || version_less_than(&local_version, server_version);
if need_update {
if tmp_size > 0 {
println!("{} [续传] BootLoader.exe 发现未完成下载,请求 hash 校验...", ts);
request_file_md5(&sender, "BootLoader.exe", tmp_size, debug_msg);
log_print!("{} [续传] BootLoader.exe 发现未完成下载,请求 hash 校验...", ts);
request_file_md5(&sender, "BootLoader.exe", tmp_size);
} else {
println!("{} [升级] BootLoader.exe 需要更新,开始下载...", ts);
request_download(&sender, "BootLoader.exe", 0, debug_msg);
log_print!("{} [升级] BootLoader.exe 需要更新,开始下载...", ts);
request_download(&sender, "BootLoader.exe", 0);
}
update_performed_clone2.store(true, std::sync::atomic::Ordering::SeqCst);
} else {
println!("{} [版本] BootLoader.exe 版本已是最新", ts);
log_print!("{} [版本] BootLoader.exe 版本已是最新", ts);
// 推进到阶段2
let next_phase = UpdatePhase::Updater;
*ctx_clone.current_phase.lock().unwrap() = next_phase;
println!("{} [阶段2] 检查 Updater...", ts);
log_print!("{} [阶段2] 检查 Updater...", ts);
let msg_str = format!(
r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":["Updater.exe"]}}}}"#,
device_number
);
log_print!("{} 发送消息:{}", ts, msg_str);
sender.send(msg_str);
}
} else {
// BootLoader 不在响应中推进到阶段2
*ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Updater;
println!("{} [阶段2] 检查 Updater...", ts);
log_print!("{} [阶段2] 检查 Updater...", ts);
let msg_str = format!(
r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":["Updater.exe"]}}}}"#,
device_number
);
log_print!("{} 发送消息:{}", ts, msg_str);
sender.send(msg_str);
}
}
@@ -1322,25 +1378,25 @@ async fn run_updater(debug_mode: bool) -> bool {
// 保存 Updater.exe 的服务端版本号
*ctx_clone.server_updater_version.lock().unwrap() = Some(server_version.to_string());
println!("{} [版本] Updater.exe: 服务端={}, 本地={}", ts, server_version, local_version);
log_print!("{} [版本] Updater.exe: 服务端={}, 本地={}", ts, server_version, local_version);
let need_update = local_version == "0.0.0" || version_less_than(&local_version, server_version);
if need_update {
if tmp_size > 0 {
println!("{} [续传] Updater.exe 发现未完成下载,请求 hash 校验...", ts);
request_file_md5(&sender, "Updater.exe", tmp_size, debug_msg);
log_print!("{} [续传] Updater.exe 发现未完成下载,请求 hash 校验...", ts);
request_file_md5(&sender, "Updater.exe", tmp_size);
} else {
println!("{} [升级] Updater.exe 需要更新,开始下载...", ts);
request_download(&sender, "Updater.exe", 0, debug_msg);
log_print!("{} [升级] Updater.exe 需要更新,开始下载...", ts);
request_download(&sender, "Updater.exe", 0);
}
update_performed_clone2.store(true, std::sync::atomic::Ordering::SeqCst);
} else {
println!("{} [版本] Updater.exe 版本已是最新", ts);
log_print!("{} [版本] Updater.exe 版本已是最新", ts);
// 检查是否有旧的 Updater.new.exe
if has_updater_new_exe() {
let updater_new_path = get_updater_data_dir().join("Updater.new.exe");
let _ = fs::remove_file(&updater_new_path);
println!("{} [清理] 删除旧的 Updater.new.exe", ts);
log_print!("{} [清理] 删除旧的 Updater.new.exe", ts);
}
// 推进到阶段3
@@ -1348,7 +1404,7 @@ async fn run_updater(debug_mode: bool) -> bool {
if candidates.is_empty() {
// 没有候选应用,所有阶段完成
*ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Complete;
println!("{} [阶段3] 没有候选应用,所有阶段完成", ts);
log_print!("{} [阶段3] 没有候选应用,所有阶段完成", ts);
update_check_done_clone2.store(true, std::sync::atomic::Ordering::SeqCst);
// 发送断连信号
if let Some(tx) = shutdown_tx_arc_clone.lock().unwrap().take() {
@@ -1356,7 +1412,7 @@ async fn run_updater(debug_mode: bool) -> bool {
}
} else {
*ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Apps;
println!("{} [阶段3] 检查应用: {:?}", ts, candidates);
log_print!("{} [阶段3] 检查应用: {:?}", ts, candidates);
// 构建应用版本查询
let mut file_list = Vec::new();
@@ -1368,6 +1424,7 @@ async fn run_updater(debug_mode: bool) -> bool {
r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":{}}}}}"#,
device_number, file_list_json
);
log_print!("{} 发送消息:{}", ts, msg_str);
sender.send(msg_str);
}
}
@@ -1394,6 +1451,7 @@ async fn run_updater(debug_mode: bool) -> bool {
r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":{}}}}}"#,
device_number, file_list_json
);
log_print!("{} 发送消息:{}", ts, msg_str);
sender.send(msg_str);
}
}
@@ -1408,9 +1466,9 @@ async fn run_updater(debug_mode: bool) -> bool {
let exe_name = format!("{}.exe", app_name);
if let Some(server_ver) = file_versions.get(&exe_name) {
let server_version = server_ver.as_str().unwrap_or("0.0.0");
let local_version = get_local_app_version(app_name, &exe_name, debug_msg);
let local_version = get_local_app_version(app_name, &exe_name);
println!("{} [应用] {}: 服务端={}, 本地={}", ts, app_name, server_version, local_version);
log_print!("{} [应用] {}: 服务端={}, 本地={}", ts, app_name, server_version, local_version);
if local_version == "0.0.0" || version_less_than(&local_version, server_version) {
apps_to_update.push(app_name.clone());
@@ -1421,18 +1479,18 @@ async fn run_updater(debug_mode: bool) -> bool {
// 处理需要更新的应用
let mut has_downloads = false;
for app_name in &apps_to_update {
let upgrade_files = scan_upgrade_dir(app_name, debug_msg);
let upgrade_files = scan_upgrade_dir(app_name);
if upgrade_files.is_empty() {
println!("{} [应用] {} 没有升级文件,跳过", ts, app_name);
log_print!("{} [应用] {} 没有升级文件,跳过", ts, app_name);
continue;
}
for (rel_path, _) in upgrade_files {
let tmp_size = get_app_tmp_file_size(app_name, &rel_path);
if tmp_size > 0 {
request_file_md5_for_app(&sender, app_name, &rel_path, tmp_size, debug_msg);
request_file_md5_for_app(&sender, app_name, &rel_path, tmp_size);
} else {
request_download_for_app(&sender, &rel_path, 0, debug_msg);
request_download_for_app(&sender, &rel_path, 0);
}
update_performed_clone2.store(true, std::sync::atomic::Ordering::SeqCst);
has_downloads = true;
@@ -1491,15 +1549,14 @@ async fn run_updater(debug_mode: bool) -> bool {
let tmp_filename = format!("{}.tmp", filename);
let local_md5 = compute_file_hash_in_dir(app_name, &tmp_filename, bytes, debug_msg);
if debug_msg {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
if let Some(ref lm) = local_md5 {
println!("{} [应用] {} md5: 本地={}, 服务端={}", ts, filename, lm, server_md5);
}
log_print!("{} [应用] {} md5: 本地={}, 服务端={}", ts, filename, lm, server_md5);
}
if local_md5.as_deref() == Some(server_md5) {
request_download_for_app(&sender, filename, bytes, debug_msg);
request_download_for_app(&sender, filename, bytes);
} else {
let tmp_path = get_updater_data_dir()
.join("Updater")
@@ -1507,7 +1564,7 @@ async fn run_updater(debug_mode: bool) -> bool {
.join(app_name)
.join(&tmp_filename);
let _ = fs::remove_file(&tmp_path);
request_download_for_app(&sender, filename, 0, debug_msg);
request_download_for_app(&sender, filename, 0);
}
break;
}
@@ -1539,20 +1596,16 @@ async fn run_updater(debug_mode: bool) -> bool {
// 比较 md5
if local_md5.as_deref() == Some(server_md5) {
// md5 相同,从 offset bytes 处续传
if debug_msg {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [续传] {} md5匹配{} 字节处续传", ts, filename, bytes);
}
request_download(&sender, filename, bytes, debug_msg);
log_print!("{} [续传] {} md5匹配{} 字节处续传", ts, filename, bytes);
request_download(&sender, filename, bytes);
} else {
// md5 不同,删除临时文件,重新下载
if debug_msg {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [续传] {} md5不匹配重新下载", ts, filename);
}
log_print!("{} [续传] {} md5不匹配重新下载", ts, filename);
let tmp_path = get_updater_data_dir().join(&tmp_filename);
let _ = fs::remove_file(&tmp_path);
request_download(&sender, filename, 0, debug_msg);
request_download(&sender, filename, 0);
}
}
}
@@ -1639,26 +1692,31 @@ async fn run_updater(debug_mode: bool) -> bool {
// ========== BootLoader/Updater 的 DownloadComplete 处理 ==========
let completed = handle_download_complete(&ctx_clone, data_obj, debug_msg);
if let Some(ref completed_file) = completed {
// BootLoader.exe 下载完成
// BootLoader.exe 下载完成推进到阶段2检查 Updater 版本
if completed_file == "BootLoader.exe" {
if !bootloader_downloaded_clone2.load(std::sync::atomic::Ordering::SeqCst) {
bootloader_downloaded_clone2.store(true, std::sync::atomic::Ordering::SeqCst);
if debug_msg {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [升级] BootLoader.exe 下载完成,开始下载 Updater.exe...", ts);
log_print!("{} [升级] BootLoader.exe 下载完成,推进到阶段2检查 Updater...", ts);
}
// 开始下载 Updater.exe
request_download(&sender, "Updater.exe", 0, debug_msg);
// 推进到阶段2由阶段2决定是否需要下载 Updater
*ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Updater;
let msg = format!(
r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":["Updater.exe"]}}}}"#,
device_number
);
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
log_print!("{} 发送消息:{}", ts, msg);
sender.send(msg);
}
}
// Updater.new.exe 下载完成
else if completed_file == "Updater.new.exe" {
if !updater_downloaded_clone2.load(std::sync::atomic::Ordering::SeqCst) {
updater_downloaded_clone2.store(true, std::sync::atomic::Ordering::SeqCst);
if debug_msg {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [升级] Updater.new.exe 下载完成DownloadComplete准备启动 BootLoader...", ts);
}
log_print!("{} [升级] Updater.new.exe 下载完成DownloadComplete准备启动 BootLoader...", ts);
schedule_bootloader_launch(debug_msg, shutdown_tx_arc.clone());
}
}
@@ -1679,7 +1737,7 @@ async fn run_updater(debug_mode: bool) -> bool {
// 设置错误回调
client.on_error(|error| {
eprintln!("[错误] WebSocket: {}", error);
log_eprintln!("[错误] WebSocket: {}", error);
});
// 设置首次连接回调
@@ -1763,8 +1821,8 @@ async fn run_updater(debug_mode: bool) -> bool {
println!("Updater 本次运行结束");
}
// 返回是否执行了更新(下载了文件
update_performed.load(std::sync::atomic::Ordering::SeqCst)
// 返回 Updater 是否被更新(只有 Updater 更新了才需要退出重启
updater_downloaded.load(std::sync::atomic::Ordering::SeqCst)
}
#[tokio::main]
@@ -1799,11 +1857,11 @@ async fn main() {
// 主循环:常驻运行,定期检查更新
loop {
// 运行一次 Updater连接、检查、下载
let update_performed = run_updater(config.debug_mode).await;
let updater_updated = run_updater(config.debug_mode).await;
if update_performed {
if updater_updated {
if config.debug_mode {
println!("Updater 执行了更新,即将退出(等待 BootLoader 重启)");
println!("Updater 自身已更新,即将退出(等待 BootLoader 重启)");
}
std::process::exit(0);
}