use tracing_subscriber::filter::EnvFilter;
use crate::config::{LogLevel, OtelConfig};
pub fn build_env_filter(config: &OtelConfig) -> EnvFilter {
let mut directives = vec!["off".to_string()];
let level = config.log_level.as_str();
for crate_name in &config.allowed_crates {
directives.push(format!("{crate_name}={level}"));
}
for custom in &config.custom_filters {
directives.push(custom.clone());
}
let filter_string = directives.join(",");
EnvFilter::try_from_default_env().unwrap_or_else(|_| {
EnvFilter::try_new(&filter_string).unwrap_or_else(|_| EnvFilter::new("off"))
})
}
#[derive(Debug, Clone, Default)]
pub struct FilterBuilder {
default_level: String,
allowed: Vec<(String, String)>,
directives: Vec<String>,
}
impl FilterBuilder {
#[must_use]
pub fn new() -> Self {
Self {
default_level: "info".to_string(),
allowed: Vec::new(),
directives: Vec::new(),
}
}
#[must_use]
pub fn default_level(mut self, level: impl Into<String>) -> Self {
self.default_level = level.into();
self
}
#[must_use]
pub fn allow(mut self, crate_name: impl Into<String>) -> Self {
let name = crate_name.into();
let level = self.default_level.clone();
self.allowed.push((name, level));
self
}
#[must_use]
pub fn allow_at(mut self, crate_name: impl Into<String>, level: impl Into<String>) -> Self {
self.allowed.push((crate_name.into(), level.into()));
self
}
#[must_use]
pub fn directive(mut self, directive: impl Into<String>) -> Self {
self.directives.push(directive.into());
self
}
#[must_use]
pub fn build(self) -> EnvFilter {
let mut all = vec!["off".to_string()];
for (name, level) in self.allowed {
all.push(format!("{name}={level}"));
}
all.extend(self.directives);
let filter_string = all.join(",");
EnvFilter::try_new(&filter_string).unwrap_or_else(|_| EnvFilter::new("off"))
}
}
pub const fn level_to_directive(level: LogLevel) -> &'static str {
level.as_str()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn filter_builder_basic() {
let filter = FilterBuilder::new()
.default_level("debug")
.allow("my_app")
.allow_at("my_lib", "trace")
.build();
assert!(!format!("{filter:?}").is_empty());
}
#[test]
fn filter_builder_with_directives() {
let filter = FilterBuilder::new()
.allow("my_app")
.directive("hyper=warn")
.build();
assert!(!format!("{filter:?}").is_empty());
}
}