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 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}