aviutl2/
logger.rs

1//! AviUtl2のロガーへのインターフェースを提供します。
2
3use crate::common::{CWString, NullByteError};
4pub use log::LevelFilter;
5
6// NOTE:
7// InitializeLoggerは可能な限り早く実行されるらしいので、まぁ捨てられるログはないとしていいはず...
8
9/// フォーマッター。
10///
11/// # See Also
12///
13/// [`LogBuilder::formatter`]
14pub type Formatter = dyn Fn(&log::Record) -> String + Send + Sync + 'static;
15
16/// [`log`]クレートを使用してAviUtl2のログ出力を設定するためのビルダー。
17///
18/// # Note
19///
20/// Debug、TraceレベルのログはVERBOSEとしてまとめられます。
21///
22/// # See Also
23///
24/// - [`env_filter::Builder`](https://docs.rs/env_filter/latest/env_filter/struct.Builder.html)
25#[must_use]
26pub struct LogBuilder {
27    filter: env_filter::Builder,
28    formatter: Option<Box<Formatter>>,
29}
30
31impl LogBuilder {
32    /// 新しい`LogBuilder`を作成します。
33    pub fn new() -> Self {
34        LogBuilder {
35            filter: env_filter::Builder::new(),
36            formatter: None,
37        }
38    }
39
40    /// 全てのモジュールのログレベルを設定します。
41    pub fn filter_level(mut self, level: log::LevelFilter) -> Self {
42        self.filter.filter_level(level);
43        self
44    }
45
46    /// 指定したモジュールのログレベルを設定します。
47    pub fn filter_module(mut self, module: &str, level: log::LevelFilter) -> Self {
48        self.filter.filter_module(module, level);
49        self
50    }
51
52    /// ログのフォーマッタを設定します。
53    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    /// ログのフォーマッターをデフォルトのものに設定します。
62    pub fn default_formatter(mut self) -> Self {
63        self.formatter = None;
64        self
65    }
66
67    /// ロガーを初期化します。
68    ///
69    /// [`LogBuilder::try_init`]と異なり、エラーが発生した場合にパニックします。
70    pub fn init(self) {
71        self.try_init().expect("Failed to initialize logger")
72    }
73
74    /// ロガーを初期化します。
75    ///
76    /// # Errors
77    ///
78    /// ロガーを2回以上初期化しようとした場合にエラーを返します。
79    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
110// NOTE: env_filterがいい感じにやってくれるらしい(ありがたい)
111impl 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        // No-op
123    }
124}
125
126/// プラグイン用ログに出力する[`dbg!`]マクロ。
127///
128/// # See Also
129/// <https://github.com/rust-lang/rust/blob/29483883eed69d5fb4db01964cdf2af4d86e9cb2/library/std/src/macros.rs#L352>
130#[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/// プラグイン用ログに出力する[`println!`]マクロ。
155///
156/// ```rust
157/// # use aviutl2::lprintln;
158/// lprintln!("This is a plugin log message.");  // デフォルトはpluginログに出力
159/// lprintln!(plugin, "This is also a plugin log message.");
160/// lprintln!(info, "This is an info log message.");
161/// lprintln!(warn, "This is a warning log message.");
162/// lprintln!(error, "This is an error log message.");
163/// lprintln!(verbose, "This is a verbose log message.");
164/// ```
165#[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
187/// プラグイン用ログにメッセージを書き込みます。
188///
189/// # Note
190///
191/// ロガーが初期化されていない場合は何も行いません。
192///
193/// # See Also
194///
195/// - [`ldbg!`]
196/// - [`lprintln!`]
197pub 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, "レベルのメッセージを書き込みます。")]
214///
215/// # Note
216///
217/// ロガーが初期化されていない場合は何も行いません。
218///
219/// # See Also
220///
221/// - [`ldbg!`]
222/// - [`lprintln!`]
223pub 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}