@@ -42,6 +42,8 @@ pub struct EventsFormatter {
4242 pub ( crate ) with_location : bool ,
4343 pub ( crate ) with_module_path : bool ,
4444 pub ( crate ) with_timestamp : bool ,
45+ pub ( crate ) with_thread_names : bool ,
46+ pub ( crate ) with_thread_ids : bool ,
4547 #[ cfg( feature = "ansi_logs" ) ]
4648 pub ( crate ) with_ansi_color : bool ,
4749}
@@ -56,6 +58,8 @@ impl Default for EventsFormatter {
5658 with_location : false ,
5759 with_module_path : false ,
5860 with_timestamp : true ,
61+ with_thread_names : false ,
62+ with_thread_ids : false ,
5963 #[ cfg( feature = "ansi_logs" ) ]
6064 with_ansi_color : default_enable_ansi_color ( ) ,
6165 }
@@ -135,6 +139,29 @@ where
135139 serializer. serialize_entry ( "target" , metadata. target ( ) ) ?;
136140 }
137141
142+ // Use same logic as tracing-subscriber for thread names and ids
143+ // https://github.com/tokio-rs/tracing/blob/efc690fa6bd1d9c3a57528b9bc8ac80504a7a6ed/tracing-subscriber/src/fmt/format/json.rs#L306
144+ if self . with_thread_names {
145+ let current_thread = std:: thread:: current ( ) ;
146+ match current_thread. name ( ) {
147+ Some ( name) => {
148+ serializer. serialize_entry ( "thread.name" , name) ?;
149+ }
150+ // fall-back to thread id when name is absent and ids are not enabled
151+ None if !self . with_thread_ids => {
152+ serializer. serialize_entry (
153+ "thread.name" ,
154+ & format ! ( "{:?}" , current_thread. id( ) ) ,
155+ ) ?;
156+ }
157+ _ => { }
158+ }
159+ }
160+
161+ if self . with_thread_ids {
162+ serializer. serialize_entry_no_quote ( "thread.id" , std:: thread:: current ( ) . id ( ) ) ?;
163+ }
164+
138165 let span = if self . with_span_name || self . with_span_path {
139166 event
140167 . parent ( )
@@ -401,6 +428,36 @@ mod tests {
401428 builder:: builder ( ) . subscriber_builder ( )
402429 }
403430
431+ #[ test]
432+ #[ cfg( not( feature = "ansi_logs" ) ) ]
433+ fn test_enable_thread_name_and_id ( ) {
434+ use tracing:: subscriber;
435+
436+ let mock_writer = MockMakeWriter :: new ( ) ;
437+ let subscriber = builder:: builder ( )
438+ . with_thread_names ( true )
439+ . with_thread_ids ( true )
440+ . subscriber_builder ( )
441+ . with_writer ( mock_writer. clone ( ) )
442+ . finish ( ) ;
443+
444+ std:: thread:: Builder :: new ( )
445+ . name ( "worker-1" . to_string ( ) )
446+ . spawn ( move || {
447+ subscriber:: with_default ( subscriber, || {
448+ tracing:: info!( "message" ) ;
449+ } ) ;
450+ } )
451+ . unwrap ( )
452+ . join ( )
453+ . unwrap ( ) ;
454+
455+ let content = mock_writer. get_content ( ) ;
456+ println ! ( "{:?}" , content) ;
457+ assert ! ( content. contains( "thread.name=worker-1" ) ) ;
458+ assert ! ( content. contains( "thread.id=" ) ) ;
459+ }
460+
404461 #[ test]
405462 #[ cfg( not( feature = "ansi_logs" ) ) ]
406463 fn test_span_and_span_path_with_quoting ( ) {
@@ -509,6 +566,40 @@ mod tests {
509566 ) ;
510567 }
511568
569+ #[ test]
570+ #[ cfg( feature = "ansi_logs" ) ]
571+ fn test_enable_thread_name_and_id ( ) {
572+ use tracing:: subscriber;
573+
574+ let mock_writer = MockMakeWriter :: new ( ) ;
575+ let subscriber = builder:: builder ( )
576+ . with_thread_names ( true )
577+ . with_thread_ids ( true )
578+ . subscriber_builder ( )
579+ . with_writer ( mock_writer. clone ( ) )
580+ . finish ( ) ;
581+
582+ std:: thread:: Builder :: new ( )
583+ . name ( "worker-1" . to_string ( ) )
584+ . spawn ( move || {
585+ subscriber:: with_default ( subscriber, || {
586+ tracing:: info!( "message" ) ;
587+ } ) ;
588+ } )
589+ . unwrap ( )
590+ . join ( )
591+ . unwrap ( ) ;
592+
593+ let content = mock_writer. get_content ( ) ;
594+
595+ let thread_name_prefix = make_ansi_key_value ( "thread.name" , "=" ) ;
596+ let thread_id_prefix = make_ansi_key_value ( "thread.id" , "=" ) ;
597+
598+ println ! ( "{:?}" , content) ;
599+ assert ! ( content. contains( & ( thread_name_prefix + "worker-1" ) ) ) ;
600+ assert ! ( content. contains( & thread_id_prefix) ) ;
601+ }
602+
512603 #[ test]
513604 #[ cfg( feature = "ansi_logs" ) ]
514605 fn test_span_and_span_path_with_quoting ( ) {
0 commit comments