monitor uptime completed

This commit is contained in:
Christoph J. Scherr 2023-07-09 17:53:20 +02:00
parent 791b04f71f
commit b8ab5eaac3
Signed by: PlexSheep
GPG Key ID: 25B4ACF7D88186CC
6 changed files with 186 additions and 16 deletions

View File

@ -27,6 +27,7 @@ clap-num = "1.0.2"
clap-verbosity-flag = "2.0.1" clap-verbosity-flag = "2.0.1"
env_logger = "0.10.0" env_logger = "0.10.0"
gag = "1.0.0" gag = "1.0.0"
humantime = "2.1.0"
log = { version = "0.4.19", features = ["max_level_trace", "release_max_level_trace"] } log = { version = "0.4.19", features = ["max_level_trace", "release_max_level_trace"] }
num = "0.4.0" num = "0.4.0"
pyo3 = "0.18.1" pyo3 = "0.18.1"

View File

@ -19,7 +19,7 @@ use clap::{Parser, Subcommand};
use clap_num::number_range; use clap_num::number_range;
use clap_verbosity_flag::Verbosity; use clap_verbosity_flag::{Verbosity, InfoLevel};
//// CONSTANTS ///////////////////////////////////////////////////////////////////////////////////// //// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
/// short about section displayed in help /// short about section displayed in help
@ -51,7 +51,11 @@ r#"libpt: {version}{about-section}Author:
pub struct Cli { pub struct Cli {
/// set a verbosity, multiple allowed (f.e. -vvv) /// set a verbosity, multiple allowed (f.e. -vvv)
#[command(flatten)] #[command(flatten)]
pub verbose: Verbosity, pub verbose: Verbosity<InfoLevel>,
/// show logger meta
#[arg(short, long)]
pub log_meta: bool,
/// choose a subcommand /// choose a subcommand
#[command(subcommand)] #[command(subcommand)]

View File

@ -47,10 +47,21 @@ fn main() {
std::env::set_var(logger::LOGGER_ENV_KEY, "trace"); std::env::set_var(logger::LOGGER_ENV_KEY, "trace");
let cli = Cli::parse(); let cli = Cli::parse();
if cli.log_meta {
// set up our logger to use the given verbosity // set up our logger to use the given verbosity
env_logger::Builder::new() env_logger::Builder::new()
.filter_level(cli.verbose.log_level_filter()) .filter_module("libpt", cli.verbose.log_level_filter())
.init(); .init();
}
else {
// set up our logger to use the given verbosity
env_logger::Builder::new()
.filter_module("libpt", cli.verbose.log_level_filter())
.format_level(false)
.format_target(false)
.format_timestamp(None)
.init();
}
trace!("started the main function"); trace!("started the main function");
trace!("{:?}", &cli); trace!("{:?}", &cli);
@ -84,12 +95,7 @@ fn net(cli: &Cli, command: NetCommands) {
} }
let _verbose = cli.verbose.log_level().is_some(); let _verbose = cli.verbose.log_level().is_some();
if repeat > 0 { if repeat > 0 {
loop { uptime::continuous_uptime_monitor(success_ratio, urls, repeat * 1000);
let status = uptime::UptimeStatus::new(success_ratio, &urls);
println!("{}", status);
std::thread::sleep(std::time::Duration::from_secs(repeat));
}
} else { } else {
let status = uptime::UptimeStatus::new(success_ratio, &urls); let status = uptime::UptimeStatus::new(success_ratio, &urls);
println!("{}", status); println!("{}", status);

View File

@ -17,7 +17,9 @@
#![warn(clippy::pedantic)] #![warn(clippy::pedantic)]
//// IMPORTS /////////////////////////////////////////////////////////////////////////////////////// //// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
/// macros to make things faster in your code
pub mod macros; pub mod macros;
/// some general use printing to stdout tools
pub mod printing; pub mod printing;
//// CONSTANTS ///////////////////////////////////////////////////////////////////////////////////// //// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,48 @@
//! # tools that make printing stuff better
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
// we want Debug everywhere.
#![warn(missing_debug_implementations)]
// enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
// reimport our macros to this module, so the user does not get confused when importing the macros
pub use crate::get_stdout_for;
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
/// Quickly get a one line visual divider
#[macro_export]
macro_rules! divider {
() => {{
format!("{:=^80}", "=")
}};
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Quickly print a one line visual divider
#[macro_export]
macro_rules! println_divider {
() => {{
println!("{}", divider!())
}};
}
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////

View File

@ -1,7 +1,7 @@
//! # monitor your network uptime //! # monitor your network uptime
//! //!
//! This method offers a way to monitor your networks/hosts uptime. This is achieved by making //! This method offers a way to monitor your networks/hosts uptime. This is achieved by making
//! https requests to a given list of //! HTTPS requests to a given list of
//// ATTRIBUTES //////////////////////////////////////////////////////////////////////////////////// //// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs // we want docs
@ -14,7 +14,7 @@
// enable clippy's extra lints, the pedantic version // enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)] #![warn(clippy::pedantic)]
use std::{fmt, str::FromStr}; use std::{fmt, str::FromStr, time::Duration};
//// IMPORTS /////////////////////////////////////////////////////////////////////////////////////// //// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
// we want the log macros in any case // we want the log macros in any case
@ -23,6 +23,11 @@ use log::{debug, error, info, trace, warn};
use reqwest::{self, Url}; use reqwest::{self, Url};
use humantime::{format_duration, format_rfc3339};
use std::time::SystemTime;
use crate::divider;
//// TYPES ///////////////////////////////////////////////////////////////////////////////////////// //// TYPES /////////////////////////////////////////////////////////////////////////////////////////
//// CONSTANTS ///////////////////////////////////////////////////////////////////////////////////// //// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
@ -73,6 +78,7 @@ impl UptimeStatus {
warn!("Invalid URL: '{}", s); warn!("Invalid URL: '{}", s);
} }
} }
status.urls.dedup();
status.check(); status.check();
@ -85,7 +91,11 @@ impl UptimeStatus {
pub fn check(&mut self) { pub fn check(&mut self) {
self.reachable = 0; self.reachable = 0;
self.urls.iter().for_each(|url| { self.urls.iter().for_each(|url| {
let response = reqwest::blocking::get(url.clone()); let client = reqwest::blocking::Client::builder()
.timeout(Duration::from_millis(2000))
.build()
.expect("could not build a client for https requests");
let response = client.get(url.clone()).send();
if response.is_ok() { if response.is_ok() {
self.reachable += 1 self.reachable += 1
} }
@ -117,9 +127,10 @@ impl UptimeStatus {
return; return;
} }
let ratio: f32 = (self.reachable as f32) / (self.urls.len() as f32) * 100f32; let ratio: f32 = (self.reachable as f32) / (self.urls.len() as f32) * 100f32;
debug!("calculated success_ratio: {}", ratio); trace!("calculated success_ratio: {}", ratio);
self.success_ratio = ratio.floor() as u8; self.success_ratio = ratio.floor() as u8;
self.success = self.success_ratio >= self.success_ratio_target; self.success = self.success_ratio >= self.success_ratio_target;
trace!("calculated success as: {}", self.success)
} }
} }
@ -160,5 +171,103 @@ impl fmt::Display for UptimeStatus {
} }
//// PUBLIC FUNCTIONS ////////////////////////////////////////////////////////////////////////////// //// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
/// ## Uptime monitor
///
/// This function continuously monitors the uptime of your host/network
pub fn continuous_uptime_monitor(success_ratio_target: u8, urls: Vec<String>, interval: u64) {
if urls.len() == 0 {
error!("No URLs provided. There is nothing to monitor.");
return;
}
let interval = std::time::Duration::from_millis(interval);
let mut last_downtime: Option<SystemTime> = None;
let mut last_uptime: Option<SystemTime> = None;
let mut status = UptimeStatus::new(success_ratio_target, &urls);
let mut last_was_up: bool = false;
let mut last_ratio: u8 = status.success_ratio;
loop {
if !status.success {
if last_was_up {
display_uptime_status("fail", last_uptime, last_downtime, &status)
}
last_downtime = Some(SystemTime::now());
last_was_up = false;
} else if status.success_ratio < 100 {
if status.success_ratio != last_ratio {
let msg = format!(
"uptime check: not all urls are reachable ({}%)",
status.success_ratio
);
display_uptime_status(&msg, last_uptime, last_downtime, &status)
}
last_uptime = Some(SystemTime::now());
last_was_up = true;
} else {
if !last_was_up {
display_uptime_status("success", last_uptime, last_downtime, &status)
}
last_uptime = Some(SystemTime::now());
last_was_up = true;
}
last_ratio = status.success_ratio;
std::thread::sleep(interval);
status.check();
}
}
//// PRIVATE FUNCTIONS ///////////////////////////////////////////////////////////////////////////// //// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////
fn display_uptime_status(
msg: &str,
last_uptime: Option<SystemTime>,
last_downtime: Option<SystemTime>,
status: &UptimeStatus,
) {
// I know it's weird that this has two spaces too much, but somehow just the tabs is missing
// two spaces.
info!("uptime check: {}", msg);
info!(
"last uptime: {}",
match_format_time(last_uptime)
);
info!(
"last downtime: {}",
match_format_time(last_downtime)
);
info!(
"since downtime: {}",
match_format_duration_since(last_downtime)
);
info!(
"since uptime: {}",
match_format_duration_since(last_uptime)
);
debug!("\n{}", status);
info!("{}", divider!());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Returns "None" if the given [Option] is [None](Option::None). Otherwise, returns the time stamp
/// formatted according to rfc3999.
fn match_format_time(time: Option<SystemTime>) -> String {
match time {
Some(time) => format_rfc3339(time).to_string(),
None => String::from("None"),
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Returns "None" if the given [Option] is [None](Option::None). Otherwise, returns duration since
/// that time in a human readable format.
fn match_format_duration_since(time: Option<SystemTime>) -> String {
match time {
Some(time) => format_duration(
SystemTime::now()
.duration_since(time)
.expect("could not calculate elapsed time"),
)
.to_string(),
None => String::from("None"),
}
}