burn_telemetry 0.1.0

burn training logger
Documentation
//! Shared helpers for dealing with Burn metric entries.

use burn::train::metric::{MetricEntry, NumericEntry};

/// Extract the numeric value carried by a [`MetricEntry`].
///
/// Burn serializes numeric metrics in a couple of different formats (plain values or aggregated
/// values). This helper hides the parsing details and always returns the latest numeric value if
/// one is present.
pub(crate) fn numeric_value(entry: &MetricEntry) -> Option<f64> {
    NumericEntry::deserialize(&entry.serialize)
        .map(|numeric| numeric.current())
        .ok()
}

/// Produce a stable identifier for a metric name.
///
/// Metric backends frequently have more restrictive naming rules than Burn. We normalize all
/// identifiers to lowercase snake case and fall back to `metric` for empty inputs.
pub(crate) fn metric_identifier(name: &str) -> String {
    let mut ident = String::with_capacity(name.len());
    for ch in name.chars() {
        if ch.is_ascii_alphanumeric() {
            ident.push(ch.to_ascii_lowercase());
        } else if !ident.ends_with('_') {
            ident.push('_');
        }
    }
    ident.truncate(ident.trim_end_matches('_').len());
    if ident.is_empty() {
        ident.push_str("metric");
    }
    ident
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::Arc;

    #[test]
    fn numeric_value_plain_scalar() {
        let entry = MetricEntry::new(
            Arc::new("loss".to_string()),
            "0.42".to_string(),
            "0.42".to_string(),
        );

        assert_eq!(numeric_value(&entry), Some(0.42));
    }

    #[test]
    fn numeric_value_aggregated_metric() {
        // The serialized payload for aggregated metrics encodes <sum,count>.
        let entry = MetricEntry::new(
            Arc::new("loss".to_string()),
            "1.0".to_string(),
            "15.0,10".to_string(),
        );

        assert_eq!(numeric_value(&entry), Some(15.0));
    }

    #[test]
    fn metric_identifier_normalizes_characters() {
        assert_eq!(metric_identifier("Train/Loss"), "train_loss");
        assert_eq!(metric_identifier("loss value"), "loss_value");
        assert_eq!(metric_identifier("Weird---Name!!!"), "weird_name");
    }

    #[test]
    fn metric_identifier_falls_back_to_metric() {
        assert_eq!(metric_identifier("   "), "metric");
    }
}