@@ -128,8 +128,7 @@ def decorated_with_check_trailing_dot(*args):
128128 msg = record .msg
129129 if isinstance (msg , str ) and msg .endswith ("." ) and not msg .endswith (".." ):
130130 err_msg = (
131- f"Log message is not allowed to have a trailing dot:"
132- f' { record .name } : "{ msg } "'
131+ f'Log message is not allowed to have a trailing dot: { record .name } : "{ msg } "'
133132 )
134133 raise AssertionError (err_msg )
135134 args = list (args )
@@ -189,15 +188,20 @@ class StructuredHandler(logging.Handler):
189188
190189 """logging handler for structured logging."""
191190
192- default_fields = set (logging .LogRecord ("" , logging .NOTSET , "" , 1 , "msg" , (), None ).__dict__ )
191+ default_fields = set (
192+ logging .LogRecord ("" , logging .NOTSET , "" , 1 , "msg" , (), None ).__dict__
193+ ).union ({"discard_msg" , "discard_args" })
193194
194195 def __init__ (
195- self , level = logging .NOTSET , level_from_msg : Callable [[str ], str | None ] | None = None
196+ self ,
197+ level = logging .NOTSET ,
198+ level_from_msg : Callable [[str ], str | None ] | None = None ,
196199 ):
197200 """Initialize a new StructuredHandler."""
198201 super ().__init__ (level )
199202 self .local = threading .local ()
200203 self .level_from_msg = level_from_msg if level_from_msg is not None else lambda _ : None
204+ self .json = json
201205
202206 def emit (self , record : logging .LogRecord ): # noqa: PLR0912
203207 """Print the log record formatted as JSON to stdout."""
@@ -210,17 +214,20 @@ def emit(self, record: logging.LogRecord): # noqa: PLR0912
210214 level = level .lower ()
211215 if msg .startswith ("{" ):
212216 try :
213- msg = json .loads (msg )
217+ msg = self . json .loads (msg )
214218 except json .JSONDecodeError :
215219 pass
216220 obj = {
217221 "level" : level ,
218- "msg" : msg ,
219222 "source" : "%s:%d" % (record .filename , record .lineno ),
220223 "time" : format_datetime (created ),
221224 "thread" : reduce_thread_id (record .thread ),
222225 "name" : record .name ,
223226 }
227+ if not getattr (record , "discard_msg" , False ):
228+ obj ["msg" ] = msg
229+ if not getattr (record , "discard_args" , False ):
230+ obj ["args" ] = record .args
224231 obj .update ((k , getattr (record , k )) for k in record .__dict__ .keys () - self .default_fields )
225232 try :
226233 rank = os .environ ["RANK" ]
@@ -237,12 +244,21 @@ def emit(self, record: logging.LogRecord): # noqa: PLR0912
237244 obj ["context" ] = self .local .context
238245 except AttributeError :
239246 pass
240- print (json .dumps (obj , sort_keys = True ), file = sys .stdout , flush = True ) # noqa: T201
247+ print ( # noqa: T201
248+ self .json .dumps (obj , sort_keys = True , default = self .json_fallback ),
249+ file = sys .stdout ,
250+ flush = True ,
251+ )
241252
242253 def flush (self ):
243254 """Write all pending text to stdout."""
244255 sys .stdout .flush ()
245256
257+ @classmethod
258+ def json_fallback (cls , obj ) -> str :
259+ """Format an object that `StructuredHandler.json` doesn't support."""
260+ return f"<{ type (obj ).__name__ } >"
261+
246262
247263def setup (
248264 level : str | int | None = os .environ .get ("LOG_LEVEL" , "INFO" ),
0 commit comments