switched to tracing for logging

This commit is contained in:
Christoph J. Scherr 2023-08-01 16:04:06 +02:00
parent 5fa5cfe90a
commit 3316da93a6
Signed by: cscherrNT
GPG Key ID: 8E2B45BC51A27EA7
5 changed files with 253 additions and 96 deletions

View File

@ -13,14 +13,14 @@
// enable clippy's extra lints, the pedantic version // enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)] #![warn(clippy::pedantic)]
use std::path::PathBuf;
//// IMPORTS /////////////////////////////////////////////////////////////////////////////////////// //// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use pt::networking::monitoring::uptime; use pt::{logger, networking::monitoring::uptime};
// we want the log macros in any case // we want the log macros in any case
#[allow(unused_imports)] #[allow(unused_imports)]
use log::{debug, error, info, trace, warn}; use tracing::{debug, error, info, trace, warn};
use env_logger;
use clap::Parser; use clap::Parser;
@ -28,6 +28,8 @@ pub mod args;
use args::*; use args::*;
//// CONSTANTS ///////////////////////////////////////////////////////////////////////////////////// //// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
const EXIT_SUCCESS: i32 = 0;
const EXIT_FAILURE_USAGE: i32 = 1;
//// STATICS /////////////////////////////////////////////////////////////////////////////////////// //// STATICS ///////////////////////////////////////////////////////////////////////////////////////
@ -43,20 +45,46 @@ use args::*;
/// ## Main function of the [`pt`](crate) binary /// ## Main function of the [`pt`](crate) binary
pub fn main() { pub fn main() {
let cli = Cli::parse(); let cli = Cli::parse();
let ll: tracing::Level = match cli.verbose.log_level().unwrap().as_str() {
"TRACE" => tracing::Level::TRACE,
"DEBUG" => tracing::Level::DEBUG,
"INFO" => tracing::Level::INFO,
"WARN" => tracing::Level::WARN,
"ERROR" => tracing::Level::ERROR,
_ => {
eprintln!("'{}' is not a valid loglevel", cli.verbose.to_string());
std::process::exit(EXIT_FAILURE_USAGE);
}
};
if cli.log_meta { if cli.log_meta {
// set up our logger to use the given verbosity logger::Logger::init_customized(
env_logger::Builder::new() false,
.filter_module("pt", cli.verbose.log_level_filter()) PathBuf::from("/dev/null"),
.init(); true,
} false,
else { true,
// set up our logger to use the given verbosity true,
env_logger::Builder::new() ll,
.filter_module("pt", cli.verbose.log_level_filter()) false,
.format_level(false) false,
.format_target(false) false,
.format_timestamp(None) )
.init(); .expect("could not initialize Logger");
} else {
// less verbose version
logger::Logger::init_customized(
false,
PathBuf::from("/dev/null"),
true,
false,
true,
false,
ll,
false,
false,
false,
)
.expect("could not initialize Logger");
} }
trace!("started the main function"); trace!("started the main function");
@ -76,13 +104,12 @@ pub fn net(cli: &Cli, command: NetCommands) {
success_ratio, success_ratio,
extra_urls, extra_urls,
no_default, no_default,
timeout timeout,
} => { } => {
let urls: Vec<String>; let urls: Vec<String>;
if no_default { if no_default {
urls = extra_urls; urls = extra_urls;
} } else {
else {
let mut combined: Vec<String> = Vec::new(); let mut combined: Vec<String> = Vec::new();
for i in uptime::DEFAULT_CHECK_URLS { for i in uptime::DEFAULT_CHECK_URLS {
combined.push(i.to_string()); combined.push(i.to_string());
@ -94,11 +121,13 @@ pub fn net(cli: &Cli, command: NetCommands) {
if repeat > 0 { if repeat > 0 {
uptime::continuous_uptime_monitor(success_ratio, urls, repeat * 1000, timeout); uptime::continuous_uptime_monitor(success_ratio, urls, repeat * 1000, timeout);
} else { } else {
let status = uptime::UptimeStatus::new(success_ratio, urls, timeout); let status = uptime::UptimeStatus::new(success_ratio, urls, timeout);
println!("{}", status); info!("status:\n{}", status);
} }
} }
NetCommands::Discover {} => {todo!()} NetCommands::Discover {} => {
todo!()
}
} }
} }

98
src/logger/error.rs Normal file
View File

@ -0,0 +1,98 @@
//! # very short description
//!
//! Short description
//!
//! Details
//!
//! ## Section 1
//!
//! ## Section 2
//// 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 ///////////////////////////////////////////////////////////////////////////////////////
use pyo3::{exceptions::PyException, PyErr};
use tracing::subscriber::SetGlobalDefaultError;
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
/// a quick alias for a result with a [`LoggerError`]
pub type Result<T> = std::result::Result<T, Error>;
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
/// ## Errors for the [logger](crate::logger)
pub enum Error {
/// Bad IO operation
IO(std::io::Error),
/// Various errors raised when the messenger is used in a wrong way
Usage(String),
SetGlobalDefaultFail(SetGlobalDefaultError),
}
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Error::IO(value)
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl From<SetGlobalDefaultError> for Error {
fn from(value: SetGlobalDefaultError) -> Self {
Error::SetGlobalDefaultFail(value)
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl Into<PyErr> for Error {
fn into(self) -> PyErr {
match self {
Error::IO(err) => PyException::new_err(format!("LoggerError: IO {err:?}")),
Error::Usage(err) => PyException::new_err(format!("LoggerError: Usage {err}")),
Error::SetGlobalDefaultFail(err) => {
PyException::new_err(format!("LoggerError: SetGlobalDefaultFail {err}"))
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::IO(e) => write!(f, "<IO Error {e:?}>"),
Error::Usage(e) => write!(f, "<Usage Error {e:?}>"),
Error::SetGlobalDefaultFail(e) => write!(f, "<SetGlobalDefaultFail {e:?}>"),
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::IO(e) => write!(f, "IO Error {e}"),
Error::Usage(e) => write!(f, "Usage Error {e}"),
Error::SetGlobalDefaultFail(e) => write!(f, "SetGlobalDefaultFail {e}"),
}
}
}
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////

View File

@ -15,18 +15,21 @@
//// IMPORTS /////////////////////////////////////////////////////////////////////////////////////// //// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use std::{ use std::{
fmt, fmt,
path::PathBuf,
sync::atomic::{AtomicBool, Ordering}, sync::atomic::{AtomicBool, Ordering},
}; };
use env_logger::{Env, Target, WriteStyle}; pub mod error;
use log::{debug, error, info, trace, warn, Level}; use error::*;
pub use tracing::{debug, error, info, trace, warn, Level};
use tracing_appender;
use tracing_subscriber::prelude::*;
use pyo3::prelude::*; use pyo3::prelude::*;
//// CONSTANTS ///////////////////////////////////////////////////////////////////////////////////// //// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
/// The log level used when none is specified /// The log level used when none is specified
pub const DEFAULT_LOG_LEVEL: Level = Level::Info; pub const DEFAULT_LOG_LEVEL: Level = Level::INFO;
/// Register your level to this environment variable to override the used level
pub const LOGGER_ENV_KEY: &'static str = "LIBPT_LOGLEVEL";
//// STATICS /////////////////////////////////////////////////////////////////////////////////////// //// STATICS ///////////////////////////////////////////////////////////////////////////////////////
static INITIALIZED: AtomicBool = AtomicBool::new(false); static INITIALIZED: AtomicBool = AtomicBool::new(false);
@ -63,50 +66,68 @@ impl Logger {
/// ## initializes the logger /// ## initializes the logger
/// ///
/// Will enable the logger to be used. /// Will enable the logger to be used.
pub fn init() { ///
/// Assumes some defaults, use [`init_customized`](init_customized) for more control
pub fn init() -> Result<()> {
Self::init_customized(
false,
PathBuf::from("/dev/null"),
true,
false,
true,
false,
Level::INFO,
false,
false,
false,
)
}
/// ## initializes the logger
///
/// Will enable the logger to be used.
pub fn init_customized(
log_to_file: bool,
log_dir: PathBuf,
ansi: bool,
display_filename: bool,
display_level: bool,
display_target: bool,
max_level: Level,
display_thread_ids: bool,
display_thread_names: bool,
display_line_number: bool,
) -> Result<()> {
// only init if no init has been performed yet // only init if no init has been performed yet
if INITIALIZED.load(Ordering::Relaxed) { if INITIALIZED.load(Ordering::Relaxed) {
warn!("trying to reinitialize the logger, ignoring"); warn!("trying to reinitialize the logger, ignoring");
return; return Err(Error::Usage(format!("logging is already initialized")));
} else { } else {
let env = Env::default().filter_or(LOGGER_ENV_KEY, DEFAULT_LOG_LEVEL.to_string()); let basic_subscriber = tracing_subscriber::fmt::Subscriber::builder()
let res = env_logger::Builder::from_env(env) // subscriber configuration
.try_init(); .with_ansi(ansi)
if res.is_err() { .with_file(display_filename)
eprintln!("could not init logger: {}", res.unwrap_err()); .with_level(display_level)
} .with_target(display_target)
INITIALIZED.store(true, Ordering::Relaxed); .with_max_level(max_level)
} .with_thread_ids(display_thread_ids)
} .with_line_number(display_line_number)
.with_thread_names(display_thread_names)
//.pretty // too verbose and over multiple lines, a bit like python tracebacks
.finish();
/// ## initializes the logger to log to a target if log_to_file {
/// let file_appender = tracing_appender::rolling::daily(log_dir, "log");
/// Will enable the logger to be used. let (file_writer, _guard) = tracing_appender::non_blocking(file_appender);
pub fn init_specialized(show_module: bool, test: bool, color: bool, target: Option<Target>) { let layered_subscriber = basic_subscriber
let target = match target { .with(tracing_subscriber::fmt::Layer::default().with_writer(file_writer));
Some(t) => t, tracing::subscriber::set_global_default(layered_subscriber)?;
None => Target::Stdout, } else {
}; tracing::subscriber::set_global_default(basic_subscriber)?;
// only init if no init has been performed yet
if INITIALIZED.load(Ordering::Relaxed) {
eprintln!("trying to reinitialize the logger, ignoring");
return;
} else {
let env = Env::default().filter_or(LOGGER_ENV_KEY, DEFAULT_LOG_LEVEL.to_string());
let res = env_logger::Builder::from_env(env)
.is_test(test)
.target(target)
.write_style(if color {
WriteStyle::Auto
} else {
WriteStyle::Never
})
.format_target(show_module)
.try_init();
if res.is_err() {
eprintln!("could not init logger: {}", res.unwrap_err());
} }
INITIALIZED.store(true, Ordering::Relaxed); INITIALIZED.store(true, Ordering::Relaxed);
Ok(())
} }
} }
@ -159,14 +180,8 @@ impl Logger {
/// ## Python version of [`init()`](Logger::init) /// ## Python version of [`init()`](Logger::init)
#[pyo3(name = "init")] #[pyo3(name = "init")]
#[staticmethod] #[staticmethod]
pub fn py_init() { pub fn py_init() -> Result<()> {
Self::init_specialized(false, false, true, None) Self::init()
}
/// ## Python version of [`init_specialized()`](Logger::init_specialized)
#[pyo3(name = "init_specialized")]
#[staticmethod]
pub fn py_init_specialized(color: bool) {
Self::init_specialized(false, false, color, None)
} }
/// ## Python version of [`error()`](Logger::error) /// ## Python version of [`error()`](Logger::error)
#[pyo3(name = "error")] #[pyo3(name = "error")]

View File

@ -21,7 +21,7 @@ use std::{fmt, time::Duration};
//// IMPORTS /////////////////////////////////////////////////////////////////////////////////////// //// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
// we want the log macros in any case // we want the log macros in any case
#[allow(unused_imports)] #[allow(unused_imports)]
use log::{debug, error, info, trace, warn}; use tracing;
use reqwest; use reqwest;
@ -142,10 +142,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;
trace!("calculated success_ratio: {}", ratio); tracing::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) tracing::trace!("calculated success as: {}", self.success)
} }
} }
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
@ -218,7 +218,7 @@ pub fn continuous_uptime_monitor(
timeout: u64, timeout: u64,
) { ) {
if urls.len() == 0 { if urls.len() == 0 {
error!("No URLs provided. There is nothing to monitor."); tracing::error!("No URLs provided. There is nothing to monitor.");
return; return;
} }
@ -293,19 +293,19 @@ fn display_uptime_status(
) { ) {
// I know it's weird that this has two spaces too much, but somehow just the tabs is missing // I know it's weird that this has two spaces too much, but somehow just the tabs is missing
// two spaces. // two spaces.
info!("uptime check: {}", msg); tracing::info!("uptime check: {}", msg);
info!("last uptime: {}", match_format_time(last_uptime)); tracing::info!("last uptime: {}", match_format_time(last_uptime));
info!("last downtime: {}", match_format_time(last_downtime)); tracing::info!("last downtime: {}", match_format_time(last_downtime));
info!( tracing::info!(
"since downtime: {}", "since downtime: {}",
match_format_duration_since(last_downtime) match_format_duration_since(last_downtime)
); );
info!( tracing::info!(
"since uptime: {}", "since uptime: {}",
match_format_duration_since(last_uptime) match_format_duration_since(last_uptime)
); );
debug!("\n{}", status); tracing::debug!("\n{}", status);
info!("{}", divider!()); tracing::info!("{}", divider!());
} }
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -2,20 +2,36 @@
//! //!
//! Note: the module uses a global variable to store if the thread has //! Note: the module uses a global variable to store if the thread has
//// IMPORTS /////////////////////////////////////////////////////////////////////////////////////// //// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use pt::common::macros::get_stdout_for;
/// ## Tests for basic logging functionality /// ## Tests for basic logging functionality
use pt::logger::*; use pt::logger::*;
use pt::common::macros::get_stdout_for;
use regex::Regex; use regex::Regex;
use std::sync::Once;
//// HELPERS /////////////////////////////////////////////////////////////////////////////////////// //// HELPERS ///////////////////////////////////////////////////////////////////////////////////////
static SETUP: Once = Once::new();
// only initialize once // only initialize once
/// ## setup that's needed before testing the logger struct /// ## setup that's needed before testing the logger struct
fn setup() { fn setup() {
// we don't want to log messages during our tests! SETUP.call_once(|| {
std::env::set_var(LOGGER_ENV_KEY, "Trace"); // we don't want to log messages during our tests!
Logger::init_specialized(true, false, false, None); Logger::init_customized(
println!() false,
std::path::PathBuf::from("/dev/null"),
false,
false,
true,
false,
tracing::Level::TRACE,
false,
false,
false,
)
.expect("could not initialize Logger");
println!()
});
} }
//// IMPLEMENTATION //////////////////////////////////////////////////////////////////////////////// //// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
@ -51,8 +67,7 @@ fn test_log_basic() {
// this matches the format of the env_logger perfectly, but make sure that color is off, // this matches the format of the env_logger perfectly, but make sure that color is off,
// else the ANSI escape sequences break this test // else the ANSI escape sequences break this test
let regex = Regex::new(concat!( let regex = Regex::new(concat!(
r"(?m)\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z ", r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}Z\s+(TRACE|DEBUG|INFO|WARN|ERROR)\sMSG"
r"(TRACE|DEBUG|INFO|WARN|ERROR) +pt::logger\] MSG"
)) ))
.unwrap(); .unwrap();
@ -65,9 +80,9 @@ fn test_multi_initialize() {
setup(); setup();
let l = Logger::new(); let l = Logger::new();
// these should be ignored due to the global flag // these should be ignored due to the global flag
Logger::init(); Logger::init().unwrap_err();
Logger::init(); Logger::init().unwrap_err();
Logger::init(); Logger::init().unwrap_err();
Logger::init(); Logger::init().unwrap_err();
l.info("Successfully ignored extra init"); l.info("Successfully ignored extra init");
} }