generated from PlexSheep/baserepo
monitor uptime completed
This commit is contained in:
parent
791b04f71f
commit
b8ab5eaac3
|
@ -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"
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 /////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -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 /////////////////////////////////////////////////////////////////////////////
|
|
@ -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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Reference in New Issue