logging.rs 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. use std::path::PathBuf;
  2. use std::time::SystemTime;
  3. use rand::Rng;
  4. /// Initialize a thread local logger for a test.
  5. ///
  6. /// A log file will be created in [`LOG_DIR`]. The name of the log file will
  7. /// be derived from the fully qualified test method name, plus a timestamp and
  8. /// 10 random characters, e.g. `mod-test_method-1600000000-abcdefghij.log`.
  9. ///
  10. /// The test logger is backed by `env_logger`. By default the test module will
  11. /// have `trace` level logging. Other modules have `info` level logging. See the
  12. /// documentation of `env_logger` on how to configure log levels.
  13. #[macro_export]
  14. macro_rules! init_test_log {
  15. () => {
  16. $crate::init_log(stdext::function_name!())
  17. .expect("Initializing test log should never fail")
  18. };
  19. }
  20. pub const LOG_DIR: &str = "/tmp/ruaft-test-logs/";
  21. /// Initialize a thread local logger for `module`.
  22. ///
  23. /// See [`init_test_log`] for more details.
  24. pub fn init_log(module: &str) -> std::io::Result<PathBuf> {
  25. let module_file = module.replace("::", "-");
  26. let timestamp = SystemTime::now()
  27. .duration_since(SystemTime::UNIX_EPOCH)
  28. .map(|d| d.as_secs())
  29. .unwrap_or(0);
  30. let suffix: String = rand::thread_rng()
  31. .sample_iter(&rand::distributions::Alphanumeric)
  32. .take(10)
  33. .map(char::from)
  34. .collect();
  35. let log_file_name =
  36. format!("{}-{:010}-{}.log", module_file, timestamp, suffix);
  37. let log_dir = option_env!("LOG_DIR").unwrap_or(LOG_DIR);
  38. let mut path = PathBuf::from(log_dir);
  39. std::fs::create_dir_all(path.as_path())?;
  40. path.push(log_file_name);
  41. let log_file = std::fs::File::create(path.as_path())?;
  42. {
  43. let mut latest_path = path.clone();
  44. latest_path.pop();
  45. latest_path.push(format!("{}-latest", module_file));
  46. let _ = std::fs::remove_file(latest_path.as_path());
  47. #[cfg(unix)]
  48. let _ = std::os::unix::fs::symlink(path.as_path(), latest_path);
  49. #[cfg(windows)]
  50. let _ = std::os::windows::fs::symlink_file(path.as_path(), latest_path);
  51. }
  52. let module = match module.rfind("::") {
  53. Some(pos) => &module[..pos],
  54. None => &module,
  55. };
  56. let env = env_logger::Env::default().default_filter_or("info");
  57. let logger = env_logger::Builder::from_env(env)
  58. .target(env_logger::Target::Pipe(Box::new(log_file)))
  59. .filter(Some(module), log::LevelFilter::Trace)
  60. .format_timestamp_millis()
  61. .is_test(true)
  62. .build();
  63. crate::thread_local_logger::thread_init(logger);
  64. Ok(path)
  65. }