pub use color_eyre;
pub use color_eyre::eyre::{self, Report};
use tracing::{debug, error};
use tracing_error::ErrorLayer;
use tracing_subscriber::prelude::*;
use tracing_subscriber::{EnvFilter, filter::LevelFilter};
const SYSLOG_IDENTIFIER: &str = "SYSLOG_IDENTIFIER";
const DEFAULT_LEVEL: LevelFilter = LevelFilter::INFO;
pub fn setup_eyre() -> Result<(), Report> {
color_eyre::install()?;
Ok(())
}
pub fn setup_tracing_with<D: AsRef<str>>(
fallback_tracing_filter_directives: D,
) -> Result<(), Report> {
setup_tracing(fallback_tracing_filter_directives)?;
Ok(())
}
pub fn setup_tracing_default() -> Result<(), Report> {
setup_tracing_with("")
}
fn setup_tracing<D: AsRef<str>>(fallback_tracing_filter_directives: D) -> Result<(), Report> {
let fallback = fallback_envfilter(fallback_tracing_filter_directives.as_ref())?;
match is_running_as_service() {
false => install_stderr_tracing(fallback),
true => install_journald_tracing(fallback).unwrap_or_else(|_| {
let fallback = fallback_envfilter(fallback_tracing_filter_directives.as_ref()).unwrap();
install_stderr_tracing(fallback);
error!(
"{} is set, but failed to connect to journald. Falling back to logging to stderr",
SYSLOG_IDENTIFIER
);
}),
};
Ok(())
}
fn is_running_as_service() -> bool {
!std::env::var(SYSLOG_IDENTIFIER)
.unwrap_or_default()
.is_empty()
}
fn install_stderr_tracing(fallback_filter: EnvFilter) {
let filter_layer = get_envfilter(fallback_filter);
let filter_layer_repr = format!("{:?}", &filter_layer);
let fmt_layer = tracing_subscriber::fmt::layer()
.with_target(false)
.with_writer(std::io::stderr);
tracing_subscriber::registry()
.with(filter_layer)
.with(ErrorLayer::default())
.with(fmt_layer)
.init();
debug!("Logging to stderr with {:?}", filter_layer_repr);
}
fn install_journald_tracing(fallback_filter: EnvFilter) -> Result<(), Report> {
let filter_layer = get_envfilter(fallback_filter);
let filter_layer_repr = format!("{:?}", &filter_layer);
let fmt_layer = tracing_journald::layer()?;
tracing_subscriber::registry()
.with(filter_layer)
.with(ErrorLayer::default())
.with(fmt_layer)
.init();
debug!("Logging to journal with {:?}", filter_layer_repr);
Ok(())
}
fn fallback_envfilter<D: AsRef<str>>(
directives: D, ) -> Result<EnvFilter, tracing_subscriber::filter::ParseError> {
EnvFilter::builder()
.with_default_directive(DEFAULT_LEVEL.into())
.parse(directives)
}
fn get_envfilter(fallback: EnvFilter) -> EnvFilter {
EnvFilter::builder()
.with_default_directive(DEFAULT_LEVEL.into())
.try_from_env()
.unwrap_or(fallback)
}
#[cfg(test)]
mod test {
use super::*;
use insta::assert_debug_snapshot;
use serial_test::serial;
#[test]
fn fallback_envfilter_good() {
assert_debug_snapshot!(fallback_envfilter("info,sqlx=warn").unwrap());
}
#[test]
fn fallback_envfilter_case() {
assert_debug_snapshot!(fallback_envfilter("WarN").unwrap());
}
#[test]
fn fallback_envfilter_empty() {
assert_debug_snapshot!(fallback_envfilter("").unwrap());
}
#[test]
fn fallback_envfilter_bad() {
assert!(fallback_envfilter("=x").is_err());
}
#[test]
#[serial]
fn is_running_as_service_test() {
assert!(!is_running_as_service());
}
#[test]
#[serial]
fn setup_default_env() {
assert!(!is_running_as_service());
unsafe { std::env::set_var("RUST_LOG", "warn") }; setup_tracing("").unwrap();
assert_eq!(LevelFilter::current(), LevelFilter::WARN);
}
#[test]
#[serial]
fn setup_eyre_test() {
setup_eyre().unwrap();
}
}