polyc-eventlog 2026.8.1

Append-only conversation event log on a commonware-storage journal.
Documentation
//! Prometheus metric for event-log append latency.
//!
//! Registered into the process default registry — same pattern as the edge
//! `metrics.rs` modules (`polyc-slack` is the reference): no separate scrape
//! endpoint, no separate registry plumbing. The `polyc-runtime` side-server's
//! `/metrics` handler gathers the default registry, so this histogram shows up
//! there for free in any binary that links this crate.

use std::sync::OnceLock;

use prometheus::{HistogramVec, register_histogram_vec};

/// Wall-clock duration of one [`crate::EventLog::append`] call — the
/// lowest-level, single choke point every writer's batch append goes through —
/// labeled `outcome`: `ok` or `error`.
fn append_duration() -> &'static HistogramVec {
    static V: OnceLock<HistogramVec> = OnceLock::new();
    V.get_or_init(|| {
        register_histogram_vec!(
            "polychrome_eventlog_append_duration_seconds",
            "Event-log append latency (one journal write), by outcome.",
            &["outcome"]
        )
        .expect("register polychrome_eventlog_append_duration_seconds")
    })
}

/// Record one append's elapsed time, labeled `"ok"` or `"error"`.
pub(crate) fn record_append(ok: bool, elapsed: std::time::Duration) {
    append_duration()
        .with_label_values(&[if ok { "ok" } else { "error" }])
        .observe(elapsed.as_secs_f64());
}

/// Force-register [`append_duration`] with every known `outcome` label value
/// pre-created (zero-valued), so it appears in a `/metrics` scrape before any
/// event has been appended. See [`crate::init_metrics`].
///
/// A `HistogramVec` produces NO scrape output for a label combination that
/// has never been touched — registering the vec alone is not enough.
/// `with_label_values` creates the zero-valued child without recording an
/// observation, which is what makes it appear.
pub(crate) fn force() {
    for outcome in ["ok", "error"] {
        append_duration().with_label_values(&[outcome]);
    }
}

#[cfg(test)]
mod tests {
    #![allow(clippy::pedantic, clippy::nursery, missing_docs)]

    use std::time::Duration;

    use prometheus::{Encoder as _, TextEncoder};

    use super::record_append;

    /// Recording an append observation makes it visible in a default-registry
    /// scrape — the same registry `polyc-runtime`'s `/metrics` handler gathers.
    #[test]
    fn record_append_is_visible_in_a_registry_scrape() {
        record_append(true, Duration::from_millis(5));
        let mut buf = Vec::new();
        TextEncoder::new()
            .encode(&prometheus::default_registry().gather(), &mut buf)
            .expect("encode");
        let text = String::from_utf8(buf).expect("utf8");
        assert!(
            text.contains("polychrome_eventlog_append_duration_seconds_bucket"),
            "missing histogram buckets in scrape:\n{text}"
        );
    }
}