Mike
Mike

Reputation: 544

How to set up multiple loggers with different settings using logging.config.dictConfig()

I'm trying to set up three different loggers using dictConfig and for some reason the last logger always seems to overwrite the configuration of the two loggers created before it. Here is the code I'm using:

import logging
import logging.config

def setup_logger(name, level, ContentFormat='%(asctime)s %(levelname)s %(message)s', DateTimeFormat='%Y-%m-%d %H:%M:%S'):
    logging.config.dictConfig({
        'version': 1,
        'disable_existing_loggers': True,
        'formatters': {
            'default': {'format': ContentFormat, 'datefmt': DateTimeFormat},
        },
        'handlers': {
            'console': {
                'class': 'logging.StreamHandler',
                'level': level,
                'formatter': 'default',
                'stream': 'ext://sys.stdout'
            }
        },
        'loggers': {
            'a': {
                'level': level,
                'handlers': ['console']
            },
            'b': {
                'level': level,
                'handlers': ['console']
            },
            'c': {
                'level': level,
                'handlers': ['console']
            }
        }
    })
    return logging.getLogger(name)

logger_a = setup_logger(name='a', level=logging.INFO, ContentFormat='A: %(message)s')
logger_b = setup_logger(name='b', level=logging.INFO, ContentFormat='B: %(message)s')
logger_c = setup_logger(name='c', level=logging.INFO, ContentFormat='C: %(message)s')

logger_a.info('logger_a')
logger_b.info('logger_b')
logger_b.info('logger_c')

And this is the output:

C: logger_a
C: logger_b
C: logger_c

Instead what I'd like to see is:

A: logger_a
B: logger_b
C: logger_c

Any ideas what I'm doing wrong? I've even tried making multiple handlers and I still run into the same problem.

I have another version of this function that uses the following code and I'm able to call it multiple times to create multiple separate loggers with different settings, but I really wanted to figure out how to do this with dictConfig instead, or at least understand where I'm going wrong:

logger = logging.getLogger(name)
logger.setLevel(level)

# create console handler for printing logging output to the screen as well
formatter = logging.Formatter(ContentFormat, DateTimeFormat)
handler = logging.StreamHandler()
handler.setLevel(level)
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger

Upvotes: 3

Views: 4539

Answers (1)

MartenCatcher
MartenCatcher

Reputation: 2887

It happens because you created and overrode the global configuration 3 times. You used default formatter to configure specific logger and the last one C used to log you records.

You can easily check it with this code:

def setup_logger(name, level, DateTimeFormat='%Y-%m-%d %H:%M:%S'):
    cfg = {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {
            'default_for_a': {'format': 'A: %(message)s', 'datefmt': DateTimeFormat},
            'default_for_b': {'format': 'B: %(message)s', 'datefmt': DateTimeFormat},
            'default_for_c': {'format': 'C: %(message)s', 'datefmt': DateTimeFormat}
        },
        'handlers': {
            'console_for_a': {
                'class': 'logging.StreamHandler',
                'level': level,
                'formatter': 'default_for_a',
                'stream': 'ext://sys.stdout'
            },
            'console_for_b': {
                'class': 'logging.StreamHandler',
                'level': level,
                'formatter': 'default_for_b',
                'stream': 'ext://sys.stdout'
            },
            'console_for_c': {
                'class': 'logging.StreamHandler',
                'level': level,
                'formatter': 'default_for_c',
                'stream': 'ext://sys.stdout'
            },
        },
        'loggers': {
            'a': {
                'level': level,
                'handlers': ['console_for_a']
            },
            'b': {
                'level': level,
                'handlers': ['console_for_b']
            },
            'c': {
                'level': level,
                'handlers': ['console_for_c']
            }
        }
    }
    logging.config.dictConfig(cfg)
    return logging.getLogger(name)


logger_a = setup_logger(name='a', level=logging.INFO)
logger_b = setup_logger(name='b', level=logging.INFO)
logger_c = setup_logger(name='c', level=logging.INFO)

logger_a.info('logger_a')
logger_b.info('logger_b')
logger_c.info('logger_c')

Or make it more generic with the code

import logging
import logging.config


def setup_logger(name, level, ContentFormat='%(asctime)s %(levelname)s %(message)s', DateTimeFormat='%Y-%m-%d %H:%M:%S'):
    cfg = {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {
            f'default_for_{name}': {'format': ContentFormat, 'datefmt': DateTimeFormat}
        },
        'handlers': {
            f'console_for_{name}': {
                'class': 'logging.StreamHandler',
                'level': level,
                'formatter': f'default_for_{name}',
                'stream': 'ext://sys.stdout'
            },
        },
        'loggers': {
            name: {
                'level': level,
                'handlers': [f'console_for_{name}']
            }
        }
    }
    logging.config.dictConfig(cfg)
    return logging.getLogger(name)


logger_a = setup_logger(name='a', level=logging.INFO, ContentFormat='A: %(message)s')
logger_b = setup_logger(name='b', level=logging.INFO, ContentFormat='B: %(message)s')
logger_c = setup_logger(name='c', level=logging.INFO, ContentFormat='C: %(message)s')

logger_a.info('logger_a')
logger_b.info('logger_b')
logger_c.info('logger_c')

The output is:

A: logger_a
B: logger_b
C: logger_c

Upvotes: 6

Related Questions