Search code examples
pythonpython-logging

Is there a way to print a formatted dictionary to a Python log file?


I've got a logging handler setup that prints to stream and file. I'm working with a few dictionaries and modifying dictionaries. Is there a way to format a dictionary for the log file so that it shows up as one block rather than a line?

I've gone through a bunch of simpler formatting attempts and read through How to insert newline in python logging?. Before I try writing a custom formatter just for dictionaries, wanted to find out if there's a known way to do this. Thanks!

The desired output would be something like this:

2024-02-13 13:27:03,685 [DEBUG] root: shelf_name = 'some shelf',
                                      url = 'http://a_url',
                                      creation_date = '02/12/2024'
2024-02-13 13:34:55,889 [DEBUG] root:                                      

So is there any way to do this for a dictionary block?

UPDATED: Removed the old extraneous iterations. I'm posting the closest acceptable result, but still would like to make the dictionary print as a single block

class Downloader:
    def __init__(self, shelf_data) -> None:
        shelf = ShelfData(shelf_data)
        [logging.debug(f"shelf['{key}']: {val}") for key, val in shelf.__dict__.items()]

Log file:

2024-02-13 16:29:18,024 [DEBUG] root: shelf['shelf_name']: test_shelf
2024-02-13 16:29:18,024 [DEBUG] root: shelf['url']: https://site/group/show/1865-scifi-and-fantasy-book-club
2024-02-13 16:29:18,024 [DEBUG] root: shelf['base_path']: C:\MyProjects\downloads
2024-02-13 16:29:18,024 [DEBUG] root: shelf['creation_date']: 02/12/2024
2024-02-13 16:29:18,039 [DEBUG] root: shelf['sort_order']: descend
2024-02-13 16:29:18,039 [DEBUG] root: shelf['books_per_page']: 100
2024-02-13 16:29:18,039 [DEBUG] root: shelf['download_dir']: None
2024-02-13 16:29:18,039 [DEBUG] root: shelf['book_count']: 37095
2024-02-13 16:29:18,039 [DEBUG] root: shelf['page_count']: 371

Also, might as well add my logger:

    logging_config = {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {
            'standard': {
                'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
            },
        },
        'handlers': {
            'default_handler': {
                'class': 'logging.FileHandler',
                'level': 'DEBUG',
                'formatter': 'standard',
                'filename': os.path.join('logs', f'{log_name}.log'),
                'encoding': 'utf-8'
            },
            'stream_handler': {
                'class': 'logging.StreamHandler',
                'level': 'DEBUG',
                'formatter': 'standard',}
        },
        'loggers': {
            '': {
                'handlers': ['default_handler', 'stream_handler'],
                'level': 'DEBUG',
                'propagate': False
            }
        }
    }
    logging.config.dictConfig(logging_config)

Solution

  • Not sure if it's exactly what you're looking for, but I leveraged the pprint module to pretty-print dictionaries storing configurations for various things in an app I'm working on.

    The helper function and usage is below:

    import pprint
    import logging
    
    # set up pretty printer
    pp = pprint.PrettyPrinter(indent=2, sort_dicts=False)
    
    def log_pretty(obj):
        pretty_out = f"{pp.pformat(obj)}"
    
        return f'{pretty_out}\n'
    
    # set up and log as usual
    logger = logging.getLogger("myapp")
    
    logger.info(f'You could print your logging_config dict like so:\n{log_pretty(logging_config)}')
    

    You can also tweak the pprint options to your liking... see here.