use std::fmt;
use std::io::IsTerminal;
use std::io::stderr;
use tracing::Event;
use tracing::field::{Field, Visit};
use tracing_subscriber::EnvFilter;
use tracing_subscriber::fmt::format::Writer;
use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields};
use tracing_subscriber::registry::LookupSpan;
pub fn init() {
let _ = try_init();
}
#[must_use]
pub fn try_init() -> bool {
let filter =
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("modrun=info"));
try_init_with_filter(filter)
}
pub fn init_with_filter(filter: EnvFilter) {
let _ = try_init_with_filter(filter);
}
#[must_use]
pub fn try_init_with_filter(filter: EnvFilter) -> bool {
tracing_subscriber::fmt()
.with_writer(stderr)
.with_env_filter(filter)
.with_ansi(stderr().is_terminal())
.event_format(MessageOnly)
.try_init()
.is_ok()
}
#[derive(Clone, Debug, Default)]
struct MessageOnly;
struct MessageVisitor {
message: Option<String>,
}
impl Visit for MessageVisitor {
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
if field.name() == "message" && self.message.is_none() {
self.message = Some(format!("{value:?}"));
}
}
fn record_str(&mut self, field: &Field, value: &str) {
if field.name() == "message" {
self.message = Some(value.to_owned());
}
}
}
impl<S, N> FormatEvent<S, N> for MessageOnly
where
S: tracing::Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
{
fn format_event(
&self,
_ctx: &FmtContext<'_, S, N>,
mut writer: Writer<'_>,
event: &Event<'_>,
) -> fmt::Result {
let mut visitor = MessageVisitor { message: None };
event.record(&mut visitor);
let Some(message) = visitor.message else {
return Ok(());
};
let message = strip_debug_quotes(&message);
writeln!(writer, "{message}")
}
}
fn strip_debug_quotes(message: &str) -> &str {
let bytes = message.as_bytes();
if bytes.len() >= 2 && bytes[0] == b'"' && bytes[bytes.len() - 1] == b'"' {
&message[1..message.len() - 1]
} else {
message
}
}