Skip to content

Commit

Permalink
Merge pull request #45 from pragma-org/pi/panic-hook
Browse files Browse the repository at this point in the history
Add a friendly panic hook
  • Loading branch information
KtorZ authored Dec 29, 2024
2 parents bac1da1 + 5fb28df commit e230b96
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 1 deletion.
43 changes: 43 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 10 additions & 1 deletion crates/amaru/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ homepage = "https://github.com/pragma-org/amaru"
documentation = "https://docs.rs/amaru"
readme = "README.md"
rust-version = "1.81.0"
build = "build.rs"

[dependencies]
async-trait = "0.1.83"
clap = { version = "4.5.20", features = ["derive"] }
gasket = { version = "0.8.0", features = ["derive"] }
hex = "0.4.3"
miette = "7.2.0"
indoc = "2.0"
ouroboros = { git = "https://github.com/pragma-org/ouroboros", rev = "ca1d447a6c106e421e6c2b1c7d9d59abf5ca9589" }
ouroboros-praos = { git = "https://github.com/pragma-org/ouroboros", rev = "ca1d447a6c106e421e6c2b1c7d9d59abf5ca9589" }
pallas-addresses = "0.31.0"
Expand All @@ -39,10 +41,17 @@ serde = "1.0.215"
bech32 = "0.11.0"
opentelemetry = { version = "0.27.1" }
opentelemetry_sdk = { version = "0.27.1", features = ["async-std", "rt-tokio"] }
opentelemetry-otlp = { version = "0.27.0", features = ["grpc-tonic", "http-proto", "reqwest-client"] }
opentelemetry-otlp = { version = "0.27.0", features = [
"grpc-tonic",
"http-proto",
"reqwest-client",
] }
tracing-opentelemetry = { version = "0.28.0" }

[dev-dependencies]
envpath = { version = "0.0.1-beta.3", features = ["rand"] }
insta = { version = "1.41.1", features = ["json"] }
proptest = "1.5.0"

[build-dependencies]
built = { version = "0.7.1", features = ["git2"] }
3 changes: 3 additions & 0 deletions crates/amaru/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
built::write_built_file().expect("Failed to acquire build-time information");
}
4 changes: 4 additions & 0 deletions crates/amaru/src/bin/amaru/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use clap::{Parser, Subcommand};
use opentelemetry::metrics::Counter;
use panic::panic_handler;
use std::env;

mod cmd;
mod config;
mod exit;
mod panic;

pub const SERVICE_NAME: &str = "amaru";

Expand All @@ -28,6 +30,8 @@ struct Cli {

#[tokio::main]
async fn main() -> miette::Result<()> {
panic_handler();

let counter = setup_tracing();

let args = Cli::parse();
Expand Down
106 changes: 106 additions & 0 deletions crates/amaru/src/bin/amaru/panic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/// Installs a panic handler that prints some useful diagnostics and
/// asks the user to report the issue.
pub fn panic_handler() {
std::panic::set_hook(Box::new(move |info| {
let message = info
.payload()
.downcast_ref::<&str>()
.map(|s| (*s).to_string())
.or_else(|| {
info.payload()
.downcast_ref::<String>()
.map(|s| s.to_string())
})
.unwrap_or_else(|| "unknown error".to_string());

let location = info.location().map_or_else(
|| "".into(),
|location| {
format!(
"{}:{}:{}\n\n ",
location.file(),
location.line(),
location.column(),
)
},
);

// We present the user with a helpful and welcoming error message;
// Block producing nodes should be considered mission critical software, and so
// They should endeavor *never* to crash, and should always handle and recover from errors.
// So if the process panics, it should be treated as a bug, and we very much want the user to report it.
// TODO(pi): We could go a step further, and prefill some issue details like title, body, labels, etc.
// using query parameters: https://github.com/sindresorhus/new-github-issue-url?tab=readme-ov-file#api
let error_message = indoc::formatdoc! {
r#"{fatal}
Whoops! The Amaru process panicked, rather than handling the error it encountered gracefully.
This is almost certainly a bug, and we'd appreciate a report so we can improve Amaru.
Please report this error at https://github.com/pragma-org/amaru/issues/new.
In your bug report please provide the information below and if possible the code
that produced it.
{info}
{location}{message}"#,
info = node_info(),
fatal = "amaru::fatal::error",
location = location,
};

println!("\n{}", indent(&error_message, 3));
}));
}

// TODO: pulled from aiken; should we have our own utility crate for pretty printing?
// https://github.com/aiken-lang/aiken/blob/main/crates/aiken-project/src/pretty.rs#L126C1-L134C2
pub fn indent(lines: &str, n: usize) -> String {
let tab = pad_left(String::new(), n, " ");
lines
.lines()
.map(|line| format!("{tab}{line}"))
.collect::<Vec<_>>()
.join("\n")
}

pub fn pad_left(mut text: String, n: usize, delimiter: &str) -> String {
let diff = n as i32 - text.len() as i32;
if diff.is_positive() {
for _ in 0..diff {
text.insert_str(0, delimiter);
}
}
text
}

// TODO: pulled from aiken; should we have our own config utility crate?
// https://github.com/aiken-lang/aiken/blob/main/crates/aiken-project/src/config.rs#L382C1-L393C2
mod built_info {
include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

pub fn node_info() -> String {
format!(
r#"
Operating System: {}
Architecture: {}
Version: {}"#,
built_info::CFG_OS,
built_info::CFG_TARGET_ARCH,
node_version(true),
)
}

pub fn node_version(include_commit_hash: bool) -> String {
let version = built_info::PKG_VERSION;
let suffix = if include_commit_hash {
format!(
"+{}",
built_info::GIT_COMMIT_HASH_SHORT.unwrap_or("unknown")
)
} else {
"".to_string()
};
format!("v{version}{suffix}")
}

0 comments on commit e230b96

Please sign in to comment.