本文包含以下几个内容:

  1. 为什么要使用logging
  2. logging的主要组成与使用方法
  3. logging的处理逻辑
  4. logging的继承
  5. logging的其他配置方法

1.为什么要使用logging

之前调试,一直都是使用 print,将调试信息打印到控制台,而在调试结束之后,重新去注释 print 是个很麻烦的工作。另外就是写 python 脚本,部署为定时任务后,你不知道是否是正常执行了,如果执行出错,是哪个环节出错了,这些需求依靠 print 是无法解决的,因此使用 logging 是个明智的选择。

2.logging的主要组成与使用方法

2.1.logging的组成

logging有4个部分:loggers,handlers,filters,formatters.

  • Loggers:提供了应用程序可以直接使用的接口;
  • Handlers:将(Loggers创建的)日志发送到合适的目标输出;
  • Filters:提供一个精度更细致的日志记录是否输出控制;
  • Formatters:控制日志记录输出的格式。 因此在使用loggging是,我们需要对以上4个部分进行设置。

2.2.logging的使用步骤:

  • 创建Logger对象
  • 设置Logger对象的日志级别,日志级别有 logging.DEBUG,logging.INFO,logging.WARNING,logging.ERROR,logging.CRITICAL
  • 创建Handler(你可以创建多个Handler,并绑定到一个logger对象上)
  • 设置Handler的日志级别,日志级别同上
  • 设置日志格式
  • 将Handler绑定到Logger对象上

具体代码如下:

import logging

logger=logging.getLogger('mytest')  #创建logger对象
logger.setLevel(logging.DEBUG)#设置logger的日志处理级别

logfile=logging.FileHandler('test_log.log') #创建一个输出到log文件的Handler.
console=logging.StreamHandler() #创建一个输出到控制台的Handler

logfile.setLevel(logging.DEBUG) #设置handler的日志处理级别
console.setLevel(logging.ERROR)#设置另外一个handler的日志处理级别

formatter=logging.Formatter('%(asctime)s%(name)s%(levelname)s%(message)s') #设置日志输出格式
logfile.setFormatter(formatter)  #将日志输出格式绑定到handler
console.setFormatter(formatter)

logger.addHandler(logfile) #将handler绑定到logger
logger.addHandler(console) 

logger.info('this is info ')

2016-03-30 23:18:43,858 – mytest – INFO – this is info
this is info

3.logging的处理逻辑

logging的处理逻辑

4.logging的继承关系

其实有一种最简单直接使用logging的方法,如下:

import logging
logging.warning('this is warning')
logging.info('this is info')

你可以看到在控制台输出如下:

WARNING:root:this is warning

很奇怪为什么'this is info'没有输出吧。而且为什么这么简单就能使用?这与我们之前的介绍有什么区别?

1. logger间的继承关系:
logger是通过名字来决定继承关系。比如你在设置logger时,将一个logger命名为logging.getLogger('mylog'),另一个命名为logging.getLogger('mylog.test'),那么后者就是前者的子logger,如果没有为它做单独的设置,那么它就会继承使用 'mylog'的logger设置,其实这个逻辑在logging的处理逻辑关系图中可以看出。

既然有了子logger,那么系统中就有根logger。当我们直接如下操作的时候,其实是在调用系统的根logger:RootLogger.

import logging
logging.warning('this is warning')

而它的日志级别是logging.WARN,默认输出的Handler是 StreamHandler.

2. 我们可以使用basicConfig()函数配置RootLogger:

basicConfig()函数只用来配置RootLogger.而且因为RootLogger为其他Logger的根Logger,因此当你使用这个配置后,其他Logger都会继承RootLogger的配置。

basicConfig()函数的参数如下:

  • filename:使用提供的filename为RootLogger创建一个FileHandler,而不是StreamHandler;
  • filemode:如果filename已经指定,则指定这个filename的打开方式,默认为'a';
  • format:为handler设置日志格式;
  • datefmt:指定日期/时间格式;
  • level:设置 root logger的日志处理级别;
  • stream:用指定的steam去初始化StreamHandler,注意这个参数与filename参数不相容,如果filename,stream同时指定,那么stream参数会被忽略。

具体使用如下:

#coding:utf-8
import logging
logging.basicConfig(
    filename='log_root',
    filemode='a',
    format='%(asctime)s%(name)s%(levelname)s%(message)s',
    datefmt='%m/%d/%Y %I:%M:%S',
    level=logging.DEBUG)
logging.info('this in info in root logger')

5.logging的其他相关:

1.通过logger=logging.getLogger(__name__)的方法为logger设置名称,我们可以对每个模块设置独立的logger。因为logger是通过name来进行区别的。

2.捕获异常并使用traceback记录它。
在日志中捕获异常,可以十分方便今后的调试。

try:
    <code>
except Exception,e:
    logger.error(e,exc_info=True)

3.logging formatter的格式参数,请访问 logrecord-attributes
4.通过logger.setLevel()可以很方便切换日志的记录级别。
5.logger可以通过参数配置,推荐通过dict和YAML进行配置,具体使用请访问Logging configuration

# coding: utf-8
import os
import logging.config
import yaml
def setup_logging(default_path='logging.yaml', default_level=logging.INFO,env_key='LOG_CFG' ):
    """Setup logging configuration
    """
    path = default_path
    value = os.getenv(env_key, None)
    if value:
        path = value
    if os.path.exists(path):
        with open(path, 'rt') as f:
            config = yaml.load(f.read())
        logging.config.dictConfig(config)
    else:
        logging.basicConfig(level=default_level)

setup_logging(default_path='log_con.yaml')        
logger=logging.getLogger('my_module')
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

yaml设置参数如下:

version: 1
disable_existing_loggers: False
formatters:
    simple_formater:
        format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
handlers:
    console:
        class: logging.StreamHandler
        level: DEBUG
        formatter: simple_formater
        stream: ext://sys.stdout
    info_file_handler:
        class: logging.FileHandler
        level: INFO 
        formatter: simple_formater
        filename: info.log
        encoding: utf8
    error_file_handler:
        class: logging.FileHandler
        level: ERROR            
        formatter: simple_formater
        filename: errors.log
        encoding: utf8
loggers:
    my_module:
        level: INFO
        handlers: [console,info_file_handler,error_file_handler]
        propagate: no

root:
    level: INFO
    handlers: [console, info_file_handler, error_file_handler]

其他参考文档:
1.官方的Logging How toLogging Cookbook 以及Logging模块文档
2.每个 Python 程序员都要知道的日志实践
3.Python Howto 之 logging 模块
4.关于Python日志系统的几点建议