@@ -246,6 +246,7 @@ def on_request(self, request):
246246
247247
248248class NetworkTraceLoggingPolicy (SansIOHTTPPolicy ):
249+
249250 """The logging policy in the pipeline is used to output HTTP network trace to the configured logger.
250251
251252 This accepts both global configuration, and per-request level with "enable_http_logger"
@@ -264,7 +265,7 @@ class NetworkTraceLoggingPolicy(SansIOHTTPPolicy):
264265 def __init__ (self , logging_enable = False , ** kwargs ): # pylint: disable=unused-argument
265266 self .enable_http_logger = logging_enable
266267
267- def on_request (self , request ):
268+ def on_request (self , request ): # pylint: disable=too-many-return-statements
268269 # type: (PipelineRequest) -> None
269270 """Logs HTTP request to the DEBUG logger.
270271
@@ -280,27 +281,31 @@ def on_request(self, request):
280281 return
281282
282283 try :
283- _LOGGER . debug ( "Request URL: %r" , http_request .url )
284- _LOGGER . debug ( "Request method: %r" , http_request .method )
285- _LOGGER . debug ( "Request headers:")
284+ log_string = "Request URL: '{}'" . format ( http_request .url )
285+ log_string += "/nRequest method: '{}'" . format ( http_request .method )
286+ log_string += "/nRequest headers:"
286287 for header , value in http_request .headers .items ():
287- _LOGGER . debug ( " %r: %r" , header , value )
288- _LOGGER . debug ( "Request body:")
288+ log_string += "/n '{}': '{}'" . format ( header , value )
289+ log_string += "/nRequest body:"
289290
290291 # We don't want to log the binary data of a file upload.
291292 if isinstance (http_request .body , types .GeneratorType ):
292- _LOGGER .debug ("File upload" )
293+ log_string += "/nFile upload"
294+ _LOGGER .debug (log_string )
293295 return
294296 try :
295297 if isinstance (http_request .body , types .AsyncGeneratorType ):
296- _LOGGER .debug ("File upload" )
298+ log_string += "/nFile upload"
299+ _LOGGER .debug (log_string )
297300 return
298301 except AttributeError :
299302 pass
300303 if http_request .body :
301- _LOGGER .debug (str (http_request .body ))
304+ log_string += "/n{}" .format (str (http_request .body ))
305+ _LOGGER .debug (log_string )
302306 return
303- _LOGGER .debug ("This request has no body" )
307+ log_string += "/nThis request has no body"
308+ _LOGGER .debug (log_string )
304309 except Exception as err : # pylint: disable=broad-except
305310 _LOGGER .debug ("Failed to log request: %r" , err )
306311
@@ -320,28 +325,29 @@ def on_response(self, request, response):
320325 if not _LOGGER .isEnabledFor (logging .DEBUG ):
321326 return
322327
323- _LOGGER . debug ( "Response status: %r" , http_response .status_code )
324- _LOGGER . debug ( "Response headers:")
328+ log_string = "Response status: '{}'" . format ( http_response .status_code )
329+ log_string += "/nResponse headers:"
325330 for res_header , value in http_response .headers .items ():
326- _LOGGER . debug ( " %r: %r" , res_header , value )
331+ log_string += "/n '{}': '{}'" . format ( res_header , value )
327332
328333 # We don't want to log binary data if the response is a file.
329- _LOGGER . debug ( "Response content:")
334+ log_string += "/nResponse content:"
330335 pattern = re .compile (r'attachment; ?filename=["\w.]+' , re .IGNORECASE )
331336 header = http_response .headers .get ('content-disposition' )
332337
333338 if header and pattern .match (header ):
334339 filename = header .partition ('=' )[2 ]
335- _LOGGER . debug ( "File attachments: %s" , filename )
340+ log_string += "/nFile attachments: {}" . format ( filename )
336341 elif http_response .headers .get ("content-type" , "" ).endswith ("octet-stream" ):
337- _LOGGER . debug ( "Body contains binary data.")
342+ log_string += "/nBody contains binary data."
338343 elif http_response .headers .get ("content-type" , "" ).startswith ("image" ):
339- _LOGGER . debug ( "Body contains image data.")
344+ log_string += "/nBody contains image data."
340345 else :
341346 if response .context .options .get ('stream' , False ):
342- _LOGGER . debug ( "Body is streamable" )
347+ log_string += "/nBody is streamable."
343348 else :
344- _LOGGER .debug (http_response .text ())
349+ log_string += "/n{}" .format (http_response .text ())
350+ _LOGGER .debug (log_string )
345351 except Exception as err : # pylint: disable=broad-except
346352 _LOGGER .debug ("Failed to log response: %s" , repr (err ))
347353
@@ -376,6 +382,7 @@ class HttpLoggingPolicy(SansIOHTTPPolicy):
376382 "User-Agent" ,
377383 ])
378384 REDACTED_PLACEHOLDER = "REDACTED"
385+ MULTI_RECORD_LOG = "AZURE_SDK_LOGGING_MULTIRECORD"
379386
380387 def __init__ (self , logger = None , ** kwargs ): # pylint: disable=unused-argument
381388 self .logger = logger or logging .getLogger (
@@ -396,7 +403,7 @@ def _redact_header(self, key, value):
396403 ]
397404 return value if key .lower () in lower_case_allowed_header_names else HttpLoggingPolicy .REDACTED_PLACEHOLDER
398405
399- def on_request (self , request ):
406+ def on_request (self , request ): # pylint: disable=too-many-return-statements
400407 # type: (PipelineRequest) -> None
401408 """Logs HTTP method, url and headers.
402409 :param request: The PipelineRequest object.
@@ -420,26 +427,52 @@ def on_request(self, request):
420427 parsed_url [4 ] = "&" .join (["=" .join (part ) for part in filtered_qp ])
421428 redacted_url = urllib .parse .urlunparse (parsed_url )
422429
423- logger .info ("Request URL: %r" , redacted_url )
424- logger .info ("Request method: %r" , http_request .method )
425- logger .info ("Request headers:" )
430+ multi_record = os .environ .get (HttpLoggingPolicy .MULTI_RECORD_LOG , False )
431+ if multi_record :
432+ logger .info ("Request URL: %r" , redacted_url )
433+ logger .info ("Request method: %r" , http_request .method )
434+ logger .info ("Request headers:" )
435+ for header , value in http_request .headers .items ():
436+ value = self ._redact_header (header , value )
437+ logger .info (" %r: %r" , header , value )
438+ if isinstance (http_request .body , types .GeneratorType ):
439+ logger .info ("File upload" )
440+ return
441+ try :
442+ if isinstance (http_request .body , types .AsyncGeneratorType ):
443+ logger .info ("File upload" )
444+ return
445+ except AttributeError :
446+ pass
447+ if http_request .body :
448+ logger .info ("A body is sent with the request" )
449+ return
450+ logger .info ("No body was attached to the request" )
451+ return
452+ log_string = "Request URL: '{}'" .format (redacted_url )
453+ log_string += "/nRequest method: '{}'" .format (http_request .method )
454+ log_string += "/nRequest headers:"
426455 for header , value in http_request .headers .items ():
427456 value = self ._redact_header (header , value )
428- logger . info ( " %r: %r" , header , value )
457+ log_string += "/n '{}': '{}'" . format ( header , value )
429458 if isinstance (http_request .body , types .GeneratorType ):
430- logger .info ("File upload" )
459+ log_string += "/nFile upload"
460+ logger .info (log_string )
431461 return
432462 try :
433463 if isinstance (http_request .body , types .AsyncGeneratorType ):
434- logger .info ("File upload" )
464+ log_string += "/nFile upload"
465+ logger .info (log_string )
435466 return
436467 except AttributeError :
437468 pass
438469 if http_request .body :
439- logger .info ("A body is sent with the request" )
470+ log_string += "/nA body is sent with the request"
471+ logger .info (log_string )
440472 return
441- logger .info ("No body was attached to the request" )
442- return
473+ log_string += "/nNo body was attached to the request"
474+ logger .info (log_string )
475+
443476 except Exception as err : # pylint: disable=broad-except
444477 logger .warning ("Failed to log request: %s" , repr (err ))
445478
@@ -453,15 +486,23 @@ def on_response(self, request, response):
453486 if not logger .isEnabledFor (logging .INFO ):
454487 return
455488
456- logger .info ("Response status: %r" , http_response .status_code )
457- logger .info ("Response headers:" )
489+ multi_record = os .environ .get (HttpLoggingPolicy .MULTI_RECORD_LOG , False )
490+ if multi_record :
491+ logger .info ("Response status: %r" , http_response .status_code )
492+ logger .info ("Response headers:" )
493+ for res_header , value in http_response .headers .items ():
494+ value = self ._redact_header (res_header , value )
495+ logger .info (" %r: %r" , res_header , value )
496+ return
497+ log_string = "Response status: {}" .format (http_response .status_code )
498+ log_string += "/nResponse headers:"
458499 for res_header , value in http_response .headers .items ():
459500 value = self ._redact_header (res_header , value )
460- logger .info (" %r: %r" , res_header , value )
501+ log_string += "/n '{}': '{}'" .format (res_header , value )
502+ logger .info (log_string )
461503 except Exception as err : # pylint: disable=broad-except
462504 logger .warning ("Failed to log response: %s" , repr (err ))
463505
464-
465506class ContentDecodePolicy (SansIOHTTPPolicy ):
466507 """Policy for decoding unstreamed response content.
467508
0 commit comments