示例#1
0
def logging_config(log_path, logfilename=''):
    """Create a root logger with a stdout console handler with level=DEBUG,
    and, if logfilename is not empty, a file handler with level=INFO *and*
    with delay=True, so that the logfile is created only when written to.
    Root logger level will be DEBUG.
    """
    lcd = LCDict(log_path=log_path,
                 root_level='DEBUG',
                 attach_handlers_to_root=True)
    lcd.add_stdout_handler('con', formatter='msg', level='DEBUG')

    if logfilename:
        # add a file handler, which will write to log_path + '/' + logfilename
        lcd.add_formatter('my_file_formatter',
                          format='%(levelname)-8s: %(message)s')
        # Defaults:
        #   level='DEBUG',
        #   attach_to_root=True
        lcd.add_file_handler(
            'app_file',
            filename=logfilename,
            formatter='my_file_formatter',
            level='INFO',
            mode='w',  # default, just as a reminder
            delay=True  # default: False
        )
    # lcd.dump()           # | DEBUG

    lcd.config()
示例#2
0
def config_logging():
    """Create a root logger with a stdout console handler with level=WARNING,
    and a file handler with level=DEBUG.
    Root logger level will be DEBUG.
    """
    # Defaults:
    #   attach_handlers_to_root=False,
    lcd = LCDict(log_path=LOG_PATH,
                 attach_handlers_to_root=True,
                 locking=True,
                 root_level='DEBUG')

    lcd.add_stdout_handler('console', level='WARNING', formatter='msg')

    # add a file handler, which will write to log_path + '/' + logfilename
    lcd.add_formatter(
        'my_file_formatter',
        format='%(levelname)-8s: %(message)s',
    )
    lcd.add_rotating_file_handler(
        'rot_fh',
        filename=LOGFILENAME,
        formatter='my_file_formatter',
        max_bytes=200,
        backup_count=10,
        mode='w',
    )
    # lcd.dump()           # | DEBUG

    lcd.config()
def config_1():
    """ Attach stdout handler to root. """
    if USE_PRELOGGING:
        lcd = LCDict()
        lcd.add_formatter('my-fmt', format='** %(message)s')
        lcd.add_stdout_handler('root-out', formatter='my-fmt')
        lcd.attach_root_handlers('root-out')
        lcd.config()
        # lcd.dump()    # generates the dict below
    else:
        d = {
            'disable_existing_loggers': False,
            'filters': {},
            'formatters': {
                'my-fmt': {
                    'class': 'logging.Formatter',
                    'format': '** %(message)s'
                }
            },
            'handlers': {
                'root-out': {
                    'class': 'logging.StreamHandler',
                    'formatter': 'my-fmt',
                    'stream': 'ext://sys.stdout'
                }
            },
            'incremental': False,
            'loggers': {},
            'root': {
                'handlers': ['root-out'],
                'level': 'WARNING'
            },
            'version': 1
        }
        logging.config.dictConfig(d)
def main_process_config_logging():
    # DON'T attach handlers to root
    lcd = LCDict(log_path='_log/mproc_QHLT', root_level='DEBUG')

    lcd.add_formatter(
        'detailed',
        format='%(asctime)s %(name)-15s %(levelname)-8s '
        '%(processName)-10s %(message)s',
    ).add_formatter('less-detailed',
                    format='%(name)-15s %(levelname)-8s '
                    '%(processName)-10s %(message)s')

    # lcd.add_stderr_handler('console', level='INFO',
    #                                   formatter='less-detailed')

    lcd.add_file_handler(
        'file', filename='mplog.log', mode='w',
        formatter='detailed').add_file_handler(
            'errors',
            level='ERROR',
            filename='mplog-errors.log',
            mode='w',
            formatter='detailed').attach_root_handlers(  # 'console',
                'file', 'errors')

    lcd.add_file_handler('foofile',
                         filename='mplog-foo.log',
                         mode='w',
                         formatter='detailed').add_logger('foo',
                                                          handlers='foofile')

    lcd.config()
示例#5
0
def config_logging():
    lcd = LCDict(attach_handlers_to_root=True, root_level='DEBUG')
    lcd.add_stdout_handler('console-out', level='DEBUG', formatter='level_msg')
    lcd.add_callable_filter(
        'count_info',
        filter_fn,
        # extra, static data
        filtername='count_info',
        loglevel_to_count=logging.INFO)
    lcd.attach_root_filters('count_info')

    lcd.config()
