Python 日志功能详解

本文首发于Gevin的博客javascript

原文连接:Python 日志功能详解html

未经 Gevin 受权,禁止转载java

软件开发中经过日志记录程序的运行状况是一个开发的好习惯,对于错误排查和系统运维都有很大帮助。Python标准库自带日志模块,已经足够强大,大部分状况下,python程序的日志功能直接调用标准库的日志模块便可。《The Hitchhiker’s Guide to Python》已对“日志”进行了详细阐述,python的官方文档也对日志作了说明,但Gevin依然感受,经过这些英文资料,还不能让初学者在短期迅速掌握python日志模块的使用,所以按照本身的思路,把相关内容作了以下整理,并附加一些Gevin认为文档中没有说明清楚的内容。python

1. 基本用法

若是开发轻量级的应用,对日志的需求也比较简单,直接参考以下示例,在相关代码逻辑中加入日志功能便可:bash

#!/usr/local/bin/python
# -*- coding: utf-8 -*-

import logging

logging.debug('debug message')
logging.info('info message')
logging.warn('warn message')
logging.error('error message')
logging.critical('critical message')复制代码

output:app

WARNING:root:warn message
ERROR:root:error message
CRITICAL:root:critical message复制代码

默认状况下,logging模块将日志打印到屏幕上,日志级别为WARNING(即只有日志级别等于或高于WARNING的日志信息才会输出),日志格式为 warning level:instance name:warning message运维

1.1 将日志记录到文件中

将日志记录到文件中,只需在调用logging模块记录日志前,作个简单的配置便可:ide

#!/usr/local/bin/python
# -*- coding: utf-8 -*-

import logging

# 配置日志文件和日志级别
logging.basicConfig(filename='logger.log', level=logging.INFO)

logging.debug('debug message')
logging.info('info message')
logging.warn('warn message')
logging.error('error message')
logging.critical('critical message')复制代码

本样例在记录日志前,经过logging.basicConfig作简单配置,将日志记录到日志文件logger.log中,也修改了默认的日志等级,高于或等于INFO级别的日志信息,都会记录到日志文件中。函数

2. 更加完善的日志功能

2.1 几个关键概念

若是要更加灵活的使用日志模块,首先要了解日志模块是怎样工做的。 LoggerHandlerFormatterFilter是日志模块的几个基本概念,日志模块的工做原理要从这四个基本概念提及。post

  • Logger 即记录器,Logger提供了日志相关功能的调用接口。
  • Handler 即处理器,将(记录器产生的)日志记录发送至合适的目的地。
  • Filter 即过滤器,提供了更好的粒度控制,它能够决定输出哪些日志记录。
  • Formatter 即格式化器,指明了最终输出中日志记录的格式。

2.1.1 Logger

Logger 即“记录器”,Logger对象实例是日志记录功能的载体,如:

#!/usr/local/bin/python
# -*- coding: utf-8 -*-

import logging

logger = logging.getLogger('simple_example')

logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')复制代码

值得一提的是,Logger对象从不直接实例化,而是经过模块级的功能logging.getLogger(name)建立Logger实例。调用 logging.getLogger(name) 功能时,若是传入的name参数值相同,则老是返回同一个Logger对象实例的引用。

若是没有显式的进行建立,则默认建立一个root logger,并应用默认的日志级别(WARN)、默认的处理器Handler(StreamHandler,即将日志信息打印输出在标准输出上),和默认的格式化器Formatter(默认的格式即为第一个简单使用程序中输出的格式)。

Logger类包含的成员和方法能够查看官方文档

2.1.2 Handler

Handler 将日志信息发送到设置的位置,能够经过Logger对象的addHandler()方法为Logger对象添加0个或多个handler。一种日志的典型应用场景为,系统但愿将全部的日志信息保存到log文件中,其中日志等级等于或高于ERROR的消息还要在屏幕标准输出上显示,日志等级为CRITICAL的还须要发送邮件通知;这种场景就须要3个独立的handler来实现需求,这三个handler分别与指定的日志等级或日志位置作响应

须要一提的是,为Logger配置的handler不能是Handler基类对象,而是Handler的子类对象,经常使用的Handler为StreamHandler, FileHandler, 和NullHandler,Handler的所有子类及详细介绍能够查看官方文档相应页面若是须要了解更多关于Handler的信息,直接查看官方文档便可。

2.1.3 Formatter

Formatter 用于设置日志输出的格式,与前两个基本概念不一样的是,该类能够直接初始化对象,即 formatter=logging.Formatter(fmt=None, datefmt=None),建立formatter时,传入分别fmtdatefmt参数来修改日志格式和时间格式,默认的日志格式为%(asctime)s - %(levelname)s - %(message)s,默认的时间格式为%Y-%m-%d %H:%M:%S

2.1.4 Filter

Filter 可用于Logger对象或Handler对象,用于提供比日志等级更加复杂的日志过滤方式。默认的filter只容许在指定logger层级下的日志消息经过过滤。例如,若是把filter设置为filter=logging.Filter('A.B'),则logger ‘A.B’, ‘A.B.C’, ‘A.B.C.D’, ‘A.B.D’ 产生的日志信息能够经过过滤,但'A.BB', 'B.A.B'均不行。若是以空字符串初始化filter,则全部的日志消息均可以经过过滤。

