diff --git a/src/config.rs b/src/config.rs index 04e723f4..c1ffde91 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,8 +8,10 @@ use serde::Deserialize; use crate::error; +mod env_filter_clone; mod proxy; +use env_filter_clone::EnvFilterClone; use proxy::ProxyConfig; #[allow(clippy::struct_excessive_bools)] @@ -69,7 +71,7 @@ pub(crate) struct Config { #[serde(default = "default_trusted_servers")] pub(crate) trusted_servers: Vec, #[serde(default = "default_log")] - pub(crate) log: String, + pub(crate) log: EnvFilterClone, #[serde(default)] pub(crate) turn_username: String, #[serde(default)] @@ -146,8 +148,10 @@ fn default_trusted_servers() -> Vec { vec![OwnedServerName::try_from("matrix.org").unwrap()] } -fn default_log() -> String { - "warn,state_res=warn,_=off".to_owned() +fn default_log() -> EnvFilterClone { + "warn,state_res=warn,_=off" + .parse() + .expect("hardcoded env filter should be valid") } fn default_turn_ttl() -> u64 { diff --git a/src/config/env_filter_clone.rs b/src/config/env_filter_clone.rs new file mode 100644 index 00000000..183c827f --- /dev/null +++ b/src/config/env_filter_clone.rs @@ -0,0 +1,44 @@ +//! A workaround for [`EnvFilter`] not directly implementing [`Clone`] +//! +//! This will be unnecessary after [tokio-rs/tracing#2956][0] is merged. +//! +//! [0]: https://github.com/tokio-rs/tracing/pull/2956 +#![warn(missing_docs, clippy::missing_docs_in_private_items)] + +use std::str::FromStr; + +use serde::{de, Deserialize, Deserializer}; +use tracing_subscriber::EnvFilter; + +/// A workaround for [`EnvFilter`] not directly implementing [`Clone`] +/// +/// Use [`FromStr`] or [`Deserialize`] to construct this type, then [`From`] or +/// [`Into`] to convert it into an [`EnvFilter`] when needed. +#[derive(Debug)] +pub(crate) struct EnvFilterClone(String); + +impl FromStr for EnvFilterClone { + type Err = ::Err; + + fn from_str(s: &str) -> Result { + EnvFilter::from_str(s)?; + Ok(Self(s.to_owned())) + } +} + +impl From<&EnvFilterClone> for EnvFilter { + fn from(other: &EnvFilterClone) -> Self { + EnvFilter::from_str(&other.0) + .expect("env filter syntax should have been validated already") + } +} + +impl<'de> Deserialize<'de> for EnvFilterClone { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + Self::from_str(&s).map_err(de::Error::custom) + } +} diff --git a/src/error.rs b/src/error.rs index 94d9a847..c8e9d8e2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -61,9 +61,6 @@ pub(crate) enum Observability { #[error("opentelemetry error")] Otel(#[from] opentelemetry::trace::TraceError), - #[error("invalid log filter syntax")] - EnvFilter(#[from] tracing_subscriber::filter::ParseError), - #[error("failed to install global default tracing subscriber")] SetSubscriber(#[from] tracing::subscriber::SetGlobalDefaultError), diff --git a/src/observability.rs b/src/observability.rs index f182219f..b6ded983 100644 --- a/src/observability.rs +++ b/src/observability.rs @@ -82,8 +82,6 @@ pub(crate) enum FoundIn { /// Initialize observability pub(crate) fn init(config: &Config) -> Result { - let config_filter_layer = || EnvFilter::try_new(&config.log); - let jaeger_layer = config .allow_jaeger .then(|| { @@ -101,7 +99,7 @@ pub(crate) fn init(config: &Config) -> Result { let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); Ok::<_, error::Observability>( - telemetry.with_filter(config_filter_layer()?), + telemetry.with_filter(EnvFilter::from(&config.log)), ) }) .transpose()?; @@ -114,7 +112,7 @@ pub(crate) fn init(config: &Config) -> Result { let flame_layer = flame_layer.with_empty_samples(false); Ok::<_, error::Observability>(( - flame_layer.with_filter(config_filter_layer()?), + flame_layer.with_filter(EnvFilter::from(&config.log)), guard, )) }) @@ -122,7 +120,7 @@ pub(crate) fn init(config: &Config) -> Result { .unzip(); let fmt_layer = tracing_subscriber::fmt::Layer::new() - .with_filter(config_filter_layer()?); + .with_filter(EnvFilter::from(&config.log)); let subscriber = Registry::default() .with(jaeger_layer)