开放的编程资料库

当前位置:我爱分享网 > Python教程 > 正文

Python日志记录

Python日志记录教程展示了如何使用logging模块在Python中进行日志记录。

记录

Logging是将信息写入日志文件的过程。日志文件包含有关操作系统、软件或通信中发生的各种事件的信息。

记录的目的

记录是为了以下目的:

  • 信息收集
  • 故障排除
  • 生成统计数据
  • 审计
  • 分析

日志记录不仅限于识别软件开发中的错误。它还用于检测安全事件、监控策略违规、在出现问题时提供信息、查找应用程序瓶颈或生成使用数据。

记录哪些事件

应记录的事件包括输入验证失败、身份验证和授权失败、应用程序错误、配置更改以及应用程序启动和关闭。

哪些事件不记录

不应记录的事件包括应用程序源代码、会话标识值、访问令牌、敏感的个人数据、密码、数据库连接字符串、加密密钥、银行账户和持卡人数据。

记录最佳实践

以下是进行日志记录的一些最佳实践:

  • 日志记录应该有意义。
  • 日志记录应该包含上下文。
  • 日志记录应该结构化并在不同级别完成。
  • 日志记录应该是平衡的;它不应该包含太少或太多的信息。
  • 记录消息应该是人类可以理解的并且可以被机器解析。
  • 更复杂的应用程序的记录应该在几个日志文件中完成。
  • 日志记录应适应开发和生产。

日志模块

Pythonlogging模块定义函数和类,为应用程序和库实现灵活的事件日志系统。

日志模块组件

日志模块有四个主要组件:记录器、处理程序、过滤器和格式化程序。记录器公开应用程序代码直接使用的接口。处理程序将日志记录(由记录器创建)发送到适当的目的地。过滤器提供更细粒度的工具来确定要输出的日志记录。格式化程序指定日志记录在最终输出中的布局.

Python日志层次结构

Python记录器形成一个层次结构。名为main的记录器是main.new的父项。

子记录器将消息传播到与其祖先记录器关联的处理程序。因此,无需为应用程序中的所有记录器定义和配置处理程序。为顶级记录器配置处理程序并根据需要创建子记录器就足够了。

Python日志级别

级别用于标识事件的严重性。有六个日志记录级别:

  • 严重
  • 错误
  • 警告
  • 信息
  • 调试
  • 未设置

如果日志记录级别设置为WARNING,则写入所有WARNINGERRORCRITICAL消息到日志文件或控制台。如果它设置为ERROR,则仅记录ERRORCRITICAL消息。

记录器有一个有效级别的概念。如果未在记录器上明确设置级别,则使用其父级别作为其有效级别。如果父级没有明确的级别集,则检查其父级,依此类推-搜索所有祖先,直到找到明确的级别集。

当使用getLogger创建记录器时,级别设置为NOTSET。如果未使用setLevel显式设置日志记录级别,则消息将传播到记录器父级。遍历记录器的祖先记录器链,直到找到级别不是NOTSET的祖先,或者到达根。根记录器设置了默认的WARNING级别。

根记录器

所有记录器都是根记录器的后代。每个记录器将日志消息传递给它的父级。新记录器是使用getLogger(name)方法创建的。调用没有名称的函数(getLogger)返回根记录器。

根记录器总是有一个明确的级别集,默认为WARNING

rootlooger位于层次结构的顶部并且始终存在,即使未配置也是如此。通常,程序或库不应直接向根记录器记录。相反,应该为程序配置一个特定的记录器。根记录器可用于轻松打开和关闭所有库中的所有记录器。

Python日志记录简单示例

logging模块具有简单的方法,无需任何配置即可立即使用。这可用于简单的日志记录。

#!/usr/bin/python

import logging

logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')

该示例调用了logging模块的五个方法。将消息写入控制台。

$ simple.py
WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical message

请注意,使用了根记录器,并且只写入了三个消息。这是因为默认情况下,只写入具有警告级别及以上级别的消息。

Python设置日志级别

日志记录级别使用setLevel设置。它将此记录器的阈值设置为lvl。记录低于lvl严重程度的消息将被忽略。

#!/usr/bin/python

import logging

logger = logging.getLogger('dev')
logger.setLevel(logging.DEBUG)

logger.debug('This is a debug message')
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')
logger.critical('This is a critical message')

在示例中,我们将日志级别更改为DEBUG

logger = logging.getLogger('dev')

getLogger返回具有指定名称的记录器。如果名称为None,则返回根记录器。名称可以是定义日志层次结构的点分隔字符串;例如“a”、“a.b”或“a.b.c”。请注意,有一个隐含的根名称,未显示。

$ set_level.py
This is a warning message
This is an error message
This is a critical message

现在所有消息都已写入。

Python有效日志级别

有效的日志记录级别是显式设置的级别或由记录器父级确定的级别。

#!/usr/bin/python

import logging

main_logger = logging.getLogger('main')
main_logger.setLevel(5)

dev_logger = logging.getLogger('main.dev')

print(main_logger.getEffectiveLevel())
print(dev_logger.getEffectiveLevel())

