Detailed python log

       In software development, it is a good habit to record the operation of the program through the log, which is very helpful for error troubleshooting and system operation and maintenance. The Python standard library comes with a log module, which is powerful enough. In most cases, the log function of the python program can directly call the log module of the standard library. "The Hitchhiker's Guide to Python" has explained the "log" in detail , and the official python documentation has also explained the log , but Gevin still feels that through these English materials, it is not enough for beginners to quickly grasp the python log in a short time. Therefore, according to my own thinking, I organized the relevant content as follows, and added some content that Gevin thought was not clearly explained in the document.

1. Basic usage

If you develop a lightweight application, the requirements for logging are relatively simple. You can directly refer to the following example and add the logging function to the relevant code logic:

#!/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:

WARNING:root:warn message
ERROR:root:error message
CRITICAL:root:critical message

By default, the logging module prints logs to the screen, and the log level is WARNING (that is, only log information whose log level is equal to or higher than WARNING will be output), and the log format is warning level:instance name:warning message

1.1 Logging to a file

To record the log to a file, just make a simple configuration before calling the logging module to record the log:

#!/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')

Before recording the log, this example records the log logging.basicConfigto the log file through simple configuration, logger.logand also modifies the default log level. Log information higher than or equal to the INFO level will be recorded in the log file.

2. More complete log function

2.1 Several key concepts

If you want to use the logging module more flexibly, you must first understand how the logging module works. Logger, Handler, Formatterand Filterare the basic concepts of the log module. The working principle of the log module should start with these four basic concepts.

  • Logger is a logger, and Logger provides a calling interface for log-related functions.
  • A Handler is a handler that sends log records (produced by a logger) to the appropriate destination.
  • 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为StreamHandlerFileHandler, 和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 日志产生流程

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

logging flow

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

通过配置文件配置日志模块,逻辑与代码中配置一样,也是把loggerhandlerformatter定义好,然后组装到一起即可,无非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如下,严重等级为NOTSETDEBUGINFOWARNINGERRORCRITICAL, 严重程度依次递增

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()

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326844248&siteId=291194637