@@ -21,6 +21,22 @@ pub enum StdStream {
2121 Stderr ,
2222}
2323
24+ impl StdStream {
25+ fn via_write ( & self ) -> StdStreamDest < io:: StdoutLock < ' _ > , io:: StderrLock < ' _ > > {
26+ match self {
27+ Self :: Stdout => StdStreamDest :: Stdout ( io:: stdout ( ) . lock ( ) ) ,
28+ Self :: Stderr => StdStreamDest :: Stderr ( io:: stderr ( ) . lock ( ) ) ,
29+ }
30+ }
31+
32+ fn via_macro ( & self ) -> StdStreamDest < via_macro:: Stdout , via_macro:: Stderr > {
33+ match self {
34+ Self :: Stdout => StdStreamDest :: Stdout ( via_macro:: Stdout ) ,
35+ Self :: Stderr => StdStreamDest :: Stderr ( via_macro:: Stderr ) ,
36+ }
37+ }
38+ }
39+
2440// `io::stdout()` and `io::stderr()` return different types, and
2541// `Std***::lock()` is not in any trait, so we need this struct to abstract
2642// them.
@@ -30,27 +46,12 @@ enum StdStreamDest<O, E> {
3046 Stderr ( E ) ,
3147}
3248
33- impl StdStreamDest < io:: Stdout , io:: Stderr > {
34- #[ must_use]
35- fn new ( stream : StdStream ) -> Self {
36- match stream {
37- StdStream :: Stdout => StdStreamDest :: Stdout ( io:: stdout ( ) ) ,
38- StdStream :: Stderr => StdStreamDest :: Stderr ( io:: stderr ( ) ) ,
39- }
40- }
41-
42- #[ must_use]
43- fn lock ( & self ) -> StdStreamDest < io:: StdoutLock < ' _ > , io:: StderrLock < ' _ > > {
44- match self {
45- StdStreamDest :: Stdout ( stream) => StdStreamDest :: Stdout ( stream. lock ( ) ) ,
46- StdStreamDest :: Stderr ( stream) => StdStreamDest :: Stderr ( stream. lock ( ) ) ,
47- }
48- }
49-
49+ impl < O , E > StdStreamDest < O , E > {
50+ #[ allow( dead_code) ]
5051 fn stream_type ( & self ) -> StdStream {
5152 match self {
52- StdStreamDest :: Stdout ( _) => StdStream :: Stdout ,
53- StdStreamDest :: Stderr ( _) => StdStream :: Stderr ,
53+ Self :: Stdout ( _) => StdStream :: Stdout ,
54+ Self :: Stderr ( _) => StdStream :: Stderr ,
5455 }
5556 }
5657}
@@ -74,8 +75,42 @@ macro_rules! impl_write_for_dest {
7475 }
7576 } ;
7677}
77- impl_write_for_dest ! ( StdStreamDest <io:: Stdout , io:: Stderr >) ;
7878impl_write_for_dest ! ( StdStreamDest <io:: StdoutLock <' _>, io:: StderrLock <' _>>) ;
79+ impl_write_for_dest ! ( StdStreamDest <via_macro:: Stdout , via_macro:: Stderr >) ;
80+
81+ mod via_macro {
82+ use super :: * ;
83+
84+ fn bytes_to_str ( buf : & [ u8 ] ) -> io:: Result < & str > {
85+ str:: from_utf8 ( buf) . map_err ( |err| io:: Error :: new ( io:: ErrorKind :: InvalidData , err) )
86+ }
87+
88+ pub ( crate ) struct Stdout ;
89+
90+ impl Write for Stdout {
91+ fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
92+ print ! ( "{}" , bytes_to_str( buf) ?) ;
93+ Ok ( buf. len ( ) )
94+ }
95+
96+ fn flush ( & mut self ) -> io:: Result < ( ) > {
97+ io:: stdout ( ) . flush ( )
98+ }
99+ }
100+
101+ pub ( crate ) struct Stderr ;
102+
103+ impl Write for Stderr {
104+ fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
105+ eprint ! ( "{}" , bytes_to_str( buf) ?) ;
106+ Ok ( buf. len ( ) )
107+ }
108+
109+ fn flush ( & mut self ) -> io:: Result < ( ) > {
110+ io:: stderr ( ) . flush ( )
111+ }
112+ }
113+ }
79114
80115/// A sink with a std stream as the target.
81116///
@@ -85,35 +120,39 @@ impl_write_for_dest!(StdStreamDest<io::StdoutLock<'_>, io::StderrLock<'_>>);
85120/// Note that this sink always flushes the buffer once with each logging.
86121pub struct StdStreamSink {
87122 prop : SinkProp ,
88- dest : StdStreamDest < io:: Stdout , io:: Stderr > ,
123+ via_print_macro : bool ,
124+ std_stream : StdStream ,
89125 should_render_style : bool ,
90126 level_styles : LevelStyles ,
91127}
92128
93129impl StdStreamSink {
94130 /// Gets a builder of `StdStreamSink` with default parameters:
95131 ///
96- /// | Parameter | Default Value |
97- /// |-------------------|-----------------------------|
98- /// | [level_filter] | `All` |
99- /// | [formatter] | `FullFormatter` |
100- /// | [error_handler] | [`ErrorHandler::default()`] |
101- /// | | |
102- /// | [std_stream] | *must be specified* |
103- /// | [style_mode] | `Auto` |
132+ /// | Parameter | Default Value |
133+ /// |-------------------|------------------------------------------------------|
134+ /// | [level_filter] | `All` |
135+ /// | [formatter] | `FullFormatter` |
136+ /// | [error_handler] | [`ErrorHandler::default()`] |
137+ /// | | |
138+ /// | [std_stream] | *must be specified* |
139+ /// | [style_mode] | `Auto` |
140+ /// | [via_print_macro] | `false`, or `true` if feature gate `test` is enabled |
104141 ///
105142 /// [level_filter]: StdStreamSinkBuilder::level_filter
106143 /// [formatter]: StdStreamSinkBuilder::formatter
107144 /// [error_handler]: StdStreamSinkBuilder::error_handler
108145 /// [`ErrorHandler::default()`]: crate::error::ErrorHandler::default()
109146 /// [std_stream]: StdStreamSinkBuilder::std_stream
110147 /// [style_mode]: StdStreamSinkBuilder::style_mode
148+ /// [via_print_macro]: StdStreamSinkBuilder::via_print_macro
111149 #[ must_use]
112150 pub fn builder ( ) -> StdStreamSinkBuilder < ( ) > {
113151 StdStreamSinkBuilder {
114152 prop : SinkProp :: default ( ) ,
115153 std_stream : ( ) ,
116154 style_mode : StyleMode :: Auto ,
155+ via_print_macro : cfg ! ( feature = "test" ) ,
117156 }
118157 }
119158
@@ -138,7 +177,7 @@ impl StdStreamSink {
138177
139178 /// Sets the style mode.
140179 pub fn set_style_mode ( & mut self , style_mode : StyleMode ) {
141- self . should_render_style = Self :: should_render_style ( style_mode, self . dest . stream_type ( ) ) ;
180+ self . should_render_style = Self :: should_render_style ( style_mode, self . std_stream ) ;
142181 }
143182
144183 #[ must_use]
@@ -171,8 +210,34 @@ impl Sink for StdStreamSink {
171210 . formatter ( )
172211 . format ( record, & mut string_buf, & mut ctx) ?;
173212
174- let mut dest = self . dest . lock ( ) ;
213+ if !self . via_print_macro {
214+ self . log_write ( record, & string_buf, & ctx, self . std_stream . via_write ( ) )
215+ } else {
216+ self . log_write ( record, & string_buf, & ctx, self . std_stream . via_macro ( ) )
217+ }
218+ }
219+
220+ fn flush ( & self ) -> Result < ( ) > {
221+ if !self . via_print_macro {
222+ self . std_stream . via_write ( ) . flush ( )
223+ } else {
224+ self . std_stream . via_macro ( ) . flush ( )
225+ }
226+ . map_err ( Error :: FlushBuffer )
227+ }
228+ }
175229
230+ impl StdStreamSink {
231+ fn log_write < O : Write , E : Write > (
232+ & self ,
233+ record : & Record ,
234+ string_buf : & StringBuf ,
235+ ctx : & FormatterContext < ' _ > ,
236+ mut dest : StdStreamDest < O , E > ,
237+ ) -> Result < ( ) >
238+ where
239+ StdStreamDest < O , E > : Write ,
240+ {
176241 ( || {
177242 // TODO: Simplify the if block when our MSRV reaches let-chain support.
178243 if self . should_render_style {
@@ -197,16 +262,12 @@ impl Sink for StdStreamSink {
197262
198263 // stderr is not buffered, so we don't need to flush it.
199264 // https://doc.rust-lang.org/std/io/fn.stderr.html
200- if let StdStreamDest :: Stdout ( _ ) = dest {
265+ if let StdStream :: Stdout = self . std_stream {
201266 dest. flush ( ) . map_err ( Error :: FlushBuffer ) ?;
202267 }
203268
204269 Ok ( ( ) )
205270 }
206-
207- fn flush ( & self ) -> Result < ( ) > {
208- self . dest . lock ( ) . flush ( ) . map_err ( Error :: FlushBuffer )
209- }
210271}
211272
212273// --------------------------------------------------
@@ -217,6 +278,7 @@ pub struct StdStreamSinkBuilder<ArgSS> {
217278 prop : SinkProp ,
218279 std_stream : ArgSS ,
219280 style_mode : StyleMode ,
281+ via_print_macro : bool ,
220282}
221283
222284impl < ArgSS > StdStreamSinkBuilder < ArgSS > {
@@ -245,6 +307,7 @@ impl<ArgSS> StdStreamSinkBuilder<ArgSS> {
245307 prop : self . prop ,
246308 std_stream,
247309 style_mode : self . style_mode ,
310+ via_print_macro : self . via_print_macro ,
248311 }
249312 }
250313
@@ -257,6 +320,33 @@ impl<ArgSS> StdStreamSinkBuilder<ArgSS> {
257320 self
258321 }
259322
323+ /// Specifies to use `print!` and `eprint!` macros for output.
324+ ///
325+ /// If enabled, the sink will use [`print!`] and [`eprint!`] macros instead
326+ /// of [`io::Write`] trait with [`io::stdout`] and [`io::stderr`] to output
327+ /// logs. This is useful if you want the logs to be [captured] by `cargo
328+ /// test` and `cargo bench`.
329+ ///
330+ /// This parameter is **optional**, and defaults to `false`, or defaults to
331+ /// `true` if feature gate `test` is enabled.
332+ ///
333+ /// A convienient way to enable it for `cargo test` and `cargo bench` is to
334+ /// add the following lines to your `Cargo.toml`:
335+ ///
336+ /// ```toml
337+ /// # Note that it's not [dependencies]
338+ ///
339+ /// [dev-dependencies]
340+ /// spdlog-rs = { version = "...", features = ["test"] }
341+ /// ```
342+ ///
343+ /// [captured]: https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output
344+ #[ must_use]
345+ pub fn via_print_macro ( mut self ) -> Self {
346+ self . via_print_macro = true ;
347+ self
348+ }
349+
260350 // Prop
261351 //
262352
@@ -305,7 +395,8 @@ impl StdStreamSinkBuilder<StdStream> {
305395 pub fn build ( self ) -> Result < StdStreamSink > {
306396 Ok ( StdStreamSink {
307397 prop : self . prop ,
308- dest : StdStreamDest :: new ( self . std_stream ) ,
398+ via_print_macro : self . via_print_macro ,
399+ std_stream : self . std_stream ,
309400 should_render_style : StdStreamSink :: should_render_style (
310401 self . style_mode ,
311402 self . std_stream ,
0 commit comments