Python日志记录教程展示了如何使用logging模块在Python中进行日志记录。
记录
Logging是将信息写入日志文件的过程。日志文件包含有关操作系统、软件或通信中发生的各种事件的信息。
记录的目的
记录是为了以下目的:
- 信息收集
- 故障排除
- 生成统计数据
- 审计
- 分析
日志记录不仅限于识别软件开发中的错误。它还用于检测安全事件、监控策略违规、在出现问题时提供信息、查找应用程序瓶颈或生成使用数据。
记录哪些事件
应记录的事件包括输入验证失败、身份验证和授权失败、应用程序错误、配置更改以及应用程序启动和关闭。
哪些事件不记录
不应记录的事件包括应用程序源代码、会话标识值、访问令牌、敏感的个人数据、密码、数据库连接字符串、加密密钥、银行账户和持卡人数据。
记录最佳实践
以下是进行日志记录的一些最佳实践:
- 日志记录应该有意义。
- 日志记录应该包含上下文。
- 日志记录应该结构化并在不同级别完成。
- 日志记录应该是平衡的;它不应该包含太少或太多的信息。
- 记录消息应该是人类可以理解的并且可以被机器解析。
- 更复杂的应用程序的记录应该在几个日志文件中完成。
- 日志记录应适应开发和生产。
日志模块
Pythonlogging模块定义函数和类,为应用程序和库实现灵活的事件日志系统。
日志模块组件
日志模块有四个主要组件:记录器、处理程序、过滤器和格式化程序。记录器公开应用程序代码直接使用的接口。处理程序将日志记录(由记录器创建)发送到适当的目的地。过滤器提供更细粒度的工具来确定要输出的日志记录。格式化程序指定日志记录在最终输出中的布局.
Python日志层次结构
Python记录器形成一个层次结构。名为main
的记录器是main.new
的父项。
子记录器将消息传播到与其祖先记录器关联的处理程序。因此,无需为应用程序中的所有记录器定义和配置处理程序。为顶级记录器配置处理程序并根据需要创建子记录器就足够了。
Python日志级别
级别用于标识事件的严重性。有六个日志记录级别:
- 严重
- 错误
- 警告
- 信息
- 调试
- 未设置
如果日志记录级别设置为WARNING
,则写入所有WARNING
、ERROR
和CRITICAL
消息到日志文件或控制台。如果它设置为ERROR
,则仅记录ERROR
和CRITICAL
消息。
记录器有一个有效级别的概念。如果未在记录器上明确设置级别,则使用其父级别作为其有效级别。如果父级没有明确的级别集,则检查其父级,依此类推-搜索所有祖先,直到找到明确的级别集。
当使用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
配置根记录器。它通过使用默认格式化程序创建流处理程序来为日志系统进行基本配置。debug
、info
、warning
、error
和critical
调用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教程。