Reimplement logging by utilizing the tracing crate
Signed-off-by: trivernis <trivernis@protonmail.com>i18n
parent
f1bbb2626c
commit
052d176f4a
@ -0,0 +1,146 @@
|
|||||||
|
use colored::Colorize;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tracing_subscriber::registry::LookupSpan;
|
||||||
|
|
||||||
|
use tracing::field::Visit;
|
||||||
|
use tracing::{span, Level, Metadata, Subscriber};
|
||||||
|
use tracing_subscriber::Layer;
|
||||||
|
|
||||||
|
use super::handler::LogHandler;
|
||||||
|
use super::Verbosity;
|
||||||
|
|
||||||
|
const ENABLED_MODULES: &[&str] = &["ame"];
|
||||||
|
|
||||||
|
pub struct AmeFormatLayer {
|
||||||
|
logger: Arc<LogHandler>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AmeFormatLayer {
|
||||||
|
pub fn new(logger: Arc<LogHandler>) -> Self {
|
||||||
|
Self { logger }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_level_loggable(&self, level: &Level) -> bool {
|
||||||
|
self.logger.is_loggable(Verbosity::from_level(level))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_enabled(&self, metadata: &Metadata) -> bool {
|
||||||
|
let level = metadata.level();
|
||||||
|
if !self.is_level_loggable(level) {
|
||||||
|
false
|
||||||
|
} else if let Some(module_path) = metadata.module_path() {
|
||||||
|
ENABLED_MODULES.iter().any(|m| module_path.starts_with(m))
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&self, msg: String, level: &Level) {
|
||||||
|
match Verbosity::from_level(level) {
|
||||||
|
Verbosity::Error => self.logger.log_error(msg),
|
||||||
|
Verbosity::Warning => self.logger.log_warning(msg),
|
||||||
|
Verbosity::Info => self.logger.log_info(msg),
|
||||||
|
Verbosity::Debug => self.logger.log_debug(msg),
|
||||||
|
Verbosity::Trace => self.logger.log_trace(msg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Subscriber + for<'a> LookupSpan<'a>> Layer<S> for AmeFormatLayer {
|
||||||
|
/// When entering a span
|
||||||
|
fn on_new_span(
|
||||||
|
&self,
|
||||||
|
attrs: &span::Attributes<'_>,
|
||||||
|
_id: &span::Id,
|
||||||
|
_ctx: tracing_subscriber::layer::Context<'_, S>,
|
||||||
|
) {
|
||||||
|
let metadata = attrs.metadata();
|
||||||
|
if self.is_enabled(metadata) {
|
||||||
|
let mut visitor = ValueDebugStorage::default();
|
||||||
|
attrs.record(&mut visitor);
|
||||||
|
let fields: Vec<String> = visitor
|
||||||
|
.values
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, v)| format!("{k} = {v}"))
|
||||||
|
.collect();
|
||||||
|
let mut fields_str = fields.join("\n ");
|
||||||
|
|
||||||
|
if !fields_str.is_empty() {
|
||||||
|
fields_str = format!("\n {fields_str}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(module) = metadata.module_path() {
|
||||||
|
self.log(
|
||||||
|
format!(
|
||||||
|
"{} {}::{} {}",
|
||||||
|
"ENTER".italic(),
|
||||||
|
module,
|
||||||
|
metadata.name(),
|
||||||
|
fields_str.dimmed()
|
||||||
|
),
|
||||||
|
metadata.level(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
self.log(
|
||||||
|
format!(
|
||||||
|
"{} {} {}",
|
||||||
|
"ENTER".italic(),
|
||||||
|
metadata.name(),
|
||||||
|
fields_str.dimmed()
|
||||||
|
),
|
||||||
|
metadata.level(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_exit(&self, id: &span::Id, ctx: tracing_subscriber::layer::Context<'_, S>) {
|
||||||
|
let span = ctx.span(id).unwrap();
|
||||||
|
let metadata = span.metadata();
|
||||||
|
|
||||||
|
if self.is_enabled(metadata) {
|
||||||
|
if let Some(module) = metadata.module_path() {
|
||||||
|
self.log(
|
||||||
|
format!("{} {}::{}", "EXIT".italic(), module, metadata.name(),),
|
||||||
|
metadata.level(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.log(
|
||||||
|
format!("{} {}", "EXIT".italic(), metadata.name()),
|
||||||
|
metadata.level(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_event(
|
||||||
|
&self,
|
||||||
|
event: &tracing::Event<'_>,
|
||||||
|
_ctx: tracing_subscriber::layer::Context<'_, S>,
|
||||||
|
) {
|
||||||
|
let metadata = event.metadata();
|
||||||
|
|
||||||
|
if self.is_enabled(metadata) {
|
||||||
|
let mut visitor = ValueDebugStorage::default();
|
||||||
|
event.record(&mut visitor);
|
||||||
|
let mut values = visitor.values;
|
||||||
|
|
||||||
|
if let Some(msg) = values.remove("message") {
|
||||||
|
self.log(msg, metadata.level())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ValueDebugStorage {
|
||||||
|
pub values: HashMap<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Visit for ValueDebugStorage {
|
||||||
|
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
|
||||||
|
self.values
|
||||||
|
.insert(field.name().to_string(), format!("{:?}", value));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,177 @@
|
|||||||
|
use colored::Colorize;
|
||||||
|
use indicatif::{MultiProgress, ProgressBar};
|
||||||
|
use std::{
|
||||||
|
sync::{atomic::AtomicBool, Arc},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::uwu;
|
||||||
|
use dialoguer::Confirm;
|
||||||
|
|
||||||
|
use super::Verbosity;
|
||||||
|
use parking_lot::RwLock;
|
||||||
|
|
||||||
|
const OK_SYMBOL: &str = "❖";
|
||||||
|
const ERR_SYMBOL: &str = "X";
|
||||||
|
const WARN_SYMBOL: &str = "!";
|
||||||
|
const DEBUG_SYMBOL: &str = "⌘";
|
||||||
|
const TRACE_SYMBOL: &str = "🗲";
|
||||||
|
const PROMPT_SYMBOL: &str = "?";
|
||||||
|
|
||||||
|
pub struct LogHandler {
|
||||||
|
level: Arc<RwLock<Verbosity>>,
|
||||||
|
output_type: Arc<RwLock<OutputType>>,
|
||||||
|
uwu_enabled: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for LogHandler {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
level: Arc::new(RwLock::new(Verbosity::Info)),
|
||||||
|
output_type: Arc::new(RwLock::new(OutputType::Stderr)),
|
||||||
|
uwu_enabled: Arc::new(AtomicBool::new(false)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub enum OutputType {
|
||||||
|
Stdout,
|
||||||
|
Stderr,
|
||||||
|
MultiProgress(Arc<MultiProgress>),
|
||||||
|
Progress(Arc<ProgressBar>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub enum PromptDefault {
|
||||||
|
Yes,
|
||||||
|
No,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LogHandler {
|
||||||
|
pub fn log_error(&self, msg: String) {
|
||||||
|
if self.is_loggable(Verbosity::Error) {
|
||||||
|
let msg = self.preformat_msg(msg);
|
||||||
|
let msg = format!("{} {}", ERR_SYMBOL.red().bold(), msg.bold().red());
|
||||||
|
self.log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn log_warning(&self, msg: String) {
|
||||||
|
if self.is_loggable(Verbosity::Warning) {
|
||||||
|
let msg = self.preformat_msg(msg);
|
||||||
|
let msg = format!("{} {}", WARN_SYMBOL.yellow(), msg.yellow().bold());
|
||||||
|
self.log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn log_info(&self, msg: String) {
|
||||||
|
if self.is_loggable(Verbosity::Info) {
|
||||||
|
let msg = self.preformat_msg(msg);
|
||||||
|
let msg = format!("{} {}", OK_SYMBOL.purple(), msg.bold());
|
||||||
|
self.log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn log_debug(&self, msg: String) {
|
||||||
|
if self.is_loggable(Verbosity::Debug) {
|
||||||
|
let msg = self.preformat_msg(msg);
|
||||||
|
let msg = format!("{} {}", DEBUG_SYMBOL.blue(), msg);
|
||||||
|
|
||||||
|
self.log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn log_trace(&self, msg: String) {
|
||||||
|
if self.is_loggable(Verbosity::Trace) {
|
||||||
|
let msg = self.preformat_msg(msg);
|
||||||
|
let msg = format!("{} {}", TRACE_SYMBOL.cyan(), msg.dimmed());
|
||||||
|
self.log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prompts the user with a question and a default selection
|
||||||
|
pub fn prompt(&self, question: String, p_default: PromptDefault) -> bool {
|
||||||
|
let question = self.preformat_msg(question);
|
||||||
|
let question = format!("{} {}", PROMPT_SYMBOL.purple(), question.bold());
|
||||||
|
let mut confirm = Confirm::new();
|
||||||
|
confirm.with_prompt(question);
|
||||||
|
|
||||||
|
match p_default {
|
||||||
|
PromptDefault::Yes => {
|
||||||
|
confirm.default(true);
|
||||||
|
}
|
||||||
|
PromptDefault::No => {
|
||||||
|
confirm.default(false);
|
||||||
|
}
|
||||||
|
PromptDefault::None => {}
|
||||||
|
}
|
||||||
|
confirm.interact().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_verbosity(&self, level: Verbosity) {
|
||||||
|
(*self.level.write()) = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset_output_type(&self) {
|
||||||
|
self.set_output_type(OutputType::Stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new progress spinner and registers it on the log handler
|
||||||
|
pub fn new_progress_spinner(&self) -> Arc<ProgressBar> {
|
||||||
|
let progress_bar = ProgressBar::new_spinner().with_message("Scanning for pacnew files");
|
||||||
|
progress_bar.enable_steady_tick(Duration::from_millis(250));
|
||||||
|
let pb = Arc::new(progress_bar);
|
||||||
|
self.set_progress_bar(pb.clone());
|
||||||
|
|
||||||
|
pb
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registeres a progress bar on the log handler
|
||||||
|
pub fn set_progress_bar(&self, pb: Arc<ProgressBar>) {
|
||||||
|
self.set_output_type(OutputType::Progress(pb))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the output type of the log handler to either stdout/stderr or a progress bar
|
||||||
|
pub fn set_output_type(&self, output: OutputType) {
|
||||||
|
(*self.output_type.write()) = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_uwu_enabled(&self, enabled: bool) {
|
||||||
|
self.uwu_enabled
|
||||||
|
.store(enabled, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_loggable(&self, level: Verbosity) -> bool {
|
||||||
|
(*self.level.read()) >= level
|
||||||
|
}
|
||||||
|
|
||||||
|
fn preformat_msg(&self, msg: String) -> String {
|
||||||
|
let msg = self.apply_uwu(msg);
|
||||||
|
let opts = textwrap::Options::new(crossterm::terminal::size().unwrap().0 as usize - 2)
|
||||||
|
.subsequent_indent(" ");
|
||||||
|
|
||||||
|
textwrap::wrap(&msg, opts).join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_uwu(&self, msg: String) -> String {
|
||||||
|
if self.uwu_enabled.load(std::sync::atomic::Ordering::Relaxed) {
|
||||||
|
uwu!(msg)
|
||||||
|
} else {
|
||||||
|
msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&self, msg: String) {
|
||||||
|
let output_type = self.output_type.read();
|
||||||
|
match &*output_type {
|
||||||
|
OutputType::Stdout => println!("{}", msg),
|
||||||
|
OutputType::Stderr => eprintln!("{}", msg),
|
||||||
|
OutputType::MultiProgress(m) => {
|
||||||
|
let _ = m.println(msg);
|
||||||
|
}
|
||||||
|
OutputType::Progress(p) => p.println(msg),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use tracing::Level;
|
||||||
|
use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt;
|
||||||
|
use tracing_subscriber::Registry;
|
||||||
|
|
||||||
|
mod fmt_layer;
|
||||||
|
use fmt_layer::AmeFormatLayer;
|
||||||
|
|
||||||
|
use crate::internal::uwu_enabled;
|
||||||
|
|
||||||
|
use self::handler::LogHandler;
|
||||||
|
pub mod handler;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum Verbosity {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
Error = 0,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
Warning = 1,
|
||||||
|
Info = 2,
|
||||||
|
Debug = 3,
|
||||||
|
Trace = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<usize> for Verbosity {
|
||||||
|
fn from(num_verbosity: usize) -> Self {
|
||||||
|
match num_verbosity {
|
||||||
|
0 => Self::Info,
|
||||||
|
1 => Self::Debug,
|
||||||
|
2 => Self::Trace,
|
||||||
|
_ => Self::Info,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Verbosity {
|
||||||
|
fn from_level(l: &Level) -> Self {
|
||||||
|
match *l {
|
||||||
|
Level::ERROR => Self::Error,
|
||||||
|
Level::WARN => Self::Warning,
|
||||||
|
Level::INFO => Self::Info,
|
||||||
|
Level::DEBUG => Self::Debug,
|
||||||
|
Level::TRACE => Self::Trace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes the tracing logger
|
||||||
|
/// Can be used for debug purposes _or_ verbose output
|
||||||
|
pub fn init_logger(verbosity: Verbosity) {
|
||||||
|
let logger = get_logger();
|
||||||
|
logger.set_verbosity(verbosity);
|
||||||
|
logger.set_uwu_enabled(uwu_enabled());
|
||||||
|
let ame_layer = AmeFormatLayer::new(logger);
|
||||||
|
|
||||||
|
let subscriber = Registry::default().with(ame_layer);
|
||||||
|
tracing::subscriber::set_global_default(subscriber).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the global logger instance
|
||||||
|
pub fn get_logger() -> Arc<LogHandler> {
|
||||||
|
lazy_static! {
|
||||||
|
static ref LOGGER: Arc<LogHandler> = Arc::new(LogHandler::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
Arc::clone(&LOGGER)
|
||||||
|
}
|
Loading…
Reference in New Issue