示例#6
0
def logging_config():
    lcd = LCDict(attach_handlers_to_root=True)
    # root level: WARNING
    lcd.add_stderr_handler('con-err', formatter='level_msg')
    # console handler level: NOTSET

    # Add TWO SMTPHandlers, one for each level ERROR and CRITICAL,
    #    which will email technical staff with logged messages of levels >= ERROR.
    # We use a filter to make the first handler squelch CRITICAL messages:
    lcd.add_callable_filter("filter-error-only", filter_error_only)

    # TEST_TO_ADDRESS included just for testing/trying out the example
    basic_toaddrs = [TEST_TO_ADDRESS, '*****@*****.**']

    # add error-only SMTP handler
    add_smtp_handler_to_lcd(lcd,
                            'email-error',
                            level='ERROR',
                            toaddrs=basic_toaddrs,
                            subject='ERROR (Alert from SMTPHandler)',
                            filters=['filter-error-only'])
    # add critical-only SMTP handler
    add_smtp_handler_to_lcd(lcd,
                            'email-critical',
                            level='CRITICAL',
                            toaddrs=basic_toaddrs + ['*****@*****.**'],
                            subject='CRITICAL (Alert from SMTPHandler)')
    lcd.config()
示例#7
0
def init_logging_config(logfilename):

    lcd = LCDict(log_path=LOG_PATH, attach_handlers_to_root=True)
    lcd.set_root_level('DEBUG')

    # Set format of console handler to show logger name and loglevel.
    ## Make sure 'console' handler level is higher than DEBUG
    lcd.add_formatter('busier_console_fmt',
                      format='%(name)-34s: %(levelname)-8s: %(message)s'
    ).add_stderr_handler('console',
                         formatter='busier_console_fmt',
                         level='INFO'
    )
    # Add main file handler that writes to '_log/child_loggers2/' + logfilename,
    # and add a logger that uses it
    lcd.add_formatter(
        'my_file_formatter',
        format='%(name)-34s: %(levelname)-8s: %(asctime)24s: %(message)s'
    ).add_file_handler(
        'app_file',
        filename=logfilename,
        mode='w',
        level='DEBUG',
        formatter='my_file_formatter'
    )
    return lcd
示例#8
0
    def test_no_root_handlers_no_lock(self):
        """
        DON'T add handlers to root, locking=False
        """
        lcd = LCDict(root_level='DEBUG')

        self.assertEqual(lcd.locking, False)
        self.assertEqual(lcd.attach_handlers_to_root, False)

        # lcd.dump()      # | DEBUG comment out

        expected = self.get_expected_starting_dict('DEBUG')
        self.assertEqual(lcd, expected)

        lcd.add_stderr_handler('console', level='WARNING', formatter='msg')
        # lcd.dump()      # | DEBUG comment out

        self.assertEqual(lcd['formatters'], {
            'msg': {
                'format': '%(message)s'
            },
        })

        lcd.add_file_handler('default_file',
                             filename='blather.log',
                             formatter='msg')

        # lcd.dump()      # | DEBUG comment out

        self.assertEqual(
            lcd['handlers'],
            {
                'console': {
                    'class': 'logging.StreamHandler',
                    'formatter': 'msg',
                    'level': 'WARNING',
                    'stream': 'ext://sys.stderr'
                },
                'default_file': {
                    'class': 'logging.FileHandler',
                    'delay': False,
                    'filename': 'blather.log',
                    'formatter': 'msg',
                    # 'level': 'NOTSET',
                    'mode': 'a'
                }
            })

        lcd.add_logger('default',
                       handlers=('console', 'default_file'),
                       level='DEBUG')

        # lcd.dump()      # | DEBUG comment out

        self.assertEqual(lcd['loggers'], {
            'default': {
                'handlers': ['console', 'default_file'],
                'level': 'DEBUG'
            }
        })
def main():

    q = Queue(-1)  # no limit on size

    LCDict(attach_handlers_to_root=True).add_formatter(
        'fmtr',
        format='%(threadName)s: %(name)s: %(levelname)-8s: %(message)s'
    ).add_stderr_handler('con',
                         formatter='fmtr').add_queue_handler('qhandler',
                                                             queue=q).config()

    root = logging.getLogger()

    # NOTE the following kludge for obtaining a reference to the QueueHandler
    # attached to root. As of Py3.7, the ``handlers`` attribute of a Logger
    # is undocumented. A more concise way to write the condition is to use
    # the ``name`` property of Handlers, also undocumented as of 3.7:
    #       if handler.name == 'qhandler'
    #
    the_qhandler = [
        handler for handler in root.handlers
        if isinstance(handler, logging.handlers.QueueHandler)
    ][0]
    listener = logging.handlers.QueueListener(q, the_qhandler)
    listener.start()
    # The log output will display the thread which generated
    # the event (the main thread) rather than the internal
    # thread which monitors the internal queue. This is what
    # you want to happen.
    root.warning('Look out!')
    sleep(1)
    root.error('Too late!')
    listener.stop()
