美文网首页学习之Python学习Python
Python 标准库模块 - logging

Python 标准库模块 - logging

作者: 山药鱼儿 | 来源:发表于2021-07-02 22:05 被阅读0次

logging 模块是 Python 内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等。

相比 print 函数,具备如下优点:

  • 可以通过设置不同的日志等级,在 release 版本中只输出重要信息,而不必显示大量的调试信息。
  • print 将所有信息都输出到标准输出中,严重影响开发者从标准输出中查看其它数据;logging 则可以由开发者决定将信息输出到什么地方,以及怎么输出。

下面,我们就来学习一下标准库模块 logging 的基本用法吧~

一. 日志级别

logging 模块提供的日志级别如下:

日志等级 描述
DEBUG 最详细的日志信息,典型应用场景是问题诊断。
INFO 记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作。
WARNING 当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的。
ERROR 由于一个更严重的问题导致某些功能不能正常运行时记录的信息。
CRITICAL 当发生严重错误,导致应用程序不能继续运行时记录的信息。

上面列表中的日志等级是从上到下依次升高的,即:DEBUG < INFO < WARNING < ERROR < CRITICAL,而日志的信息量是依次减少的。当为某个应用程序指定一个日志级别后,应用程序会记录所有日志级别大于或等于指定日志级别的日志信息

默认情况下,logging 模块将日志打印到屏幕上,日志级别为 WARNING,即只有日志级别高于 WARNING 的日志才会被输出。下面是一个简单的例子说明:

从运行结果中,我们可以看到默认只有 WARNING、ERROR 及 CRITICAL 级别的日志打印到了标准输出。这里需要补充说明一下默认输出的日志格式:

>> logging.BASIC_FORMAT
'%(levelname)s:%(name)s:%(message)s'

日志格式默认由三部分组成,中间以冒号分隔,分别为:日志级别、logger 实例的名称以及日志消息内容。
想要个性化设置日志级别、日志输出的目的地以及日志格式等,下面就来展开学习 logging 模块把!

二. logging 模块的使用

2.1 使用 basciConfig 进行简单配置

logging 模块级别的函数 basciConfig 用于对日志系统进行基本配置。通常用于一次性配置简单脚本的日志。

logging.basicConfig 函数最常用的几个关键字参数如下:

关键字参数 释义
filename 指定日志文件名;
filemode 指定日志文件的打开格式:w 或者 a,默认为追加模式;
format 指定日志输出内容和格式;
datefmt 指定 %(asctime)s 显示的时间日期格式;
level 设置根记录器输出日志的级别;
stream 使用指定的流初始化 StreamHandler
handlers 由日志处理器 Handler 组成的可迭代对象,这些 Handler 都将被添加到根日志器;


stream 参数与 filename 不兼容,如果两个都有,stream 将被忽略。
默认创建的 StreamHandlersys.stderr 输出。

下面是一个简单的例子:

import logging

logging.basicConfig(
    level=logging.INFO, 
    format='[%(asctime)s] [%(levelname)s] [%(name)s] - %(message)s')

logger = logging.getLogger(__name__)
 
logger.debug('debug message log.')
logger.info('info message log')
logger.warn('warn message log')
logger.error('error message log')
logger.critical('critical message log') 

运行结果:

在使用 2>/dev/null 将标准错误重定向之后,屏幕没有任何打印。下面我们指定 stream=sys.stdout

import logging
import sys


logging.basicConfig(
    level=logging.INFO, 
    format='[%(asctime)s] [%(levelname)s] [%(name)s] - %(message)s',
    stream=sys.stdout)

logger = logging.getLogger(__name__)
 
logger.debug('debug message log.')
logger.info('info message log')
logger.warn('warn message log')
logger.error('error message log')
logger.critical('critical message log')

运行结果:

我们也可以不指定 stream ,而是使用 filename 参数将日志输出到一个文件:

import logging


logging.basicConfig(
    level=logging.INFO, 
    format='[%(asctime)s] [%(levelname)s] [%(name)s] - %(message)s',
    filename='test_log.log')

logger = logging.getLogger(__name__)
 
logger.debug('debug message log.')
logger.info('info message log')
logger.warn('warn message log')
logger.error('error message log')
logger.critical('critical message log')

运行结果:

image.png

上面的例子中,参数 format 用到了很多占位符,下面是常用的格式占位符与含义:

格式占位符 释义
%(levelno)s 打印日志级别的数值。
%(levelname)s 打印日志级别名称。
%(name)s 日志记录器 logger 的名称,通常设置为:__name__
%(pathname)s 打印当前执行程序的路径,其实就是 sys.argv[0]
%(filename)s 打印当前执行程序名。
%(funcName)s 打印日志的当前函数。
%(lineno)d 打印日志的当前行号。
%(asctime)s 打印日志的时间。
%(threadName)s 打印线程名称。
%(process)d 打印进程 id
%(message)s 打印日志信息。

