Logging #26

Open
leon wants to merge 33 commits from Logging into main
2 changed files with 159 additions and 0 deletions
Showing only changes of commit d330a48c55 - Show all commits

10
logging/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "logging"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = { version = "0.4.34", default-features = false, features = ["now"] }
config = { path = "../config"}

149
logging/src/lib.rs Normal file
View File

@ -0,0 +1,149 @@
/*!
* This module handles logging messages asynchronously and is thread-safe.
* It should mostly be called statically.
*/
use std::fmt::Display;
use std::fs::OpenOptions;
use std::io::Write;
use config::LogVerbosity;
use chrono::Utc;
use LogVerbosity::Warning;
use LogVerbosity::Information;
use LogMessageType::GenericErr;
use LogMessageType::GenericWarn;
use LogMessageType::GenericInfo;
use LogMessageType::GenericDebug;
/**
* Logs the given message.
*
* # Panics
* Panics if readlock on [`config::CONFIG`] could not be acquired
* or if another error occurs, such as a full disk.
*/
// TODO: Create macro to make last three parameters optional.
pub fn log_message(msg: &LogMessage, file: &str, line: &str, column: &str)
{
let conf = config::CONFIG.read().expect(&format!("Failed aqcuire read lock on config\nTried to log message: {msg}"));
if conf.log_verbosity() < &msg.1 { return; }
let mut log_line = String::new();
// add time substring
if *conf.log_time()
{
log_line += &format!("{} ", Utc::now().format(conf.log_time_format()));
}
// add code location substring
if *conf.log_location()
{
log_line += &format!("{file}:{line},{column} ");
}
log_line += &msg.to_string();
// May panic if file cannot be opened or written to.
conf.log_path().as_ref().map_or_else(
|| {}, |path|
{
let mut file = OpenOptions::new()
.write(true)
.append(true)
.open(path)
.expect(
&format!("Could not open log file: {path:#?}")
);
writeln!(file, "{log_line}").expect(&format!("Could not write log to file: {path:#?}"));
}
);
if msg.1 <= Warning && *conf.log_stderr()
{
// May panic if writing to stderr fails.
eprintln!("{log_line}");
}
else if msg.1 >= Information && *conf.log_stdout()
{
// May panic if writing to stdout fails.
println!("{log_line}");
}
drop(conf); // remove read lock
}
/**
* A named typle assigning a log [`LogVerbosity`] to a [`LogMessageType`]
*/
pub struct LogMessage(LogMessageType, LogVerbosity);
impl Display for LogMessage
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>
{
self.0.fmt(f) // just display LogMessageType
}
}
/**
* Every possible Message, which may be logged. Grouped the following:
*
* These groups correspond to the four levels of logging verbosity. See [`LogVerbosity`].
*/
#[derive(Clone, Debug)]
pub enum LogMessageType {
/// Errors
/// An error of any type; please avoid using this.
GenericErr(String),
/// Warnings
/// A warning of any type; please avoid using this.
GenericWarn(String),
/// Info
/// Some information of any type; please avoid using this.
GenericInfo(String),
/// Debug
/// a debug message of any type; please avoid using this.
GenericDebug(String),
}
/*
* Please don't put anything other except new match arms below this comment, since it is expected that
* the following code will have a lot of lines, due to exhausting all possible enum values
* respectively and (hopefully) detailed messages.
*/
impl LogMessageType
{
/// Returns a new [`LogMessage`] based on the default verbosity of the given [`LogMessageType`].
pub fn log_message(&self) -> LogMessage
{
let verbosity = match self
{
GenericErr(_) => LogVerbosity::Error,
GenericWarn(_) => LogVerbosity::Warning,
GenericInfo(_) => LogVerbosity::Information,
GenericDebug(_) => LogVerbosity::Debugging,
};
LogMessage(self.clone(), verbosity)
}
}
impl Display for LogMessageType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error>
{
match self
{
GenericErr(err) => { write!(f, "Generic Error: {err}") },
GenericWarn(warn) => { write!(f, "Generic Warning: {warn}") },
GenericInfo(info) => { write!(f, "Generic Information: {info}") },
GenericDebug(debug) => { write!(f, "Generic Debug Message: {debug}") },
}
}
}