def config_2():
    """ Attach a stdout handler to logger 'L'. """
    if USE_PRELOGGING:
        lcd = LCDict()
        lcd.add_formatter('my-other-fmt',
                          format='%(name)s - %(levelname)s - %(message)s')
        lcd.add_stdout_handler('L-out', formatter='my-other-fmt')
        lcd.add_logger('L', handlers='L-out', propagate=False)
        if preserve_root:
            lcd['root'] = {}
        lcd.config()
        # lcd.dump()    # generates the dict below
    else:
        root_dict = ({} if preserve_root else {
            'handlers': [],
            'level': 'WARNING'
        })
        d = {
            'disable_existing_loggers': False,
            'filters': {},
            'formatters': {
                'my-other-fmt': {
                    'class': 'logging.Formatter',
                    'format': '%(name)s - %(levelname)s - '
                    '%(message)s'
                }
            },
            'handlers': {
                'L-out': {
                    'class': 'logging.StreamHandler',
                    'formatter': 'my-other-fmt',
                    'stream': 'ext://sys.stdout'
                }
            },
            'incremental': False,
            'loggers': {
                'L': {
                    'handlers': ['L-out'],
                    'level': 'NOTSET',
                    'propagate': False
                }
            },
            'root': root_dict,
            'version': 1
        }
        logging.config.dictConfig(d)
示例#11
0
def main():
    """
    Not a test because... your mileage *will* vary (hard to test).
    """
    lcd = LCDict(attach_handlers_to_root=True)

    # style='%' is the default
    lcd.add_formatter('fmtr1',
                      format='%(asctime)s %(levelname)s: %(message)s',
                      dateformat='%H:%M:%S')

    lcd.add_formatter(
        'fmtr2', fmt='%(asctime)s -- %(message)s',
        datefmt='%Y.%m.%d')  # %Y: 4-digit years; %y: 2-digit years

    lcd.add_stdout_handler('con1', formatter='fmtr1')
    lcd.add_stdout_handler('con2', formatter='fmtr2')

    lcd.config()

    logging.getLogger().warning('Danger, Will Robinson!')
    def test_init_trailing_space(self):
        from prelogging import LCDict
        s = '''\
        ' my formatter '
            format: '%(name)s - %(levelname)s - %(message)s'
            style: %
        '''
        update_formatter_presets(s)

        # Swap stderr
        _stderr = sys.stderr
        sio_out = io.StringIO()
        sys.stderr = sio_out

        lcd = LCDict(attach_handlers_to_root=True)
        lcd.add_stderr_handler('con', formatter=' my formatter ')
        lcd.config()

        import logging
        root = logging.getLogger()
        root.warning(u'Hello')  # PY2: 'u' prefix

        self.assertEqual(sio_out.getvalue(), "root - WARNING - Hello\n")
        # unswap stderr, unnecessarily
        sys.stderr = _stderr
示例#13
0
def config_logging(use_locking):
    """
    """
    logfilename = ('mproc_LOCKING.log'
                   if use_locking else 'mproc_NOLOCKING.log')
    # add handlers to root == False, default
    lcd = LCDict(log_path=LOG_PATH, locking=use_locking)

    ## Add 'console' handler with level higher than 'DEBUG';
    # add main file handler, which will write to '_log/' + logfilename;
    # add logger that uses them.
    lcd.add_stdout_handler(
        'console', formatter='msg', level='INFO'
    ).add_formatter(
        'my_file_formatter',
        format=
        '%(processName)-12s: %(name)-14s: %(levelname)-8s: %(asctime)24s: %(message)s',
    ).add_file_handler(
        'app_file',
        filename=logfilename,
        mode='w',
        level='DEBUG',
        formatter='my_file_formatter').add_logger(
            __name__,
            handlers=('app_file', 'console'),
            level='DEBUG',
            propagate=False  # so it DOESN'T propagate to root logger
        )
    lcd.config()
