Fork me on GitHub

logging模块配置共享以及使用文件配置

1.配置共享

如果每个文件都配置logging,那就太繁琐了,logging提供了父子模块共享配置的机制,

会根据Logger的名称来自动加载父模块的配置.首先定义一个 main.py 文件:

import logging
import core
 
logger = logging.getLogger('main')
logger.setLevel(level=logging.DEBUG)
 
# Handler
handler = logging.FileHandler('result.log')
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
 
logger.info('Main Info')
logger.debug('Main Debug')
logger.error('Main Error')
core.run()

定义了Logger的名称为 main,接下来我们定义core.py

import logging
 
logger = logging.getLogger('main.core')
 
def run():
    logger.info('Core Info')
    logger.debug('Core Debug')
    logger.error('Core Error')

运行之后会生成一个 result.log 文件,内容如下:

2018-06-03 16:55:56,259 - main - INFO - Main Info
2018-06-03 16:55:56,259 - main - ERROR - Main Error
2018-06-03 16:55:56,259 - main.core - INFO - Core Info
2018-06-03 16:55:56,259 - main.core - ERROR - Core Error

2.文件配置

在开发过程中,将配置在代码里面写死并不是一个好的习惯,

更好的做法是将配置写在配置文件里面,我们可以将配置写入到配置文件,

然后运行时读取配置文件里面的配置,这样是更方便管理和维护的.

定义一个 yaml 配置文件:

version: 1
formatters:
  brief:
    format: "%(asctime)s - %(message)s"
  simple:
    format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
handlers:
  console:
    class : logging.StreamHandler
    formatter: brief
    level   : INFO
    stream  : ext://sys.stdout
  file:
    class : logging.FileHandler
    formatter: simple
    level: DEBUG
    filename: debug.log
  error:
    class: logging.handlers.RotatingFileHandler
    level: ERROR
    formatter: simple
    filename: error.log
    maxBytes: 10485760
    backupCount: 20
    encoding: utf8
loggers:
  main.core:
    level: DEBUG
    handlers: [console, file, error]
root:
  level: DEBUG
  handlers: [console]

定义了 formatters、handlers、loggers、root等模块,

实际上对应的就是各个Formatter、Handler、Logger的配置.

3.定义一个主入口文件--main.py:

import logging
import core
import yaml
import logging.config
import os
 
 
def setup_logging(default_path='config.yaml', default_level=logging.INFO):
    path = default_path
    if os.path.exists(path):
        with open(path, 'r', encoding='utf-8') as f:
            config = yaml.load(f)
            logging.config.dictConfig(config)
    else:
        logging.basicConfig(level=default_level)
 
 
def log():
    logging.debug('Start')
    logging.info('Exec')
    logging.info('Finished')
 
 
if __name__ == '__main__':
    yaml_path = 'config.yaml'
    setup_logging(yaml_path)
    log()
    core.run()

文件core.py内容不变,

观察配置文件,主入口文件main.py实际上对应的是root一项配置,

它指定了handlers是console,即只输出到控制台;另外在loggers一项配置里面,

我们定义了main.core模块,handlers是console、file、error三项,

即输出到控制台、输出到普通文件和回滚文件.

4.日志记录使用常见误区

使用字符串的 format() 来构造一个字符串,但这其实并不是一个好的方法
# bad
logging.debug('Hello {0}, {1}!'.format('World', 'Congratulations'))
# good
logging.debug('Hello %s, %s!', 'World', 'Congratulations')

在进行异常处理的时候,通常我们会直接将异常进行字符串格式化,

但其实可以直接指定一个参数将 traceback 打印出来,示例如下:

import logging
 
logging.basicConfig(level=logging.DEBUG, 
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
 
try:
    result = 5 / 0
except Exception as e:
    # bad
    logging.error('Error: %s', e)
    # good
    logging.error('Error', exc_info=True)
    # good
    logging.exception('Error')

如果我们直接使用字符串格式化的方法将错误输出的话,是不会包含Traceback信息的,

如果我们加上 exc_info参数或者直接使用exception()方法打印的话,

那就会输出Traceback信息了.

posted @ 2018-11-16 16:03  法外狂徒  阅读(1519)  评论(0编辑  收藏  举报