Skip to content

Send an email when there are error logs #1561

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 28, 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
562 changes: 493 additions & 69 deletions Cargo.lock

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions codechain/codechain.yml
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,23 @@ args:
value_name: PATH
help: Specify the path for the network blacklist file.
takes_value: true
- no-email-alarm:
long: no-email-alarm
help: Do not use email alarm
- email-alarm-to:
long: email-alarm-to
value_name: EMAIL
help: Specify the email address to receive the alarm.
takes_value: true
conflicts_with:
- no-email-alarm
- email-alarm-sendgrid-key:
long: email-alarm-sendgrid-key
value_name: KEY
help: Specify the sendgrid key which is used to send alarms.
takes_value: true
conflicts_with:
- no-email-alarm
subcommands:
- account:
about: account managing commands
Expand Down
52 changes: 51 additions & 1 deletion codechain/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ pub struct Config {
pub ws: Ws,
pub snapshot: Snapshot,
pub stratum: Stratum,
#[serde(default)]
pub email_alarm: EmailAlarm,
}

impl Config {
Expand All @@ -54,6 +56,7 @@ impl Config {
self.ws.merge(&other.ws);
self.snapshot.merge(&other.snapshot);
self.stratum.merge(&other.stratum);
self.email_alarm.merge(&other.email_alarm);
}

pub fn miner_options(&self) -> Result<MinerOptions, String> {
Expand Down Expand Up @@ -288,6 +291,15 @@ pub struct Stratum {
pub port: Option<u16>,
}


#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EmailAlarm {
pub disable: Option<bool>,
pub to: Option<String>,
pub sendgrid_key: Option<String>,
}

impl Ipc {
pub fn merge(&mut self, other: &Ipc) {
if other.disable.is_some() {
Expand Down Expand Up @@ -691,6 +703,44 @@ impl Stratum {
}
}

impl EmailAlarm {
pub fn merge(&mut self, other: &EmailAlarm) {
if other.disable.is_some() {
self.disable = other.disable;
}
if other.to.is_some() {
self.to = other.to.clone();
}
if other.sendgrid_key.is_some() {
self.sendgrid_key = other.sendgrid_key.clone();
}
}

pub fn overwrite_with(&mut self, matches: &clap::ArgMatches) -> Result<(), String> {
if matches.is_present("no-email-alarm") {
self.disable = Some(true);
}
if let Some(to) = matches.value_of("email-alarm-to") {
self.to = Some(to.to_string());
}
if let Some(sendgrid_key) = matches.value_of("email-alarm-sendgrid-key") {
self.sendgrid_key = Some(sendgrid_key.to_string());
}

Ok(())
}
}

impl Default for EmailAlarm {
fn default() -> Self {
Self {
disable: Some(true),
to: None,
sendgrid_key: None,
}
}
}

#[cfg(not(debug_assertions))]
pub fn read_preset_config() -> &'static str {
let bytes = include_bytes!("presets/config.prod.toml");
Expand Down Expand Up @@ -724,6 +774,6 @@ pub fn load_config(matches: &clap::ArgMatches) -> Result<Config, String> {
config.ws.overwrite_with(&matches)?;
config.snapshot.overwrite_with(&matches)?;
config.stratum.overwrite_with(&matches)?;

config.email_alarm.overwrite_with(&matches)?;
Ok(config)
}
3 changes: 3 additions & 0 deletions codechain/config/presets/config.dev.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,6 @@ path = "snapshot"
[stratum]
disable = false
port = 8008

[email_alarm]
disable = true
3 changes: 3 additions & 0 deletions codechain/config/presets/config.prod.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,6 @@ path = "snapshot"
[stratum]
disable = true
port = 8008

[email_alarm]
disable = true
14 changes: 12 additions & 2 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, LoggerConfig};
use clogger::{self, EmailAlarmConfig, LoggerConfig};
use cnetwork::{Filters, NetworkConfig, NetworkControl, NetworkService, RoutingTable, SocketAddr};
use csync::{BlockSyncExtension, BlockSyncSender, SnapshotService, TransactionSyncExtension};
use ctimer::TimerLoop;
Expand Down Expand Up @@ -238,7 +238,17 @@ pub fn run_node(matches: &ArgMatches) -> Result<(), String> {
.expect("Current time should be later than unix epoch")
.subsec_nanos() as usize,
);
clogger::init(&LoggerConfig::new(instance_id)).expect("Logger must be successfully initialized");
let email_alarm_config = if !config.email_alarm.disable.unwrap() {
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()),
}
} else {
None
};
clogger::init(&LoggerConfig::new(instance_id), &email_alarm_config)
.expect("Logger must be successfully initialized");

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)).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/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ env_logger = "0.6.0"
lazy_static = "1.2"
log = "0.4.6"
parking_lot = "0.6.0"
sendgrid = "0.8.1"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
Expand Down
46 changes: 46 additions & 0 deletions util/logger/src/email.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use sendgrid::v3 as sendgrid;

