|
@@ -92,25 +92,17 @@ mod tests {
|
|
|
use super::*;
|
|
use super::*;
|
|
|
use chrono::{Local};
|
|
use chrono::{Local};
|
|
|
use chrono_tz::Asia::Shanghai;
|
|
use chrono_tz::Asia::Shanghai;
|
|
|
- use std::{fs, io::Read, path::Path, thread, time::Duration};
|
|
|
|
|
-
|
|
|
|
|
- // // 辅助函数:清理日志文件 (保持不变)
|
|
|
|
|
- // fn cleanup_log_file(filename: &str) {
|
|
|
|
|
- // // ... (代码不变) ...
|
|
|
|
|
- // if Path::new(filename).exists() {
|
|
|
|
|
- // let _ = fs::remove_file(filename);
|
|
|
|
|
- // println!("Cleaned up log file: {}", filename);
|
|
|
|
|
- // }
|
|
|
|
|
- // let log_dir = Path::new("logs");
|
|
|
|
|
- // if log_dir.exists() {
|
|
|
|
|
- // if let Ok(mut read_dir) = log_dir.read_dir() {
|
|
|
|
|
- // if read_dir.next().is_none() { // 检查目录是否为空
|
|
|
|
|
- // let _ = fs::remove_dir(log_dir);
|
|
|
|
|
- // println!("Cleaned up empty log directory: logs");
|
|
|
|
|
- // }
|
|
|
|
|
- // }
|
|
|
|
|
- // }
|
|
|
|
|
- // }
|
|
|
|
|
|
|
+ use serde::Serialize;
|
|
|
|
|
+
|
|
|
|
|
+ // 定义一个用于测试的可序列化结构体
|
|
|
|
|
+ #[derive(Serialize)]
|
|
|
|
|
+ #[derive(Debug)]
|
|
|
|
|
+ struct TestData {
|
|
|
|
|
+ id: u32,
|
|
|
|
|
+ name: String,
|
|
|
|
|
+ is_active: bool,
|
|
|
|
|
+ tags: Vec<String>,
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
#[test]
|
|
#[test]
|
|
|
fn test_logging_setup_and_output() {
|
|
fn test_logging_setup_and_output() {
|
|
@@ -120,86 +112,49 @@ mod tests {
|
|
|
"logs/pin_trading_tool.log.{}",
|
|
"logs/pin_trading_tool.log.{}",
|
|
|
now_shanghai.format("%Y-%m-%d")
|
|
now_shanghai.format("%Y-%m-%d")
|
|
|
);
|
|
);
|
|
|
- println!("Expected log file: {}", expected_log_filename);
|
|
|
|
|
-
|
|
|
|
|
- // // 测试开始前清理
|
|
|
|
|
- // cleanup_log_file(&expected_log_filename);
|
|
|
|
|
- //
|
|
|
|
|
- // // 使用 RAII 清理,确保即使 panic 也会执行
|
|
|
|
|
- // struct LogCleaner<'a>(&'a str);
|
|
|
|
|
- // impl<'a> Drop for LogCleaner<'a> {
|
|
|
|
|
- // fn drop(&mut self) {
|
|
|
|
|
- // cleanup_log_file(self.0);
|
|
|
|
|
- // println!("Log cleaner finished cleanup for: {}", self.0);
|
|
|
|
|
- // }
|
|
|
|
|
- // }
|
|
|
|
|
- // let _cleaner = LogCleaner(&expected_log_filename);
|
|
|
|
|
|
|
+ println!("--- Log Test Start ---");
|
|
|
|
|
+ println!("Logging to console and file: {}", expected_log_filename);
|
|
|
|
|
+ println!("NOTE: This test will *not* assert content and *not* clean up the log file.");
|
|
|
|
|
|
|
|
// --- 执行 ---
|
|
// --- 执行 ---
|
|
|
- // 1. 初始化日志系统并获取 guards
|
|
|
|
|
- // 注意:全局日志记录器只能初始化一次。如果多个测试需要初始化,
|
|
|
|
|
- // 需要使用 `serial_test` crate 或类似的机制来确保测试串行执行。
|
|
|
|
|
|
|
+ // 1. 初始化日志
|
|
|
let setup_result = setup_logging();
|
|
let setup_result = setup_logging();
|
|
|
- assert!(setup_result.is_ok(), "Failed to setup logging: {:?}", setup_result.err());
|
|
|
|
|
- let guards = setup_result.unwrap(); // <-- 获取 guards
|
|
|
|
|
- println!("Logging setup successful, guards acquired.");
|
|
|
|
|
|
|
+ let _guards = match setup_result {
|
|
|
|
|
+ Ok(g) => {
|
|
|
|
|
+ println!("Logging setup successful, guards acquired.");
|
|
|
|
|
+ g // 返回 guards
|
|
|
|
|
+ },
|
|
|
|
|
+ Err(e) => {
|
|
|
|
|
+ // 如果初始化失败,打印错误并提前退出测试
|
|
|
|
|
+ eprintln!("FATAL: Failed to setup logging: {:?}. Test cannot continue.", e);
|
|
|
|
|
+ // 在测试函数中使用 panic! 来表示严重失败是合适的
|
|
|
|
|
+ panic!("Logging setup failed: {:?}", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- // 2. 记录日志消息
|
|
|
|
|
- // 现在 guards 还存活,日志应该能被处理
|
|
|
|
|
- let test_message = "这是测试信息\n这是测试信息换行"; // 使用唯一字符串便于查找
|
|
|
|
|
|
|
+ // 2. 记录各种日志
|
|
|
|
|
+ let test_message = "这是测试信息\n这是测试信息换行";
|
|
|
tracing::error!(target: "test_target", param = "value1", "错误消息内容 Error = {:?}", std::io::Error::new(std::io::ErrorKind::Other, "测试错误"));
|
|
tracing::error!(target: "test_target", param = "value1", "错误消息内容 Error = {:?}", std::io::Error::new(std::io::ErrorKind::Other, "测试错误"));
|
|
|
tracing::warn!("警告信息。 Value = {}", 42);
|
|
tracing::warn!("警告信息。 Value = {}", 42);
|
|
|
- tracing::info!(message = test_message);
|
|
|
|
|
- tracing::debug!("调试信息 (可能被过滤)"); // 默认INFO级别,这个可能不显示
|
|
|
|
|
- tracing::trace!("追踪信息 (可能被过滤)"); // 默认INFO级别,这个可能不显示
|
|
|
|
|
-
|
|
|
|
|
- // 3. **显式丢弃 guards 以触发刷新**
|
|
|
|
|
- // 在读取文件之前,确保所有缓冲的日志都已刷新。
|
|
|
|
|
- // drop(guards) 会调用每个 WorkerGuard 的 drop 实现,这会负责 flush。
|
|
|
|
|
- println!("Dropping logging guards to force flush...");
|
|
|
|
|
- drop(guards); // <-- 在这里显式 drop guards
|
|
|
|
|
- println!("Guards dropped.");
|
|
|
|
|
-
|
|
|
|
|
- // 4. (可选)短暂等待,以防万一文件系统写入有延迟
|
|
|
|
|
- // 在显式 drop guards 后,这个等待通常不再是必需的,但保留也无妨。
|
|
|
|
|
- thread::sleep(Duration::from_millis(100)); // 可以尝试缩短或移除
|
|
|
|
|
-
|
|
|
|
|
- // --- 验证 ---
|
|
|
|
|
- // 5. 检查日志文件
|
|
|
|
|
- println!("Checking log file: {}", expected_log_filename);
|
|
|
|
|
- assert!(
|
|
|
|
|
- Path::new(&expected_log_filename).exists(),
|
|
|
|
|
- "Log file '{}' was not created.", expected_log_filename
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- let mut file = match fs::File::open(&expected_log_filename) {
|
|
|
|
|
- Ok(f) => f,
|
|
|
|
|
- Err(e) => panic!("Failed to open log file '{}': {}", expected_log_filename, e),
|
|
|
|
|
|
|
+ tracing::info!(plain_message = test_message, "记录普通文本信息");
|
|
|
|
|
+
|
|
|
|
|
+ // --- 记录 JSON 数据 ---
|
|
|
|
|
+ let json_data = TestData {
|
|
|
|
|
+ id: 123,
|
|
|
|
|
+ name: "示例 \"数据\"".to_string(), // 包含引号的字符串
|
|
|
|
|
+ is_active: true,
|
|
|
|
|
+ tags: vec!["tag1".to_string(), "tag2".to_string()],
|
|
|
};
|
|
};
|
|
|
- let mut contents = String::new();
|
|
|
|
|
- match file.read_to_string(&mut contents) {
|
|
|
|
|
- Ok(_) => (),
|
|
|
|
|
- Err(e) => panic!("Failed to read log file '{}': {}", expected_log_filename, e),
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- println!("Log file content length: {}", contents.len());
|
|
|
|
|
- // 打印一部分内容用于调试
|
|
|
|
|
- // println!("Log content sample: {}", contents.chars().take(500).collect::<String>());
|
|
|
|
|
-
|
|
|
|
|
- assert!(!contents.is_empty(), "Log file is empty. File: '{}'", expected_log_filename);
|
|
|
|
|
-
|
|
|
|
|
- // 检查关键内容
|
|
|
|
|
- assert!(contents.contains(test_message), "Log content missing info message");
|
|
|
|
|
- assert!(contents.contains("ERROR"), "Log content missing ERROR level");
|
|
|
|
|
- assert!(contents.contains("test_target"), "Log content missing target 'test_target'");
|
|
|
|
|
- assert!(contents.contains("错误消息内容"), "Log content missing error message body");
|
|
|
|
|
- assert!(contents.contains("WARN"), "Log content missing WARN level");
|
|
|
|
|
- assert!(contents.contains("警告信息"), "Log content missing warn message");
|
|
|
|
|
- assert!(contents.contains("INFO"), "Log content missing INFO level");
|
|
|
|
|
- // 检查文件名和行号模式 (注意行号会变)
|
|
|
|
|
- assert!(contents.contains("log_setup.rs:"), "Log content missing file/line info indicator ('log_setup.rs:')");
|
|
|
|
|
-
|
|
|
|
|
- println!("Log content verified successfully.");
|
|
|
|
|
- // _cleaner 会在函数结束时自动调用 drop 进行清理
|
|
|
|
|
|
|
+ tracing::info!("json_data: {}", serde_json::to_string_pretty(&json_data).unwrap());
|
|
|
|
|
+ // match serde_json::to_string_pretty(&json_data) {
|
|
|
|
|
+ // Ok(json_string) => {
|
|
|
|
|
+ // tracing::info!(json_payload = %json_string, "记录 JSON 数据为字段");
|
|
|
|
|
+ // // tracing::info!("直接嵌入 JSON 示例:\n{}", json_string); // 也可以选择这种方式记录
|
|
|
|
|
+ // }
|
|
|
|
|
+ // Err(e) => {
|
|
|
|
|
+ // tracing::error!("序列化 JSON 失败: {:?}", e);
|
|
|
|
|
+ // }
|
|
|
|
|
+ // }
|
|
|
|
|
+ // --- 结束 JSON 记录 ---
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|