示例#14
0
def init_logging_config(loggername, logfilename):
    # add handlers to root == False (default)
    lcd = LCDict(log_path=LOG_PATH)

    # Create stderr console handler; output shows logger name and loglevel;
    ## loglevel higher than DEBUG.
    lcd.add_formatter('busier_console_fmt',
                      format='%(name)-40s: %(levelname)-8s: %(message)s'
    ).add_stderr_handler('console',
                         formatter='busier_console_fmt',
                         level='INFO'
    )
    # Add main file handler, which will write to LOG_PATH + '/' + logfilename,
    # and add logger (loggername == __name__) that uses it
    lcd.add_formatter('my_file_formatter',
                      format='%(name)-40s: %(levelname)-8s: '
                             '%(asctime)24s: %(message)s'
    ).add_file_handler('app_file',
                       filename=logfilename,
                       mode='w',
                       level='DEBUG',
                       formatter='my_file_formatter'
    ).add_logger(loggername,
                 handlers=('app_file', 'console'),
                 level='DEBUG',
                 propagate=False    # so it DOESN'T propagate to parent logger
    )
    return lcd
示例#15
0
 def test_set_handler_formatter(self):
     d = LCDict()
     d.add_handler('h')
     d.set_handler_formatter('h', 'msg')
     self.assertEqual(d.handlers['h']['formatter'], 'msg')
     # d.dump()         # TODO Comment out
     # In fact,
     self.assertEqual(
         d,
         {
             'disable_existing_loggers': False,
             'filters': {},
             'formatters': {
                 'msg': {
                     'format': '%(message)s'
                 }
             },
             'handlers': {
                 'h': {  # 'level': 'NOTSET',
                     'formatter': 'msg'
                 }
             },
             'incremental': False,
             'loggers': {},
             'root': {
                 'handlers': [],
                 'level': 'WARNING'
             },
             'version': 1
         })
示例#16
0
    def test_no_lock_clone_handler(self):
        """
        clone handler with locking=False (so 'class' is in its dict)
        """
        lcd = LCDict()

        expected = self.get_expected_starting_dict()
        self.assertEqual(lcd, expected)

        lcd.add_stdout_handler('con', level='WARNING', formatter='msg')
        lcd.clone_handler(clone='con2', handler='con')

        # lcd.dump()      # | DEBUG comment out

        self.assertEqual(
            lcd['handlers'], {
                'con': {
                    'class': 'logging.StreamHandler',
                    'formatter': 'msg',
                    'level': 'WARNING',
                    'stream': 'ext://sys.stdout'
                },
                'con2': {
                    'class': 'logging.StreamHandler',
                    'formatter': 'msg',
                    'level': 'WARNING',
                    'stream': 'ext://sys.stdout'
                }
            })
示例#17
0
def config_logging(use_locking):
    """
    """
    logfilename = ('mproc2_LOCKING.log'
                   if use_locking else 'mproc2_NOLOCKING.log')

    lcd = LCDict(log_path=LOG_PATH,
                 locking=use_locking,
                 attach_handlers_to_root=True,
                 root_level='DEBUG')
    # add main file handler, which will write to LOG_PATH + '/' + logfilename
    lcd.add_stderr_handler(
        'console', formatter='msg', level='DEBUG'
    ).add_formatter(
        'my_file_formatter',
        format=
        '%(processName)-12s: %(name)-14s: %(levelname)-8s: %(asctime)24s: %(message)s'
    ).add_file_handler(
        'app_file',
        filename=logfilename,
        mode='w',
        # level='DEBUG',
        formatter='my_file_formatter',
    )
    lcd.config()
示例#18
0
def config_logging(use_locking):

    if not sys.platform.startswith('darwin'):
        raise NotImplementedError(
            "This example is currently implemented only for OS X / macOS")

    print("%s locking" % ("Using" if use_locking else "NOT using"))

    lcd = LCDict(root_level='DEBUG',
                 attach_handlers_to_root=True,
                 locking=use_locking)
    # Set up console handler to show process name, time, handler name
    lcd.add_stderr_handler(
        'console', level='WARNING', formatter='process_level_msg'
    )
    # Add syslog handler with same formatter
    lcd.add_syslog_handler(
        'h_syslog',
        formatter='process_level_msg',
        address='/var/run/syslog',      # works for OS X; '/dev/log' for *nix
    )
    lcd.check()
    lcd.config()
