use tracing::Level;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::{SubscriberInitExt, TryInitError};
use tracing_subscriber::{EnvFilter, Layer, Registry, fmt};
#[derive(Debug, Clone)]
pub struct TracingConfig {
pub json: bool,
pub level: Level,
pub with_target: bool,
pub with_file: bool,
pub with_line_number: bool,
}
impl Default for TracingConfig {
fn default() -> Self {
Self {
json: false,
level: Level::INFO,
with_target: true,
with_file: false,
with_line_number: false,
}
}
}
impl TracingConfig {
pub fn new() -> Self {
Self::default()
}
pub fn json(mut self) -> Self {
self.json = true;
self
}
pub fn level(mut self, level: Level) -> Self {
self.level = level;
self
}
pub fn with_target(mut self, enabled: bool) -> Self {
self.with_target = enabled;
self
}
pub fn with_file(mut self, enabled: bool) -> Self {
self.with_file = enabled;
self
}
pub fn with_line_number(mut self, enabled: bool) -> Self {
self.with_line_number = enabled;
self
}
pub fn init(self) {
let _ = init_subscriber(Some(self), None);
}
}
fn fmt_layer(config: &TracingConfig) -> Box<dyn Layer<Registry> + Send + Sync> {
let layer = fmt::layer()
.with_target(config.with_target)
.with_file(config.with_file)
.with_line_number(config.with_line_number);
if config.json {
layer.json().boxed()
} else {
layer.boxed()
}
}
pub(crate) fn init_subscriber(
tracing: Option<TracingConfig>,
extra_layer: Option<Box<dyn Layer<Registry> + Send + Sync>>,
) -> Result<(), TryInitError> {
if tracing.is_none() && extra_layer.is_none() {
return Ok(());
}
let config = tracing.unwrap_or_default();
let filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new(config.level.to_string()));
let mut layers: Vec<Box<dyn Layer<Registry> + Send + Sync>> = vec![fmt_layer(&config)];
if let Some(layer) = extra_layer {
layers.push(layer);
}
Registry::default()
.with(layers.with_filter(filter))
.try_init()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tracing_config_default() {
let config = TracingConfig::default();
assert!(!config.json);
assert_eq!(config.level, Level::INFO);
assert!(config.with_target);
assert!(!config.with_file);
assert!(!config.with_line_number);
}
#[test]
fn test_tracing_config_new() {
let config = TracingConfig::new();
assert!(!config.json);
}
#[test]
fn test_tracing_config_json() {
let config = TracingConfig::new().json();
assert!(config.json);
}
#[test]
fn test_tracing_config_level() {
let config = TracingConfig::new().level(Level::DEBUG);
assert_eq!(config.level, Level::DEBUG);
}
#[test]
fn test_tracing_config_builder_chain() {
let config = TracingConfig::new()
.json()
.level(Level::TRACE)
.with_target(false)
.with_file(true)
.with_line_number(true);
assert!(config.json);
assert_eq!(config.level, Level::TRACE);
assert!(!config.with_target);
assert!(config.with_file);
assert!(config.with_line_number);
}
}