use std::str::FromStr;
use crate::{errors::LoggerError, time::LocalTimer};
use tracing::Level;
use tracing_subscriber::{EnvFilter, fmt::format::FmtSpan};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum LogFormat {
#[default]
Text,
Json,
}
impl LogFormat {
fn as_str(self) -> &'static str {
match self {
Self::Text => "text",
Self::Json => "json",
}
}
}
impl std::fmt::Display for LogFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for LogFormat {
type Err = LoggerError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"text" => Ok(Self::Text),
"json" => Ok(Self::Json),
other => Err(LoggerError::InvalidFormat(other.to_string())),
}
}
}
pub struct Logger {
level: Level,
format: LogFormat,
env_filter: Option<String>,
with_file: bool,
with_target: bool,
}
impl Logger {
#[must_use]
pub fn new() -> Self {
Self {
level: Level::INFO,
format: LogFormat::Text,
env_filter: None,
with_file: false,
with_target: true,
}
}
#[must_use]
pub fn with_level(mut self, level: Level) -> Self {
self.level = level;
self
}
#[must_use]
pub fn with_format(mut self, format: LogFormat) -> Self {
self.format = format;
self
}
#[must_use]
pub fn with_env_filter(mut self, filter: impl Into<String>) -> Self {
self.env_filter = Some(filter.into());
self
}
#[must_use]
pub fn with_file(mut self, enabled: bool) -> Self {
self.with_file = enabled;
self
}
#[must_use]
pub fn with_target(mut self, enabled: bool) -> Self {
self.with_target = enabled;
self
}
#[must_use]
pub fn level(&self) -> Level {
self.level
}
#[must_use]
pub fn format(&self) -> LogFormat {
self.format
}
pub fn init(self) -> Result<(), LoggerError> {
let base = tracing_subscriber::fmt()
.with_thread_names(false)
.with_span_events(FmtSpan::FULL)
.with_file(self.with_file)
.with_target(self.with_target)
.with_timer(LocalTimer);
let env_filter = self
.env_filter
.as_deref()
.map(EnvFilter::try_new)
.transpose()
.map_err(|e| LoggerError::InvalidEnvFilter(e.to_string()))?;
let result = match self.format {
LogFormat::Json => {
let b = base.json();
match env_filter {
Some(f) => b.with_env_filter(f).try_init(),
None => b.with_max_level(self.level).try_init(),
}
}
LogFormat::Text => match env_filter {
Some(f) => base.with_env_filter(f).try_init(),
None => base.with_max_level(self.level).try_init(),
},
};
result.map_err(|e| LoggerError::TryInitError(e.to_string()))
}
}
impl Default for Logger {
fn default() -> Self {
Self::new()
}
}