diff --git a/plugins/log/Cargo.toml b/plugins/log/Cargo.toml index 60f21510..883ffd95 100644 --- a/plugins/log/Cargo.toml +++ b/plugins/log/Cargo.toml @@ -18,8 +18,6 @@ serde_repr = "0.1" byte-unit = "4.0" log = { workspace = true, features = ["kv_unstable"] } time = { version = "0.3", features = ["formatting"] } - -[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] fern = "0.6" [target."cfg(target_os = \"android\")".dependencies] diff --git a/plugins/log/src/lib.rs b/plugins/log/src/lib.rs index 925eb262..4323e5a1 100644 --- a/plugins/log/src/lib.rs +++ b/plugins/log/src/lib.rs @@ -2,34 +2,26 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -#[cfg(desktop)] use fern::FormatCallback; -use log::LevelFilter; use log::{logger, RecordBuilder}; +use log::{LevelFilter, Record}; use serde::Serialize; use serde_repr::{Deserialize_repr, Serialize_repr}; use std::borrow::Cow; use std::collections::HashMap; -use std::{iter::FromIterator, path::PathBuf}; +use std::{ + fmt::Arguments, + fs::{self, File}, + iter::FromIterator, + path::{Path, PathBuf}, +}; use tauri::{ plugin::{self, TauriPlugin}, - Runtime, + Manager, Runtime, }; -#[cfg(desktop)] -use desktop::*; -#[cfg(desktop)] pub use fern; -#[cfg(desktop)] -mod desktop { - pub use std::{ - fs::{self, File}, - path::Path, - }; - pub use tauri::Manager; -} - const DEFAULT_MAX_FILE_SIZE: u128 = 40000; const DEFAULT_ROTATION_STRATEGY: RotationStrategy = RotationStrategy::KeepOne; const DEFAULT_LOG_TARGETS: [LogTarget; 2] = [LogTarget::Stdout, LogTarget::LogDir]; @@ -151,41 +143,31 @@ fn log( } pub struct Builder { - #[cfg(desktop)] dispatch: fern::Dispatch, rotation_strategy: RotationStrategy, max_file_size: u128, targets: Vec, - level_filter: Option, - levels: Vec<(Cow<'static, str>, log::LevelFilter)>, } impl Default for Builder { fn default() -> Self { - #[cfg(desktop)] - let dispatch = { - let format = time::format_description::parse( - "[[[year]-[month]-[day]][[[hour]:[minute]:[second]]", - ) - .unwrap(); - fern::Dispatch::new().format(move |out, message, record| { - out.finish(format_args!( - "{}[{}][{}] {}", - time::OffsetDateTime::now_utc().format(&format).unwrap(), - record.target(), - record.level(), - message - )) - }) - }; + let format = + time::format_description::parse("[[[year]-[month]-[day]][[[hour]:[minute]:[second]]") + .unwrap(); + let dispatch = fern::Dispatch::new().format(move |out, message, record| { + out.finish(format_args!( + "{}[{}][{}] {}", + time::OffsetDateTime::now_utc().format(&format).unwrap(), + record.target(), + record.level(), + message + )) + }); Self { - #[cfg(desktop)] dispatch, rotation_strategy: DEFAULT_ROTATION_STRATEGY, max_file_size: DEFAULT_MAX_FILE_SIZE, targets: DEFAULT_LOG_TARGETS.into(), - level_filter: None, - levels: Vec::new(), } } } @@ -205,37 +187,24 @@ impl Builder { self } - #[cfg(desktop)] pub fn format(mut self, formatter: F) -> Self where - F: Fn(FormatCallback, &std::fmt::Arguments, &log::Record) + Sync + Send + 'static, + F: Fn(FormatCallback, &Arguments, &Record) + Sync + Send + 'static, { self.dispatch = self.dispatch.format(formatter); self } pub fn level(mut self, level_filter: impl Into) -> Self { - self.level_filter.replace(level_filter.into()); + self.dispatch = self.dispatch.level(level_filter.into()); self } pub fn level_for(mut self, module: impl Into>, level: LevelFilter) -> Self { - let module = module.into(); - - if let Some((index, _)) = self - .levels - .iter() - .enumerate() - .find(|&(_, &(ref name, _))| name == &module) - { - self.levels.remove(index); - } - - self.levels.push((module, level)); + self.dispatch = self.dispatch.level_for(module, level); self } - #[cfg(desktop)] pub fn filter(mut self, filter: F) -> Self where F: Fn(&log::Metadata) -> bool + Send + Sync + 'static, @@ -254,7 +223,7 @@ impl Builder { self } - #[cfg(all(desktop, feature = "colored"))] + #[cfg(feature = "colored")] pub fn with_colors(self, colors: fern::colors::ColoredLevelConfig) -> Self { let format = time::format_description::parse("[[[year]-[month]-[day]][[[hour]:[minute]:[second]]") @@ -270,149 +239,106 @@ impl Builder { }) } - pub fn build(#[allow(unused_mut)] mut self) -> TauriPlugin { - #[cfg(desktop)] - { - if let Some(level) = self.level_filter { - self.dispatch = self.dispatch.level(level); - } - for (module, level) in self.levels { - self.dispatch = self.dispatch.level_for(module, level); - } - } - + pub fn build(mut self) -> TauriPlugin { plugin::Builder::new("log") .invoke_handler(tauri::generate_handler![log]) .setup(move |app_handle| { - #[cfg(target_os = "ios")] - { - let mut subsystem = String::new(); - let identifier = &app_handle.config().tauri.bundle.identifier; - let s = identifier.split('.'); - let last = s.clone().count() - 1; - for (i, w) in s.enumerate() { - if i != last { - subsystem.push_str(w); - subsystem.push('.'); - } - } - subsystem.push_str(&app_handle.package_info().crate_name); - - let mut logger = oslog::OsLogger::new(&subsystem); - logger = - logger.level_filter(self.level_filter.unwrap_or(log::LevelFilter::Trace)); - for (module, level) in self.levels { - logger = logger.category_level_filter(&module, level); - } - logger.init()?; - } - - #[cfg(target_os = "android")] - { - let mut logger = android_logger::Config::default(); - if let Some(level_filter) = self.level_filter { - if let Some(level) = level_filter.to_level() { - logger = logger.with_min_level(level); + let app_name = &app_handle.package_info().name; + + // setup targets + for target in &self.targets { + let logger = match target { + #[cfg(target_os = "android")] + LogTarget::Stdout | LogTarget::Stderr => { + fern::Output::call(android_logger::log) } - } else { - logger = logger.with_min_level(log::Level::Trace); - } - if !self.levels.is_empty() { - let mut filter = android_logger::FilterBuilder::new(); - for (module, level) in self.levels { - filter.filter_module(&module, level); - } - logger = logger.with_filter(filter.build()); - } - println!( - "with tag {}", - app_handle - .config() - .tauri - .bundle - .identifier - .split('.') - .rev() - .next() - .unwrap(), - ); - android_logger::init_once( - logger.with_tag( - app_handle - .config() - .tauri - .bundle - .identifier - .split('.') - .rev() - .next() - .unwrap(), - ), - ); - } - - #[cfg(desktop)] - { - let app_name = &app_handle.package_info().name; - // setup targets - for target in &self.targets { - self.dispatch = self.dispatch.chain(match target { - LogTarget::Stdout => fern::Output::from(std::io::stdout()), - LogTarget::Stderr => fern::Output::from(std::io::stderr()), - LogTarget::Folder(path) => { - if !path.exists() { - fs::create_dir_all(path).unwrap(); + #[cfg(target_os = "ios")] + LogTarget::Stdout | LogTarget::Stderr => { + use std::{collections::HashMap, sync::Mutex}; + let loggers: Mutex> = Default::default(); + let mut subsystem = String::new(); + let identifier = &app_handle.config().tauri.bundle.identifier; + let s = identifier.split('.'); + let last = s.clone().count() - 1; + for (i, w) in s.enumerate() { + if i != last { + subsystem.push_str(w); + subsystem.push('.'); } - - fern::log_file(get_log_file_path( - &path, - app_name, - &self.rotation_strategy, - self.max_file_size, - )?)? - .into() } - LogTarget::LogDir => { - let path = app_handle.path_resolver().app_log_dir().unwrap(); - if !path.exists() { - fs::create_dir_all(&path).unwrap(); - } + subsystem.push_str(&app_handle.package_info().crate_name); - fern::log_file(get_log_file_path( - &path, - app_name, - &self.rotation_strategy, - self.max_file_size, - )?)? - .into() + fern::Output::call(move |record| { + let mut loggers = loggers.lock().unwrap(); + let pair = + loggers.entry(record.target().into()).or_insert_with(|| { + oslog::OsLog::new(&subsystem, record.target()) + }); + + let message = format!("{}", record.args()); + (*pair).with_level(record.level().into(), &message); + }); + } + #[cfg(desktop)] + LogTarget::Stdout => std::io::stdout().into(), + #[cfg(desktop)] + LogTarget::Stderr => std::io::stderr().into(), + LogTarget::Folder(path) => { + if !path.exists() { + fs::create_dir_all(path).unwrap(); } - LogTarget::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 { - app_handle.emit_all("log://log", payload).unwrap(); - }); - }) + fern::log_file(get_log_file_path( + &path, + app_name, + &self.rotation_strategy, + self.max_file_size, + )?)? + .into() + } + #[cfg(mobile)] + LogTarget::LogDir => continue, + #[cfg(desktop)] + LogTarget::LogDir => { + let path = app_handle.path_resolver().app_log_dir().unwrap(); + if !path.exists() { + fs::create_dir_all(&path).unwrap(); } - }); - } - self.dispatch.apply()?; + fern::log_file(get_log_file_path( + &path, + app_name, + &self.rotation_strategy, + self.max_file_size, + )?)? + .into() + } + LogTarget::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 { + app_handle.emit_all("log://log", payload).unwrap(); + }); + }) + } + }; + self.dispatch = self.dispatch.chain(logger); } + self.dispatch.apply()?; + Ok(()) }) .build() } } -#[cfg(desktop)] fn get_log_file_path( dir: &impl AsRef, app_name: &str,