Logging #26

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

9
config/Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "config"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
getset = "0.1.2"

148
config/src/lib.rs Normal file
View File

@ -0,0 +1,148 @@
/*!
* A singleton, thread-safe struct used for accessing
*/
use std::path::PathBuf;
use std::sync::RwLock;
use std::cmp::Ordering;
use crate::LogVerbosity::Warning;
use getset::Getters;
use getset::Setters;
/**
* Represents a valid `WANessa` configuration. Intended as a read-only singleton.
* See [`DEFAULTS`]
*/
#[derive(Clone,PartialEq, Eq)]
#[derive(Getters, Setters)]
#[getset(get = "pub")]
#[allow(clippy::struct_excessive_bools)] // False positive, since it is a config struct.
pub struct Config {
Outdated
Review

-> Maybe just remove the setters and use an initializer to ensure immutability

I considered this.
I just thought that setters are more convenient than using mem::swap or *config = Config::new(...). I also was not sure, if the config is truly read-only since we never specified if hot-reloading the config is a desired feature. Depending on how the CLI is implemented, hot-applying config-changes might also be a necessity to disable stdout logging, which would pollute the CLIs visuals.

@hendrik

> -> Maybe just remove the setters and use an initializer to ensure immutability I considered this. I just thought that setters are more convenient than using `mem::swap` or `*config = Config::new(...)`. I also was not sure, if the config is truly read-only since we never specified if hot-reloading the config is a desired feature. Depending on how the CLI is implemented, hot-applying config-changes might also be a necessity to disable stdout logging, which would pollute the CLIs visuals. @hendrik

I assumed the config would be read only, because some attribute changes after initialization could easily cause unexpected problems. For example, changing the database server at runtime is not something the current code is designed for, nor would it be a sensible feature to implement. If the log-settings should be mutable after the initialization phase, we should add a mutable reference to some "LogPreferences" structure to our Config instead of generally supporting hot-reload for the whole Config

@leon

I assumed the config would be read only, because some attribute changes after initialization could easily cause unexpected problems. For example, changing the database server at runtime is not something the current code is designed for, nor would it be a sensible feature to implement. If the log-settings should be mutable after the initialization phase, we should add a mutable reference to some "LogPreferences" structure to our Config instead of generally supporting hot-reload for the whole Config @leon
Outdated
Review

Seems sensible. It is also probably a good idea to split the config struct into multiple parts.

Seems sensible. It is also probably a good idea to split the config struct into multiple parts.
Outdated
Review
See a787dd93e5
/// See [`LogVerbosity`].<br>
/// Default: [`Warning`]
pub log_verbosity: LogVerbosity,
/// Logs UTC time and date of message, if true.<br>
/// Default: `false`
pub log_time: bool,
/// Time and date format.<br>
/// Defaults to `%F/%T:%f`.<br>
/// See [chrono](https://docs.rs/chrono/latest/chrono/format/strftime/index.html).<br>
/// Default example : `2001-12-31/23:59:33:26490000`
#[getset(skip)]
pub log_time_format: Option<String>,
/// Logs location in code, where the message was logged, if true.<br>
/// Default: `false`
pub log_location: bool,
/// If `Some(path)` tries to also write the log to `path` in addition to stderr/stderr.<br>
/// Default: [None]
pub log_path: Option<PathBuf>,
/// Logs to standard out, if true.<br>
/// Default: `true`
pub log_stdout: bool,
/// Logs to standard err, if true.<br>
/// Default: `true`
pub log_stderr: bool,
/// Database connection address.
/// Is an option to allow constructing a default config during compile time.<br>
/// Default: `localhost`.
#[getset(skip)]
pub db_addr: Option<String>,
/// Database connection port.<br>
/// Default: `6969` (nice).
pub db_port: u16,
}
impl Config {
/// Getter for [`Self::db_addr`].
pub fn db_addr(&self) -> &str {
self.db_addr.as_ref().map_or("localhost", |addr| addr)
}
/// Getter for the [`Self::log_time_format`].
pub fn log_time_format(&self) -> &str
{
self.log_time_format.as_ref().map_or("%F-%T:%f", |fmt| fmt)
}
}
/// See [`DEFAULTS`].
impl Default for Config {
/// See [`DEFAULTS`].
fn default() -> Self {
DEFAULTS
}
}
/// Default configuration.
/// ```rust
/// # use config::Config;
/// # use config::LogVerbosity::Warning;
/// let DEFAULTS = Config
/// {
/// log_verbosity: Warning,
/// log_time: false,
/// log_time_format: None,
/// log_location: false,
/// log_stdout: true,
/// log_stderr: true,
/// log_path: None,
/// db_addr: None,
/// db_port: 6969,
/// };
/// # assert!(DEFAULTS == config::DEFAULTS)
/// ```
pub const DEFAULTS: Config = Config {
log_verbosity: Warning,
log_time: false,
log_time_format: None,
log_location: false,
log_stdout: true,
log_stderr: true,
log_path: None,
db_addr: None,
db_port: 6969,
};
/// Configuration singleton.
pub static CONFIG: RwLock<Config> = RwLock::new(DEFAULTS);
/**
* Each level includes the previous ones.
*/
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum LogVerbosity
{
/// Critical Errors, may lead to crashes or deactivating certain features.
Error,
/// Very minor and recovered errors, such as invalid configs.
Warning,
/// Very verbose and detailed. Basically gives a step-by-step instruction on what is currently done.
Information,
/// Very technical and even more verbose.
/// May contain secrets and private information.
/// **Do not use in production environments!**
Debugging,
}
impl PartialOrd for LogVerbosity
{
/// Some operator overloading of comparison symbols (==, <,>=, etc.) as syntactic sugar.
/// See [`PartialOrd`].
fn partial_cmp(&self, other: &Self) -> Option<Ordering>
{
(*self as usize).partial_cmp(&(*other as usize))
}
}
hendrik marked this conversation as resolved Outdated
Outdated
Review

LogVerbosity doesn't specify values, but logging/src/lib.rs:75 assumes order
-> Accidental reordering of the enum would lead to errors

It is mentioned here, that each level includes the previous ones, but including explicit ordinals is still a good idea.

> LogVerbosity doesn't specify values, but logging/src/lib.rs:75 assumes order > -> Accidental reordering of the enum would lead to errors It is mentioned here, that each level includes the previous ones, but including explicit ordinals is still a good idea.
Outdated
Review
@hendrik
Outdated
Review
See 6e73c2a7b7
impl Ord for LogVerbosity
{
/// Some operator overloading of comparison symbols (==, <,>=, etc.) as syntactic sugar.
/// See [`Ord`].
fn cmp(&self, other: &Self) -> Ordering
{
(*self as usize).cmp(&(*other as usize))
}
}