You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
334 lines
7.0 KiB
334 lines
7.0 KiB
//! Global logging state
|
|
|
|
use std::{
|
|
fmt::{
|
|
self,
|
|
Display,
|
|
},
|
|
io::{
|
|
self,
|
|
Write,
|
|
},
|
|
borrow::Borrow,
|
|
};
|
|
use once_cell::sync::OnceCell;
|
|
|
|
/// Logging level
|
|
#[derive(PartialEq,Copy,Eq,Debug,Clone,Hash,Ord,PartialOrd)]
|
|
pub enum Level
|
|
{
|
|
Silent,
|
|
Error,
|
|
Warn,
|
|
Info,
|
|
Debug,
|
|
}
|
|
|
|
/// Append this trait to allow you to
|
|
pub trait AsLevel: fmt::Display + Borrow<Level>{
|
|
/// Used for derived levels
|
|
fn prefix(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
write!(f, "{}/", Borrow::<Level>::borrow(self))
|
|
}
|
|
}
|
|
|
|
impl AsLevel for Level{
|
|
#[inline] fn prefix(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result{Ok(())}
|
|
}
|
|
impl AsLevel for &Level{
|
|
#[inline] fn prefix(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result{Ok(())}
|
|
}
|
|
|
|
impl Default for Level
|
|
{
|
|
#[inline]
|
|
fn default() -> Level
|
|
{
|
|
#[cfg(debug_assertions)]
|
|
return Level::Debug;
|
|
#[cfg(not(debug_assertions))]
|
|
return Level::Warn;
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for Level
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
use recolored::Colorize;
|
|
write!(f, "{}", match &self {
|
|
Self::Silent => "Fatal".bright_red().bold(),
|
|
Self::Error => "Error".red(),
|
|
Self::Warn => "Warning".yellow(),
|
|
Self::Info => "Info".normal(),
|
|
Self::Debug => "Debug".dimmed(),
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
/// A logger that prints to stdout and stderr idk.
|
|
/// # TODO
|
|
/// Make this a trait and have variants that print to files and such
|
|
#[derive(Debug)]
|
|
pub struct Logger
|
|
{
|
|
level: Level,
|
|
title: String,
|
|
use_local_time: bool,
|
|
}
|
|
|
|
static INSTANCE: OnceCell<Logger> = OnceCell::new();
|
|
|
|
impl Logger
|
|
{
|
|
pub fn new(level: Level) -> Self
|
|
{
|
|
Self {
|
|
level,
|
|
title: String::new(),
|
|
use_local_time: false,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn global() -> &'static Logger
|
|
{
|
|
INSTANCE.get().expect("[logger] uninitialised")
|
|
}
|
|
|
|
|
|
fn initialise(level: Level) -> &'static Logger
|
|
{
|
|
INSTANCE.set(Logger::new(level)).expect("[logger] already initialised");
|
|
Logger::global()
|
|
}
|
|
|
|
pub fn println<W,L,D>(&self, mut to: W, level: L, what: D) -> io::Result<()>
|
|
where W: Write,
|
|
L: AsLevel,
|
|
D: Display,
|
|
{
|
|
//lol
|
|
enum Date {
|
|
Local(chrono::DateTime<chrono::offset::Local>),
|
|
Utc(chrono::DateTime<chrono::offset::Utc>),
|
|
}
|
|
impl std::fmt::Display for Date
|
|
{
|
|
#[inline]
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
match self {
|
|
Self::Local(l) => write!(f, "{}", l),
|
|
Self::Utc(l) => write!(f, "{}", l),
|
|
}
|
|
}
|
|
}
|
|
impl From<chrono::DateTime<chrono::offset::Local>> for Date
|
|
{
|
|
#[inline]
|
|
fn from(from: chrono::DateTime<chrono::offset::Local>) -> Self
|
|
{
|
|
Self::Local(from)
|
|
}
|
|
}
|
|
|
|
impl From<chrono::DateTime<chrono::offset::Utc>> for Date
|
|
{
|
|
#[inline]
|
|
fn from(from: chrono::DateTime<chrono::offset::Utc>) -> Self
|
|
{
|
|
Self::Utc(from)
|
|
}
|
|
}
|
|
|
|
|
|
if &self.level >= level.borrow() {
|
|
let now: Date = if self.use_local_time {
|
|
chrono::offset::Local::now().into()
|
|
} else {
|
|
chrono::offset::Utc::now().into()
|
|
};
|
|
|
|
write!(to, "{} [", now)?;
|
|
|
|
struct Prefix<'a,L: AsLevel>(&'a L);
|
|
|
|
impl<'a,L: AsLevel> std::fmt::Display for Prefix<'a, L>
|
|
{
|
|
#[inline]
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
self.0.prefix(f)
|
|
}
|
|
}
|
|
|
|
write!(to, "{}", Prefix(&level))?; //what a mess...
|
|
write!(to, "{}", level)?;
|
|
write!(to, "]")?;
|
|
if self.title.len() > 0 {
|
|
write!(to, " <{}>: ", self.title)?;
|
|
} else {
|
|
write!(to, ": ")?;
|
|
}
|
|
writeln!(to, "{}", what)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[macro_export] macro_rules! debug {
|
|
($obj:expr) => {
|
|
{
|
|
let stdout = std::io::stdout();
|
|
let stdout = stdout.lock();
|
|
$crate::log::Logger::global().println(stdout, $crate::log::Level::Debug, $obj).expect("i/o error");
|
|
}
|
|
};
|
|
($fmt:literal, $($args:expr),*) => {
|
|
debug!(format!($fmt, $($args,)*));
|
|
};
|
|
}
|
|
|
|
#[macro_export] macro_rules! info {
|
|
($obj:expr) => {
|
|
{
|
|
let stdout = std::io::stdout();
|
|
let stdout = stdout.lock();
|
|
$crate::log::Logger::global().println(stdout, $crate::log::Level::Info, $obj).expect("i/o error");
|
|
}
|
|
};
|
|
($fmt:literal, $($args:expr),*) => {
|
|
info!(format!($fmt, $($args,)*));
|
|
};
|
|
}
|
|
|
|
#[macro_export] macro_rules! important {
|
|
($obj:expr) => {
|
|
{
|
|
struct Important;
|
|
use std::{
|
|
borrow::Borrow,
|
|
fmt,
|
|
};
|
|
impl Borrow<$crate::log::Level> for Important {
|
|
fn borrow(&self) -> &$crate::log::Level
|
|
{
|
|
&$crate::log::Level::Info
|
|
}
|
|
}
|
|
impl fmt::Display for Important
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
use recolored::Colorize;
|
|
write!(f, "{}", "Important".bright_blue())
|
|
}
|
|
}
|
|
|
|
impl $crate::log::AsLevel for Important{}
|
|
|
|
let stdout = std::io::stdout();
|
|
let stdout = stdout.lock();
|
|
$crate::log::Logger::global().println(stdout, Important, $obj).expect("i/o error");
|
|
}
|
|
};
|
|
($fmt:literal, $($args:expr),*) => {
|
|
important!(format!($fmt, $($args,)*));
|
|
};
|
|
}
|
|
|
|
|
|
#[macro_export] macro_rules! warn {
|
|
($obj:expr) => {
|
|
{
|
|
let stderr = std::io::stderr();
|
|
let stderr = stderr.lock();
|
|
$crate::log::Logger::global().println(stderr, $crate::log::Level::Warn, $obj).expect("i/o error");
|
|
}
|
|
};
|
|
($fmt:literal, $($args:expr),*) => {
|
|
warn!(format!($fmt, $($args,)*));
|
|
};
|
|
}
|
|
|
|
|
|
#[macro_export] macro_rules! dangerous {
|
|
($obj:expr) => {
|
|
{
|
|
struct Dangerous;
|
|
use std::{
|
|
borrow::Borrow,
|
|
fmt,
|
|
};
|
|
impl Borrow<$crate::log::Level> for Dangerous {
|
|
fn borrow(&self) -> &$crate::log::Level
|
|
{
|
|
&$crate::log::Level::Warn
|
|
}
|
|
}
|
|
impl fmt::Display for Dangerous
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
use recolored::Colorize;
|
|
write!(f, "{}", "Dangerous".bright_yellow())
|
|
}
|
|
}
|
|
|
|
impl $crate::log::AsLevel for Dangerous{}
|
|
|
|
let stderr = std::io::stderr();
|
|
let stderr = stderr.lock();
|
|
$crate::log::Logger::global().println(stderr, Dangerous, $obj).expect("i/o error");
|
|
}
|
|
};
|
|
($fmt:literal, $($args:expr),*) => {
|
|
dangerous!(format!($fmt, $($args,)*));
|
|
};
|
|
}
|
|
|
|
#[macro_export] macro_rules! error {
|
|
($obj:expr) => {
|
|
{
|
|
let stderr = std::io::stderr();
|
|
let stderr = stderr.lock();
|
|
$crate::log::Logger::global().println(stderr, $crate::log::Level::Error, $obj).expect("i/o error");
|
|
}
|
|
};
|
|
($fmt:literal, $($args:expr),*) => {
|
|
error!(format!($fmt, $($args,)*));
|
|
};
|
|
}
|
|
|
|
#[macro_export] macro_rules! fatal {
|
|
($obj:expr) => {
|
|
{
|
|
let stdout = std::io::stdout();
|
|
let stdout = stdout.lock();
|
|
$crate::log::Logger::global().println(stdout, $crate::log::Level::Silent, $obj).expect("i/o error");
|
|
|
|
std::process::exit(-1)
|
|
}
|
|
};
|
|
($fmt:literal, $($args:expr),*) => {
|
|
error!(format!($fmt, $($args,)*));
|
|
};
|
|
}
|
|
|
|
/// Initialise the global logger instance. If logging macros are called before this, they will panic.
|
|
#[inline]
|
|
pub fn init(level: Level)
|
|
{
|
|
Logger::initialise(level);
|
|
}
|
|
|
|
/// The global logger's level
|
|
#[inline]
|
|
pub fn level() -> &'static Level
|
|
{
|
|
&Logger::global().level
|
|
}
|