use crate::formatter::{Formatter, Pretty};
use crate::processor::{BlockingProcessor, Processor};
use crate::tag::{NoTag, Tag};
use crate::{cfg_json, cfg_sync, TreeLayer};
cfg_json! {
use crate::formatter::Json;
}
cfg_sync! {
use crate::processor::AsyncProcessor;
use std::future::Future;
use std::io::Write;
}
use std::marker::PhantomData;
use tracing::Subscriber;
use tracing_subscriber::fmt::{MakeWriter, TestWriter};
use tracing_subscriber::layer::Layered;
use tracing_subscriber::{Layer, Registry};
pub fn builder() -> LayerBuilder<Pretty, fn() -> std::io::Stdout, NoTag> {
LayerBuilder {
formatter: Pretty::new(),
make_writer: std::io::stdout,
tag: PhantomData,
}
}
pub struct LayerBuilder<F, W, T> {
formatter: F,
make_writer: W,
tag: PhantomData<fn(T)>,
}
impl<F, W, T> LayerBuilder<F, W, T>
where
F: 'static + Formatter + Send,
W: 'static + for<'a> MakeWriter<'a> + Send,
T: Tag,
{
pub fn with_test_writer(self) -> LayerBuilder<F, TestWriter, T> {
self.with_writer(TestWriter::new())
}
pub fn with_writer<W2>(self, make_writer: W2) -> LayerBuilder<F, W2, T>
where
W2: for<'a> MakeWriter<'a>,
{
LayerBuilder {
formatter: self.formatter,
make_writer,
tag: self.tag,
}
}
cfg_json! {
pub fn json(self) -> LayerBuilder<Json<true>, W, T> {
self.with_formatter(Json::compact())
}
pub fn json_pretty(self) -> LayerBuilder<Json<false>, W, T> {
self.with_formatter(Json::pretty())
}
}
pub fn pretty(self) -> LayerBuilder<Pretty, W, T> {
self.with_formatter(Pretty::new())
}
pub fn with_formatter<F2>(self, formatter: F2) -> LayerBuilder<F2, W, T> {
LayerBuilder {
formatter,
make_writer: self.make_writer,
tag: self.tag,
}
}
pub fn with_tag<T2>(self) -> LayerBuilder<F, W, T2>
where
T2: Tag,
{
LayerBuilder {
formatter: self.formatter,
make_writer: self.make_writer,
tag: PhantomData,
}
}
#[cfg(feature = "sync")]
#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
pub fn async_layer(
self,
) -> SubscriberBuilder<
Layered<TreeLayer<AsyncProcessor>, Registry>,
AsyncExtensions<impl Future<Output = ()>>,
> {
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
let processor = AsyncProcessor::from(tx);
let handle = async move {
while let Some(tree) = rx.recv().await {
let mut buf = Vec::with_capacity(0);
#[allow(clippy::expect_used)]
{
self.formatter
.fmt(tree, &mut buf)
.expect("formatting failed");
self.make_writer
.make_writer()
.write_all(&buf[..])
.expect("writing failed");
}
}
};
let subscriber = processor.into_layer().tag::<T>().into_subscriber();
SubscriberBuilder {
subscriber,
extensions: AsyncExtensions(handle),
}
}
pub fn blocking_layer(
self,
) -> SubscriberBuilder<Layered<TreeLayer<BlockingProcessor<F, W>>, Registry>, BlockingExtensions>
{
let processor = BlockingProcessor::new(self.formatter, self.make_writer);
let subscriber = processor.into_layer().tag::<T>().into_subscriber();
SubscriberBuilder {
subscriber,
extensions: BlockingExtensions,
}
}
}
pub struct AsyncExtensions<E>(E);
pub struct BlockingExtensions;
pub struct SubscriberBuilder<S, E> {
subscriber: S,
extensions: E,
}
impl<S, E> SubscriberBuilder<S, E>
where
S: Subscriber,
{
pub fn with<L>(self, layer: L) -> SubscriberBuilder<Layered<L, S>, E>
where
L: Layer<S>,
{
SubscriberBuilder {
subscriber: layer.with_subscriber(self.subscriber),
extensions: self.extensions,
}
}
}
#[cfg(feature = "sync")]
#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
impl<S, E> SubscriberBuilder<S, AsyncExtensions<E>>
where
S: Subscriber + Send + Sync,
E: 'static + Future<Output = ()> + Send,
{
pub async fn on_future(self, future: impl Future<Output = ()>) {
let handle = tokio::spawn(self.extensions.0);
let guard = tracing::subscriber::set_default(self.subscriber);
future.await;
drop(guard);
handle.await.expect("failed closing the writing task");
}
}
impl<S> SubscriberBuilder<S, BlockingExtensions>
where
S: Subscriber + Send + Sync,
{
pub fn on_closure<R>(self, closure: impl FnOnce() -> R) -> R {
let _guard = tracing::subscriber::set_default(self.subscriber);
closure()
}
}