1use crate::{OutputFormat, console_fields::ConsoleFields, console_format::ConsoleFormat};
5use std::{
6 io::{self, IsTerminal, Write},
7 sync::Mutex,
8 time::Instant,
9};
10use tracing::{Subscriber, subscriber::SetGlobalDefaultError};
11use tracing_error::ErrorLayer;
12use tracing_subscriber::{
13 EnvFilter, Layer, Registry, filter::FromEnvError, layer::SubscriberExt, registry::LookupSpan,
14};
15
16pub struct Console<W = io::Stderr> {
20 writer: W,
21 format: OutputFormat,
22 terminal: bool,
23 ansi: Option<bool>,
24 filter: EnvFilter,
25}
26
27impl Default for Console {
28 fn default() -> Self {
29 let writer = io::stderr();
30 let terminal = writer.is_terminal();
31 Self {
32 writer,
33 format: OutputFormat::Auto,
34 terminal,
35 ansi: None,
36 filter: EnvFilter::new("info"),
37 }
38 }
39}
40
41impl<W> Console<W> {
42 pub fn with_format(mut self, format: OutputFormat) -> Self {
44 self.format = format;
45 self
46 }
47
48 pub fn with_ansi(mut self, enabled: bool) -> Self {
50 self.ansi = Some(enabled);
51 self
52 }
53
54 pub fn with_writer<T>(self, writer: T) -> Console<T> {
56 Console {
57 writer,
58 format: self.format,
59 terminal: false,
60 ansi: self.ansi,
61 filter: self.filter,
62 }
63 }
64
65 pub fn with_filter(mut self, filter: EnvFilter) -> Self {
67 self.filter = filter;
68 self
69 }
70
71 pub fn with_env_filter(mut self) -> Result<Self, FromEnvError> {
73 self.filter = EnvFilter::builder()
74 .with_default_directive(tracing::level_filters::LevelFilter::INFO.into())
75 .from_env()?;
76 Ok(self)
77 }
78}
79
80impl<W: Write + Send + 'static> Console<W> {
81 pub fn layer<S>(self) -> impl Layer<S>
85 where
86 S: Subscriber + for<'lookup> LookupSpan<'lookup>,
87 {
88 let filter = self.filter.clone();
89 self.output_layer().with_filter(filter)
90 }
91
92 fn output_layer<S>(self) -> impl Layer<S>
93 where
94 S: Subscriber + for<'lookup> LookupSpan<'lookup>,
95 {
96 let format = self.format.resolve(self.terminal);
97 let ansi = self.ansi.unwrap_or(self.terminal) && format == OutputFormat::Text;
98 let formatter = ConsoleFormat {
99 format,
100 started: Instant::now(),
101 ansi,
102 };
103 let output = tracing_subscriber::fmt::layer()
104 .fmt_fields(ConsoleFields)
105 .event_format(formatter)
106 .with_ansi(ansi)
107 .with_writer(Mutex::new(RecordWriter(self.writer)));
108 ErrorLayer::new(ConsoleFields).and_then(output)
109 }
110
111 pub fn subscriber(self) -> impl Subscriber + Send + Sync {
113 let filter = self.filter.clone();
114 Registry::default().with(filter).with(self.output_layer())
115 }
116
117 #[track_caller]
125 pub fn init(self) {
126 self.try_init().expect("could not initialize the console");
127 }
128
129 pub fn try_init(self) -> Result<(), SetGlobalDefaultError> {
133 tracing::subscriber::set_global_default(self.subscriber())
134 }
135}
136
137struct RecordWriter<W>(W);
139
140impl<W: Write> Write for RecordWriter<W> {
141 fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
142 let written = self.0.write(bytes)?;
143 self.0.flush()?;
144 Ok(written)
145 }
146
147 fn flush(&mut self) -> io::Result<()> {
148 self.0.flush()
149 }
150}
151
152#[cfg(test)]
153mod tests;