Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions crates/starknet_transaction_prover/src/server/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub mod names {
pub const BUILD_INFO: &str = "prover_build_info";
/// Requests rejected because the concurrency semaphore was full.
pub const CONCURRENCY_REJECTED_TOTAL: &str = "prover_concurrency_rejected_total";
/// Unhandled panics caught by the global panic hook.
pub const PANICS_TOTAL: &str = "prover_panics_total";
/// Wall-clock duration of `prove_transaction` end-to-end. Bucketed.
pub const PROVE_TRANSACTION_DURATION_SECONDS: &str =
"prover_prove_transaction_duration_seconds";
Expand Down Expand Up @@ -72,6 +74,7 @@ pub fn install_exporter(version: &str, git_sha: &str) -> anyhow::Result<Promethe
// before the first request — dashboards relying on `rate(...) > 0`
// need the series to exist.
metrics::counter!(names::CONCURRENCY_REJECTED_TOTAL).increment(0);
metrics::counter!(names::PANICS_TOTAL).increment(0);
super::http_metrics::preregister_http_metrics();
Ok(handle)
}
Expand Down
10 changes: 8 additions & 2 deletions crates/starknet_transaction_prover/src/server/panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
//! Without an explicit hook, panics in `tokio::spawn`ed work hit the runtime's
//! default handler and print to stderr in an ad-hoc format. We want one
//! structured `tracing` event with location + backtrace so log aggregators
//! can index it. The hook only emits a log line — runtime abort-on-panic
//! behavior is preserved.
//! can index it, plus a `prover_panics_total` bump so dashboards/alerts can
//! fire on panic rate rather than relying on log search. The hook does not
//! abort — runtime abort-on-panic behavior is preserved.

use std::backtrace::Backtrace;
use std::panic::PanicHookInfo;

use tracing::error;

use crate::server::metrics::names::PANICS_TOTAL;

#[cfg(test)]
#[path = "panic_test.rs"]
mod panic_test;
Expand All @@ -20,6 +23,9 @@ pub fn install_panic_hook() {
}

fn panic_hook(info: &PanicHookInfo<'_>) {
// Increment first — if `Backtrace::force_capture` or the `error!` macro
// panic recursively, the counter still reflects the original panic.
metrics::counter!(PANICS_TOTAL).increment(1);
let message = extract_payload(info);
let location = info
.location()
Expand Down
30 changes: 27 additions & 3 deletions crates/starknet_transaction_prover/src/server/panic_test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::sync::{Arc, Mutex};

use crate::server::panic::extract_payload;
use crate::server::metrics::names::PANICS_TOTAL;
use crate::server::panic::{extract_payload, install_panic_hook};
use crate::server::test_recorder::shared_handle;

fn capture_payload<F: FnOnce() + std::panic::UnwindSafe>(f: F) -> String {
let captured: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
Expand All @@ -15,10 +17,32 @@ fn capture_payload<F: FnOnce() + std::panic::UnwindSafe>(f: F) -> String {
value
}

// Panic-capturing tests share global state (the panic hook), so they must
// run serially. Keep as a single `#[test]` so ordering is explicit.
// Panic-capturing tests share global state (the panic hook); both tests in
// this module install/restore the hook around a single `catch_unwind`.
#[test]
fn extracts_static_str_and_formatted_payloads() {
assert_eq!(capture_payload(|| panic!("static literal")), "static literal");
assert_eq!(capture_payload(|| panic!("formatted {}", 42)), "formatted 42");
}

#[test]
fn panic_hook_bumps_panics_total_counter() {
let handle = shared_handle();
let before = counter_value(&handle.render(), PANICS_TOTAL);

let prev_hook = std::panic::take_hook();
install_panic_hook();
let _ = std::panic::catch_unwind(|| panic!("counter-test panic"));
std::panic::set_hook(prev_hook);

let after = counter_value(&handle.render(), PANICS_TOTAL);
assert_eq!(after - before, 1.0);
}
Comment thread
cursor[bot] marked this conversation as resolved.

fn counter_value(scrape: &str, name: &str) -> f64 {
scrape
.lines()
.find(|line| line.starts_with(name) && !line.starts_with("# "))
.and_then(|line| line.rsplit_once(' ').and_then(|(_, v)| v.parse().ok()))
.unwrap_or(0.0)
}
Loading