2023-09-20 20:14:10 +02:00
|
|
|
//! # A specialized Logger for [`pt`](../libpt/index.html)
|
2023-09-15 17:15:09 +02:00
|
|
|
//!
|
2023-09-20 20:35:07 +02:00
|
|
|
//! This crate is part of [`pt`](../libpt/index.html), but can also be used as a standalone
|
|
|
|
//! module.
|
|
|
|
//!
|
2023-09-20 20:14:10 +02:00
|
|
|
//! For the library version, only the basic [`tracing`] is used, so that it is possible for
|
|
|
|
//! the end user to use the [`tracing`] frontend they desire.
|
2023-09-15 17:15:09 +02:00
|
|
|
//!
|
|
|
|
//! I did however decide to create a [`Logger`] struct. This struct is mainly intended to be used
|
2023-09-20 20:14:10 +02:00
|
|
|
//! with the python module of [`pt`](../libpt/index.html), but is still just as usable in other contexts.
|
2023-09-15 17:15:09 +02:00
|
|
|
//!
|
|
|
|
//! ## Technologies used for logging:
|
2023-09-20 20:14:10 +02:00
|
|
|
//! - [`tracing`]: base logging crate
|
|
|
|
//! - [`tracing_appender`]: Used to log to files
|
|
|
|
//! - [`tracing_subscriber`]: Used to do actual logging, formatting, to stdout
|
2023-09-15 17:15:09 +02:00
|
|
|
|
|
|
|
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
use std::{
|
|
|
|
fmt,
|
|
|
|
path::PathBuf,
|
|
|
|
sync::atomic::{AtomicBool, Ordering},
|
|
|
|
};
|
|
|
|
|
|
|
|
pub mod error;
|
|
|
|
use error::*;
|
|
|
|
|
|
|
|
pub use tracing::{debug, error, info, trace, warn, Level};
|
|
|
|
use tracing_appender;
|
|
|
|
use tracing_subscriber::{prelude::*, fmt::format::FmtSpan};
|
2023-09-20 14:32:25 +02:00
|
|
|
|
|
|
|
use pyo3::prelude::*;
|
2023-09-15 17:15:09 +02:00
|
|
|
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// The log level used when none is specified
|
|
|
|
pub const DEFAULT_LOG_LEVEL: Level = Level::INFO;
|
|
|
|
/// The path where logs are stored when no path is given.
|
|
|
|
///
|
|
|
|
/// Currently, this is `/dev/null`, meaning they will be written to the void = discarded.
|
|
|
|
pub const DEFAULT_LOG_DIR: &'static str = "/dev/null";
|
|
|
|
|
|
|
|
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static INITIALIZED: AtomicBool = AtomicBool::new(false);
|
|
|
|
|
|
|
|
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
|
2023-09-20 20:14:10 +02:00
|
|
|
/// ## Logger for [`pt`](../libpt/index.html)
|
2023-09-15 17:15:09 +02:00
|
|
|
///
|
|
|
|
/// This struct exists mainly for the python module, so that we can use the same logger with both
|
|
|
|
/// python and rust.
|
2023-09-20 14:32:25 +02:00
|
|
|
#[pyclass]
|
2023-09-15 17:15:09 +02:00
|
|
|
pub struct Logger {}
|
|
|
|
|
|
|
|
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// ## Main implementation
|
|
|
|
impl Logger {
|
|
|
|
/// ## create a `Logger`
|
|
|
|
///
|
|
|
|
/// Creates a new uninitialized [`Logger`] object.
|
|
|
|
pub fn new() -> Self {
|
|
|
|
let l = Logger {};
|
|
|
|
l
|
|
|
|
}
|
|
|
|
/// ## initializes the logger
|
|
|
|
///
|
|
|
|
/// Will enable the logger to be used.
|
|
|
|
///
|
2023-09-20 20:14:10 +02:00
|
|
|
/// Assumes some defaults, use [`init_customized`](Self::init_customized) for more control
|
2023-09-15 17:15:09 +02:00
|
|
|
pub fn init(log_dir: Option<PathBuf>, max_level: Option<Level>) -> Result<()> {
|
|
|
|
Self::init_customized(
|
|
|
|
log_dir.is_some(),
|
|
|
|
log_dir.unwrap_or(PathBuf::from(DEFAULT_LOG_DIR)),
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
max_level.unwrap_or(DEFAULT_LOG_LEVEL),
|
|
|
|
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 Err(Error::Usage(format!("logging is already initialized")));
|
|
|
|
} else {
|
2023-09-20 18:15:53 +02:00
|
|
|
let filter = tracing_subscriber::filter::FilterFn::new(|_metadata| {
|
2023-09-15 17:15:09 +02:00
|
|
|
// let mut filter = false;
|
|
|
|
//
|
|
|
|
// // if it's this lib, continue
|
|
|
|
// filter |= metadata.target().contains(env!("CARGO_PKG_NAME"));
|
|
|
|
// filter |= metadata.target().contains("pt");
|
|
|
|
//
|
|
|
|
// // if it's another crate, only show above debug
|
|
|
|
// filter |= metadata.level() > &Level::DEBUG;
|
|
|
|
//
|
|
|
|
// filter
|
|
|
|
// FIXME: Make the filter customizable with sane defaults. Don't block the
|
|
|
|
// executing crate.
|
|
|
|
true
|
|
|
|
});
|
|
|
|
|
|
|
|
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)
|
|
|
|
.with_span_events(FmtSpan::FULL)
|
|
|
|
//.pretty // too verbose and over multiple lines, a bit like python tracebacks
|
|
|
|
.finish()
|
|
|
|
// add layers
|
|
|
|
.with(filter);
|
|
|
|
|
|
|
|
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(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-20 20:14:10 +02:00
|
|
|
/// ## logging at [`Level::ERROR`]
|
2023-09-15 17:15:09 +02:00
|
|
|
pub fn error<T>(&self, printable: T)
|
|
|
|
where
|
|
|
|
T: fmt::Display,
|
|
|
|
{
|
|
|
|
error!("{}", printable)
|
|
|
|
}
|
2023-09-20 20:14:10 +02:00
|
|
|
/// ## logging at [`Level::WARN`]
|
2023-09-15 17:15:09 +02:00
|
|
|
pub fn warn<T>(&self, printable: T)
|
|
|
|
where
|
|
|
|
T: fmt::Display,
|
|
|
|
{
|
|
|
|
warn!("{}", printable)
|
|
|
|
}
|
2023-09-20 20:14:10 +02:00
|
|
|
/// ## logging at [`Level::INFO`]
|
2023-09-15 17:15:09 +02:00
|
|
|
pub fn info<T>(&self, printable: T)
|
|
|
|
where
|
|
|
|
T: fmt::Display,
|
|
|
|
{
|
|
|
|
info!("{}", printable)
|
|
|
|
}
|
2023-09-20 20:14:10 +02:00
|
|
|
/// ## logging at [`Level::DEBUG`]
|
2023-09-15 17:15:09 +02:00
|
|
|
pub fn debug<T>(&self, printable: T)
|
|
|
|
where
|
|
|
|
T: fmt::Display,
|
|
|
|
{
|
|
|
|
debug!("{}", printable)
|
|
|
|
}
|
2023-09-20 20:14:10 +02:00
|
|
|
/// ## logging at [`Level::TRACE`]
|
2023-09-15 17:15:09 +02:00
|
|
|
pub fn trace<T>(&self, printable: T)
|
|
|
|
where
|
|
|
|
T: fmt::Display,
|
|
|
|
{
|
|
|
|
trace!("{}", printable)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-20 14:32:25 +02:00
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// ## Implementation of the python interface
|
|
|
|
#[pymethods]
|
|
|
|
impl Logger {
|
|
|
|
/// ## Python version of [`new()`](Logger::new)
|
|
|
|
#[new]
|
|
|
|
pub fn py_new() -> PyResult<Self> {
|
|
|
|
Ok(Logger::new())
|
|
|
|
}
|
|
|
|
/// ## Python version of [`init()`](Logger::init)
|
|
|
|
#[pyo3(name = "init")]
|
|
|
|
#[staticmethod]
|
|
|
|
pub fn py_init(log_dir: Option<PathBuf>, max_level: Option<String>) -> Result<()> {
|
|
|
|
Self::init(
|
|
|
|
log_dir,
|
|
|
|
match max_level {
|
|
|
|
Some(s) => match s.to_uppercase().as_str() {
|
|
|
|
"TRACE" => Some(tracing::Level::TRACE),
|
|
|
|
"DEBUG" => Some(tracing::Level::DEBUG),
|
|
|
|
"INFO" => Some(tracing::Level::INFO),
|
|
|
|
"WARN" => Some(tracing::Level::WARN),
|
|
|
|
"ERROR" => Some(tracing::Level::ERROR),
|
|
|
|
_ => return Err(Error::Usage(format!("'{s}' is not a valid log level"))),
|
|
|
|
},
|
|
|
|
None => None,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
/// ## Python version of [`error()`](Logger::error)
|
|
|
|
#[pyo3(name = "error")]
|
|
|
|
pub fn py_error(&self, printable: String) {
|
|
|
|
self.error(printable)
|
|
|
|
}
|
|
|
|
/// ## Python version of [`warn()`](Logger::warn)
|
|
|
|
#[pyo3(name = "warn")]
|
|
|
|
pub fn py_warn(&self, printable: String) {
|
|
|
|
self.warn(printable)
|
|
|
|
}
|
|
|
|
/// ## Python version of [`info()`](Logger::info)
|
|
|
|
#[pyo3(name = "info")]
|
|
|
|
pub fn py_info(&self, printable: String) {
|
|
|
|
self.info(printable)
|
|
|
|
}
|
|
|
|
/// ## Python version of [`debug()`](Logger::debug)
|
|
|
|
#[pyo3(name = "debug")]
|
|
|
|
pub fn py_debug(&self, printable: String) {
|
|
|
|
self.debug(printable)
|
|
|
|
}
|
|
|
|
/// ## Python version of [`trace()`](Logger::trace)
|
|
|
|
#[pyo3(name = "trace")]
|
|
|
|
pub fn py_trace(&self, printable: String) {
|
|
|
|
self.trace(printable)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-15 17:15:09 +02:00
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
impl fmt::Debug for Logger {
|
2023-09-20 20:14:10 +02:00
|
|
|
/// ## DEBUG representation for [`Logger`]
|
2023-09-15 17:15:09 +02:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Logger: {{initialized: {}}} ",
|
|
|
|
INITIALIZED.load(Ordering::Relaxed)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////
|
2023-09-20 14:32:25 +02:00
|
|
|
|