mirror of
https://github.com/Zenithsiz/zutil.git
synced 2026-02-03 10:31:34 +00:00
Logger now has a builder.
This commit is contained in:
parent
2913756373
commit
5f6c00fca2
142
zutil-logger/src/builder.rs
Normal file
142
zutil-logger/src/builder.rs
Normal file
@ -0,0 +1,142 @@
|
||||
//! Logger builder
|
||||
|
||||
use {
|
||||
crate::{
|
||||
Logger,
|
||||
LoggerSubscriber,
|
||||
file::{self, FileWriter},
|
||||
pre_init::PreInitLogger,
|
||||
term,
|
||||
},
|
||||
std::{
|
||||
collections::HashMap,
|
||||
io::{self, Write},
|
||||
},
|
||||
tracing::Subscriber,
|
||||
tracing_subscriber::{fmt::MakeWriter, prelude::*, registry::LookupSpan},
|
||||
};
|
||||
|
||||
/// Logger builder
|
||||
pub struct LoggerBuilder<W, S> {
|
||||
/// Pre-initialization logger
|
||||
pre_init_logger: PreInitLogger,
|
||||
|
||||
/// Stderr
|
||||
stderr: W,
|
||||
|
||||
/// Subscriber
|
||||
subscriber: S,
|
||||
|
||||
/// Stderr filters
|
||||
stderr_filters: HashMap<Option<String>, String>,
|
||||
|
||||
/// Filter filters
|
||||
file_filters: HashMap<Option<String>, String>,
|
||||
}
|
||||
|
||||
impl LoggerBuilder<fn() -> io::Stderr, LoggerSubscriber> {
|
||||
/// Creates a new builder
|
||||
pub fn new() -> Self {
|
||||
// Create the pre-init logger to log everything until we have our loggers running.
|
||||
let pre_init_logger = PreInitLogger::new();
|
||||
|
||||
Self {
|
||||
pre_init_logger,
|
||||
stderr: io::stderr,
|
||||
subscriber: LoggerSubscriber::default(),
|
||||
stderr_filters: [(None, "info".to_owned())].into(),
|
||||
file_filters: [(None, "debug".to_owned())].into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W, S> LoggerBuilder<W, S> {
|
||||
/// Sets the stderr output of this logger
|
||||
pub fn stderr<W2>(self, stderr: W2) -> LoggerBuilder<W2, S> {
|
||||
LoggerBuilder { stderr, ..self }
|
||||
}
|
||||
|
||||
/// Adds a layer to this logger's subscriber
|
||||
pub fn layer<L>(self, layer: L) -> LoggerBuilder<W, tracing_subscriber::layer::Layered<L, S>>
|
||||
where
|
||||
S: Subscriber,
|
||||
L: tracing_subscriber::Layer<S>,
|
||||
{
|
||||
LoggerBuilder {
|
||||
subscriber: self.subscriber.with(layer),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the default stderr filter
|
||||
pub fn stderr_filter_default(mut self, filter: &str) -> Self {
|
||||
self.stderr_filters.insert(None, filter.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a stderr filter
|
||||
pub fn stderr_filter(mut self, key: &str, filter: &str) -> Self {
|
||||
self.stderr_filters.insert(Some(key.to_owned()), filter.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the default file filter
|
||||
pub fn file_filter_default(mut self, filter: &str) -> Self {
|
||||
self.file_filters.insert(None, filter.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a file filter
|
||||
pub fn file_filter(mut self, key: &str, filter: &str) -> Self {
|
||||
self.file_filters.insert(Some(key.to_owned()), filter.to_owned());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a filter for both the stderr and file layers
|
||||
pub fn filter(self, key: &str, filter: &str) -> Self {
|
||||
self.stderr_filter(key, filter).file_filter(key, filter)
|
||||
}
|
||||
|
||||
/// Builds the logger
|
||||
pub fn build(self) -> Logger
|
||||
where
|
||||
W: for<'a> MakeWriter<'a> + Clone + Send + Sync + 'static,
|
||||
S: Subscriber + for<'a> LookupSpan<'a> + Send + Sync + 'static,
|
||||
{
|
||||
// Then initialize our logging
|
||||
let file_writer = FileWriter::memory();
|
||||
|
||||
// Note: Due to [this issue](https://github.com/tokio-rs/tracing/issues/1817),
|
||||
// the order here matters, and the stderr ones must be last.
|
||||
let file_layer = file::layer(file_writer.clone(), self::filters_iter(&self.file_filters));
|
||||
let term_layer = term::layer(self.stderr.clone(), self::filters_iter(&self.stderr_filters));
|
||||
let subscriber = self.subscriber.with(file_layer).with(term_layer);
|
||||
if let Err(err) = subscriber.try_init() {
|
||||
eprintln!("Failed to set global logger: {err}");
|
||||
}
|
||||
|
||||
// Finally write the pre-init output to our writes
|
||||
self.pre_init_logger
|
||||
.into_output()
|
||||
.with_bytes(|bytes| {
|
||||
self.stderr.make_writer().write_all(bytes)?;
|
||||
file_writer.make_writer().write_all(bytes)
|
||||
})
|
||||
.expect("Unable to write pre-init output");
|
||||
|
||||
tracing::info!("Successfully initialized logger");
|
||||
|
||||
Logger { file_writer }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LoggerBuilder<fn() -> io::Stderr, LoggerSubscriber> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the filters field into an iterator
|
||||
fn filters_iter(filters: &HashMap<Option<String>, String>) -> impl Iterator<Item = (Option<&'_ str>, &'_ str)> {
|
||||
filters.iter().map(|(key, value)| (key.as_deref(), value.as_str()))
|
||||
}
|
||||
@ -1,27 +1,35 @@
|
||||
//! Logger
|
||||
|
||||
// Features
|
||||
#![feature(nonpoison_mutex, sync_nonpoison, anonymous_lifetime_in_impl_trait)]
|
||||
#![feature(
|
||||
nonpoison_mutex,
|
||||
sync_nonpoison,
|
||||
anonymous_lifetime_in_impl_trait,
|
||||
type_changing_struct_update,
|
||||
never_type
|
||||
)]
|
||||
|
||||
// Modules
|
||||
mod builder;
|
||||
mod file;
|
||||
mod pre_init;
|
||||
mod term;
|
||||
|
||||
// Exports
|
||||
pub use self::builder::LoggerBuilder;
|
||||
|
||||
// Imports
|
||||
use {
|
||||
self::{file::FileWriter, pre_init::PreInitLogger},
|
||||
self::file::FileWriter,
|
||||
itertools::Itertools,
|
||||
std::{
|
||||
self,
|
||||
collections::{HashMap, hash_map},
|
||||
env::{self, VarError},
|
||||
fs,
|
||||
io::Write,
|
||||
io,
|
||||
path::Path,
|
||||
},
|
||||
tracing::Subscriber,
|
||||
tracing_subscriber::{Layer, Registry, fmt::MakeWriter, layer::Layered, prelude::*, registry::LookupSpan},
|
||||
tracing_subscriber::Registry,
|
||||
};
|
||||
|
||||
/// Logger
|
||||
@ -31,49 +39,14 @@ pub struct Logger {
|
||||
}
|
||||
|
||||
impl Logger {
|
||||
/// Creates a new logger
|
||||
///
|
||||
/// Starts already logging to stderr.
|
||||
pub fn new<W, L>(
|
||||
stderr: W,
|
||||
extra_layers: L,
|
||||
default_stderr_filters: impl IntoIterator<Item = (Option<&'_ str>, &'_ str)>,
|
||||
default_file_filters: impl IntoIterator<Item = (Option<&'_ str>, &'_ str)>,
|
||||
) -> Self
|
||||
where
|
||||
W: for<'a> MakeWriter<'a> + Clone + Send + Sync + 'static,
|
||||
L: ExtraLayers<LoggerSubscriber>,
|
||||
L::Subscriber: Subscriber + for<'a> LookupSpan<'a> + Send + Sync + 'static,
|
||||
{
|
||||
// Create the pre-init logger to log everything until we have our loggers running.
|
||||
let pre_init_logger = PreInitLogger::new();
|
||||
/// Creates a default logger
|
||||
pub fn new() -> Self {
|
||||
Self::builder().build()
|
||||
}
|
||||
|
||||
// Then initialize our logging
|
||||
let file_writer = FileWriter::memory();
|
||||
|
||||
// Note: Due to [this issue](https://github.com/tokio-rs/tracing/issues/1817),
|
||||
// the order here matters, and the stderr ones must be last.
|
||||
let subscriber = LoggerSubscriber::default();
|
||||
let subscriber = extra_layers
|
||||
.layer_on(subscriber)
|
||||
.with(file::layer(file_writer.clone(), default_file_filters))
|
||||
.with(term::layer(stderr.clone(), default_stderr_filters));
|
||||
if let Err(err) = subscriber.try_init() {
|
||||
eprintln!("Failed to set global logger: {err}");
|
||||
}
|
||||
|
||||
// Finally write the pre-init output to our writes
|
||||
pre_init_logger
|
||||
.into_output()
|
||||
.with_bytes(|bytes| {
|
||||
stderr.make_writer().write_all(bytes)?;
|
||||
file_writer.make_writer().write_all(bytes)
|
||||
})
|
||||
.expect("Unable to write pre-init output");
|
||||
|
||||
tracing::info!("Successfully initialized logger");
|
||||
|
||||
Self { file_writer }
|
||||
/// Creates a builder for the logger
|
||||
pub fn builder() -> LoggerBuilder<fn() -> io::Stderr, LoggerSubscriber> {
|
||||
LoggerBuilder::new()
|
||||
}
|
||||
|
||||
/// Sets a file to log into.
|
||||
@ -97,6 +70,12 @@ impl Logger {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Logger {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Logger subscriber
|
||||
// TODO: Hide this behind a trait impl type alias
|
||||
pub type LoggerSubscriber = Registry;
|
||||
@ -150,45 +129,3 @@ fn get_env_filters(env: &str, default_filters: impl IntoIterator<Item = (Option<
|
||||
|
||||
var
|
||||
}
|
||||
|
||||
/// Extra layers
|
||||
pub trait ExtraLayers<S> {
|
||||
/// Subscriber with all layers
|
||||
type Subscriber;
|
||||
|
||||
/// Layers all layers onto a subscriber
|
||||
fn layer_on(self, subscriber: S) -> Self::Subscriber;
|
||||
}
|
||||
|
||||
impl<S> ExtraLayers<S> for () {
|
||||
type Subscriber = S;
|
||||
|
||||
fn layer_on(self, subscriber: S) -> Self::Subscriber {
|
||||
subscriber
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, L> ExtraLayers<S> for (L,)
|
||||
where
|
||||
S: Subscriber,
|
||||
L: Layer<S>,
|
||||
{
|
||||
type Subscriber = Layered<L, S>;
|
||||
|
||||
fn layer_on(self, subscriber: S) -> Self::Subscriber {
|
||||
subscriber.with(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, L0, L1> ExtraLayers<S> for (L0, L1)
|
||||
where
|
||||
S: Subscriber,
|
||||
L0: Layer<S>,
|
||||
L1: Layer<Layered<L0, S>>,
|
||||
{
|
||||
type Subscriber = Layered<L1, Layered<L0, S>>;
|
||||
|
||||
fn layer_on(self, subscriber: S) -> Self::Subscriber {
|
||||
subscriber.with(self.0).with(self.1)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user