Skip to main content

ringo_core/
log.rs

1use std::fs::OpenOptions;
2use std::io::Write;
3use std::path::Path;
4use std::sync::{Mutex, OnceLock};
5
6/// Process-global log sink. The log is process-global by design (one RE thread
7/// serves all UAs, so writes carry no per-agent context). Uninitialized = the
8/// log is silent (e.g. ringo-flow without `--log`). The binary picks the
9/// destination — ringo-core never writes to a fixed path.
10static LOG_SINK: OnceLock<Mutex<Box<dyn Write + Send>>> = OnceLock::new();
11
12#[derive(Debug, Clone, Copy)]
13pub enum Level {
14    Error,
15    Warn,
16    Info,
17    Debug,
18}
19
20impl Level {
21    fn as_str(self) -> &'static str {
22        match self {
23            Self::Error => "ERROR",
24            Self::Warn => "WARN",
25            Self::Info => "INFO",
26            Self::Debug => "DEBUG",
27        }
28    }
29}
30
31/// Log to `path` (created, truncated; parent dirs made). First init wins; a
32/// failure to open leaves the log silent rather than crashing.
33pub fn init_file(path: impl AsRef<Path>) {
34    let path = path.as_ref();
35    if let Some(parent) = path.parent() {
36        let _ = std::fs::create_dir_all(parent);
37    }
38    if let Ok(f) = OpenOptions::new()
39        .create(true)
40        .write(true)
41        .truncate(true)
42        .open(path)
43    {
44        let _ = LOG_SINK.set(Mutex::new(Box::new(f)));
45    }
46}
47
48/// Log to stderr. First init wins.
49pub fn init_stderr() {
50    let _ = LOG_SINK.set(Mutex::new(Box::new(std::io::stderr())));
51}
52
53/// Write a single log line. No-op until a sink is initialized.
54pub fn write(level: Level, msg: &str) {
55    if let Some(mtx) = LOG_SINK.get() {
56        if let Ok(mut w) = mtx.lock() {
57            let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S");
58            let _ = writeln!(w, "[{}] {}: {}", ts, level.as_str(), msg);
59        }
60    }
61}
62
63/// Log with `format!`-style syntax. No-op until a sink is initialized.
64///
65/// Usage: `rlog!(info, "baresip pid={}", pid);`
66#[macro_export]
67macro_rules! rlog {
68    ($level:ident, $($arg:tt)+) => {
69        $crate::log::write($crate::log::Level::$level, &format!($($arg)+))
70    };
71}