在示例中,我们检查了两个记录器的有效日志记录级别。

dev_logger = logging.getLogger('main.dev')

未设置dev_logger的级别;然后使用其父级别。

$ effective_level.py
5
5

Python日志处理程序

处理程序是一个对象,负责将适当的日志消息(基于日志消息的严重性)分派到处理程序的指定目的地。

处理程序像级别一样传播。如果记录器没有设置处理程序,则其祖先链正在搜索处理程序。

#!/usr/bin/python

import logging

logger = logging.getLogger('dev')
logger.setLevel(logging.INFO)

fileHandler = logging.FileHandler('test.log')
fileHandler.setLevel(logging.INFO)

consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logging.INFO)

logger.addHandler(fileHandler)
logger.addHandler(consoleHandler)

logger.info('information message')

该示例为记录器创建了两个处理程序:一个文件处理程序和一个控制台处理程序。

fileHandler = logging.FileHandler('test.log')

FileHandler将日志记录发送到test.log文件。

consoleHandler = logging.StreamHandler()

StreamHandler将日志记录发送到流。如果未指定流,则使用sys.stderr

logger.addHandler(fileHandler)

使用addHandler将处理程序添加到记录器。

Python日志格式化程序

Formatter是配置日志记录的最终顺序、结构和内容的对象。除了消息字符串,日志记录还包括日期和时间、日志名称和日志级别严重性。

#!/usr/bin/python

import logging

logger = logging.getLogger('dev')
logger.setLevel(logging.INFO)

consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logging.INFO)

logger.addHandler(consoleHandler)

formatter = logging.Formatter('%(asctime)s  %(name)s  %(levelname)s: %(message)s')
consoleHandler.setFormatter(formatter)

logger.info('information message')

该示例创建了一个控制台记录器并向其处理程序添加了一个格式化程序。

formatter = logging.Formatter('%(asctime)s  %(name)s  %(levelname)s: %(message)s')

格式化程序已创建。它包括日期时间、日志记录器名称、日志记录级别名称和日志消息。

consoleHandler.setFormatter(formatter)

使用setFormatter将格式化程序设置为处理程序。

$ formatter.py
2019-03-28 14:53:27,446  dev  INFO: information message

具有定义格式的消息显示在控制台中。

Python日志记录基本配置

basicConfig配置根记录器。它通过使用默认格式化程序创建流处理程序来为日志系统进行基本配置。debuginfowarningerrorcritical调用basicConfig如果没有为根记录器定义处理程序,则自动。

#!/usr/bin/python

import logging

logging.basicConfig(filename='test.log', format='%(filename)s: %(message)s',
                    level=logging.DEBUG)

logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')

该示例使用basicConfig配置根记录器。

logging.basicConfig(filename='test.log', format='%(filename)s: %(message)s',
    level=logging.DEBUG)

使用filename,我们设置我们写入日志消息的文件。format决定了记录到文件中的内容;我们有文件名和消息。通过level,我们设置了日志记录阈值。

$ basic_config.py
$ cat test.log
basic_config.py: This is a debug message
basic_config.py: This is an info message
basic_config.py: This is a warning message
basic_config.py: This is an error message
basic_config.py: This is a critical message

运行程序后,我们将五条消息写入test.log文件。

Python日志文件配置

fileConfig从configparser格式文件中读取日志配置。

[loggers]
keys=root,dev

[handlers]
keys=consoleHandler

[formatters]
keys=extend,simple

[logger_root]
level=INFO
handlers=consoleHandler

[logger_dev]
level=INFO
handlers=consoleHandler
qualname=dev
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=extend
args=(sys.stdout,)

[formatter_extend]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s

[formatter_simple]
format=%(asctime)s - %(message)s

log.conf定义了记录器、处理程序和格式化程序。

#!/usr/bin/python

import logging
import logging.config

logging.config.fileConfig(fname='log.conf')

logger = logging.getLogger('dev')
logger.info('This is an information message')

该示例从log.conf中读取日志记录配置文件。

$ file_config.py
2019-03-28 15:26:31,137 - dev - INFO - This is an information message

Python日志记录变量

使用字符串格式记录动态数据。

#!/usr/bin/python

import logging

root = logging.getLogger()
root.setLevel(logging.INFO)

log_format = '%(asctime)s %(filename)s: %(message)s'
logging.basicConfig(filename="test.log", format=log_format)

# incident happens

error_message = 'authentication failed'

root.error(f'error: {error_message}')

该示例将自定义数据写入日志消息。

2019-03-21 14:17:23,196 log_variable.py: error: authentication failed

这是日志消息。

Python日志格式日期时间

日期时间包含在带有asctime日志记录的日志消息中。使用datefmt配置选项,我们可以格式化日期时间字符串。

#!/usr/bin/python

import logging

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

log_format = '%(asctime)s %(filename)s: %(message)s'
logging.basicConfig(filename="test.log", format=log_format,
                    datefmt='%Y-%m-%d %H:%M:%S')

logger.info("information message")

该示例格式化日志消息的日期时间。

log_format = '%(asctime)s %(filename)s: %(message)s'

