1use crate::common::{CWString, NullByteError};
4pub use log::LevelFilter;
5
6pub type Formatter = dyn Fn(&log::Record) -> String + Send + Sync + 'static;
15
16#[must_use]
26pub struct LogBuilder {
27 filter: env_filter::Builder,
28 formatter: Option<Box<Formatter>>,
29}
30
31impl LogBuilder {
32 pub fn new() -> Self {
34 LogBuilder {
35 filter: env_filter::Builder::new(),
36 formatter: None,
37 }
38 }
39
40 pub fn filter_level(mut self, level: log::LevelFilter) -> Self {
42 self.filter.filter_level(level);
43 self
44 }
45
46 pub fn filter_module(mut self, module: &str, level: log::LevelFilter) -> Self {
48 self.filter.filter_module(module, level);
49 self
50 }
51
52 pub fn formatter<F>(mut self, formatter: F) -> Self
54 where
55 F: Fn(&log::Record) -> String + Send + Sync + 'static,
56 {
57 self.formatter = Some(Box::new(formatter));
58 self
59 }
60
61 pub fn default_formatter(mut self) -> Self {
63 self.formatter = None;
64 self
65 }
66
67 pub fn init(self) {
71 self.try_init().expect("Failed to initialize logger")
72 }
73
74 pub fn try_init(self) -> Result<(), log::SetLoggerError> {
80 let LogBuilder {
81 mut filter,
82 formatter,
83 } = self;
84 let filter = filter.build();
85 let logger = InternalLogger::new(formatter.unwrap_or_else(|| {
86 Box::new(|record: &log::Record| format!("[{}] {}", record.target(), record.args()))
87 }));
88 log::set_max_level(filter.filter());
89 let logger = env_filter::FilteredLog::new(logger, filter);
90 log::set_boxed_logger(Box::new(logger))?;
91 Ok(())
92 }
93}
94
95impl Default for LogBuilder {
96 fn default() -> Self {
97 Self::new()
98 }
99}
100struct InternalLogger {
101 formatter: Box<Formatter>,
102}
103
104impl InternalLogger {
105 fn new(formatter: Box<Formatter>) -> Self {
106 InternalLogger { formatter }
107 }
108}
109
110impl log::Log for InternalLogger {
112 fn enabled(&self, _metadata: &log::Metadata) -> bool {
113 true
114 }
115
116 fn log(&self, record: &log::Record) {
117 let message = (self.formatter)(record);
118 send_record(record.level(), message);
119 }
120
121 fn flush(&self) {
122 }
124}
125
126#[macro_export]
131macro_rules! ldbg {
132 () => {
133 $crate::lprintln!(verbose, "[{}:{}:{}]", ::std::file!(), ::std::line!(), ::std::column!());
134 };
135 ($val:expr $(,)?) => {
136 match $val {
137 tmp => {
138 $crate::lprintln!(verbose, "[{}:{}:{}] {} = {:#?}",
139 ::std::file!(),
140 ::std::line!(),
141 ::std::column!(),
142 ::std::stringify!($val),
143 &&tmp as &dyn ::std::fmt::Debug,
144 );
145 tmp
146 }
147 }
148 };
149 ($($val:expr),+ $(,)?) => {
150 ($($crate::ldbg!($val)),+,)
151 };
152}
153
154#[macro_export]
166macro_rules! lprintln {
167 (plugin, $($arg:tt)*) => {
168 let _ = $crate::logger::write_plugin_log(&format!($($arg)*));
169 };
170 (info, $($arg:tt)*) => {
171 let _ = $crate::logger::write_info_log(&format!($($arg)*));
172 };
173 (warn, $($arg:tt)*) => {
174 let _ = $crate::logger::write_warn_log(&format!($($arg)*));
175 };
176 (error, $($arg:tt)*) => {
177 let _ = $crate::logger::write_error_log(&format!($($arg)*));
178 };
179 (verbose, $($arg:tt)*) => {
180 let _ = $crate::logger::write_verbose_log(&format!($($arg)*));
181 };
182 ($($arg:tt)*) => {
183 $crate::lprintln!(plugin, $($arg)*);
184 };
185}
186
187pub fn write_plugin_log(message: &str) -> Result<(), NullByteError> {
198 with_logger_handle(|handle| unsafe {
199 let wide_message = CWString::new(message)?;
200 ((*handle).log)(handle, wide_message.as_ptr());
201 Ok(())
202 })
203 .unwrap_or(Ok(()))
204}
205
206#[duplicate::duplicate_item(
207 level function_name log_method;
208 ["ERROR"] [write_error_log] [error];
209 ["WARN"] [write_warn_log] [warn];
210 ["INFO"] [write_info_log] [info];
211 ["VERBOSE"] [write_verbose_log] [verbose];
212)]
213#[doc = concat!("ログに", level, "レベルのメッセージを書き込みます。")]
214pub fn function_name(message: &str) -> Result<(), NullByteError> {
224 with_logger_handle(|handle| unsafe {
225 let wide_message = CWString::new(message)?;
226 ((*handle).log_method)(handle, wide_message.as_ptr());
227 Ok(())
228 })
229 .unwrap_or(Ok(()))
230}
231
232struct InternalLoggerHandle(*mut aviutl2_sys::logger2::LOG_HANDLE);
233unsafe impl Send for InternalLoggerHandle {}
234
235static LOGGER_HANDLE: std::sync::OnceLock<std::sync::Mutex<InternalLoggerHandle>> =
236 std::sync::OnceLock::new();
237
238#[doc(hidden)]
239pub fn __initialize_logger(handle: *mut aviutl2_sys::logger2::LOG_HANDLE) {
240 let internal_handle = InternalLoggerHandle(handle);
241 LOGGER_HANDLE
242 .set(std::sync::Mutex::new(internal_handle))
243 .unwrap_or_else(|_| {
244 panic!("Logger has already been initialized");
245 });
246}
247
248impl InternalLoggerHandle {
249 fn ptr(&self) -> *mut aviutl2_sys::logger2::LOG_HANDLE {
250 self.0
251 }
252}
253
254fn with_logger_handle<F, T>(f: F) -> Option<T>
255where
256 F: FnOnce(*mut aviutl2_sys::logger2::LOG_HANDLE) -> T,
257{
258 let handle = LOGGER_HANDLE.get()?;
259 let handle = handle.lock().unwrap();
260 let handle_ptr = handle.ptr();
261 Some(f(handle_ptr))
262}
263
264fn send_record(level: log::Level, message: String) {
265 match level {
266 log::Level::Error => {
267 let _ = write_error_log(&message);
268 }
269 log::Level::Warn => {
270 let _ = write_warn_log(&message);
271 }
272 log::Level::Info => {
273 let _ = write_info_log(&message);
274 }
275 log::Level::Debug | log::Level::Trace => {
276 let _ = write_verbose_log(&message);
277 }
278 }
279}
280
281#[cfg(test)]
282mod tests {
283 #[test]
284 fn test_can_compile_ldbg() {
285 let x = 42;
286 ldbg!();
287 ldbg!(x);
288 ldbg!(x + 1, x * 2);
289 }
290}