Filter在日志功能配置中是非必须的,只在对日志消息过滤需求比较复杂时配置使用便可。

2.2 日志产生流程

日志产生的流程逻辑参考下图便可:

2.3 日志模块的使用

日志模块使用的关键是“日志的配置”,日志配置好后,只要调用logger.INFO(), logger.ERROR()等方法便可建立日志内容。

开发者能够经过三种方法配置日志模块:

  1. 在Python代码中显示建立loggers, handlers, formatters甚至filters,并调用这几个对象中的各个配置函数来完成日志配置
  2. 将配置信息写到配置文件中,而后读取配置文件信息来完成日志配置
  3. 将配置信息写到一个Dict中,而后读取这个配置字典来完成日志配置

2.3.1 经过代码配置并使用日志模块

经过代码配置日志模块简单方便,但若是须要修改配置时,须要改代码,所以不建议在大型项目中使用这种方法。

经过代码配置日志模块能够很好的理解日志模块的工做原理,用于学习,是一个很好的案例,所以Gevin也在下文中对此详细介绍。

1. 建立Logger

import logging

# create logger
logger = logging.getLogger('simple_example')

# Set default log level
logger.setLevel(logging.DEBUG)复制代码

2. 建立Handler

# create console handler and set level to warn

ch = logging.StreamHandler()
ch.setLevel(logging.WARN)复制代码

3. 建立Fomatter

# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')复制代码

4. 配置Logger

# add formatter to ch
ch.setFormatter(formatter)

# add ch to logger
# The final log level is the higher one between the default and the one in handler
logger.addHandler(ch)复制代码

5. 使用日志模块

logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')复制代码

6. 完整的例子

#!/usr/local/bin/python
# -*- coding: utf-8 -*-

import logging

# create logger
logger = logging.getLogger('simple_example')

# Set default log level
logger.setLevel(logging.DEBUG)


ch = logging.StreamHandler()
ch.setLevel(logging.WARN)

ch2 = logging.FileHandler('logging.log')
ch2.setLevel(logging.INFO)

# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# add formatter to ch
ch.setFormatter(formatter)
ch2.setFormatter(formatter)

# add ch to logger
# The final log level is the higher one between the default and the one in handler
logger.addHandler(ch)
logger.addHandler(ch2)

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')复制代码

2.3.2 经过配置文件配置并使用日志模块

经过配置文件配置日志模块时,配置文件一般使用.ini格式,日志模块须要调用fileConfig,即logging.config.fileConfig('logging_config.ini'),而后logger的使用方法与上面相同:

#!/usr/local/bin/python
# -*- coding: utf-8 -*-

import logging
import logging.config

logging.config.fileConfig('logging_config.ini')

# create logger
logger = logging.getLogger('root')

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')复制代码

其中,logging_config.ini文件内容以下:

[loggers]
keys=root,simpleExample

[handlers]
keys=consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_simpleExample]
level=INFO
handlers=consoleHandler
qualname=simpleExample
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s复制代码

经过配置文件配置日志模块,逻辑与代码中配置同样,也是把logger, handlerformatter定义好,而后组装到一块儿便可,无非ini配置和代码配置时的语法不通而已,开发者在基于ini文件配置日志模块时,只要参考上面例子作相应修改便可。

2.3.3 经过Dict对象配置并使用日志模块

基于Dict对象配置日志模块在python中应用普遍,不少Django或Flask项目都采用这种方式,但不少官方文档对这种方法介绍并很少,所以,本文提供一个使用样例,之后开发中参考该样例修改一下便可。

#!/usr/local/bin/python
# -*- coding: utf-8 -*-

import logging
import logging.config

config = {
    'version': 1,
    'formatters': {
        'simple': {
            'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        },
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'level': 'DEBUG',
            'formatter': 'simple'
        },
        'file': {
            'class': 'logging.FileHandler',
            'filename': 'logging.log',
            'level': 'DEBUG',
            'formatter': 'simple'
        },
    },
    'loggers':{
        'root': {
            'handlers': ['console'],
            'level': 'DEBUG',
            # 'propagate': True,
        },
        'simple': {
            'handlers': ['console', 'file'],
            'level': 'WARN',
        }
    }
}

logging.config.dictConfig(config)


print 'logger:'
logger = logging.getLogger('root')

logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')


print 'logger2:'
logger2 = logging.getLogger('simple')

logger2.debug('debug message')
logger2.info('info message')
logger2.warn('warn message')
logger2.error('error message')
logger2.critical('critical message')复制代码

注:

日志的严重等级

Log Level以下,严重等级为NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL, 严重程度依次递增

CRITICAL: 50
ERROR: 40
WARNING: 30
INFO: 20
DEBUG: 10
NOTSET: 0复制代码

修改日志消息的格式

日志的默认显示格式为:%(asctime)s - %(name)s - %(levelname)s - %(message)s,若是只想显示日志等级和日志信息,能够把格式改成:%(levelname)s:%(message)s,想了解所有Formatter中的可用变量,请查阅LogRecord attributes

日期时间的默认格式是ISO8601,修改日期时间格式请参考 time.strftime()

相关文章
相关标签/搜索