use metrics::{counter, describe_counter, describe_histogram, histogram};
use minijinja::Error;
use std::{sync::Once, time::Instant};
use tracing::field;
const MACRO_INVOCATIONS_TOTAL: &str = "netsuke_manifest_macro_invocations_total";
const MACRO_INVOCATION_DURATION: &str = "netsuke_manifest_macro_invocation_duration_seconds";
const TEMPLATE_RENDERS_TOTAL: &str = "netsuke_manifest_template_renders_total";
const TEMPLATE_RENDER_DURATION: &str = "netsuke_manifest_template_render_duration_seconds";
pub(super) fn describe_macro_metrics() {
static DESCRIBE: Once = Once::new();
DESCRIBE.call_once(|| {
describe_counter!(
MACRO_INVOCATIONS_TOTAL,
"Counts manifest macro invocation outcomes labelled as success or error."
);
describe_histogram!(
MACRO_INVOCATION_DURATION,
"Measures manifest macro invocation duration in seconds."
);
});
}
fn describe_render_metrics() {
static DESCRIBE: Once = Once::new();
DESCRIBE.call_once(|| {
describe_counter!(
TEMPLATE_RENDERS_TOTAL,
"Counts manifest template renders by bounded outcome and macro-import presence."
);
describe_histogram!(
TEMPLATE_RENDER_DURATION,
"Measures manifest template rendering duration in seconds."
);
});
}
pub(super) fn instrument_macro_invocation<T>(
invoke: impl FnOnce() -> Result<T, Error>,
) -> Result<T, Error> {
let span = tracing::trace_span!(
"manifest.macro.invoke",
outcome = field::Empty,
error_category = field::Empty,
);
let _guard = span.enter();
let started = Instant::now();
let result = invoke();
let outcome = outcome_label(&result);
span.record("outcome", outcome);
if let Err(error) = &result {
span.record("error_category", format_args!("{:?}", error.kind()));
tracing::debug!(error_category = ?error.kind(), "manifest macro invocation failed");
}
counter!(MACRO_INVOCATIONS_TOTAL, "outcome" => outcome).increment(1);
histogram!(MACRO_INVOCATION_DURATION).record(started.elapsed());
result
}
pub(super) fn instrument_template_render<T>(
has_macro_imports: bool,
render: impl FnOnce() -> Result<T, Error>,
) -> Result<T, Error> {
describe_render_metrics();
let span = tracing::trace_span!(
"manifest.template.render",
outcome = field::Empty,
has_macro_imports,
error_category = field::Empty,
);
let _guard = span.enter();
let started = Instant::now();
let result = render();
let outcome = outcome_label(&result);
span.record("outcome", outcome);
if let Err(error) = &result {
span.record("error_category", format_args!("{:?}", error.kind()));
tracing::debug!(error_category = ?error.kind(), "manifest template render failed");
}
counter!(
TEMPLATE_RENDERS_TOTAL,
"outcome" => outcome,
"has_macro_imports" => if has_macro_imports { "true" } else { "false" },
)
.increment(1);
histogram!(TEMPLATE_RENDER_DURATION).record(started.elapsed());
result
}
const fn outcome_label<T>(result: &Result<T, Error>) -> &'static str {
if result.is_ok() { "success" } else { "error" }
}