use std::fmt::{Display, Formatter};
use std::str::FromStr;
use tracing::subscriber::Interest;
use tracing::Metadata;
use tracing_subscriber::filter::LevelFilter;
use tracing_subscriber::layer::{Context, Filter, SubscriberExt};
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{registry, Layer};
pub fn setup_logging(level: LoggingLevel, kind: LoggingKind) {
if level == LoggingLevel::None {
return;
}
let format = tracing_subscriber::fmt::layer()
.with_level(true)
.with_target(false)
.with_thread_names(true)
.with_file(true)
.with_ansi(true);
match kind {
LoggingKind::Pretty => {
let format = format.pretty();
registry()
.with(format.with_filter(LoggingFilter { level }))
.init()
}
LoggingKind::Compact => {
let format = format.compact();
registry()
.with(format.with_filter(LoggingFilter { level }))
.init()
}
LoggingKind::Json => {
let format = format.json().flatten_event(true);
registry()
.with(format.with_filter(LoggingFilter { level }))
.init()
}
};
}
#[derive(Copy, Debug, Default, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum LoggingLevel {
#[default]
None,
Debug,
Info,
Warn,
Error,
}
impl LoggingLevel {
fn to_filter_level(self) -> Option<LevelFilter> {
match self {
LoggingLevel::None => None,
LoggingLevel::Info => Some(LevelFilter::INFO),
LoggingLevel::Warn => Some(LevelFilter::WARN),
LoggingLevel::Error => Some(LevelFilter::ERROR),
LoggingLevel::Debug => Some(LevelFilter::DEBUG),
}
}
}
impl FromStr for LoggingLevel {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"none" => Ok(Self::None),
"info" => Ok(Self::Info),
"warn" => Ok(Self::Warn),
"error" => Ok(Self::Error),
"debug" => Ok(Self::Debug),
_ => Err("Unexpected value".to_string()),
}
}
}
impl Display for LoggingLevel {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
LoggingLevel::None => write!(f, "none"),
LoggingLevel::Debug => write!(f, "debug"),
LoggingLevel::Info => write!(f, "info"),
LoggingLevel::Warn => write!(f, "warn"),
LoggingLevel::Error => write!(f, "error"),
}
}
}
struct LoggingFilter {
level: LoggingLevel,
}
const SELF_FILTER: LevelFilter = if cfg!(debug_assertions) {
LevelFilter::TRACE
} else {
LevelFilter::DEBUG
};
impl LoggingFilter {
fn check_enabled(&self, meta: &Metadata<'_>) -> bool {
let filter = if meta.target().starts_with("webview_bundle") {
if let Some(level) = self.level.to_filter_level() {
level
} else {
return false;
}
} else {
LevelFilter::INFO
};
meta.level() <= &filter
}
}
impl<S> Filter<S> for LoggingFilter {
fn enabled(&self, meta: &Metadata<'_>, _: &Context<'_, S>) -> bool {
self.check_enabled(meta)
}
fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
if self.check_enabled(meta) {
Interest::always()
} else {
Interest::never()
}
}
fn max_level_hint(&self) -> Option<LevelFilter> {
Some(SELF_FILTER)
}
}
#[derive(Copy, Debug, Default, Clone, Eq, PartialEq)]
pub enum LoggingKind {
#[default]
Pretty,
Compact,
Json,
}
impl Display for LoggingKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
LoggingKind::Pretty => write!(f, "pretty"),
LoggingKind::Compact => write!(f, "compact"),
LoggingKind::Json => write!(f, "json"),
}
}
}
impl FromStr for LoggingKind {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"compact" => Ok(Self::Compact),
"pretty" => Ok(Self::Pretty),
"json" => Ok(Self::Json),
_ => Err("Unexpected value".to_string()),
}
}
}