1

Below references to questions that have helped me get this far:

I currently have this:

# got from second reference link
class MultiLineFormatter(logging.Formatter):

    def get_header_length(self, record):
        # Get the header length of a given record
        return len(super().format(logging.LogRecord(name=record.name,
                                                    level=record.levelno,
                                                    pathname=record.pathname,
                                                    lineno=record.lineno,
                                                    msg='',
                                                    args=(),
                                                    exc_info=None)))

    def format(self, record):
        # Format a record with added indentation
        indent = ' ' * self.get_header_length(record)
        head, *trailing = super().format(record).splitlines(True)

        # return
        return head + ''.join(indent + line for line in trailing)



def report_logger(log_level: logging.INFO):
    # set format for logging style
    formatter = MultiLineFormatter(fmt='%(asctime)-8s - %(levelname)-8s - %(name)-30s  : %(message)s',
                                   datefmt='%y/%b/%Y %H:%M:%S', )

    # create console logger
    console = logging.StreamHandler()
    console.setLevel(log_level)

    # apply formatter to console logger
    console.setFormatter(formatter)

    # request logger
    final_logger = logging.getLogger(__name__)
    final_logger.setLevel(log_level)

    # prevent double logs in console
    final_logger.propagate = False

    # add handler
    final_logger.addHandler(console)

    # return
    return final_logger

def print_request(request_data):
    format_headers = lambda d: '\n                '.join(f'{k}: {v}' for k, v in d.items())

    request_body = json.dumps(request_data.req_body, indent=20, sort_keys=True, default=str)
    response_text = json.dumps(request_data.resp_text, indent=20, sort_keys=True, default=str)
    response_body = json.dumps(request_data.resp_as_dict, indent=20, sort_keys=True, default=str)

    msg_print = f'---------------- Request ----------------\n' \
                f'Headers     :   {format_headers(request_data.req_headers)}\n' \
                f'URL         :   {request_data.req_url}\n' \
                f'Method      :   {request_data.req_method}\n' \
                f'Body        :   {request_body}\n' \
                f'\n' \
                f'---------------- Response ----------------\n' \
                f'Headers     :   {format_headers(request_data.resp_headers)}\n' \
                f'Status Code :   {request_data.resp_status_code}\n' \
                f'Text        :   {response_text}\n' \
                f'Response    :   {response_body}\n'

    logger.info(msg_print)

At the moment, it's pretty much on par for what I'm trying to achieve, however, I'm just trying to get the output of the response bodies a little more "nice".

In the last line above logger.info(msg_print) I get this output:

22/Feb/2022 16:11:09 - INFO     - logger_function                 : ---------------- Request ----------------
                                                                    Headers     :   User-Agent: python-requests/2.25.1
                                                                                    Accept-Encoding: gzip, deflate
                                                                                    Accept: */*
                                                                                    Connection: keep-alive
                                                                                    authorization: AuthToken
                                                                    URL         :   my_url
                                                                    Method      :   GET
                                                                    Body        :   null
                                                                    
                                                                    ---------------- Response ----------------
                                                                    Headers     :   content-type: application/json
                                                                                    Content-Length: 119
                                                                                    x-envoy-upstream-service-time: 187
                                                                                    date: today
                                                                                    server: server
                                                                                    Via: 1.1 service
                                                                                    Alt-Svc: alt_service
                                                                    Status Code :   401
                                                                    Text        :   {
                                                                                        "code": 111111,
                                                                                        "component": "ABC",
                                                                                        "errorType": "DEF",
                                                                                        "message": "",
                                                                                        "traceId": UUID4
                                                                    }
                                                                    Response    :   {
                                                                                        "code": 111111,
                                                                                        "component": "ABC",
                                                                                        "errorType": "DEF",
                                                                                        "message": "",
                                                                                        "traceId": UUID4
                                                                    }

But I'd really like to get those dictionaries to be like this:

22/Feb/2022 16:11:09 - INFO     - logger_function                 : ---------------- Response ----------------
                                                                    Text        :   {"code": 111111,
                                                                                     "component": "ABC",
                                                                                     "errorType": "DEF",
                                                                                     "message": "",
                                                                                     "traceId": UUID4}
                                                                    Response    :   {"code": 111111,
                                                                                     "component": "ABC",
                                                                                     "errorType": "DEF",
                                                                                     "message": "",
                                                                                     "traceId": UUID4}

Edit:

Sample dict with nested values:

{
    "level1": {
        "myInt": "Original",
        "level2": {
            "myInt": "Original",
            "myBool": "Original",
            "level3": {
                "myBool": "Original"
            }
        }
    },
    "level4": [
        {
            "myList": "Original"
        },
        {
            "myList": "Original"
        }
        ,
        {
            "myList": "Original"
        }
    ]
}
Eitel Dagnin
  • 959
  • 4
  • 24
  • 61
  • Is it really important that your log has formatted json? I would post process the log and format the json there is I needed it to be human friendly. – JonSG Feb 03 '22 at 17:09
  • @JonSG I wouldn't say it's absolutely crucial for it to be formatted, but it does create a nicer to read log file. I also wouldn't be completely opposed to pot-processing, but since I am able get 99% of what I require done at run time, I wouldn't really want to add extra effort after the fact. But if you have some ideas, please do share :) – Eitel Dagnin Feb 03 '22 at 18:41

1 Answers1

2

Though it seems a bit fragile, it looks like you can slightly adjust the indentation then use the re package to strip out the unwanted spacing between the leading and closing curly braces.

import json
import re

req_body = {"code": 111111, "component": "ABC", "errorType": "DEF", "message": "", "traceId": "UUID4"}
resp_text = {"code": 111111, "component": "ABC", "errorType": "DEF", "message": "", "traceId": "UUID4"}

request_body = json.dumps(req_body, indent=20, sort_keys=True, default=str)

response_text = json.dumps(resp_text, indent=17, sort_keys=True, default=str)
response_text = re.sub(r"^{\s*", "{", response_text)
response_text = re.sub(r"\s*}$", "}", response_text)

msg_print = f'---------------- Response ----------------\n' \
            f'Request     :   {request_body}\n' \
            f'Response    :   {response_text}\n'

print(msg_print)

That looks like it might give you what you are after:

---------------- Response ----------------
Request     :   {
                    "code": 111111,
                    "component": "ABC",
                    "errorType": "DEF",
                    "message": "",
                    "traceId": "UUID4"
}
Response    :   {"code": 111111,
                 "component": "ABC",
                 "errorType": "DEF",
                 "message": "",
                 "traceId": "UUID4"}
JonSG
  • 10,542
  • 2
  • 25
  • 36
  • Coming back to this after discovering something else.. This approach doesn't seem to work completely if you have nested `dict` or `list`.. It gets formatted like the request body in your answer and not the response body.. Do you perhaps know how to include nested `dict` and `list`? – Eitel Dagnin Mar 11 '22 at 09:49
  • Sure, can you update your question with an example of that data and the data? – JonSG Mar 11 '22 at 13:40
  • 1
    Sure, just added it now. Thank you for taking the time to come to this :) – Eitel Dagnin Mar 11 '22 at 14:00