1use crate::common::{CWString, NullByteError};
41use tracing_log::NormalizeEvent;
42use tracing_subscriber::fmt::FormatFields;
43
44#[derive(Debug, Clone, Default)]
51pub struct AviUtl2Formatter;
52
53impl<C, N> tracing_subscriber::fmt::FormatEvent<C, N> for AviUtl2Formatter
54where
55 C: tracing::Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
56 N: for<'a> tracing_subscriber::fmt::FormatFields<'a> + 'static,
57{
58 fn format_event(
59 &self,
60 ctx: &tracing_subscriber::fmt::FmtContext<'_, C, N>,
61 mut writer: tracing_subscriber::fmt::format::Writer<'_>,
62 event: &tracing::Event<'_>,
63 ) -> std::fmt::Result {
64 let meta = event.normalized_metadata();
65 let meta = meta.as_ref().unwrap_or_else(|| event.metadata());
66 let target = meta.target();
67 write!(writer, "[{target}] ")?;
68 ctx.format_fields(writer.by_ref(), event)?;
69 writer.write_str("\n")?;
70 Ok(())
71 }
72}
73
74#[derive(Debug, Clone, Default)]
78pub struct AviUtl2LogWriter;
79
80impl tracing_subscriber::fmt::MakeWriter<'_> for AviUtl2LogWriter {
81 type Writer = LockedInternalWriter;
82
83 fn make_writer(&self) -> Self::Writer {
84 LockedInternalWriter::plugin()
85 }
86
87 fn make_writer_for(&'_ self, meta: &tracing::Metadata<'_>) -> Self::Writer {
88 match *meta.level() {
89 tracing::Level::ERROR => LockedInternalWriter::error(),
90 tracing::Level::WARN => LockedInternalWriter::warn(),
91 tracing::Level::INFO => LockedInternalWriter::info(),
92 tracing::Level::DEBUG | tracing::Level::TRACE => LockedInternalWriter::verbose(),
93 }
94 }
95}
96
97static INTERNAL_WRITER_MUTEX_PLUGIN: std::sync::LazyLock<std::sync::Mutex<InternalWriter>> =
98 std::sync::LazyLock::new(|| {
99 std::sync::Mutex::new(InternalWriter::new(InternalWriterLevel::Plugin))
100 });
101static INTERNAL_WRITER_MUTEX_INFO: std::sync::LazyLock<std::sync::Mutex<InternalWriter>> =
102 std::sync::LazyLock::new(|| {
103 std::sync::Mutex::new(InternalWriter::new(InternalWriterLevel::Info))
104 });
105static INTERNAL_WRITER_MUTEX_WARN: std::sync::LazyLock<std::sync::Mutex<InternalWriter>> =
106 std::sync::LazyLock::new(|| {
107 std::sync::Mutex::new(InternalWriter::new(InternalWriterLevel::Warn))
108 });
109static INTERNAL_WRITER_MUTEX_ERROR: std::sync::LazyLock<std::sync::Mutex<InternalWriter>> =
110 std::sync::LazyLock::new(|| {
111 std::sync::Mutex::new(InternalWriter::new(InternalWriterLevel::Error))
112 });
113static INTERNAL_WRITER_MUTEX_VERBOSE: std::sync::LazyLock<std::sync::Mutex<InternalWriter>> =
114 std::sync::LazyLock::new(|| {
115 std::sync::Mutex::new(InternalWriter::new(InternalWriterLevel::Verbose))
116 });
117
118pub struct LockedInternalWriter {
119 mutex: &'static std::sync::Mutex<InternalWriter>,
120}
121
122impl LockedInternalWriter {
123 pub fn plugin() -> Self {
124 Self {
125 mutex: &INTERNAL_WRITER_MUTEX_PLUGIN,
126 }
127 }
128
129 pub fn info() -> Self {
130 Self {
131 mutex: &INTERNAL_WRITER_MUTEX_INFO,
132 }
133 }
134
135 pub fn warn() -> Self {
136 Self {
137 mutex: &INTERNAL_WRITER_MUTEX_WARN,
138 }
139 }
140
141 pub fn error() -> Self {
142 Self {
143 mutex: &INTERNAL_WRITER_MUTEX_ERROR,
144 }
145 }
146
147 pub fn verbose() -> Self {
148 Self {
149 mutex: &INTERNAL_WRITER_MUTEX_VERBOSE,
150 }
151 }
152}
153impl std::io::Write for LockedInternalWriter {
154 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
155 let mut writer = self.mutex.lock().unwrap();
156 writer.write(buf)
157 }
158
159 fn flush(&mut self) -> std::io::Result<()> {
160 let mut writer = self.mutex.lock().unwrap();
161 writer.flush()
162 }
163}
164
165enum InternalWriterLevel {
166 Plugin,
167 Info,
168 Warn,
169 Error,
170 Verbose,
171}
172
173struct InternalWriter {
174 level: InternalWriterLevel,
175 buffer: Vec<u8>,
176}
177impl InternalWriter {
178 fn new(level: InternalWriterLevel) -> Self {
179 Self {
180 level,
181 buffer: Vec::new(),
182 }
183 }
184}
185
186impl std::io::Write for InternalWriter {
187 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
188 self.buffer.extend_from_slice(buf);
189 self.flush()?;
190 Ok(buf.len())
191 }
192
193 fn flush(&mut self) -> std::io::Result<()> {
194 while let Some(pos) = self.buffer.iter().position(|&b| b == b'\n') {
195 let line = self.buffer.drain(..=pos).collect::<Vec<u8>>();
196 let line = String::from_utf8_lossy(&line);
197 let line = line.trim_end_matches('\n');
198 match self.level {
199 InternalWriterLevel::Plugin => {
200 let _ = write_plugin_log(line);
201 }
202 InternalWriterLevel::Info => {
203 let _ = write_info_log(line);
204 }
205 InternalWriterLevel::Warn => {
206 let _ = write_warn_log(line);
207 }
208 InternalWriterLevel::Error => {
209 let _ = write_error_log(line);
210 }
211 InternalWriterLevel::Verbose => {
212 let _ = write_verbose_log(line);
213 }
214 }
215 }
216 Ok(())
217 }
218}
219
220#[macro_export]
225macro_rules! ldbg {
226 () => {
227 $crate::lprintln!(verbose, "[{}:{}:{}]", ::std::file!(), ::std::line!(), ::std::column!());
228 };
229 ($val:expr $(,)?) => {
230 match $val {
231 tmp => {
232 $crate::lprintln!(verbose, "[{}:{}:{}] {} = {:#?}",
233 ::std::file!(),
234 ::std::line!(),
235 ::std::column!(),
236 ::std::stringify!($val),
237 &&tmp as &dyn ::std::fmt::Debug,
238 );
239 tmp
240 }
241 }
242 };
243 ($($val:expr),+ $(,)?) => {
244 ($($crate::ldbg!($val)),+,)
245 };
246}
247
248#[macro_export]
260macro_rules! lprintln {
261 (plugin, $($arg:tt)*) => {
262 ::std::mem::drop($crate::logger::write_plugin_log(&format!($($arg)*)));
263 };
264 (info, $($arg:tt)*) => {
265 ::std::mem::drop($crate::logger::write_info_log(&format!($($arg)*)));
266 };
267 (warn, $($arg:tt)*) => {
268 ::std::mem::drop($crate::logger::write_warn_log(&format!($($arg)*)));
269 };
270 (error, $($arg:tt)*) => {
271 ::std::mem::drop($crate::logger::write_error_log(&format!($($arg)*)));
272 };
273 (verbose, $($arg:tt)*) => {
274 ::std::mem::drop($crate::logger::write_verbose_log(&format!($($arg)*)));
275 };
276 ($($arg:tt)*) => {
277 $crate::lprintln!(plugin, $($arg)*);
278 };
279}
280
281#[cfg(feature = "wrap_log")]
282fn log_length_limit(kind_length: usize) -> usize {
283 static DLL_LENGTH: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
284 let dll_length = *DLL_LENGTH.get_or_init(|| {
285 process_path::get_dylib_path()
286 .map_or(0, |path| path.file_name().unwrap().to_string_lossy().len())
287 });
288 1023 - 35 - dll_length - kind_length
290}
291#[cfg(not(feature = "wrap_log"))]
292fn log_length_limit(_kind_length: usize) -> usize {
293 usize::MAX
295}
296
297fn split_into_chunks(message: &str, kind_length: usize) -> Vec<String> {
298 let log_length_limit = log_length_limit(kind_length);
300 let mut chunks = Vec::with_capacity(message.len() / log_length_limit + 1);
301 let mut current_chunk = String::new();
302 for letter in message.chars() {
303 let letter_len = letter.len_utf8();
304 if current_chunk.len() + letter_len > log_length_limit {
305 chunks.push(std::mem::take(&mut current_chunk));
306 }
307 current_chunk.push(letter);
308 }
309 if !current_chunk.is_empty() {
310 chunks.push(current_chunk);
311 }
312 chunks
313}
314
315pub fn write_plugin_log(message: &str) -> Result<(), NullByteError> {
326 with_logger_handle(|handle| unsafe {
327 for chunk in split_into_chunks(message, "PLUGIN".len()) {
328 let wide_message = CWString::new(&chunk)?;
329 ((*handle).log)(handle, wide_message.as_ptr());
330 }
331 Ok(())
332 })
333 .unwrap_or(Ok(()))
334}
335
336#[duplicate::duplicate_item(
337 level function_name log_method;
338 ["ERROR"] [write_error_log] [error];
339 ["WARN"] [write_warn_log] [warn];
340 ["INFO"] [write_info_log] [info];
341 ["VERBOSE"] [write_verbose_log] [verbose];
342)]
343#[doc = concat!("ログに", level, "レベルのメッセージを書き込みます。")]
344pub fn function_name(message: &str) -> Result<(), NullByteError> {
354 with_logger_handle(|handle| unsafe {
355 for chunk in split_into_chunks(message, level.len()) {
356 let wide_message = CWString::new(&chunk)?;
357 ((*handle).log_method)(handle, wide_message.as_ptr());
358 }
359 Ok(())
360 })
361 .unwrap_or(Ok(()))
362}
363
364struct InternalLoggerHandle(*mut aviutl2_sys::logger2::LOG_HANDLE);
365unsafe impl Send for InternalLoggerHandle {}
366
367static LOGGER_HANDLE: std::sync::OnceLock<std::sync::Mutex<InternalLoggerHandle>> =
368 std::sync::OnceLock::new();
369
370#[doc(hidden)]
371pub fn __initialize_logger(handle: *mut aviutl2_sys::logger2::LOG_HANDLE) {
372 let internal_handle = InternalLoggerHandle(handle);
373 LOGGER_HANDLE
374 .set(std::sync::Mutex::new(internal_handle))
375 .unwrap_or_else(|_| {
376 panic!("Logger has already been initialized");
377 });
378}
379
380#[doc(hidden)]
381pub fn __initialize_logger_unwind(handle: *mut aviutl2_sys::logger2::LOG_HANDLE) {
382 if let Err(panic_info) =
383 crate::utils::catch_unwind_with_panic_info(|| __initialize_logger(handle))
384 {
385 crate::tracing::error!("Panic occurred during InitializeLogger: {}", panic_info);
386 let _ = crate::logger::write_error_log(&panic_info);
387 }
388}
389
390impl InternalLoggerHandle {
391 fn ptr(&self) -> *mut aviutl2_sys::logger2::LOG_HANDLE {
392 self.0
393 }
394}
395
396fn with_logger_handle<F, T>(f: F) -> Option<T>
397where
398 F: FnOnce(*mut aviutl2_sys::logger2::LOG_HANDLE) -> T,
399{
400 let handle = LOGGER_HANDLE.get()?;
401 let handle = handle.lock().unwrap();
402 let handle_ptr = handle.ptr();
403 Some(f(handle_ptr))
404}
405
406#[cfg(test)]
407mod tests {
408 #[test]
409 fn test_can_compile_ldbg() {
410 let x = 42;
411 ldbg!();
412 ldbg!(x);
413 ldbg!(x + 1, x * 2);
414 }
415
416 #[test]
417 fn test_can_compile_lprintln() {
418 lprintln!("This is a test log message.");
419 lprintln!(info, "This is an info log message.");
420 lprintln!(warn, "This is a warning log message.");
421 lprintln!(error, "This is an error log message.");
422 lprintln!(verbose, "This is a verbose log message.");
423 }
424
425 #[test]
426 #[cfg(feature = "wrap_log")]
427 fn test_split_into_chunks() {
428 let message = "a".repeat(5000);
429 let chunks = super::split_into_chunks(&message, "VERBOSE".len());
430 let dylib_name = process_path::get_dylib_path()
431 .and_then(|path| {
432 path.file_name()
433 .map(|name| name.to_string_lossy().into_owned())
434 })
435 .unwrap();
436 for chunk in chunks {
437 assert!(chunk.len() <= super::log_length_limit("VERBOSE".len()));
438 assert!(
439 format!(
440 "[01/23 08:43:47] [VERBOSE] [Plugin::{}] {}",
441 dylib_name, chunk
442 )
443 .len()
444 <= 1023
445 );
446 }
447 }
448}