refactor(log): extend target with filter fn and file name, closes #123 (#386)

pull/393/head
Lucas Fernandes Nogueira 2 years ago committed by GitHub
parent a0133cb5b3
commit 3c8577bc9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,7 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use fern::FormatCallback;
//! Logging for Tauri applications.
use fern::{Filter, FormatCallback};
use log::{logger, RecordBuilder};
use log::{LevelFilter, Record};
use serde::Serialize;
@ -23,6 +25,8 @@ use tauri::{
pub use fern;
use time::OffsetDateTime;
pub const WEBVIEW_TARGET: &str = "Webview";
#[cfg(target_os = "ios")]
mod ios {
use cocoa::base::id;
@ -58,7 +62,10 @@ mod ios {
const DEFAULT_MAX_FILE_SIZE: u128 = 40000;
const DEFAULT_ROTATION_STRATEGY: RotationStrategy = RotationStrategy::KeepOne;
const DEFAULT_TIMEZONE_STRATEGY: TimezoneStrategy = TimezoneStrategy::UseUtc;
const DEFAULT_LOG_TARGETS: [LogTarget; 2] = [LogTarget::Stdout, LogTarget::LogDir];
const DEFAULT_LOG_TARGETS: [Target; 2] = [
Target::new(TargetKind::Stdout),
Target::new(TargetKind::LogDir { file_name: None }),
];
/// An enum representing the available verbosity levels of the logger.
///
@ -141,7 +148,7 @@ struct RecordPayload {
}
/// An enum representing the available targets of the logger.
pub enum LogTarget {
pub enum TargetKind {
/// Print logs to stdout.
Stdout,
/// Print logs to stderr.
@ -149,7 +156,10 @@ pub enum LogTarget {
/// Write logs to the given directory.
///
/// The plugin will ensure the directory exists before writing logs.
Folder(PathBuf),
Folder {
path: PathBuf,
file_name: Option<String>,
},
/// Write logs to the OS specififc logs directory.
///
/// ### Platform-specific
@ -159,13 +169,38 @@ pub enum LogTarget {
/// | Linux | `{configDir}/{bundleIdentifier}` | `/home/alice/.config/com.tauri.dev` |
/// | macOS | `{homeDir}/Library/Logs/{bundleIdentifier}` | `/Users/Alice/Library/Logs/com.tauri.dev` |
/// | Windows | `{configDir}/{bundleIdentifier}` | `C:\Users\Alice\AppData\Roaming\com.tauri.dev` |
LogDir,
LogDir { file_name: Option<String> },
/// Forward logs to the webview (via the `log://log` event).
///
/// This requires the webview to subscribe to log events, via this plugins `attachConsole` function.
Webview,
}
/// A log target.
pub struct Target {
kind: TargetKind,
filters: Vec<Box<Filter>>,
}
impl Target {
#[inline]
pub const fn new(kind: TargetKind) -> Self {
Self {
kind,
filters: Vec::new(),
}
}
#[inline]
pub fn filter<F>(mut self, filter: F) -> Self
where
F: Fn(&log::Metadata) -> bool + Send + Sync + 'static,
{
self.filters.push(Box::new(filter));
self
}
}
#[tauri::command]
fn log(
level: LogLevel,
@ -176,9 +211,18 @@ fn log(
key_values: Option<HashMap<String, String>>,
) {
let location = location.unwrap_or("webview");
let level = log::Level::from(level);
let metadata = log::MetadataBuilder::new()
.level(level)
.target(WEBVIEW_TARGET)
.build();
let mut builder = RecordBuilder::new();
builder
.level(level.into())
.level(level)
.metadata(metadata)
.target(location)
.file(file)
.line(line);
@ -198,7 +242,7 @@ pub struct Builder {
rotation_strategy: RotationStrategy,
timezone_strategy: TimezoneStrategy,
max_file_size: u128,
targets: Vec<LogTarget>,
targets: Vec<Target>,
}
impl Default for Builder {
@ -290,12 +334,37 @@ impl Builder {
self
}
pub fn target(mut self, target: LogTarget) -> Self {
/// Removes all targets. Useful to ignore the default targets and reconfigure them.
pub fn clear_targets(mut self) -> Self {
self.targets.clear();
self
}
/// Adds a log target to the logger.
///
/// ```rust
/// use tauri_plugin_log::{Target, TargetKind};
/// tauri_plugin_log::Builder::new()
/// .target(Target::new(TargetKind::Webview));
/// ```
pub fn target(mut self, target: Target) -> Self {
self.targets.push(target);
self
}
pub fn targets(mut self, targets: impl IntoIterator<Item = LogTarget>) -> Self {
/// Adds a collection of targets to the logger.
///
/// ```rust
/// use tauri_plugin_log::{Target, TargetKind, WEBVIEW_TARGET};
/// tauri_plugin_log::Builder::new()
/// .clear_targets()
/// .targets([
/// Target::new(TargetKind::Webview),
/// Target::new(TargetKind::LogDir { file_name: Some("webview".into()) }).filter(|metadata| metadata.target() == WEBVIEW_TARGET),
/// Target::new(TargetKind::LogDir { file_name: Some("rust".into()) }).filter(|metadata| metadata.target() != WEBVIEW_TARGET),
/// ]);
/// ```
pub fn targets(mut self, targets: impl IntoIterator<Item = Target>) -> Self {
self.targets = Vec::from_iter(targets);
self
}
@ -325,14 +394,19 @@ impl Builder {
let app_name = &app_handle.package_info().name;
// setup targets
for target in &self.targets {
let logger = match target {
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")]
LogTarget::Stdout | LogTarget::Stderr => {
TargetKind::Stdout | TargetKind::Stderr => {
fern::Output::call(android_logger::log)
}
#[cfg(target_os = "ios")]
LogTarget::Stdout | LogTarget::Stderr => {
TargetKind::Stdout | TargetKind::Stderr => {
fern::Output::call(move |record| {
let message = format!("{}", record.args());
unsafe {
@ -348,17 +422,17 @@ impl Builder {
})
}
#[cfg(desktop)]
LogTarget::Stdout => std::io::stdout().into(),
TargetKind::Stdout => std::io::stdout().into(),
#[cfg(desktop)]
LogTarget::Stderr => std::io::stderr().into(),
LogTarget::Folder(path) => {
TargetKind::Stderr => std::io::stderr().into(),
TargetKind::Folder { path, file_name } => {
if !path.exists() {
fs::create_dir_all(path).unwrap();
fs::create_dir_all(&path).unwrap();
}
fern::log_file(get_log_file_path(
&path,
app_name,
file_name.as_deref().unwrap_or(app_name),
&self.rotation_strategy,
&self.timezone_strategy,
self.max_file_size,
@ -366,9 +440,9 @@ impl Builder {
.into()
}
#[cfg(mobile)]
LogTarget::LogDir => continue,
TargetKind::LogDir { .. } => continue,
#[cfg(desktop)]
LogTarget::LogDir => {
TargetKind::LogDir { file_name } => {
let path = app_handle.path().app_log_dir().unwrap();
if !path.exists() {
fs::create_dir_all(&path).unwrap();
@ -376,14 +450,14 @@ impl Builder {
fern::log_file(get_log_file_path(
&path,
app_name,
file_name.as_deref().unwrap_or(app_name),
&self.rotation_strategy,
&self.timezone_strategy,
self.max_file_size,
)?)?
.into()
}
LogTarget::Webview => {
TargetKind::Webview => {
let app_handle = app_handle.clone();
fern::Output::call(move |record| {
@ -398,7 +472,9 @@ impl Builder {
})
}
};
self.dispatch = self.dispatch.chain(logger);
target_dispatch = target_dispatch.chain(logger);
self.dispatch = self.dispatch.chain(target_dispatch);
}
self.dispatch.apply()?;
@ -411,12 +487,12 @@ impl Builder {
fn get_log_file_path(
dir: &impl AsRef<Path>,
app_name: &str,
file_name: &str,
rotation_strategy: &RotationStrategy,
timezone_strategy: &TimezoneStrategy,
max_file_size: u128,
) -> plugin::Result<PathBuf> {
let path = dir.as_ref().join(format!("{app_name}.log"));
let path = dir.as_ref().join(format!("{file_name}.log"));
if path.exists() {
let log_size = File::open(&path)?.metadata()?.len() as u128;
@ -425,7 +501,7 @@ fn get_log_file_path(
RotationStrategy::KeepAll => {
let to = dir.as_ref().join(format!(
"{}_{}.log",
app_name,
file_name,
timezone_strategy
.get_now()
.format(

Loading…
Cancel
Save