Files
orchid/orchid-base/src/logging.rs
Lawrence Bethlenfalvy 48942b3b2c Unit tests pass
Fixed a nasty deadlock in reqnot
2026-01-19 00:56:03 +01:00

78 lines
2.1 KiB
Rust

use std::any::Any;
use std::cell::RefCell;
use std::fmt::Arguments;
use std::io::Write;
use std::rc::Rc;
use futures::future::LocalBoxFuture;
use task_local::task_local;
use crate::api;
task_local! {
static DEFAULT_WRITER: RefCell<Box<dyn Write>>
}
/// Set the stream used for [api::LogStrategy::Default]. If not set,
/// [std::io::stderr] will be used.
pub async fn with_default_stream<F: Future>(stderr: impl Write + 'static, fut: F) -> F::Output {
DEFAULT_WRITER.scope(RefCell::new(Box::new(stderr)), fut).await
}
pub trait LogWriter {
fn write_fmt<'a>(&'a self, fmt: Arguments<'a>) -> LocalBoxFuture<'a, ()>;
}
pub trait Logger: Any {
fn writer(&self, category: &str) -> Rc<dyn LogWriter>;
fn strat(&self, category: &str) -> api::LogStrategy;
fn is_active(&self, category: &str) -> bool {
!matches!(self.strat(category), api::LogStrategy::Discard)
}
}
task_local! {
static LOGGER: Rc<dyn Logger>;
}
pub async fn with_logger<F: Future>(logger: impl Logger + 'static, fut: F) -> F::Output {
LOGGER.scope(Rc::new(logger), fut).await
}
pub fn log(category: &str) -> Rc<dyn LogWriter> {
LOGGER.try_with(|l| l.writer(category)).expect("Logger not set!")
}
pub fn get_logger() -> Rc<dyn Logger> { LOGGER.try_with(|l| l.clone()).expect("Logger not set!") }
pub mod test {
use std::fmt::Arguments;
use std::rc::Rc;
use futures::future::LocalBoxFuture;
use crate::clone;
use crate::logging::{LogWriter, Logger};
#[derive(Clone)]
pub struct TestLogger(Rc<dyn Fn(String) -> LocalBoxFuture<'static, ()>>);
impl LogWriter for TestLogger {
fn write_fmt<'a>(&'a self, fmt: Arguments<'a>) -> LocalBoxFuture<'a, ()> {
(self.0)(fmt.to_string())
}
}
impl Logger for TestLogger {
fn strat(&self, _category: &str) -> orchid_api::LogStrategy { orchid_api::LogStrategy::Default }
fn writer(&self, _category: &str) -> std::rc::Rc<dyn LogWriter> { Rc::new(self.clone()) }
}
impl TestLogger {
pub fn new(f: impl AsyncFn(String) + 'static) -> Self {
let f = Rc::new(f);
Self(Rc::new(move |s| clone!(f; Box::pin(async move { f(s).await }))))
}
}
impl Default for TestLogger {
fn default() -> Self { TestLogger::new(async |s| eprint!("{s}")) }
}
}