unrust/
logger.rs

1use std::{ffi::c_char, io::Write};
2
3use parking_lot::Once;
4use tracing::{Level, Metadata};
5use tracing_subscriber::{
6    fmt::{
7        self,
8        format::{Compact, DefaultFields, Format},
9        Layer, MakeWriter,
10    },
11    prelude::*,
12    reload, Registry,
13};
14
15type Reloader =
16    reload::Handle<Layer<Registry, DefaultFields, Format<Compact, ()>, MakeLogger>, Registry>;
17
18static mut RELOAD: Option<Reloader> = None;
19static INIT: Once = Once::new();
20
21#[repr(u8)]
22#[derive(Clone, Copy)]
23pub enum LogLevel {
24    Error = 0,
25    Warning = 1,
26    Info = 2,
27    Debug = 3,
28}
29
30pub type LoggerFunc = extern "C" fn(level: LogLevel, str: *mut c_char, len: usize);
31
32type LogFn = dyn Fn(LogLevel, String) + Send + Sync + 'static;
33
34type LogCallback = Option<Box<LogFn>>;
35
36fn get_cached_reloader() -> &'static Reloader {
37    unsafe {
38        INIT.call_once(|| {
39            let layer = fmt::layer()
40                .without_time()
41                .with_ansi(false)
42                .compact()
43                .with_writer(MakeLogger { logger: None });
44            let (layer, reload_handle) = reload::Layer::new(layer);
45            tracing_subscriber::registry().with(layer).init();
46
47            RELOAD = Some(reload_handle);
48        });
49
50        if let Some(val) = &RELOAD {
51            val
52        } else {
53            panic!("reload not set")
54        }
55    }
56}
57
58pub(crate) fn setup_logging(logger: Box<LogFn>) {
59    let reloader = get_cached_reloader();
60    let _ = reloader.modify(|layer| {
61        *layer.writer_mut() = MakeLogger {
62            logger: Some(logger),
63        };
64    });
65}
66
67pub(crate) fn teardown_logging() {
68    let reloader = get_cached_reloader();
69    let _ = reloader.modify(|layer| {
70        *layer.writer_mut() = MakeLogger {
71            logger: LogCallback::default(),
72        };
73    });
74}
75
76struct MakeLogger {
77    logger: LogCallback,
78}
79
80enum UnityLogger<'a> {
81    Error(&'a LogCallback),
82    Warning(&'a LogCallback),
83    Info(&'a LogCallback),
84    Debug(&'a LogCallback),
85}
86
87impl<'a> Write for UnityLogger<'a> {
88    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
89        let str = String::from_utf8_lossy(buf).to_string();
90
91        let (level, log) = match self {
92            UnityLogger::Error(log) => (LogLevel::Error, log),
93            UnityLogger::Warning(log) => (LogLevel::Warning, log),
94            UnityLogger::Info(log) => (LogLevel::Info, log),
95            UnityLogger::Debug(log) => (LogLevel::Debug, log),
96        };
97
98        if let Some(logger) = log {
99            (logger)(level, str)
100        }
101
102        Ok(buf.len())
103    }
104
105    fn flush(&mut self) -> std::io::Result<()> {
106        Ok(())
107    }
108}
109
110impl<'a> MakeWriter<'a> for MakeLogger {
111    type Writer = UnityLogger<'a>;
112
113    fn make_writer(&'a self) -> Self::Writer {
114        // We must have an implementation of `make_writer` that makes
115        // a "default" writer without any configuring metadata. Let's
116        // just return stdout in that case.
117        UnityLogger::Info(&self.logger)
118    }
119
120    fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer {
121        match *meta.level() {
122            Level::DEBUG => UnityLogger::Debug(&self.logger),
123            Level::ERROR => UnityLogger::Error(&self.logger),
124            Level::INFO => UnityLogger::Info(&self.logger),
125            Level::WARN => UnityLogger::Warning(&self.logger),
126            Level::TRACE => UnityLogger::Info(&self.logger),
127        }
128    }
129}