Skip to content

Send an email when a panic occurred #1571

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 29, 2019
Merged
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
83 changes: 83 additions & 0 deletions Cargo.lock

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

14 changes: 9 additions & 5 deletions codechain/run_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use ckey::{Address, NetworkId, PlatformAddress};
use ckeystore::accounts_dir::RootDiskDirectory;
use ckeystore::KeyStore;
use clap::ArgMatches;
use clogger::{self, EmailAlarmConfig, LoggerConfig};
use clogger::{self, EmailAlarm, EmailAlarmConfig, LoggerConfig};
use cnetwork::{Filters, NetworkConfig, NetworkControl, NetworkService, RoutingTable, SocketAddr};
use csync::{BlockSyncExtension, BlockSyncSender, SnapshotService, TransactionSyncExtension};
use ctimer::TimerLoop;
Expand Down Expand Up @@ -238,17 +238,21 @@ pub fn run_node(matches: &ArgMatches) -> Result<(), String> {
.expect("Current time should be later than unix epoch")
.subsec_nanos() as usize,
);
let email_alarm_config = if !config.email_alarm.disable.unwrap() {
match (&config.email_alarm.to, &config.email_alarm.sendgrid_key) {
let email_alarm = if !config.email_alarm.disable.unwrap() {
let config = match (&config.email_alarm.to, &config.email_alarm.sendgrid_key) {
(Some(to), Some(sendgrid_key)) => Some(EmailAlarmConfig::new(to.to_string(), sendgrid_key.to_string())),
(None, _) => return Err("email-alarm-to is not specified".to_string()),
(_, None) => return Err("email-alarm-sendgrid-key is not specified".to_string()),
}
};
config.as_ref().map(EmailAlarm::new)
} else {
None
};
clogger::init(&LoggerConfig::new(instance_id), &email_alarm_config)
clogger::init(&LoggerConfig::new(instance_id), email_alarm.clone())
.expect("Logger must be successfully initialized");
if let Some(email_alarm) = email_alarm {
panic_hook::set_with_email_alarm(email_alarm);
}

let pf = load_password_file(&config.operating.password_path)?;
let base_path = config.operating.base_path.as_ref().unwrap().clone();
Expand Down
2 changes: 1 addition & 1 deletion codechain/subcommand/account_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub fn run_account_command(matches: &ArgMatches) -> Result<(), String> {
return Ok(())
}

clogger::init(&LoggerConfig::new(0), &None).expect("Logger must be successfully initialized");
clogger::init(&LoggerConfig::new(0), None).expect("Logger must be successfully initialized");

let keys_path = get_global_argument(matches, "keys-path").unwrap_or_else(|| DEFAULT_KEYS_PATH.into());
let dir = RootDiskDirectory::create(keys_path).expect("Cannot read key path directory");
Expand Down
1 change: 1 addition & 0 deletions util/logger/src/email.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ impl EmailAlarmConfig {
}
}

#[derive(Clone)]
pub struct EmailAlarm {
pub to: String,
pub sendgrid_key: String,
Expand Down
6 changes: 2 additions & 4 deletions util/logger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ use logger::Logger;

pub use log::Level;

pub fn init(config: &LoggerConfig, email_alarm_config: &Option<EmailAlarmConfig>) -> Result<(), SetLoggerError> {
let email_alarm = email_alarm_config.as_ref().map(EmailAlarm::new);
pub fn init(config: &LoggerConfig, email_alarm: Option<EmailAlarm>) -> Result<(), SetLoggerError> {
let logger = Logger::new(config, email_alarm);
log::set_max_level(logger.filter());
log::set_boxed_logger(Box::new(logger))
Expand All @@ -53,5 +52,4 @@ lazy_static! {
pub static ref SLOGGER: StructuredLogger = StructuredLogger::create();
}

use email::EmailAlarm;
pub use email::EmailAlarmConfig;
pub use email::{EmailAlarm, EmailAlarmConfig};
3 changes: 3 additions & 0 deletions util/panic_hook/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ authors = ["Parity Technologies <[email protected]>", "CodeChain Team <hi@codechai

[dependencies]
backtrace = "0.3.2"
codechain-logger = { path = "../logger" }
my_internet_ip = "0.1.1"
get_if_addrs = "0.5.3"
69 changes: 56 additions & 13 deletions util/panic_hook/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@
//! Custom panic hook with bug report link

extern crate backtrace;
extern crate codechain_logger as clogger;
extern crate get_if_addrs;
extern crate my_internet_ip;

use backtrace::Backtrace;
use std::io::{self, Write};
use clogger::EmailAlarm;
use std::panic::{self, PanicInfo};
use std::thread;

Expand All @@ -28,13 +31,33 @@ pub fn set() {
panic::set_hook(Box::new(panic_hook));
}

pub fn set_with_email_alarm(email_alarm: clogger::EmailAlarm) {
panic::set_hook(Box::new(move |info| panic_hook_with_email_alarm(&email_alarm, info)));
}

static ABOUT_PANIC: &str = "
This is a bug. Please report it at:

https://github.com/CodeChain-io/codechain/issues/new
";

fn panic_hook(info: &PanicInfo) {
let message = panic_message(info);
eprintln!("{}", message);
exit_on_debug_mode();
}

fn panic_hook_with_email_alarm(email_alarm: &EmailAlarm, info: &PanicInfo) {
let message = panic_message(info);
eprintln!("{}", message);
let ip_addresses = get_ip_addresses();

let message_for_email = message.replace("\n", "<br>");
email_alarm.send(&format!("IP: {}<br>{}", ip_addresses, message_for_email));
exit_on_debug_mode();
}

fn panic_message(info: &PanicInfo) -> String {
let location = info.location();
let file = location.as_ref().map(|l| l.file()).unwrap_or("<unknown>");
let line = location.as_ref().map(|l| l.line()).unwrap_or(0);
Expand All @@ -52,18 +75,17 @@ fn panic_hook(info: &PanicInfo) {

let backtrace = Backtrace::new();

let mut stderr = io::stderr();

let _ = writeln!(stderr);
let _ = writeln!(stderr, "====================");
let _ = writeln!(stderr);
let _ = writeln!(stderr, "{:?}", backtrace);
let _ = writeln!(stderr);
let _ = writeln!(stderr, "Thread '{}' panicked at '{}', {}:{}", name, msg, file, line);

let _ = writeln!(stderr, "{}", ABOUT_PANIC);

exit_on_debug_mode();
let lines = [
"".to_string(),
"====================".to_string(),
"".to_string(),
format!("{:?}", backtrace),
"".to_string(),
format!("Thread '{}' panicked at '{}', {}:{}", name, msg, file, line),
ABOUT_PANIC.to_string(),
];

lines.join("\n")
}

#[cfg(debug_assertions)]
Expand All @@ -73,3 +95,24 @@ fn exit_on_debug_mode() {

#[cfg(not(debug_assertions))]
fn exit_on_debug_mode() {}

fn get_ip_addresses() -> String {
match my_internet_ip::get() {
Ok(ip) => return ip.to_string(),
Err(e) => {
eprintln!("Failed get internet IP: {:?}", e);
}
};

match get_if_addrs::get_if_addrs() {
Ok(interfaces) => {
let ip_addresses: Vec<String> =
interfaces.iter().map(|interface| format!("{:?}", interface.ip())).collect();
return ip_addresses.join(", ")
}
Err(err) => {
eprintln!("Failed to get local IPs: {}", err);
}
}
"Unknown".to_string()
}