示例#19
0
def config_logging(use_locking):
    logfilename = 'logfile (%s).log' % ('LOCKING'
                                        if use_locking else 'NOLOCKING')
    lcd = LCDict(log_path='_log/mproc_deco/',
                 root_level='DEBUG',
                 attach_handlers_to_root=True,
                 locking=use_locking)
    # Set up console handler to show process name, time, handler name
    lcd.add_stderr_handler('console',
                           formatter='process_time_logger_level_msg',
                           level='INFO')
    # Add main file handler, which will write to log_path + '/' + logfilename
    lcd.add_file_handler(
        'app_file',
        filename=logfilename,
        mode='w',
        formatter='process_time_logger_level_msg',
    )
    lcd.config()
示例#20
0
def config_logging(log_path, logfilename=''):
    """Create a root logger with a stdout console handler with loglevel=INFO,
    and, if logfilename is not empty, a file handler with default loglevel=NOTSET.
    Root loglevel will be INFO.
    """
    lcd = LCDict(log_path=log_path,
                 attach_handlers_to_root=True,
                 root_level='INFO')
    lcd.add_stdout_handler('console', level='INFO', formatter='msg')
    if logfilename:
        # add a file handler, which will write to log_path + '/' + logfilename
        lcd.add_formatter(
            'my_file_formatter',
            format='%(levelname)-8s: %(message)s',
        ).add_file_handler('app_file',
                           filename=logfilename,
                           mode='w',
                           formatter='my_file_formatter')
    # lcd.dump()           # | DEBUG

    lcd.config()
示例#21
0
def config_logging(use_locking):
    # NOTE: log_path dir should already exist
    log_path = os.path.join('_log/mproc_deco_rot_fh',
                            'LOCKING' if use_locking else 'NOLOCKING')
    lcd = LCDict(log_path=log_path,
                 root_level='DEBUG',
                 attach_handlers_to_root=True,
                 locking=use_locking)
    # Set up console handler to show process name, time, handler name
    lcd.add_stderr_handler('console',
                           formatter='process_level_msg',
                           level='INFO')
    # Add main file handler, which will write to log_path + '/' + logfilename
    lcd.add_rotating_file_handler(
        'rot_fh',
        filename=LOGFILENAME,
        # formatter='process_time_level_msg',
        max_bytes=1024,
        backup_count=10,
        # mode='w',
    )
    lcd.config()
示例#22
0
def config_logging():
    """Add a stdout console handler with level=WARNING to the root logger,
    and a syslog handler with default level=NOTSET.
    Root logger level will be DEBUG.
    """
    # Defaults:
    #   attach_handlers_to_root=False,
    lcd = LCDict(attach_handlers_to_root=True,
                 locking=True,
                 root_level='DEBUG')

    lcd.add_stdout_handler('console', level='WARNING', formatter='msg')

    if not sys.platform.startswith('darwin'):
        raise NotImplementedError(
            "This example is currently implemented only for OS X / macOS")

    # add a syslog handler
    lcd.add_syslog_handler(
        'h_syslog',
        formatter='logger_level_msg',
        address='/var/run/syslog',
    )
    lcd.config()
    def test_style_no_quotes(self):
        from prelogging import LCDict
        s = '''\
        myformatter
            format: '__%(message)s__'
            style: %
        '''
        update_formatter_presets(s)

        # Swap stderr
        _stderr = sys.stderr
        sio_out = io.StringIO()
        sys.stderr = sio_out

        lcd = LCDict(attach_handlers_to_root=True)
        lcd.add_stderr_handler('con', formatter='myformatter')
        lcd.config()
        root = logging.getLogger()
        root.warning(u'Yo, muh man')  # PY2: 'u' prefix

        self.assertEqual(sio_out.getvalue(), "__Yo, muh man__\n")
        # unswap stderr, unnecessarily
        sys.stderr = _stderr
示例#24
0
def main():
    # root, console handler levels: WARNING.
    lcd = LCDict(attach_handlers_to_root=True)
    lcd.add_stderr_handler('con-err', formatter='msg').add_email_handler(
        'email-handler',
        level='ERROR',
        formatter='time_logger_level_msg',
        # SMTPHandler-specific kwargs:
        mailhost=SMTP_SERVER,
        fromaddr=FROM_ADDRESS,
        toaddrs=[TEST_TO_ADDRESS,
                 '*****@*****.**'],  # string or list of strings
        subject='Alert from SMTPHandler',
        username=SMTP_USERNAME,
        password=SMTP_PASSWORD)

    lcd.config()

    root = logging.getLogger()
    root.debug("1.")  # not logged (loglevel too low)
    root.info("2.")  # ditto
    root.warning("3.")  # logged to console
    root.error("4.")  # logged to console, emailed
    root.critical("5.")  # ditto
