11use crate :: storage_layer:: JsonStorage ;
2- use serde:: ser:: { SerializeMap , Serializer } ;
2+ use ahash:: { HashSet , HashSetExt } ;
3+ use serde:: ser:: { Serialize , SerializeMap , Serializer } ;
34use serde_json:: Value ;
45use std:: collections:: HashMap ;
56use std:: fmt;
67use std:: io:: Write ;
8+ use time:: format_description:: well_known:: Rfc3339 ;
79use tracing:: { Event , Id , Subscriber } ;
810use tracing_core:: metadata:: Level ;
911use tracing_core:: span:: Attributes ;
@@ -12,7 +14,6 @@ use tracing_subscriber::fmt::MakeWriter;
1214use tracing_subscriber:: layer:: Context ;
1315use tracing_subscriber:: registry:: SpanRef ;
1416use tracing_subscriber:: Layer ;
15- use time:: format_description:: well_known:: Rfc3339 ;
1617
1718/// Keys for core fields of the Bunyan format (https://github.com/trentm/node-bunyan#core-fields)
1819const BUNYAN_VERSION : & str = "v" ;
@@ -48,8 +49,26 @@ pub struct BunyanFormattingLayer<W: for<'a> MakeWriter<'a> + 'static> {
4849 bunyan_version : u8 ,
4950 name : String ,
5051 default_fields : HashMap < String , Value > ,
52+ skip_fields : HashSet < String > ,
53+ }
54+
55+ /// This error will be returned in [`BunyanFormattingLayer::skip_fields`] if trying to skip a core field.
56+ #[ non_exhaustive]
57+ #[ derive( Debug ) ]
58+ pub struct InvalidFieldError ( String ) ;
59+
60+ impl fmt:: Display for InvalidFieldError {
61+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
62+ write ! (
63+ f,
64+ "{} is a core field in the bunyan log format, it can't be skipped" ,
65+ & self . 0
66+ )
67+ }
5168}
5269
70+ impl std:: error:: Error for InvalidFieldError { }
71+
5372impl < W : for < ' a > MakeWriter < ' a > + ' static > BunyanFormattingLayer < W > {
5473 /// Create a new `BunyanFormattingLayer`.
5574 ///
@@ -74,15 +93,36 @@ impl<W: for<'a> MakeWriter<'a> + 'static> BunyanFormattingLayer<W> {
7493 Self :: with_default_fields ( name, make_writer, HashMap :: new ( ) )
7594 }
7695
77- pub fn with_default_fields ( name : String , make_writer : W , default_fields : HashMap < String , Value > ) -> Self {
96+ pub fn with_default_fields (
97+ name : String ,
98+ make_writer : W ,
99+ default_fields : HashMap < String , Value > ,
100+ ) -> Self {
78101 Self {
79102 make_writer,
80103 name,
81104 pid : std:: process:: id ( ) ,
82105 hostname : gethostname:: gethostname ( ) . to_string_lossy ( ) . into_owned ( ) ,
83106 bunyan_version : 0 ,
84107 default_fields,
108+ skip_fields : HashSet :: new ( ) ,
109+ }
110+ }
111+
112+ /// Add fields to skip when formatting with this layer.
113+ pub fn skip_fields < T > ( mut self , fields : & [ T ] ) -> Result < Self , InvalidFieldError >
114+ where
115+ T : AsRef < str > ,
116+ {
117+ for field in fields {
118+ let field = field. as_ref ( ) ;
119+ if BUNYAN_RESERVED_FIELDS . contains ( & field) {
120+ return Err ( InvalidFieldError ( field. to_string ( ) ) ) ;
121+ }
122+ self . skip_fields . insert ( field. to_string ( ) ) ;
85123 }
124+
125+ Ok ( self )
86126 }
87127
88128 fn serialize_bunyan_core_fields (
@@ -103,6 +143,22 @@ impl<W: for<'a> MakeWriter<'a> + 'static> BunyanFormattingLayer<W> {
103143 Ok ( ( ) )
104144 }
105145
146+ fn serialize_field < V > (
147+ & self ,
148+ map_serializer : & mut impl SerializeMap < Error = serde_json:: Error > ,
149+ key : & str ,
150+ value : & V ,
151+ ) -> Result < ( ) , std:: io:: Error >
152+ where
153+ V : Serialize + ?Sized ,
154+ {
155+ if !self . skip_fields . contains ( key) {
156+ map_serializer. serialize_entry ( key, value) ?;
157+ }
158+
159+ Ok ( ( ) )
160+ }
161+
106162 /// Given a span, it serialised it to a in-memory buffer (vector of bytes).
107163 fn serialize_span < S : Subscriber + for < ' a > tracing_subscriber:: registry:: LookupSpan < ' a > > (
108164 & self ,
@@ -117,27 +173,27 @@ impl<W: for<'a> MakeWriter<'a> + 'static> BunyanFormattingLayer<W> {
117173 // Additional metadata useful for debugging
118174 // They should be nested under `src` (see https://github.com/trentm/node-bunyan#src )
119175 // but `tracing` does not support nested values yet
120- map_serializer . serialize_entry ( "target" , span. metadata ( ) . target ( ) ) ?;
121- map_serializer . serialize_entry ( "line" , & span. metadata ( ) . line ( ) ) ?;
122- map_serializer . serialize_entry ( "file" , & span. metadata ( ) . file ( ) ) ?;
176+ self . serialize_field ( & mut map_serializer , "target" , span. metadata ( ) . target ( ) ) ?;
177+ self . serialize_field ( & mut map_serializer , "line" , & span. metadata ( ) . line ( ) ) ?;
178+ self . serialize_field ( & mut map_serializer , "file" , & span. metadata ( ) . file ( ) ) ?;
123179
124180 // Add all default fields
125181 for ( key, value) in self . default_fields . iter ( ) {
126182 if !BUNYAN_RESERVED_FIELDS . contains ( & key. as_str ( ) ) {
127- map_serializer . serialize_entry ( key, value) ?;
183+ self . serialize_field ( & mut map_serializer , key, value) ?;
128184 } else {
129185 tracing:: debug!(
130- "{} is a reserved field in the bunyan log format. Skipping it." ,
131- key
132- ) ;
186+ "{} is a reserved field in the bunyan log format. Skipping it." ,
187+ key
188+ ) ;
133189 }
134190 }
135191
136192 let extensions = span. extensions ( ) ;
137193 if let Some ( visitor) = extensions. get :: < JsonStorage > ( ) {
138194 for ( key, value) in visitor. values ( ) {
139195 if !BUNYAN_RESERVED_FIELDS . contains ( key) {
140- map_serializer . serialize_entry ( key, value) ?;
196+ self . serialize_field ( & mut map_serializer , key, value) ?;
141197 } else {
142198 tracing:: debug!(
143199 "{} is a reserved field in the bunyan log format. Skipping it." ,
@@ -252,16 +308,15 @@ where
252308 // Additional metadata useful for debugging
253309 // They should be nested under `src` (see https://github.com/trentm/node-bunyan#src )
254310 // but `tracing` does not support nested values yet
255- map_serializer . serialize_entry ( "target" , event. metadata ( ) . target ( ) ) ?;
256- map_serializer . serialize_entry ( "line" , & event. metadata ( ) . line ( ) ) ?;
257- map_serializer . serialize_entry ( "file" , & event. metadata ( ) . file ( ) ) ?;
311+ self . serialize_field ( & mut map_serializer , "target" , event. metadata ( ) . target ( ) ) ?;
312+ self . serialize_field ( & mut map_serializer , "line" , & event. metadata ( ) . line ( ) ) ?;
313+ self . serialize_field ( & mut map_serializer , "file" , & event. metadata ( ) . file ( ) ) ?;
258314
259315 // Add all default fields
260- for ( key, value) in self . default_fields
261- . iter ( )
262- . filter ( |( key, _) | key. as_str ( ) != "message" && !BUNYAN_RESERVED_FIELDS . contains ( & key. as_str ( ) ) )
263- {
264- map_serializer. serialize_entry ( key, value) ?;
316+ for ( key, value) in self . default_fields . iter ( ) . filter ( |( key, _) | {
317+ key. as_str ( ) != "message" && !BUNYAN_RESERVED_FIELDS . contains ( & key. as_str ( ) )
318+ } ) {
319+ self . serialize_field ( & mut map_serializer, key, value) ?;
265320 }
266321
267322 // Add all the other fields associated with the event, expect the message we already used.
@@ -270,7 +325,7 @@ where
270325 . iter ( )
271326 . filter ( |( & key, _) | key != "message" && !BUNYAN_RESERVED_FIELDS . contains ( & key) )
272327 {
273- map_serializer . serialize_entry ( key, value) ?;
328+ self . serialize_field ( & mut map_serializer , key, value) ?;
274329 }
275330
276331 // Add all the fields from the current span, if we have one.
@@ -279,7 +334,7 @@ where
279334 if let Some ( visitor) = extensions. get :: < JsonStorage > ( ) {
280335 for ( key, value) in visitor. values ( ) {
281336 if !BUNYAN_RESERVED_FIELDS . contains ( key) {
282- map_serializer . serialize_entry ( key, value) ?;
337+ self . serialize_field ( & mut map_serializer , key, value) ?;
283338 } else {
284339 tracing:: debug!(
285340 "{} is a reserved field in the bunyan log format. Skipping it." ,
0 commit comments