use tracing_subscriber::EnvFilter;
use crate::ServerError;
pub fn init() -> Result<(), ServerError> {
let filter = env_filter()?;
tracing_log::LogTracer::init().map_err(|error| ServerError::Config {
message: format!(
"failed to install the log-to-tracing bridge: {error}. Another logger is \
already installed in this process, which means half its diagnostics — \
haematite's WAL recovery among them — go somewhere this server cannot see"
),
})?;
let subscriber = tracing_subscriber::fmt()
.json()
.with_env_filter(filter)
.with_current_span(true)
.with_span_list(true)
.with_target(true)
.with_timer(tracing_subscriber::fmt::time::SystemTime)
.finish();
tracing::subscriber::set_global_default(subscriber).map_err(|error| ServerError::Config {
message: format!("failed to initialize tracing subscriber: {error}"),
})
}
fn env_filter() -> Result<EnvFilter, ServerError> {
let directive = std::env::var("AION_LOG")
.or_else(|_| std::env::var("RUST_LOG"))
.unwrap_or_else(|_| "info".to_owned());
EnvFilter::try_new(&directive).map_err(|error| ServerError::Config {
message: format!("invalid log filter `{directive}`: {error}"),
})
}
#[cfg(test)]
mod tests {
use std::sync::{Arc, Mutex};
use tracing_log::{NormalizeEvent as _, log};
use tracing_subscriber::layer::SubscriberExt as _;
#[derive(Clone, Default)]
struct TargetRecorder {
targets: Arc<Mutex<Vec<String>>>,
}
impl<S: tracing::Subscriber> tracing_subscriber::Layer<S> for TargetRecorder {
fn on_event(
&self,
event: &tracing::Event<'_>,
_context: tracing_subscriber::layer::Context<'_, S>,
) {
let Some(normalized) = event.normalized_metadata() else {
return;
};
if let Ok(mut targets) = self.targets.lock() {
targets.push(normalized.target().to_owned());
}
}
}
#[test]
fn a_log_record_reaches_a_tracing_subscriber_through_the_bridge() {
let already_installed = tracing_log::LogTracer::init().is_err();
let recorder = TargetRecorder::default();
let subscriber = tracing_subscriber::registry().with(recorder.clone());
tracing::subscriber::with_default(subscriber, || {
log::info!(target: "haematite::wal::recovery", "wal recovery completed");
});
let targets = recorder.targets.lock().map_or_else(
|poisoned| poisoned.into_inner().clone(),
|seen| seen.clone(),
);
assert!(
targets
.iter()
.any(|target| target == "haematite::wal::recovery"),
"a `log` record must reach the tracing subscriber through the LogTracer \
bridge (logger already installed by another test: {already_installed}); \
saw targets {targets:?}"
);
}
}