mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-04-27 11:56:05 +02:00
feat(log): add Builder::split to get the raw logger implementation (#1579)
* 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. * clippy * covector
This commit is contained in:
committed by
GitHub
parent
fa275731be
commit
20a1d24ee0
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"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`.
|
||||
Generated
+5
-4
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
+147
-88
@@ -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,134 @@ impl Builder {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn build<R: Runtime>(mut self) -> TauriPlugin<R> {
|
||||
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<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
mut dispatch: fern::Dispatch,
|
||||
rotation_strategy: RotationStrategy,
|
||||
timezone_strategy: TimezoneStrategy,
|
||||
max_file_size: u128,
|
||||
targets: Vec<Target>,
|
||||
) -> Result<(log::LevelFilter, Box<dyn log::Log>), Error> {
|
||||
let app_name = &app_handle.package_info().name;
|
||||
|
||||
// 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);
|
||||
// setup targets
|
||||
for target in 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<R: Runtime>() -> plugin::Builder<R> {
|
||||
plugin::Builder::new("log").invoke_handler(tauri::generate_handler![log])
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn split<R: Runtime>(
|
||||
self,
|
||||
app_handle: &AppHandle<R>,
|
||||
) -> Result<(TauriPlugin<R>, log::LevelFilter, Box<dyn log::Log>), 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<R: Runtime>(self) -> TauriPlugin<R> {
|
||||
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 +542,23 @@ impl Builder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Attaches the given logger
|
||||
pub fn attach_logger(
|
||||
max_level: log::LevelFilter,
|
||||
log: Box<dyn log::Log>,
|
||||
) -> Result<(), log::SetLoggerError> {
|
||||
log::set_boxed_logger(log)?;
|
||||
log::set_max_level(max_level);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_log_file_path(
|
||||
dir: &impl AsRef<Path>,
|
||||
file_name: &str,
|
||||
rotation_strategy: &RotationStrategy,
|
||||
timezone_strategy: &TimezoneStrategy,
|
||||
max_file_size: u128,
|
||||
) -> Result<PathBuf, Box<dyn std::error::Error>> {
|
||||
) -> Result<PathBuf, Error> {
|
||||
let path = dir.as_ref().join(format!("{file_name}.log"));
|
||||
|
||||
if path.exists() {
|
||||
|
||||
Reference in New Issue
Block a user