use std::error::Error;
use std::path::Path;
use tracing::Level;
use tracing_appender::non_blocking::{NonBlocking, WorkerGuard};
use tracing_appender::rolling;
use tracing_subscriber::fmt;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::EnvFilter;
#[cfg(feature = "otlp")]
use super::otlp::new_tracer_provider;
#[cfg(feature = "otlp")]
use opentelemetry::{global, trace::TracerProvider as _};
#[cfg(feature = "otlp")]
use tracing_opentelemetry::OpenTelemetryLayer;
pub fn setup_file_appender<P: AsRef<Path>>(datadir: P, who: &str) -> (NonBlocking, WorkerGuard) {
let file_appender = rolling::never(datadir.as_ref(), format!("{}.log", who));
tracing_appender::non_blocking(file_appender)
}
pub fn env_filter() -> EnvFilter {
EnvFilter::builder().with_default_directive(Level::INFO.into()).from_env_lossy()
}
pub fn init_tracing_subscriber<P: AsRef<Path>>(
datadir: P,
who: &str,
) -> Result<OtelGuard, Box<dyn Error>> {
let (file_writer, file_guard) = setup_file_appender(datadir, who);
let format = fmt::format()
.with_level(true)
.with_ansi(true)
.with_target(false)
.with_source_location(true)
.compact();
let stdout_layer = fmt::layer().event_format(format.clone()).with_writer(std::io::stdout);
let file_layer = fmt::layer().event_format(format).with_writer(file_writer);
let env_filter = env_filter();
let default_subscriber =
tracing_subscriber::registry().with(stdout_layer).with(file_layer).with(env_filter);
#[cfg(feature = "otlp")]
let default_subscriber = {
let tracer = new_tracer_provider()?.tracer(who.to_string());
let otlp_trace_layer = OpenTelemetryLayer::new(tracer);
default_subscriber.with(otlp_trace_layer)
};
match default_subscriber.try_init() {
Ok(_) => Ok(OtelGuard::new(file_guard)),
Err(err) => Err(Box::new(err)),
}
}
pub struct OtelGuard {
_file_appender_guard: WorkerGuard,
}
impl OtelGuard {
pub fn new(file_appender_guard: WorkerGuard) -> Self {
Self { _file_appender_guard: file_appender_guard }
}
}
impl Drop for OtelGuard {
fn drop(&mut self) {
#[cfg(feature = "otlp")]
global::shutdown_tracer_provider();
}
}
#[cfg(test)]
mod tests {
use crate::observability::env_filter;
use std::time::Duration;
use tokio::time::sleep;
use tracing_subscriber::{fmt, layer::SubscriberExt};
#[tokio::test]
async fn test_setup_file_appender() {
std::env::set_var("RUST_LOG", "info");
let temp_dir = std::env::temp_dir();
let file_path = temp_dir.join(format!("test.log"));
let handle = tokio::spawn(async move {
let (file_writer, _file_guard) = super::setup_file_appender(temp_dir, "test");
let subscriber = tracing_subscriber::registry()
.with(fmt::layer().with_writer(file_writer))
.with(env_filter());
tracing::subscriber::set_global_default(subscriber)
.expect("setting default subscriber failed");
tracing::info!("test random date: 11/08/2001");
sleep(Duration::from_millis(10000)).await;
});
let _ = handle.await;
assert_eq!(file_path.exists(), true);
let contents = std::fs::read_to_string(&file_path).expect("failed to read file");
assert_eq!(contents.contains("test random date: 11/08/2001"), true);
}
}