#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use core::fmt;
use tracing::level_filters::LevelFilter;
use tracing::{Event, Subscriber};
use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields, format};
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{EnvFilter, Layer};
mod sealed;
#[cfg(feature = "clap4")]
pub mod clap4;
#[cfg(feature = "otlp")]
mod otlp;
#[derive(Debug, Clone)]
#[must_use]
pub struct Initializer {
env_var_prefix: String,
stderr_default_level: LevelFilter,
stderr_logging_format: Option<StderrLogFormat>,
}
impl Initializer {
pub fn new(env_var_prefix: &str) -> Self {
Self {
env_var_prefix: env_var_prefix.to_owned(),
stderr_default_level: LevelFilter::INFO,
stderr_logging_format: None,
}
}
pub fn apply(mut self, configuration: impl Configuration) -> Self {
configuration.apply_to(&mut self);
self
}
pub fn init(self) -> FinalizeGuard {
let stderr_filter = EnvFilter::builder()
.with_default_directive(self.stderr_default_level.into())
.with_env_var(format!("{}_LOG", &self.env_var_prefix))
.from_env_lossy();
let stderr_format = self.stderr_logging_format.clone().unwrap_or_else(|| {
let format_env_var = format!("{}_LOG_FORMAT", self.env_var_prefix);
match std::env::var(&format_env_var).as_deref() {
Ok("full") => StderrLogFormat::Full,
Ok("compact") => StderrLogFormat::Compact,
Ok(_) | Err(std::env::VarError::NotUnicode(_)) => {
eprintln!("WARNING: Unsupported log format in '{format_env_var}' environment variable.");
StderrLogFormat::Compact
}
_ => StderrLogFormat::Compact,
}
});
let stderr_formatter = match stderr_format {
StderrLogFormat::Compact => StderrLogFormatter::Compact(
tracing_subscriber::fmt::format()
.without_time()
.with_ansi(console::colors_enabled_stderr())
.with_target(false)
.compact(),
),
StderrLogFormat::Full => StderrLogFormatter::Full(
tracing_subscriber::fmt::format().with_ansi(console::colors_enabled_stderr()),
),
};
let stderr_layer = tracing_subscriber::fmt::layer()
.with_writer(std::io::stderr)
.event_format(stderr_formatter)
.with_filter(stderr_filter);
let registry = tracing_subscriber::registry().with(stderr_layer);
#[cfg(feature = "otlp")]
let (registry, otlp_guard) = {
let (otlp_layer, otlp_guard) = otlp::setup_otlp_layer(&self);
(registry.with(otlp_layer), otlp_guard)
};
registry.init();
FinalizeGuard {
#[cfg(feature = "otlp")]
_otlp_guard: otlp_guard,
}
}
}
pub trait Configuration: sealed::ConfigurationSealed {
fn apply_to(&self, initializer: &mut Initializer);
}
impl<C: Configuration> Configuration for &C {
fn apply_to(&self, initializer: &mut Initializer) {
(**self).apply_to(initializer);
}
}
#[derive(Debug, Clone)]
enum StderrLogFormat {
Compact,
Full,
}
enum StderrLogFormatter {
Compact(tracing_subscriber::fmt::format::Format<tracing_subscriber::fmt::format::Compact, ()>),
Full(tracing_subscriber::fmt::format::Format),
}
impl<S, N> FormatEvent<S, N> for StderrLogFormatter
where
S: Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
{
fn format_event(
&self,
ctx: &FmtContext<'_, S, N>,
writer: format::Writer<'_>,
event: &Event<'_>,
) -> fmt::Result {
match self {
StderrLogFormatter::Compact(formatter) => formatter.format_event(ctx, writer, event),
StderrLogFormatter::Full(formatter) => formatter.format_event(ctx, writer, event),
}
}
}
#[derive(Debug)]
#[must_use]
pub struct FinalizeGuard {
#[cfg(feature = "otlp")]
_otlp_guard: otlp::FinalizeGuard,
}
impl FinalizeGuard {
pub fn finalize(self) {
drop(self);
}
}