malvin 0.2.5

Non-interactive research and coding agent
use std::sync::Once;

static INIT: Once = Once::new();

pub fn init_tracing() {
    INIT.call_once(install_malvin_tracing);
}

fn install_malvin_tracing() {
    if tracing::subscriber::set_global_default(MalvinTracingSubscriber).is_ok() {
        return;
    }
    crate::output::print_log_warning(
        "tracing subscriber already initialized; malvin log layer not installed",
    );
}

#[must_use]
pub(crate) fn malvin_log_accepts_tracing_level(level: tracing::Level) -> bool {
    level <= tracing::Level::INFO
}

#[must_use]
pub(crate) fn malvin_log_accepts_tracing_metadata(metadata: &tracing::Metadata<'_>) -> bool {
    let level = *metadata.level();
    if is_llama_cpp_crate_target(metadata.target()) {
        return level <= tracing::Level::WARN;
    }
    malvin_log_accepts_tracing_level(level)
}

fn is_llama_cpp_crate_target(target: &str) -> bool {
    target == "llama-cpp-2" || target.starts_with("llama-cpp-2::")
}

pub(crate) fn emit_malvin_tracing_log(level: tracing::Level, msg: &str) {
    if msg.is_empty() {
        return;
    }
    match level {
        tracing::Level::ERROR => crate::output::print_log_error(msg),
        tracing::Level::WARN => crate::output::print_log_warning(msg),
        _ => crate::output::print_stderr_line(crate::output::MALVIN_WHO, msg),
    }
}

pub(crate) fn process_malvin_tracing_event(event: &tracing::Event<'_>) {
    if !malvin_log_accepts_tracing_metadata(event.metadata()) {
        return;
    }
    let mut msg = String::new();
    event.record(&mut LogFieldVisitor(&mut msg));
    emit_malvin_tracing_log(*event.metadata().level(), &msg);
}

struct MalvinTracingSubscriber;

impl tracing::Subscriber for MalvinTracingSubscriber {
    fn register_callsite(
        &self,
        _metadata: &'static tracing::Metadata<'static>,
    ) -> tracing::subscriber::Interest {
        tracing::subscriber::Interest::always()
    }

    fn enabled(&self, metadata: &tracing::Metadata<'_>) -> bool {
        malvin_log_accepts_tracing_metadata(metadata)
    }

    fn event(&self, event: &tracing::Event<'_>) {
        process_malvin_tracing_event(event);
    }

    fn new_span(&self, _attrs: &tracing::span::Attributes<'_>) -> tracing::span::Id {
        tracing::span::Id::from_u64(1)
    }

    fn record(&self, _span: &tracing::span::Id, _values: &tracing::span::Record<'_>) {}

    fn record_follows_from(&self, _span: &tracing::span::Id, _follows: &tracing::span::Id) {}

    fn enter(&self, _span: &tracing::span::Id) {}

    fn exit(&self, _span: &tracing::span::Id) {}
}

struct LogFieldVisitor<'a>(&'a mut String);

impl tracing::field::Visit for LogFieldVisitor<'_> {
    fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
        push_log_field(self.0, field.name(), value);
    }

    fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
        push_log_field(
            self.0,
            field.name(),
            &format_debug_tracing_field(field.name(), value),
        );
    }
}

pub(crate) fn format_debug_tracing_field(name: &str, value: &dyn std::fmt::Debug) -> String {
    if name == "message" {
        return strip_debug_string_quotes(&format!("{value:?}"));
    }
    format!("{value:?}")
}

fn strip_debug_string_quotes(s: &str) -> String {
    if s.len() >= 2 && s.starts_with('"') && s.ends_with('"') {
        return s[1..s.len() - 1].to_string();
    }
    s.to_string()
}

fn push_log_field(buf: &mut String, name: &str, value: &str) {
    use std::fmt::Write;
    if name == "message" {
        if !buf.is_empty() {
            buf.push_str("; ");
        }
        buf.push_str(value);
        return;
    }
    if !buf.is_empty() {
        buf.push(' ');
    }
    let _ = write!(buf, "{name}={value}");
}

#[cfg(test)]
#[path = "tracing_init_tests.rs"]
mod tracing_init_tests;