如何将Rust子进程日志分存独立文件并添加进程标识前缀?
解决方案
要实现你的两个需求(子进程日志分文件存储、终端输出带进程编号前缀),可以通过修改std::process::Command的输出重定向逻辑来完成,以下是具体实现:
1. 将每个子进程日志写入独立文件
在创建子进程时,为每个进程指定独立的日志文件作为stdout和stderr的输出目标,代码示例如下:
use std::fs::File; use std::process::Command; fn main() { let args = ["run", "--features", "my-feat"]; for i in 0..3 { // 创建对应编号的日志文件 let log_file = File::create(format!("testfile{}.log", i + 1)) .expect(format!("Failed to create testfile{}.log", i + 1).as_str()); // 配置子进程,将stdout和stderr重定向到日志文件 let _process = Command::new("cargo") .env("RUST_LOG", "trace") // 传递日志环境变量 .args(&args) .stdout(log_file.try_clone().expect("Failed to clone file handle")) .stderr(log_file) .spawn() .expect("Failed to spawn child process"); } }
这样每个子进程的标准输出和错误都会被写入对应的testfile1.log、testfile2.log、testfile3.log文件,无需再用shell的重定向命令。
2. 终端输出带进程编号前缀
如果需要在终端直接查看带前缀的输出,需要捕获子进程的输出流,在父进程中为每一行添加前缀后再打印。这里需要用线程来异步处理每个子进程的输出,避免阻塞:
use std::io::{BufRead, BufReader}; use std::process::{Command, Stdio}; use std::thread; fn main() { let args = ["run", "--features", "my-feat"]; let mut handles = vec![]; for i in 0..3 { // 配置子进程,捕获stdout和stderr let mut child = Command::new("cargo") .env("RUST_LOG", "trace") .args(&args) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() .expect("Failed to spawn child process"); // 处理stdout let stdout = child.stdout.take().expect("Failed to take stdout"); let handle_stdout = thread::spawn(move || { let reader = BufReader::new(stdout); for line in reader.lines() { if let Ok(line) = line { println!("[{}] {}", i, line); } } }); // 处理stderr let stderr = child.stderr.take().expect("Failed to take stderr"); let handle_stderr = thread::spawn(move || { let reader = BufReader::new(stderr); for line in reader.lines() { if let Ok(line) = line { eprintln!("[{}] {}", i, line); // 错误输出到stderr } } }); handles.push((child, handle_stdout, handle_stderr)); } // 等待所有子进程和线程完成 for (mut child, handle_stdout, handle_stderr) in handles { let _ = child.wait(); let _ = handle_stdout.join(); let _ = handle_stderr.join(); } }
这段代码会为每个子进程的stdout和stderr输出行添加[0]、[1]、[2]的前缀,区分不同子进程的输出。
3. 同时实现日志分文件和终端带前缀输出
如果需要既将日志写入独立文件,又在终端显示带前缀的输出,可以结合上述两种方式,在捕获输出后,同时写入文件和终端:
use std::fs::File; use std::io::{BufRead, BufReader, Write}; use std::process::{Command, Stdio}; use std::thread; fn main() { let args = ["run", "--features", "my-feat"]; let mut handles = vec![]; for i in 0..3 { let log_file = File::create(format!("testfile{}.log", i + 1)) .expect(format!("Failed to create testfile{}.log", i + 1).as_str()); // 配置子进程,捕获stdout和stderr let mut child = Command::new("cargo") .env("RUST_LOG", "trace") .args(&args) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() .expect("Failed to spawn child process"); // 处理stdout:同时打印到终端和写入文件 let stdout = child.stdout.take().expect("Failed to take stdout"); let mut log_file_stdout = log_file.try_clone().expect("Failed to clone file handle"); let handle_stdout = thread::spawn(move || { let reader = BufReader::new(stdout); for line in reader.lines() { if let Ok(line) = line { let prefixed_line = format!("[{}] {}\n", i, line); println!("{}", prefixed_line.trim_end()); let _ = log_file_stdout.write_all(prefixed_line.as_bytes()); } } }); // 处理stderr:同时打印到终端和写入文件 let stderr = child.stderr.take().expect("Failed to take stderr"); let mut log_file_stderr = log_file; let handle_stderr = thread::spawn(move || { let reader = BufReader::new(stderr); for line in reader.lines() { if let Ok(line) = line { let prefixed_line = format!("[{}] {}\n", i, line); eprintln!("{}", prefixed_line.trim_end()); let _ = log_file_stderr.write_all(prefixed_line.as_bytes()); } } }); handles.push((child, handle_stdout, handle_stderr)); } // 等待所有子进程和线程完成 for (mut child, handle_stdout, handle_stderr) in handles { let _ = child.wait(); let _ = handle_stdout.join(); let _ = handle_stderr.join(); } }
这样运行后,每个子进程的输出会同时带前缀显示在终端,并且写入对应的独立日志文件。
内容的提问来源于stack exchange,提问作者NewToCode
相关产品推荐
相关产品推荐

