3535
3636use std:: env:: { self , VarError } ;
3737use std:: fmt:: { self , Display } ;
38+ use std:: fs:: File ;
3839use std:: io:: { self , IsTerminal } ;
40+ use std:: sync:: Mutex ;
3941
4042use tracing:: dispatcher:: SetGlobalDefaultError ;
4143use tracing:: { Event , Subscriber } ;
42- use tracing_subscriber:: Registry ;
4344use tracing_subscriber:: filter:: { Directive , EnvFilter , LevelFilter } ;
4445use tracing_subscriber:: fmt:: FmtContext ;
45- use tracing_subscriber:: fmt:: format:: { self , FormatEvent , FormatFields } ;
46+ use tracing_subscriber:: fmt:: format:: { self , FmtSpan , FormatEvent , FormatFields } ;
47+ use tracing_subscriber:: fmt:: writer:: BoxMakeWriter ;
4648use tracing_subscriber:: layer:: SubscriberExt ;
49+ use tracing_subscriber:: { Layer , Registry } ;
4750// Re-export tracing
4851pub use { tracing, tracing_core, tracing_subscriber} ;
4952
@@ -55,12 +58,15 @@ pub struct LoggerConfig {
5558 pub verbose_entry_exit : Result < String , VarError > ,
5659 pub verbose_thread_ids : Result < String , VarError > ,
5760 pub backtrace : Result < String , VarError > ,
61+ pub json : Result < String , VarError > ,
62+ pub output_target : Result < String , VarError > ,
5863 pub wraptree : Result < String , VarError > ,
5964 pub lines : Result < String , VarError > ,
6065}
6166
6267impl LoggerConfig {
6368 pub fn from_env ( env : & str ) -> Self {
69+ // NOTE: documented in the dev guide. If you change this, also update it!
6470 LoggerConfig {
6571 filter : env:: var ( env) ,
6672 color_logs : env:: var ( format ! ( "{env}_COLOR" ) ) ,
@@ -69,6 +75,8 @@ impl LoggerConfig {
6975 backtrace : env:: var ( format ! ( "{env}_BACKTRACE" ) ) ,
7076 wraptree : env:: var ( format ! ( "{env}_WRAPTREE" ) ) ,
7177 lines : env:: var ( format ! ( "{env}_LINES" ) ) ,
78+ json : env:: var ( format ! ( "{env}_FORMAT_JSON" ) ) ,
79+ output_target : env:: var ( format ! ( "{env}_OUTPUT_TARGET" ) ) ,
7280 }
7381 }
7482}
@@ -136,23 +144,59 @@ where
136144 Err ( _) => false ,
137145 } ;
138146
139- let mut layer = tracing_tree:: HierarchicalLayer :: default ( )
140- . with_writer ( io:: stderr)
141- . with_ansi ( color_logs)
142- . with_targets ( true )
143- . with_verbose_exit ( verbose_entry_exit)
144- . with_verbose_entry ( verbose_entry_exit)
145- . with_indent_amount ( 2 )
146- . with_indent_lines ( lines)
147- . with_thread_ids ( verbose_thread_ids)
148- . with_thread_names ( verbose_thread_ids) ;
149-
150- if let Ok ( v) = cfg. wraptree {
151- match v. parse :: < usize > ( ) {
152- Ok ( v) => layer = layer. with_wraparound ( v) ,
153- Err ( _) => return Err ( Error :: InvalidWraptree ( v) ) ,
147+ let json = match cfg. json {
148+ Ok ( v) => & v == "1" ,
149+ Err ( _) => false ,
150+ } ;
151+
152+ let output_target: BoxMakeWriter = match cfg. output_target {
153+ Ok ( v) => match File :: options ( ) . create ( true ) . write ( true ) . truncate ( true ) . open ( & v) {
154+ Ok ( i) => BoxMakeWriter :: new ( Mutex :: new ( i) ) ,
155+ Err ( e) => {
156+ eprintln ! ( "couldn't open {v} as a log target: {e:?}" ) ;
157+ BoxMakeWriter :: new ( io:: stderr)
158+ }
159+ } ,
160+ Err ( _) => BoxMakeWriter :: new ( io:: stderr) ,
161+ } ;
162+
163+ let layer = if json {
164+ let format = tracing_subscriber:: fmt:: format ( )
165+ . json ( )
166+ . with_span_list ( true )
167+ . with_source_location ( true ) ;
168+
169+ let fmt_layer = tracing_subscriber:: fmt:: layer ( )
170+ . json ( )
171+ . event_format ( format)
172+ . with_writer ( output_target)
173+ . with_target ( true )
174+ . with_ansi ( false )
175+ . with_thread_ids ( verbose_thread_ids)
176+ . with_thread_names ( verbose_thread_ids)
177+ . with_span_events ( FmtSpan :: ACTIVE ) ;
178+ Layer :: boxed ( fmt_layer)
179+ } else {
180+ let mut layer = tracing_tree:: HierarchicalLayer :: default ( )
181+ . with_writer ( output_target)
182+ . with_ansi ( color_logs)
183+ . with_targets ( true )
184+ . with_verbose_exit ( verbose_entry_exit)
185+ . with_verbose_entry ( verbose_entry_exit)
186+ . with_indent_amount ( 2 )
187+ . with_indent_lines ( lines)
188+ . with_thread_ids ( verbose_thread_ids)
189+ . with_thread_names ( verbose_thread_ids) ;
190+
191+ if let Ok ( v) = cfg. wraptree {
192+ match v. parse :: < usize > ( ) {
193+ Ok ( v) => layer = layer. with_wraparound ( v) ,
194+ Err ( _) => return Err ( Error :: InvalidWraptree ( v) ) ,
195+ }
154196 }
155- }
197+
198+ Layer :: boxed ( layer)
199+ } ;
156200
157201 let subscriber = build_subscriber ( ) ;
158202 // NOTE: It is important to make sure that the filter is applied on the last layer
0 commit comments