This repository has been archived on 2024-10-16. You can view files and clone it, but cannot push or open issues or pull requests.
pt/members/libpt-log/src/lib.rs

284 lines
10 KiB
Rust
Raw Normal View History

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,
2024-01-16 16:12:03 +01:00
ops::Deref,
2023-09-15 17:15:09 +02:00
path::PathBuf,
2024-01-16 16:12:03 +01:00
sync::atomic::{AtomicBool, Ordering},
2023-09-15 17:15:09 +02:00
};
pub mod error;
use error::*;
pub use tracing::{debug, error, info, trace, warn, Level};
2024-01-16 14:04:21 +01:00
use tracing_appender::{
self,
non_blocking::{NonBlocking, WorkerGuard},
};
use tracing_subscriber::{
2024-01-16 16:12:03 +01:00
fmt::{
format::FmtSpan,
time::{self, FormatTime},
},
2024-01-16 14:04:21 +01:00
prelude::*,
};
2023-09-20 14:32:25 +02:00
2024-01-16 10:30:55 +01:00
use anyhow::{bail, Result};
2024-01-16 10:18:29 +01:00
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.
2024-01-16 10:18:29 +01:00
pub struct Logger;
2023-09-15 17:15:09 +02:00
//// 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,
2024-01-16 14:04:21 +01:00
false,
true,
false,
)
}
/// ## initializes the logger
///
/// Will enable the logger to be used. This is a version that shows less information,
/// useful in cases with only one sender to the logging framework.
///
/// Assumes some defaults, use [`init_customized`](Self::init_customized) for more control
pub fn init_mini(max_level: Option<Level>) -> Result<()> {
Self::init_customized(
false,
PathBuf::from(DEFAULT_LOG_DIR),
true,
false,
true,
false,
max_level.unwrap_or(DEFAULT_LOG_LEVEL),
false,
false,
false,
false,
false,
false,
2023-09-15 17:15:09 +02:00
)
}
/// ## 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,
2024-01-16 14:04:21 +01:00
pretty: bool,
show_time: bool,
uptime: bool, // uptime instead of system time
2023-09-15 17:15:09 +02:00
) -> Result<()> {
// only init if no init has been performed yet
if INITIALIZED.load(Ordering::Relaxed) {
warn!("trying to reinitialize the logger, ignoring");
2024-01-16 10:18:29 +01:00
bail!(Error::Usage(format!("logging is already initialized")));
2024-01-16 14:04:21 +01:00
}
let subscriber = tracing_subscriber::fmt::Subscriber::builder()
.with_level(display_level)
.with_max_level(max_level)
.with_ansi(ansi)
.with_target(display_target)
.with_file(display_filename)
.with_thread_ids(display_thread_ids)
.with_line_number(display_line_number)
.with_thread_names(display_thread_names)
.with_span_events(FmtSpan::FULL);
// I know this is hacky, but I couldn't get it any other way. I couldn't even find a
// project that could do it any other way. You can't apply one after another, because the
// type is changed every time. When using Box<dyn Whatever>, some methods complain about
// not being in trait bounds.
// TODO: somehow find a better solution for this
match (log_to_file, show_time, pretty, uptime) {
(true, true, true, true) => {
let subscriber = subscriber
.with_writer(new_file_appender(log_dir))
.with_timer(time::uptime())
.pretty()
.finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(true, true, true, false) => {
let subscriber = subscriber
.with_writer(new_file_appender(log_dir))
.pretty()
.finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(true, false, true, _) => {
let subscriber = subscriber
.with_writer(new_file_appender(log_dir))
.without_time()
.pretty()
.finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(true, true, false, true) => {
2024-01-16 16:12:03 +01:00
let subscriber = subscriber
.with_writer(new_file_appender(log_dir))
.with_timer(time::uptime())
.finish();
2024-01-16 14:04:21 +01:00
tracing::subscriber::set_global_default(subscriber)?;
}
(true, true, false, false) => {
let subscriber = subscriber.with_writer(new_file_appender(log_dir)).finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(true, false, false, _) => {
let file_appender = tracing_appender::rolling::daily(log_dir.clone(), "log");
2023-09-15 17:15:09 +02:00
let (file_writer, _guard) = tracing_appender::non_blocking(file_appender);
2024-01-16 14:04:21 +01:00
let subscriber = subscriber.with_writer(file_writer).without_time().finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(false, true, true, true) => {
let subscriber = subscriber.pretty().with_timer(time::uptime()).finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(false, true, true, false) => {
let subscriber = subscriber.pretty().with_timer(time::uptime()).finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(false, false, true, _) => {
let subscriber = subscriber.without_time().pretty().finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(false, true, false, true) => {
let subscriber = subscriber.with_timer(time::uptime()).finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(false, true, false, false) => {
let subscriber = subscriber.finish();
tracing::subscriber::set_global_default(subscriber)?;
}
(false, false, false, _) => {
let subscriber = subscriber.without_time().finish();
tracing::subscriber::set_global_default(subscriber)?;
2023-09-15 17:15:09 +02:00
}
}
2024-01-16 14:04:21 +01:00
INITIALIZED.store(true, Ordering::Relaxed);
Ok(())
2023-09-15 17:15:09 +02:00
}
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)
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
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 /////////////////////////////////////////////////////////////////////////////
2024-01-16 14:04:21 +01:00
fn new_file_appender(log_dir: PathBuf) -> NonBlocking {
let file_appender = tracing_appender::rolling::daily(log_dir.clone(), "log");
return tracing_appender::non_blocking(file_appender).0;
}