use std::env;
use std::ffi::OsString;
use std::io;
use std::path::Path;
use std::sync::OnceLock;
use thiserror::Error as ThisError;
use tracing::info;
use tracing_config;
use tracing_config::config::ArcMutexGuard;
use tracing_config::TracingConfigError;
use crate::config::FindError;
use crate::prelude::*;
use crate::process::ExecType;
use crate::process_note;
#[derive(Debug)]
pub struct Config {
pub exec_type: ExecType,
pub is_debug: bool,
pub log_start: bool,
pub name: OsString,
pub paths: Option<OsString>,
pub print_path: bool,
pub text_width: usize,
}
impl Config {
#[must_use]
pub fn new(exec_type: ExecType) -> Config {
use ExecType::*;
let is_debug = get_env_debug().unwrap_or(false);
let name = match exec_type {
Binary => crate::env::inv_name(),
Example => crate::env::name(),
DocTest | UnitTest | IntegTest | BenchTest => crate::env::test_name(),
};
let paths = get_env();
Config {
exec_type,
is_debug,
log_start: true,
name: name.clone(),
paths,
print_path: true,
text_width: crate::TEXT_WIDTH,
}
}
}
#[derive(Debug, ThisError)]
pub enum InitError {
#[error("Cannot find configuration file")]
Find(#[from] FindError),
#[error("I/O error")]
Io(#[from] io::Error),
#[error("Cannot configure `tracing`")]
TracingConfig(#[from] TracingConfigError),
}
impl InitError {
#[must_use]
pub fn should_print(&self) -> bool {
match self {
InitError::Find(err) => err.should_print(),
_ => true,
}
}
}
fn get_env() -> Option<OsString> { env::var_os("tracing_config") }
fn get_env_debug() -> Option<bool> { env::var_os("tracing_config_debug").map(|val| val == "true") }
fn init_file(config: &Config, file: &Path) -> Result<ArcMutexGuard, InitError> {
let tracing_config =
tracing_config::config::read_config(file, tracing_config::config::RESOLVE_FROM_ENV_DEPTH)?;
if config.print_path {
process_note!(crate::io::stdout(), "Loaded configuration file `{}` titled {:?}", file.display(), tracing_config.title)?;
}
match tracing_config::config::init_config(config.is_debug, &tracing_config) {
Ok(guard) => {
if config.log_start {
info!("\n{}", start_message(config, file));
}
Ok(guard)
}
Err(err) => Err(err.into()),
}
}
#[allow(clippy::test_attr_in_doctest)]
pub fn init(config: &Config) -> &'static ArcMutexGuard {
static VAL: OnceLock<ArcMutexGuard> = OnceLock::new();
VAL.get_or_init(|| {
assert!(config.exec_type != ExecType::Binary);
match try_init_impl(config) {
Ok(guard) => guard,
Err(err) => panic!("{:?}", anyhow::Error::from(err).context("Cannot initialize logging")),
}
})
}
fn start_message(config: &Config, config_path: &Path) -> String {
let mut ret = String::new();
let inv_name = crate::env::inv_name().to_string_lossy();
let current_dir_str = match env::current_dir() {
Ok(dir) => format!("{dir:?}"),
Err(_) => String::from("N/A"),
};
let inv_path = crate::env::inv_path();
let path = crate::env::path();
ret.push_str(&format!("\
Process started: {inv_name}
Log-configuration file: {config_path:?}
Current directory: {current_dir_str}
Invocation path : {inv_path:?}
Path : {path:?}
"));
let args: Vec<String> = env::args().skip(1).collect();
if !args.is_empty() {
ret.push_str("\nArguments:\n\n");
for arg in args {
let line = format!("- {arg:?}\n");
ret.push_str(&line);
}
}
ret.pop(); ret.fence('#', config.text_width)
}
#[allow(clippy::needless_doctest_main)]
pub fn try_init(config: &Config) -> Result<ArcMutexGuard, InitError> {
assert!(config.exec_type == ExecType::Binary);
try_init_impl(config)
}
fn try_init_impl(config: &Config) -> Result<ArcMutexGuard, InitError> {
let config_file = crate::config::find_config_file(
config.exec_type,
"{}tracing.toml", config.is_debug,
&config.name,
config.paths.as_deref(),
true, )?;
init_file(config, &config_file.1)
}
#[cfg(test)]
mod tests {
use std::thread;
use std::time::Duration;
use tracing::info;
use super::*;
fn set_up() { init(&Config::new(ExecType::UnitTest)); }
#[cfg_attr(miri, ignore)]
#[test]
fn test_init_1() {
set_up();
for i in 0..4 {
info!(i, "test_init_1");
thread::sleep(Duration::from_millis(1));
}
}
#[cfg_attr(miri, ignore)]
#[test]
fn test_init_2() {
set_up();
for i in 0..4 {
info!(i, "test_init_2");
thread::sleep(Duration::from_millis(1));
}
}
}