我们使用asctime将日期时间字符串包含到日志中。

logging.basicConfig(filename="test.log", format=log_format,
                    datefmt='%Y-%m-%d %H:%M:%S')

datefmt选项格式化日期时间字符串。

2019-03-21 14:17:23,196 log_variable.py: error: authentication failed
2019-03-21 14:23:33 date_time.py: information message

请注意日期时间字符串格式的差异。

Python日志堆栈跟踪

堆栈跟踪是运行到抛出异常点的函数调用堆栈。堆栈跟踪包含在exc_info选项中。

#!/usr/bin/python

import logging

log_format = '%(asctime)s %(filename)s: %(message)s'
logging.basicConfig(filename="test.log", format=log_format)

vals = [1, 2]

try:
    print(vals[4])

except Exception as e:
    logging.error("exception occurred", exc_info=True)

在示例中,我们记录了当我们尝试访问不存在的列表索引时抛出的异常。

logging.error("exception occurred", exc_info=True)

通过将exc_info设置为True,堆栈跟踪包含在日志中。

2019-03-21 14:56:21,313 stack_trace.py: exception occurred
Traceback (most recent call last):
  File "C:\Users\Jano\Documents\pyprogs\pylog\stack_trace.py", line 11, in <module>
    print(vals[4])
IndexError: list index out of range

堆栈跟踪包含在日志中。

Python日志记录器getLogger

getLogger返回具有指定名称的记录器。如果未指定名称,则返回根记录器。通常的做法是将模块名称放在__name__中。

使用给定名称对该函数的所有调用都会返回相同的记录器实例。这意味着记录器实例永远不需要在应用程序的不同部分之间传递。

#!/usr/bin/python

import logging
import sys

main = logging.getLogger('main')
main.setLevel(logging.DEBUG)

handler = logging.FileHandler('my.log')

format = logging.Formatter('%(asctime)s  %(name)s
    %(levelname)s: %(message)s')
handler.setFormatter(format)

main.addHandler(handler)

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

该示例使用getLogger创建了一个新的记录器。它被赋予了一个文件处理程序和一个格式化程序。

main = logging.getLogger('main')
main.setLevel(logging.DEBUG)

创建了一个名为main的记录器;我们将日志记录级别设置为DEBUG

handler = logging.FileHandler('my.log')

一个文件处理程序被创建。消息将写入my.log文件。

format = logging.Formatter('%(asctime)s  %(name)s
    %(levelname)s: %(message)s')
handler.setFormatter(format)

一个格式化程序被创建。它包括时间、记录器名称、记录级别和要记录的消息。使用setFormatter将格式化程序设置为处理程序。

main.addHandler(handler)

使用addHandler将处理程序添加到记录器。

$ cat my.log
2019-03-21 14:15:45,439  main  INFO: info message
2019-03-21 14:15:45,439  main  CRITICAL: critical message
2019-03-21 14:15:45,439  main  DEBUG: debug message
2019-03-21 14:15:45,439  main  WARNING: warning message
2019-03-21 14:15:45,439  main  ERROR: error message

这些是写入的日志消息。

Python日志记录YAML配置

日志详细信息可以在YAML配置文件中定义。YAML是一种人类可读的数据序列化语言。它通常用于配置文件。

$ pip install pyyaml

我们需要安装pyyaml模块。

version: 1

formatters:
  simple:
    format: "%(asctime)s %(name)s: %(message)s"
  extended:
    format: "%(asctime)s %(name)s %(levelname)s: %(message)s"

handlers:
  console:
    class: logging.StreamHandler
    level: INFO
    formatter: simple

  file_handler:
    class: logging.FileHandler
    level: INFO
    filename: test.log
    formatter: extended
    propagate: false

loggers:
  dev:
    handlers: [console, file_handler]
  test:
    handlers: [file_handler]
root:
  handlers: [file_handler]

在配置文件中,我们定义了各种格式化程序、处理程序和记录器。propagate选项阻止将日志消息传播到父记录器;在我们的例子中,到根记录器。否则,消息将被复制。

#!/usr/bin/python

import logging
import logging.config
import yaml

with open('config.yaml', 'r') as f:

    log_cfg = yaml.safe_load(f.read())

logging.config.dictConfig(log_cfg)

logger = logging.getLogger('dev')
logger.setLevel(logging.INFO)

logger.info('This is an info message')
logger.error('This is an error message')

在示例中,我们读取配置文件并使用dev记录器。

$ log_yaml.py
2019-03-28 11:36:54,854 dev: This is an info message
2019-03-28 11:36:54,855 dev: This is an error message

当我们运行程序时,控制台上有两条消息。控制台处理程序使用信息较少的简单格式化程序。

...
2019-03-28 11:36:54,854 dev INFO: This is an info message
2019-03-28 11:36:54,855 dev ERROR: This is an error message

test.log文件中有日志消息。它们由扩展格式化程序生成,包含更多信息。

在本教程中,我们使用了Python日志记录库。

列出所有Python教程。

未经允许不得转载:我爱分享网 » Python日志记录

感觉很棒!可以赞赏支持我哟~

赞(0) 打赏