在介绍 basicConfig 的参数时,我们提到了日志记录器,处理器以及格式化参数 format ,这就涉及到了 logging 模块中非常重要的几个概念:

  • 日志记录器 Logger:提供应用程序代码可以直接访问的接口。
  • 日志处理器 Handler:将日志记录器产生的记录输出到指定位置。
  • 日志格式化器 Formatter:指明日志输出的内容和格式。

三者的对应关系为:一个日志记录器可以添加多个处理器 Handler ,每个 Handler 可以指定一个日志格式化器 Formatter。下面,我们就来一一学习他们~

2.2 日志处理器 Handler

import logging
import sys

def log():
    logger = logging.getLogger('log_test')
    logger.setLevel(logging.DEBUG)

    if not logger.handlers:
        file_handler = logging.FileHandler("test.log",encoding="utf-8")
        logger.addHandler(file_handler)

        stdout_handler = logging.StreamHandler(sys.stdout)
        logger.addHandler(stdout_handler)

    return logger

logger = log()

logger.debug("查错")
logger.info("提示")
logger.warning("警告")
logger.error("错误")

运行结果:

由于我们为记录器配置了两个处理器:file_handlerstdout_handler ,因此在标准输出和 test.log 文件中都记录了程序中的日志。这里由于我们运行了两次程序,因此 test.log 中产生了两组输出。

使用 logging.getLogger 获取记录器,不传递任何参数时,默认返回根记录器:

>> logger = logging.getLogger()
>> logger
<RootLogger root (WARNING)>

默认情况下 logger.handlers 为空列表:

>> logger.handlers
[]

使用 logger.addHandler 为记录器添加日志处理器:

>> stdout_handler = logging.StreamHandler(sys.stdout)
>> logger.addHandler(stdout_handler)
>> logger.handlers
[<StreamHandler stdout (NOTSET)>]

2.3 日志格式化器 Formatter

import logging

def log():
    logger = logging.getLogger("log_test")
    logger.setLevel(logging.DEBUG)

    if not logger.handlers:
        file_handler = logging.FileHandler("test.log",encoding="utf-8")
        formatter = logging.Formatter(
            fmt="%(asctime)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s",
            datefmt="%Y/%m/%d %X")

        # 为handler指定输出格式
        file_handler.setFormatter(formatter)

        # 为logger添加的日志处理器
        logger.addHandler(file_handler)

    return logger

logger = log()
logger.debug("查错")
logger.info("提示")
logger.warning("警告")
logger.error("错误")

运行结果:

使用 logging.Formatter 即可创建日志格式化器:formatter = logging.Formatter(fmt=None, datefmt=None)。其中,fmt 是消息的格式化字符串,datefmt 是日期字符串。如果不指明 fmt,将使用 '%(message)s';如果不指明 datefmt,将使用 %Y-%m-%d %H:%M:%S 日期格式。

2.4 捕获 traceback

Python 中的 traceback 模块用于捕获异常信息,可以在 logging 中记录下 traceback 。

import logging
import sys

LOG_FORMAT = "[%(asctime)s]-[%(levelname)s] %(message)s"
DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
LOG_FILE = sys.argv[0].split('.')[0]
 
def log():
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)

    file_handler = logging.FileHandler(LOG_FILE)
    file_handler.setFormatter(logging.Formatter(fmt=LOG_FORMAT, datefmt=DATE_FORMAT))
    file_handler.setLevel(logging.ERROR)

    stream_handler = logging.StreamHandler()
    stream_handler.setFormatter(logging.Formatter(fmt=LOG_FORMAT, datefmt=DATE_FORMAT))
    stream_handler.setLevel(logging.INFO)

    logger.addHandler(file_handler)
    logger.addHandler(stream_handler)

    return logger
 
if __name__ == "__main__":
    logger = log()
    logger.info('################# {} #################'.format(LOG_FILE))
    try:
        print(6/0)
    except ZeroDivisionError as e:
        # logger.error("ZeroDivisionError occured in my proceger.", exc_info=True)
        logger.exception("ZeroDivisionError occured in my proceger.")

运行结果:


使用 logger.error(xxxxx, exc_info=True) 即可在日志中捕获 traceback 信息,更为方便地可以直接使用 logger.exception 方法。查阅日志文件 test_log 可以清晰看到 traceback 信息:

mia@ubuntu:~/Desktop$ cat test_log
[2021-07-03 01:21:22]-[ERROR] ZeroDivisionError occured in my proceger.
Traceback (most recent call last):
  File "test_log.py", line 29, in <module>
    print(6/0)
ZeroDivisionError: division by zero
[2021-07-03 01:21:33]-[ERROR] ZeroDivisionError occured in my proceger.
Traceback (most recent call last):
  File "test_log.py", line 29, in <module>
    print(6/0)
ZeroDivisionError: division by zero

相关文章

网友评论

    本文标题:Python 标准库模块 - logging

    本文链接:https://www.haomeiwen.com/subject/jldyyltx.html