pub struct EmailAlarmConfig {
pub to: String,
pub sendgrid_key: String,
}

impl EmailAlarmConfig {
pub fn new(to: String, sendgrid_key: String) -> Self {
Self {
to,
sendgrid_key,
}
}
}

pub struct EmailAlarm {
pub to: String,
pub sendgrid_key: String,
}

impl EmailAlarm {
pub fn new(config: &EmailAlarmConfig) -> Self {
Self {
to: config.to.clone(),
sendgrid_key: config.sendgrid_key.clone(),
}
}

pub fn send(&self, log: &str) {
let p = sendgrid::Personalization::new().add_to(sendgrid::Email::new().set_email(&self.to));
let now = time::now_utc();
let now = now.rfc3339();
let m = sendgrid::Message::new()
.set_from(sendgrid::Email::new().set_email("[email protected]"))
// FIXME: fill the network id
.set_subject(&format!("[error][?c][codechain] Error from CodeChain-{}", now))
.add_content(sendgrid::Content::new().set_content_type("text/html").set_value(log))
.add_personalization(p);
let sender = sendgrid::Sender::new(self.sendgrid_key.clone());
let send_result = sender.send(&m);
if let Err(err) = send_result {
eprintln!("Sent an email, but failed. returned error is {}", err);
}
}
}
10 changes: 8 additions & 2 deletions util/logger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ extern crate env_logger;
extern crate lazy_static;
extern crate log;
extern crate parking_lot;
extern crate sendgrid;
extern crate serde;
extern crate serde_derive;
extern crate serde_json;
extern crate time;

mod email;
mod logger;
mod macros;
mod structured_logger;
Expand All @@ -36,8 +38,9 @@ use logger::Logger;

pub use log::Level;

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

use email::EmailAlarm;
pub use email::EmailAlarmConfig;
16 changes: 12 additions & 4 deletions util/logger/src/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ use time;
use atty;
use colored::Colorize;
use env_logger::filter::{Builder as FilterBuilder, Filter};
use log::{LevelFilter, Log, Metadata, Record};
use log::{Level, LevelFilter, Log, Metadata, Record};

use crate::{structured_logger, SLOGGER};
use crate::{email::EmailAlarm, structured_logger, SLOGGER};

pub struct Config {
pub instance_id: usize,
Expand All @@ -41,10 +41,11 @@ pub struct Logger {
instance_id: usize,
filter: Filter,
stderr_is_tty: bool,
email_alarm: Option<EmailAlarm>,
}

impl Logger {
pub fn new(config: &Config) -> Self {
pub fn new(config: &Config, email_alarm: Option<EmailAlarm>) -> Self {
let mut builder = FilterBuilder::new();
builder.filter(None, LevelFilter::Info);

Expand All @@ -58,6 +59,7 @@ impl Logger {
instance_id: config.instance_id,
filter: builder.build(),
stderr_is_tty,
email_alarm,
}
}

Expand Down Expand Up @@ -103,8 +105,14 @@ impl Log for Logger {
target: log_target.to_string(),
message: log_message.to_string(),
timestamp,
thread_name,
thread_name: thread_name.clone(),
});

if log_level == Level::Error {
if let Some(email_alarm) = &self.email_alarm {
email_alarm.send(&format!("{} {} {}", thread_name, log_target, log_message))
}
}
}
}

Expand Down