示例#25
0
__author__ = 'brianoneill'

import logging
try:
    import prelogging
except ImportError:
    import sys
    sys.path[0:0] = ['../..']
from prelogging import LCDict

from .module import *
from . import module
__all__ = module.__all__

# configure logging
lcd = LCDict()  # default: disable_existing_loggers=False
lcd.add_null_handler('library-nullhandler')  # default: level='NOTSET'
lcd.add_logger('library', handlers='library-nullhandler', level='INFO')
lcd.config()
def worker_config_logging(q):
    lcd = LCDict(attach_handlers_to_root=True, root_level='DEBUG')
    lcd.add_queue_handler('qhandler', queue=q)
    lcd.config()
示例#27
0
# "class formatter"


class MyFormatter(logging.Formatter):
    def __init__(self, **kwargs):
        self.value = kwargs.pop(KEYWORD, '')
        kwargs.pop('class', None)
        s = super(MyFormatter, self).__init__(**kwargs)

    def format(self, logrecord, *args, **kwds):
        message = super(MyFormatter, self).format(logrecord, *args, **kwds)
        return 'MyFormatter [%r: %r] says: %s' % (KEYWORD, self.value, message)


if __name__ == '__main__':
    lcd = LCDict(attach_handlers_to_root=True)
    lcd.add_formatter(
        'my_formatter',
        format='%(name)s - %(levelname)s - %(message)s',
        # dateformat=...,
        # style=?,
        **{
            '()': MyFormatter,
            KEYWORD: 'my_value'
        })
    lcd.add_stdout_handler('out', formatter='my_formatter')
    lcd.config()

    root = logging.getLogger()
    root.debug("Debug.")
    root.info("Info.")
def config_logging():
    lcd = LCDict(attach_handlers_to_root=True, root_level='DEBUG')
    lcd.add_formatter(
        'user_ip_level_msg',
        format='User: %(user)-10s  IP: %(ip)-15s  %(levelname)-8s  %(message)s'
    )
    lcd.add_stdout_handler('console-out',
                           level='DEBUG',
                           formatter='user_ip_level_msg')
    lcd.add_class_filter(
        'field-adding_filter',
        FilterThatAddsFields,
        # extra, static data
        datasource=get_data)
    lcd.attach_root_filters('field-adding_filter')

    lcd.config()
def config_logging():
    lcd = LCDict(attach_handlers_to_root=True, root_level='DEBUG')
    lcd.add_stdout_handler('console-out', level='DEBUG', formatter='level_msg')
    lcd.add_class_filter(
        'count_debug',
        CountAndSquelchOdd,
        # extra, static data
        filtername='count_debug',
        loglevel_to_count=logging.DEBUG)
    lcd.add_class_filter(
        'count_info',
        CountAndSquelchOdd,
        # extra, static data
        filtername='count_info',
        loglevel_to_count=logging.INFO)
    lcd.attach_root_filters('count_debug', 'count_info')

    lcd.config()
示例#30
0
def logging_config(log_path, logfilename=''):
    """Create a root logger with a stdout console handler with level=INFO,
    and, if logfilename is not empty, a file handler with level=DEBUG.
    Root logger level will be INFO.
    """
    # . Just for coverage:
    # .     root_level='CRITICAL')
    # . Temporary, cuz of lcd.set_logger_level(...) below.
    lcd = LCDict(log_path=log_path,
                 attach_handlers_to_root=True,
                 root_level='CRITICAL')
    lcd.add_stdout_handler('con', level='WARNING', formatter="msg")

    lcd.set_logger_level(None, 'INFO')  # . coverage ho'

    if logfilename:
        # add a file handler, which will write to log_path + '/' + logfilename
        # Of course, we don't have to add a formatter, we could just
        # use formatter='level_msg' in add_file_handler(...)
        lcd.add_formatter('my_file_formatter',
                          format='%(levelname)-8s: %(message)s')
        # Defaults:
        #   level='DEBUG',
        lcd.add_file_handler(
            'app_file',
            filename=logfilename,
            mode='w',
            locking=True,  # for kicks 'n' coverage
            formatter='my_file_formatter',
        )
    # lcd.dump()           # | DEBUG

    lcd.config()