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
#![warn(clippy::pedantic)]
use std::path::PathBuf;
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use pt::networking::monitoring::uptime;
use pt::{logger, networking::monitoring::uptime};
// we want the log macros in any case
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use env_logger;
use tracing::{debug, error, info, trace, warn};
use clap::Parser;
@ -28,6 +28,8 @@ pub mod args;
use args::*;
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
const EXIT_SUCCESS: i32 = 0;
const EXIT_FAILURE_USAGE: i32 = 1;
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
@ -43,20 +45,46 @@ use args::*;
/// ## Main function of the [`pt`](crate) binary
pub fn main() {
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 {
// set up our logger to use the given verbosity
env_logger::Builder::new()
.filter_module("pt", cli.verbose.log_level_filter())
.init();
}
else {
// set up our logger to use the given verbosity
env_logger::Builder::new()
.filter_module("pt", cli.verbose.log_level_filter())
.format_level(false)
.format_target(false)
.format_timestamp(None)
.init();
logger::Logger::init_customized(
false,
PathBuf::from("/dev/null"),
true,
false,
true,
true,
ll,
false,
false,
false,
)
.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");
@ -76,13 +104,12 @@ pub fn net(cli: &Cli, command: NetCommands) {
success_ratio,
extra_urls,
no_default,
timeout
timeout,
} => {
let urls: Vec<String>;
if no_default {
urls = extra_urls;
}
else {
} else {
let mut combined: Vec<String> = Vec::new();
for i in uptime::DEFAULT_CHECK_URLS {
combined.push(i.to_string());
@ -94,11 +121,13 @@ pub fn net(cli: &Cli, command: NetCommands) {
if repeat > 0 {
uptime::continuous_uptime_monitor(success_ratio, urls, repeat * 1000, timeout);
} else {
let status = uptime::UptimeStatus::new(success_ratio, urls, timeout);
println!("{}", status);
let status = uptime::UptimeStatus::new(success_ratio, urls, timeout);
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 ///////////////////////////////////////////////////////////////////////////////////////
use std::{
fmt,
path::PathBuf,
sync::atomic::{AtomicBool, Ordering},
};
use env_logger::{Env, Target, WriteStyle};
use log::{debug, error, info, trace, warn, Level};
pub mod error;
use error::*;
pub use tracing::{debug, error, info, trace, warn, Level};
use tracing_appender;
use tracing_subscriber::prelude::*;
use pyo3::prelude::*;
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
/// The log level used when none is specified
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";
pub const DEFAULT_LOG_LEVEL: Level = Level::INFO;
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
static INITIALIZED: AtomicBool = AtomicBool::new(false);
@ -63,50 +66,68 @@ impl Logger {
/// ## initializes the logger
///
/// 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
if INITIALIZED.load(Ordering::Relaxed) {
warn!("trying to reinitialize the logger, ignoring");
return;
return Err(Error::Usage(format!("logging is already initialized")));
} else {
let env = Env::default().filter_or(LOGGER_ENV_KEY, DEFAULT_LOG_LEVEL.to_string());
let res = env_logger::Builder::from_env(env)
.try_init();
if res.is_err() {
eprintln!("could not init logger: {}", res.unwrap_err());
}
INITIALIZED.store(true, Ordering::Relaxed);
}
}
let basic_subscriber = tracing_subscriber::fmt::Subscriber::builder()
// subscriber configuration
.with_ansi(ansi)
.with_file(display_filename)
.with_level(display_level)
.with_target(display_target)
.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
///
/// Will enable the logger to be used.
pub fn init_specialized(show_module: bool, test: bool, color: bool, target: Option<Target>) {
let target = match target {
Some(t) => t,
None => Target::Stdout,
};
// 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());
if log_to_file {
let file_appender = tracing_appender::rolling::daily(log_dir, "log");
let (file_writer, _guard) = tracing_appender::non_blocking(file_appender);
let layered_subscriber = basic_subscriber
.with(tracing_subscriber::fmt::Layer::default().with_writer(file_writer));
tracing::subscriber::set_global_default(layered_subscriber)?;
} else {
tracing::subscriber::set_global_default(basic_subscriber)?;
}
INITIALIZED.store(true, Ordering::Relaxed);
Ok(())
}
}
@ -159,14 +180,8 @@ impl Logger {
/// ## Python version of [`init()`](Logger::init)
#[pyo3(name = "init")]
#[staticmethod]
pub fn py_init() {
Self::init_specialized(false, false, true, None)
}
/// ## 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)
pub fn py_init() -> Result<()> {
Self::init()
}
/// ## Python version of [`error()`](Logger::error)
#[pyo3(name = "error")]

View File

@ -21,7 +21,7 @@ use std::{fmt, time::Duration};
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
// we want the log macros in any case
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use tracing;
use reqwest;
@ -142,10 +142,10 @@ impl UptimeStatus {
return;
}
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 = 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,
) {
if urls.len() == 0 {
error!("No URLs provided. There is nothing to monitor.");
tracing::error!("No URLs provided. There is nothing to monitor.");
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
// two spaces.
info!("uptime check: {}", msg);
info!("last uptime: {}", match_format_time(last_uptime));
info!("last downtime: {}", match_format_time(last_downtime));
info!(
tracing::info!("uptime check: {}", msg);
tracing::info!("last uptime: {}", match_format_time(last_uptime));
tracing::info!("last downtime: {}", match_format_time(last_downtime));
tracing::info!(
"since downtime: {}",
match_format_duration_since(last_downtime)
);
info!(
tracing::info!(
"since uptime: {}",
match_format_duration_since(last_uptime)
);
debug!("\n{}", status);
info!("{}", divider!());
tracing::debug!("\n{}", status);
tracing::info!("{}", divider!());
}
////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -1,21 +1,37 @@
//! # Tests for pt::logger::Logger
//!
//! 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 ///////////////////////////////////////////////////////////////////////////////////////
use pt::common::macros::get_stdout_for;
/// ## Tests for basic logging functionality
use pt::logger::*;
use pt::common::macros::get_stdout_for;
use regex::Regex;
use std::sync::Once;
//// HELPERS ///////////////////////////////////////////////////////////////////////////////////////
static SETUP: Once = Once::new();
// only initialize once
/// ## setup that's needed before testing the logger struct
fn setup() {
// we don't want to log messages during our tests!
std::env::set_var(LOGGER_ENV_KEY, "Trace");
Logger::init_specialized(true, false, false, None);
println!()
SETUP.call_once(|| {
// we don't want to log messages during our tests!
Logger::init_customized(
false,
std::path::PathBuf::from("/dev/null"),
false,
false,
true,
false,
tracing::Level::TRACE,
false,
false,
false,
)
.expect("could not initialize Logger");
println!()
});
}
//// 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,
// else the ANSI escape sequences break this test
let regex = Regex::new(concat!(
r"(?m)\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z ",
r"(TRACE|DEBUG|INFO|WARN|ERROR) +pt::logger\] MSG"
r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}Z\s+(TRACE|DEBUG|INFO|WARN|ERROR)\sMSG"
))
.unwrap();
@ -65,9 +80,9 @@ fn test_multi_initialize() {
setup();
let l = Logger::new();
// these should be ignored due to the global flag
Logger::init();
Logger::init();
Logger::init();
Logger::init();
Logger::init().unwrap_err();
Logger::init().unwrap_err();
Logger::init().unwrap_err();
Logger::init().unwrap_err();
l.info("Successfully ignored extra init");
}