lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

tracing.rs (3975B)


      1 use radroots_log::{LogFileLayout, LoggingOptions};
      2 use radroots_runtime_paths::{
      3     RadrootsPathOverrides, RadrootsPathProfile, RadrootsPathResolver, RadrootsRuntimePathsError,
      4     default_shared_runtime_logs_dir as resolve_shared_runtime_logs_dir,
      5 };
      6 use std::path::{Path, PathBuf};
      7 
      8 use crate::error::RuntimeTracingError;
      9 
     10 pub fn init() -> Result<(), RuntimeTracingError> {
     11     let logs_dir = default_shared_runtime_logs_dir()?;
     12     init_with_logs_dir(logs_dir, None)
     13 }
     14 
     15 pub fn default_shared_runtime_logs_dir() -> Result<PathBuf, RadrootsRuntimePathsError> {
     16     default_shared_runtime_logs_dir_for(
     17         &current_path_resolver(),
     18         RadrootsPathProfile::InteractiveUser,
     19         &RadrootsPathOverrides::default(),
     20     )
     21 }
     22 
     23 pub fn default_shared_runtime_logs_dir_for(
     24     resolver: &RadrootsPathResolver,
     25     profile: RadrootsPathProfile,
     26     overrides: &RadrootsPathOverrides,
     27 ) -> Result<PathBuf, RadrootsRuntimePathsError> {
     28     resolve_shared_runtime_logs_dir(resolver, profile, overrides)
     29 }
     30 
     31 pub fn init_with_logs_dir(
     32     logs_dir: impl AsRef<Path>,
     33     default_level: Option<&str>,
     34 ) -> Result<(), RuntimeTracingError> {
     35     let logs_dir = logs_dir.as_ref();
     36     let env_file = env_value("LOG_FILE").or_else(|| env_value("RADROOTS_LOG_FILE"));
     37     let env_level = env_value("LOG_LEVEL").or_else(|| env_value("RUST_LOG"));
     38     let opts = LoggingOptions {
     39         dir: Some(logs_dir.to_path_buf()),
     40         file_name: env_file.unwrap_or_else(default_log_file_name),
     41         stdout: true,
     42         default_level: resolve_default_level(env_level, default_level),
     43         file_layout: LogFileLayout::PrefixedDate,
     44     };
     45     radroots_log::init_logging(opts)?;
     46     Ok(())
     47 }
     48 
     49 fn default_log_file_name() -> String {
     50     default_log_file_name_from_exe_name(log_name_from_exe())
     51 }
     52 
     53 fn default_log_file_name_from_exe_name(exe_name: Option<String>) -> String {
     54     exe_name.unwrap_or_else(|| format!("{}.log", env!("CARGO_PKG_NAME")))
     55 }
     56 
     57 fn log_name_from_exe() -> Option<String> {
     58     log_name_from_path(std::env::current_exe().ok())
     59 }
     60 
     61 fn log_name_from_path(exe: Option<PathBuf>) -> Option<String> {
     62     let exe = exe?;
     63     let name = exe.file_stem()?.to_string_lossy();
     64     log_name_from_stem(name.as_ref())
     65 }
     66 
     67 #[cfg(test)]
     68 mod test_hooks {
     69     use std::cell::{Cell, RefCell};
     70 
     71     use radroots_runtime_paths::RadrootsPathResolver;
     72 
     73     thread_local! {
     74         static IGNORE_ENV: Cell<bool> = const { Cell::new(false) };
     75         static CURRENT_RESOLVER: RefCell<Option<RadrootsPathResolver>> = RefCell::new(None);
     76     }
     77 
     78     pub fn set_ignore_env(ignore: bool) {
     79         IGNORE_ENV.with(|state| state.set(ignore));
     80     }
     81 
     82     pub fn ignore_env() -> bool {
     83         IGNORE_ENV.with(Cell::get)
     84     }
     85 
     86     pub fn set_current_resolver(resolver: Option<RadrootsPathResolver>) {
     87         CURRENT_RESOLVER.with(|state| *state.borrow_mut() = resolver);
     88     }
     89 
     90     pub fn current_resolver() -> Option<RadrootsPathResolver> {
     91         CURRENT_RESOLVER.with(|state| state.borrow().clone())
     92     }
     93 }
     94 
     95 fn current_path_resolver() -> RadrootsPathResolver {
     96     #[cfg(test)]
     97     if let Some(resolver) = test_hooks::current_resolver() {
     98         return resolver;
     99     }
    100 
    101     RadrootsPathResolver::current()
    102 }
    103 
    104 fn log_name_from_stem(stem: &str) -> Option<String> {
    105     if stem.is_empty() {
    106         None
    107     } else {
    108         Some(format!("{stem}.log"))
    109     }
    110 }
    111 
    112 fn env_value(key: &str) -> Option<String> {
    113     #[cfg(test)]
    114     if test_hooks::ignore_env() {
    115         return None;
    116     }
    117     let value = std::env::var(key).ok()?;
    118     normalize_env_value(&value)
    119 }
    120 
    121 fn normalize_env_value(value: &str) -> Option<String> {
    122     let trimmed = value.trim();
    123     if trimmed.is_empty() {
    124         None
    125     } else {
    126         Some(trimmed.to_string())
    127     }
    128 }
    129 
    130 fn resolve_default_level(env_level: Option<String>, default_level: Option<&str>) -> Option<String> {
    131     if let Some(level) = env_level {
    132         Some(level)
    133     } else {
    134         default_level.map(str::to_string)
    135     }
    136 }
    137 
    138 #[cfg(test)]
    139 mod tests;