From e4d9fd918f62a81337ba8bf63c601955f50d2505 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 23 Jul 2024 16:51:18 -0300 Subject: [PATCH 1/3] feat(log): add Builder::split to get the raw logger implementation This function lets you split the Builder to return the raw logger implementation along the TauriPlugin to be registered. Useful to pipe the logger to other implementations such as multi_log or tauri-plugin-devtools, allowing the plugin to be used along other logging systems. --- .changes/log-split.md | 5 + Cargo.lock | 9 +- plugins/log/Cargo.toml | 1 + plugins/log/src/lib.rs | 234 +++++++++++++++++++++++++---------------- 4 files changed, 157 insertions(+), 92 deletions(-) create mode 100644 .changes/log-split.md diff --git a/.changes/log-split.md b/.changes/log-split.md new file mode 100644 index 000000000..176b7efd4 --- /dev/null +++ b/.changes/log-split.md @@ -0,0 +1,5 @@ +--- +"log": patch +--- + +Added `Builder::split` which returns the raw logger implementation so you can pipe to other loggers such as `multi_log` or `tauri-plugin-devtools`. diff --git a/Cargo.lock b/Cargo.lock index a5c532b36..74622341f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -200,7 +200,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "api" -version = "2.0.0-beta.13" +version = "2.0.0-beta.14" dependencies = [ "log", "serde", @@ -6603,6 +6603,7 @@ dependencies = [ "swift-rs", "tauri", "tauri-plugin", + "thiserror", "time", ] @@ -6621,7 +6622,7 @@ dependencies = [ [[package]] name = "tauri-plugin-notification" -version = "2.0.0-beta.10" +version = "2.0.0-beta.11" dependencies = [ "color-backtrace", "ctor", @@ -6727,7 +6728,7 @@ dependencies = [ [[package]] name = "tauri-plugin-sql" -version = "2.0.0-beta.9" +version = "2.0.0-beta.10" dependencies = [ "futures-core", "indexmap 2.2.6", @@ -6778,7 +6779,7 @@ dependencies = [ [[package]] name = "tauri-plugin-updater" -version = "2.0.0-beta.10" +version = "2.0.0-beta.11" dependencies = [ "base64 0.22.1", "dirs 5.0.1", diff --git a/plugins/log/Cargo.toml b/plugins/log/Cargo.toml index 6b975548e..c62e5f259 100644 --- a/plugins/log/Cargo.toml +++ b/plugins/log/Cargo.toml @@ -25,6 +25,7 @@ byte-unit = "5" log = { workspace = true, features = [ "kv_unstable" ] } time = { version = "0.3", features = [ "formatting", "local-offset" ] } fern = "0.6" +thiserror = "1" [target."cfg(target_os = \"android\")".dependencies] android_logger = "0.14" diff --git a/plugins/log/src/lib.rs b/plugins/log/src/lib.rs index b9eb4baae..a048001e5 100644 --- a/plugins/log/src/lib.rs +++ b/plugins/log/src/lib.rs @@ -24,11 +24,11 @@ use std::{ iter::FromIterator, path::{Path, PathBuf}, }; -use tauri::Emitter; use tauri::{ plugin::{self, TauriPlugin}, Manager, Runtime, }; +use tauri::{AppHandle, Emitter}; pub use fern; use time::OffsetDateTime; @@ -75,6 +75,18 @@ const DEFAULT_LOG_TARGETS: [Target; 2] = [ Target::new(TargetKind::LogDir { file_name: None }), ]; +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + Tauri(#[from] tauri::Error), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + TimeFormat(#[from] time::error::Format), + #[error(transparent)] + InvalidFormatDescription(#[from] time::error::InvalidFormatDescription), +} + /// An enum representing the available verbosity levels of the logger. /// /// It is very similar to the [`log::Level`], but serializes to unsigned ints instead of strings. @@ -395,97 +407,133 @@ impl Builder { }) } - pub fn build(mut self) -> TauriPlugin { - plugin::Builder::new("log") - .invoke_handler(tauri::generate_handler![log]) - .setup(move |app_handle, _api| { - let app_name = &app_handle.package_info().name; + fn acquire_logger( + app_handle: &AppHandle, + mut dispatch: fern::Dispatch, + rotation_strategy: RotationStrategy, + timezone_strategy: TimezoneStrategy, + max_file_size: u128, + targets: Vec, + ) -> Result<(log::LevelFilter, Box), Error> { + let app_name = &app_handle.package_info().name; + + // setup targets + for target in targets { + let mut target_dispatch = fern::Dispatch::new(); + for filter in target.filters { + target_dispatch = target_dispatch.filter(filter); + } - // setup targets - for target in self.targets { - let mut target_dispatch = fern::Dispatch::new(); - for filter in target.filters { - target_dispatch = target_dispatch.filter(filter); + let logger = match target.kind { + #[cfg(target_os = "android")] + TargetKind::Stdout | TargetKind::Stderr => fern::Output::call(android_logger::log), + #[cfg(target_os = "ios")] + TargetKind::Stdout | TargetKind::Stderr => fern::Output::call(move |record| { + let message = format!("{}", record.args()); + unsafe { + ios::tauri_log( + match record.level() { + log::Level::Trace | log::Level::Debug => 1, + log::Level::Info => 2, + log::Level::Warn | log::Level::Error => 3, + }, + ios::NSString::new(message.as_str()).0 as _, + ); + } + }), + #[cfg(desktop)] + TargetKind::Stdout => std::io::stdout().into(), + #[cfg(desktop)] + TargetKind::Stderr => std::io::stderr().into(), + TargetKind::Folder { path, file_name } => { + if !path.exists() { + fs::create_dir_all(&path)?; } - let logger = match target.kind { - #[cfg(target_os = "android")] - TargetKind::Stdout | TargetKind::Stderr => { - fern::Output::call(android_logger::log) - } - #[cfg(target_os = "ios")] - TargetKind::Stdout | TargetKind::Stderr => { - fern::Output::call(move |record| { - let message = format!("{}", record.args()); - unsafe { - ios::tauri_log( - match record.level() { - log::Level::Trace | log::Level::Debug => 1, - log::Level::Info => 2, - log::Level::Warn | log::Level::Error => 3, - }, - ios::NSString::new(message.as_str()).0 as _, - ); - } - }) - } - #[cfg(desktop)] - TargetKind::Stdout => std::io::stdout().into(), - #[cfg(desktop)] - TargetKind::Stderr => std::io::stderr().into(), - TargetKind::Folder { path, file_name } => { - if !path.exists() { - fs::create_dir_all(&path)?; - } - - fern::log_file(get_log_file_path( - &path, - file_name.as_deref().unwrap_or(app_name), - &self.rotation_strategy, - &self.timezone_strategy, - self.max_file_size, - )?)? - .into() - } - #[cfg(mobile)] - TargetKind::LogDir { .. } => continue, - #[cfg(desktop)] - TargetKind::LogDir { file_name } => { - let path = app_handle.path().app_log_dir()?; - if !path.exists() { - fs::create_dir_all(&path)?; - } - - fern::log_file(get_log_file_path( - &path, - file_name.as_deref().unwrap_or(app_name), - &self.rotation_strategy, - &self.timezone_strategy, - self.max_file_size, - )?)? - .into() - } - TargetKind::Webview => { - let app_handle = app_handle.clone(); - - fern::Output::call(move |record| { - let payload = RecordPayload { - message: record.args().to_string(), - level: record.level().into(), - }; - let app_handle = app_handle.clone(); - tauri::async_runtime::spawn(async move { - let _ = app_handle.emit("log://log", payload); - }); - }) - } - }; - target_dispatch = target_dispatch.chain(logger); - - self.dispatch = self.dispatch.chain(target_dispatch); + fern::log_file(get_log_file_path( + &path, + file_name.as_deref().unwrap_or(app_name), + &rotation_strategy, + &timezone_strategy, + max_file_size, + )?)? + .into() } + #[cfg(mobile)] + TargetKind::LogDir { .. } => continue, + #[cfg(desktop)] + TargetKind::LogDir { file_name } => { + let path = app_handle.path().app_log_dir()?; + if !path.exists() { + fs::create_dir_all(&path)?; + } - self.dispatch.apply()?; + fern::log_file(get_log_file_path( + &path, + file_name.as_deref().unwrap_or(app_name), + &rotation_strategy, + &timezone_strategy, + max_file_size, + )?)? + .into() + } + TargetKind::Webview => { + let app_handle = app_handle.clone(); + + fern::Output::call(move |record| { + let payload = RecordPayload { + message: record.args().to_string(), + level: record.level().into(), + }; + let app_handle = app_handle.clone(); + tauri::async_runtime::spawn(async move { + let _ = app_handle.emit("log://log", payload); + }); + }) + } + }; + target_dispatch = target_dispatch.chain(logger); + + dispatch = dispatch.chain(target_dispatch); + } + + Ok(dispatch.into_log()) + } + + fn plugin_builder() -> plugin::Builder { + plugin::Builder::new("log").invoke_handler(tauri::generate_handler![log]) + } + + pub fn split( + self, + app_handle: &AppHandle, + ) -> Result<(TauriPlugin, log::LevelFilter, Box), Error> { + let plugin = Self::plugin_builder(); + let (max_level, log) = Self::acquire_logger( + app_handle, + self.dispatch, + self.rotation_strategy, + self.timezone_strategy, + self.max_file_size, + self.targets, + )?; + + Ok((plugin.build(), max_level, log)) + } + + pub fn build(self) -> TauriPlugin { + Self::plugin_builder() + .setup(move |app_handle, _api| { + let (max_level, log) = Self::acquire_logger( + app_handle, + self.dispatch, + self.rotation_strategy, + self.timezone_strategy, + self.max_file_size, + self.targets, + )?; + + attach_logger(max_level, log)?; Ok(()) }) @@ -493,13 +541,23 @@ impl Builder { } } +/// Attaches the given logger +pub fn attach_logger( + max_level: log::LevelFilter, + log: Box, +) -> Result<(), log::SetLoggerError> { + log::set_boxed_logger(log)?; + log::set_max_level(max_level); + Ok(()) +} + fn get_log_file_path( dir: &impl AsRef, file_name: &str, rotation_strategy: &RotationStrategy, timezone_strategy: &TimezoneStrategy, max_file_size: u128, -) -> Result> { +) -> Result { let path = dir.as_ref().join(format!("{file_name}.log")); if path.exists() { From f5a6e29f5184ed055e6384356b30985224d22715 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 23 Jul 2024 16:52:37 -0300 Subject: [PATCH 2/3] clippy --- plugins/log/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/log/src/lib.rs b/plugins/log/src/lib.rs index a048001e5..2691c55fe 100644 --- a/plugins/log/src/lib.rs +++ b/plugins/log/src/lib.rs @@ -504,6 +504,7 @@ impl Builder { plugin::Builder::new("log").invoke_handler(tauri::generate_handler![log]) } + #[allow(clippy::type_complexity)] pub fn split( self, app_handle: &AppHandle, From b100af0bac19f8145a68136c667315567c9b9fd4 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 23 Jul 2024 17:20:37 -0300 Subject: [PATCH 3/3] covector --- .changes/log-split.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changes/log-split.md b/.changes/log-split.md index 176b7efd4..9b9a9e48f 100644 --- a/.changes/log-split.md +++ b/.changes/log-split.md @@ -1,5 +1,5 @@ --- -"log": patch +"log-plugin": patch --- Added `Builder::split` which returns the raw logger implementation so you can pipe to other loggers such as `multi_log` or `tauri-plugin-devtools`.