2020-02-15 日志记录

2020-02-15  本文已影响0人  阿丧小威

1. 日志模块简介

运维工作有很多情况需要查问题、解决bug,而查问题和解决bug的过程离不开查看日志,我们编写脚本或程序时总是需要有日志输出,Python的logging模块就是为记录日志使用的,而且是线程安全的,意味着使用它完全不用担心因日志模块的异常导致程序崩溃。
将日志打印到屏幕:

import logging
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

输出为:

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

默认情况下,Python的logging模块将日志打印到标准输出中,而且只显示大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL>ERROR>WARNING>INFO>DEBUG)
默认的日志格式:日志级别为Logger,名称为用户输出消息。
各日志级别代表的含义如下:

将日志信息记录至文件(文件名:lx_log1.py):

import logging
logging.basicConfig(filename='./lx_log1.log')
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

执行以上代码后发现,在当前目录多了一个文件ls_log1.log,文件内容与第一个例子的输出是一致的。多次执行lx_log1.py发现log文件的内容变多了,说明默认的写log文件的方式是追加。

2. logging模块的配置与使用

我们可以通过logging模块的配置改变log文件的写入方式、日志级别、时间戳等信息。

logging.basicConfig(level=logging.DEBUG,    ---设置日志的级别
format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',    ---日志的格式
    datefmt='%Y-%m-%d &H:%M:%S',    ---时间格式
    filename='./lx_log1.log',    ---指定文件位置
    filemode='w')    ---指定写入方式

可见在logging.basicConfig()函数中可通过具体参数来更改logging模块的默认行为。

format参数中可能用到的格式化串如下:

import logging
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S',
    filename='./lx_log1.log',
    filemode='w',
)
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

运行代码后我们会看到lx_log1.log文件的内容如下:

2020-02-15 01:58:20 lx_log1.py[line:9] DEBUG debug message
2020-02-15 01:58:20 lx_log1.py[line:10] INFO info message
2020-02-15 01:58:20 lx_log1.py[line:11] WARNING warning message
2020-02-15 01:58:20 lx_log1.py[line:12] ERROR error message
2020-02-15 01:58:20 lx_log1.py[line:13] CRITICAL critical message

这样的配置已基本满足我们歇写一些小程序或Python脚本的日志需求。然而这还不够体现logging模块的强大,毕竟以上功能通过自定义一个函数也可以方便实现。下面的是几个概念以及他们之间的关系图。

日志事件信息在记录器(logger)、处理器(handler)、过滤器(filter)、格式化器(formatter)之间通过一个日志记录实例来传递。通过调用记录器实例的方法来记录日志,每一个记录器实例都有一个名字,名字相当于其命名空间,是一个树状结构。例如,一个记录器叫scan,记录器scan.tex、scan.html、scan.pdf的夫节点。记录器的名称。可以任意取,但一个比较好的实践是通过下面的方式来命名一个记录器。

logger = logging.getLogger(__name__)

上面这条语句意味着记录器的名字会通过搜索包的层级来获致,根记录器叫root logger。记录器通过debug()、info()、warning()、error()和critical()方法记录相应级别的日志,根记录器也一样。
根记录器root logger输出的名称是'root'。当然,日志的输出位置可能是不同的,logging模块支持将日志信息输出到终端、文件、HTTP GET/POST请求、邮件、网络sockets、队列或操作系统级的日志等。日志的输出位置在处理器handler类中进行配置,如果内建的handler类无法满足需求,则可以自定义handler类来实现自己特殊的需求。默认情况下,日志的输出位置为终端(标准错误输出),可以通过logging模块的basicConfig()方法指定一个具体的位置来输出日志,如终端或文件。
logger和handler的工作流程如下:

logging模块的工作流程

现在让我们从整体到局部来说明logger的日志记录过程。
第一步:获取logger的名称。

logger = logging.getLogger('logger name')    ---这里的logger name是自己定义的

第二步:配置logger。
1)配置该logger的输出级别,如logger.setLevel(loging.INFO)。
2)添加该logger的输出位置,即logger的handler,logger.addHandler(ch)。这里的ch是我们自定义的handler,如ch=logging.StreamHandler,即输出到终端。我们可以添加多个handler,一次性将日志输出到不同的位置。日志的输出格式是在handler中进行配置,如ch.setFormatter(formatter),formatter也是我们自定义的,如formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')。不同的handler可以配置不同的格式化器,可以实现不同的输出位置,不同的输出格式,完全可能灵活配置。
第三步:在应用程序中记录日志。

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

将日志信息显示在终端的同时也在文件中记录(lx_log2.py)。

# -*- coding: utf-8 -*-

import logging

# 创建logger,其名称为aimple_example,名称为任意,也可为空
logger = logging.getLogger("simple_example")
# 打印logger的名称
print(logger.name)
# 设置logger的日志级别
logger.setLevel(logger.INFO)

# 创建两个handler,一个负责将日志输出到终端,一个负责输出到文件,并分别设置它们的日志级别
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
fh = logging.FileHandler(filename="simple.log", mode="a", encoding="utf*8")
fh.setLevel(logging.WARNING)
# 创建一个格式化器,可以创建不同的格式化器用于不同的handler,这里我们使用一个
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")

# 设置两个handler的格式化器
ch.setFormatter(formatter)
fh.setFormatter(formatter)
# 为logger添加两个handler
logger.addHandler(ch)
logger.addHandler(fh)

# 在程序中记录日志
logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")

在以上程序中我们设置了logger的日志级别为INFO,handler ch的日志级别为DEBUG,handler fh的日志级别为WARNING,这样做是为了解释它们之前的优先级。
handler的日志级别以logger的日志为基础,logger的日志级别为INFO,低于INFO级别的(如DEBUG)均不会在handler中出现。handler中的日志级别如果高于logger,则只显示更高级别的日志信息,如fh应该只显示WARNING及以上的日志信息;handler中的日志级别如果低于或等于logger的日志级别,则显示logger的日志级别及以上信息,如ch应该显示INFO及以上的日志信息。
执行python lx_log2.py得到如下结果:

simple_example
2020-02-15 14:54:29,459 - simple_example - INFO - info message
2020-02-15 14:54:29,459 - simple_example - WARNING - warning message
2020-02-15 14:54:29,459 - simple_example - ERROR - error message
2020-02-15 14:54:29,459 - simple_example - CRITICAL - critical message

查看simple.log文件,内容如下:

2020-02-15 14:54:29,459 - simple_example - WARNING - warning message
2020-02-15 14:54:29,459 - simple_example - ERROR - error message
2020-02-15 14:54:29,459 - simple_example - CRITICAL - critical message

从运行结果来看,符合我们的预期。除了StreamHandler和FileHandler外,logging模块还提供了其他更为实用的Handler子类,它们都继承在Handler基类,如下所示。

日志的配置信息也可以来源于配置文件(lx_log3.py):

import logging
import logging.config

logging.config.fileConfig('logging.conf')

# 创建一个logger
logger = logging.getLogger('simpleExample')

# 日志记录
logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")

下面是配置文件的信息logging.conf:

[loggers]
keys=root,simpleExample

[handlers]
keys=fileHandler,consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=fileHandler

[logger_simpleExample]
level=DEBUG
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
datefmt=%Y-%m-%d %H:%M:%S

上面几种常用的方法已经基本满足我们的需求。

上一篇 下一篇

猜你喜欢

热点阅读