Python 日志记录:6大日志记录库的比较 您所在的位置:网站首页 python第三方库国内网址 Python 日志记录:6大日志记录库的比较

Python 日志记录:6大日志记录库的比较

2023-12-03 21:01| 来源: 网络整理| 查看: 265

Python 日志记录:6大日志记录库的比较

文章目录 Python 日志记录:6大日志记录库的比较前言一些日志框架建议1. logging - 内置的标准日志模块默认日志记录器自定义日志记录器生成结构化日志 2. Loguru - 最流行的Python第三方日志框架默认日志记录器自定义日志记录器 3. Structlog4. Eliot5. Logbook6. Picologging最后的想法

前言

日志记录框架是一种工具,可帮助您标准化应用程序中的日志记录过程。虽然某些编程语言提供内置日志记录模块作为其标准库的一部分,但大多数日志记录框架都是第三方库,例如logging (Python)、Log4j (Java)、 Zerolog (Go) 或 Winston (Node.js)。有时,组织会选择开发自定义日志记录解决方案,但这通常仅限于具有高度专业化需求的大型公司。

虽然 Python 在其标准库中提供了强大且功能丰富的日志记录解决方案,但第三方日志记录生态系统也提供了一系列引人注目的替代方案。根据您的需求,这些外部库可能更适合您的日志记录需求。

因此,本文将介绍 Python 用于跟踪应用程序和库行为的六大日志解决方案。我们将首先讨论标准logging模块,然后研究 Python 社区创建的其他五个logging frameworks。

一些日志框架建议

Pino (Node.js)

Zerolog, Zap, or Slog (Go)

Monolog (PHP)

SLF4J with Log4J2 or Logback (Java)

Loguru (Python)

Semantic Logger (Ruby)

1. logging - 内置的标准日志模块 默认日志记录器

与大多数编程语言不同,Python 在其标准库中包含了一个功能齐全的日志框架。该日志记录解决方案有效地满足了库和应用程序开发人员的需求,并包含了以下严重性级别:DEBUG、INFO、WARNING、ERROR 和 CRITICAL。有了默认日志记录器,无需任何前期设置,您就可以立即开始记录日志。

import logging logging.debug("A debug message") logging.info("An info message") logging.warning("A warning message") logging.error("An error message") logging.critical("A critical message")

此default(或root)记录器在该WARNING级别运行,这意味着只有严重性等于或超过的记录调用WARNING才会产生输出:

WARNING:root:A warning message ERROR:root:An error message CRITICAL:root:A critical message 自定义日志记录器

这种配置可确保只显示潜在的重要信息,减少日志输出中的噪音。不过,您也可以根据需要自定义日志级别并微调日志记录行为。使用logging模块的推荐方法是通过 getLogger()方法创建自定义日志记录器:

import logging logger = logging.getLogger(__name__)

一旦有了自定义记录器,您就可以通过 logging模块 提供的Handler(处理程序)、 Formatter(格式化器)和Filter(过滤器) 类来自定义其输出。

Handlers决定输出目的地,并可根据日志级别进行定制。一个日志记录器还可添加多个Handlers,以便同时向不同目的地发送日志信息。

Formatters决定了日志记录器产生的记录的格式。然而,目前还没有JSON、Logfmt等预定义格式。您必须结合可用的日志记录属性来构建自己的格式。 root日志记录器的默认格式为%(levelname)s:%(name)s:%(message)s。 然而,自定义日志记录器默认为只有%(message)s。

Filters由handler和logger objects使用,用于过滤日志记录。与日志级别相比,Filters能更好地控制哪些日志记录应被处理或忽略。在日志被发送到最终目的地之前,它们还能以某种方式增强或修改记录。例如,您可以创建一个自定义过滤器,以删除日志中的敏感数据。

下面是一个使用自定义日志记录器将日志记录到控制台和文件的示例:

import sys import logging logger = logging.getLogger("example") logger.setLevel(logging.DEBUG) # Create handlers for logging to the standard output and a file stdoutHandler = logging.StreamHandler(stream=sys.stdout) errHandler = logging.FileHandler("error.log") # Set the log levels on the handlers stdoutHandler.setLevel(logging.DEBUG) errHandler.setLevel(logging.ERROR) # Create a log format using Log Record attributes fmt = logging.Formatter( "%(name)s: %(asctime)s | %(levelname)s | %(filename)s:%(lineno)s | %(process)d >>> %(message)s" ) # Set the log format on each handler stdoutHandler.setFormatter(fmt) errHandler.setFormatter(fmt) # Add each handler to the Logger object logger.addHandler(stdoutHandler) logger.addHandler(errHandler) logger.info("Server started listening on port 8080") logger.warning( "Disk space on drive '/var/log' is running low. Consider freeing up space" ) try: raise Exception("Failed to connect to database: 'my_db'") except Exception as e: # exc_info=True ensures that a Traceback is included logger.error(e, exc_info=True)

执行上述程序时,控制台会如期打印出以下日志信息:

example: 2023-07-23 14:42:18,599 | INFO | main.py:30 | 187901 >>> Server started listening on port 8080 example: 2023-07-23 14:14:47,578 | WARNING | main.py:28 | 143936 >>> Disk space on drive '/var/log' is running low. Consider freeing up space example: 2023-07-23 14:14:47,578 | ERROR | main.py:34 | 143936 >>> Failed to connect to database: 'my_db' Traceback (most recent call last): File "/home/ayo/dev/betterstack/demo/python-logging/main.py", line 32, in raise Exception("Failed to connect to database: 'my_db'") Exception: Failed to connect to database: 'my_db'

同时还创建了 error.log 文件,该文件应仅包含 ERROR 日志,因为 errHandler 的最小级别已设置为 ERROR:

example: 2023-07-23 14:14:47,578 | ERROR | main.py:34 | 143936 >>> Failed to connect to database: 'my_db' Traceback (most recent call last): File "/home/ayo/dev/betterstack/demo/python-logging/main.py", line 32, in raise Exception("Failed to connect to database: 'my_db'") Exception: Failed to connect to database: 'my_db' 生成结构化日志

在撰写本文时,除非执行一些附加代码,否则logging模块无法生成结构化日志。值得庆幸的是,有一种更简单、更好的方法可以获得结构化输出:python-json-logger 库。

$ pip install python-json-logger

安装后,您可以按以下方式使用它:

import sys import logging from pythonjsonlogger import jsonlogger . . . # The desired Log Record attributes must be included here, and they can be # renamed if necessary fmt = jsonlogger.JsonFormatter( "%(name)s %(asctime)s %(levelname)s %(filename)s %(lineno)s %(process)d %(message)s", rename_fields={"levelname": "severity", "asctime": "timestamp"}, ) # Set the log format on each handler stdoutHandler.setFormatter(fmt) errHandler.setFormatter(fmt) . . .

如果用上面突出显示的几行修改前面的示例,执行时将观察到以下输出:

{"name": "example", "filename": "main.py", "lineno": 31, "process": 179775, "message": "Server started listening on port 8080", "severity": "INFO", "timestamp": "2023-07-23 14:39:03,265"} {"name": "example", "filename": "main.py", "lineno": 32, "process": 179775, "message": "Disk space on drive '/var/log' is running low. Consider freeing up space", "severity": "WARNING", "timestamp": "2023-07-23 14:39:03,265"} {"name": "example", "filename": "main.py", "lineno": 38, "process": 179775, "message": "Failed to connect to database: 'my_db'", "exc_info": "Traceback (most recent call last):\n File \"/home/ayo/dev/betterstack/demo/python-logging/main.py\", line 36, in \n raise Exception(\"Failed to connect to database: 'my_db'\")\nException: Failed to connect to database: 'my_db'", "severity": "ERROR", "timestamp": "2023-07-23 14:39:03,265"}

还可以通过 level 方法上的extra属性在log point添加上下文数据,如下所示:

logger.info( "Server started listening on port 8080", extra={"python_version": 3.10, "os": "linux", "host": "fedora 38"}, ) {"name": "example", "filename": "main.py", "lineno": 31, "process": 195301, "message": "Server started listening on port 8080", "python_version": 3.1, "os": "linux", "host": "fedora 38", "severity": "INFO", "timestamp": "2023-07-23 14:45:42,472"}

正如您所看到的,built-in的 logging module 能够满足各种日志记录需求,并且具有可扩展性。不过,它的初始配置和自定义可能比较麻烦,因为在开始有效记录之前,你必须创建和配置loggers、handlers和formatters。

请参阅我们的 Python 日志指南和官方文档,进一步了解日志模块的功能和最佳实践。

2. Loguru - 最流行的Python第三方日志框架

Loguru 是最流行的 Python 第三方日志框架,在撰写本文时已在 GitHub 上获得超过 15k颗星。它旨在通过预配置日志记录器来简化日志记录过程,并通过其 add() 方法使自定义日志记录器变得非常容易。

默认日志记录器

使用 Loguru 启动日志记录非常简单,只需安装软件包并导入,然后调用其级别方法即可,如下所示:

$ pip install loguru from loguru import logger logger.trace("Executing program") logger.debug("Processing data...") logger.info("Server started successfully.") logger.success("Data processing completed successfully.") logger.warning("Invalid configuration detected.") logger.error("Failed to connect to the database.") logger.critical("Unexpected system error occurred. Shutting down.")

默认配置将半结构化和彩色化的输出记录到标准错误。它还默认将 DEBUG 作为最低级别,这也解释了为什么 TRACE 输出不会被记录。

在这里插入图片描述

自定义日志记录器

通过 add() 函数,可以轻松定制 Loguru 的内部工作机制,该函数可处理从日志格式化到日志目的地设置等一切操作。例如,您可以将日志记录到标准输出,将默认级别更改为 INFO,并使用下面的配置将日志格式化为 JSON:

from loguru import logger import sys logger.remove(0) # remove the default handler configuration logger.add(sys.stdout, level="INFO", serialize=True) . . . {"text": "2023-07-17 15:26:21.597 | INFO | __main__::9 - Server started successfully.\n", "record": {"elapsed": {"repr": "0:00:00.006401", "seconds": 0.006401}, "exception": null, "extra": {}, "file": {"name": "main.py", "path": "/home/ayo/dev/betterstack/demo/python-logging/main.py"}, "function": "", "level": {"icon": "ℹ️", "name": "INFO", "no": 20}, "line": 9, "message": "Server started successfully.", "module": "main", "name": "__main__", "process": {"id": 3852028, "name": "MainProcess"}, "thread": {"id": 140653618894656, "name": "MainThread"}, "time": {"repr": "2023-07-17 15:26:21.597156+02:00", "timestamp": 1689600381.597156}}}

Loguru 生成的默认 JSON 输出可能相当冗长,但使用类似这样的自定义函数可以轻松地将日志信息序列化:

from loguru import logger import sys import json def serialize(record): subset = { "timestamp": record["time"].timestamp(), "message": record["message"], "level": record["level"].name, "file": record["file"].name, "context": record["extra"], } return json.dumps(subset) def patching(record): record["extra"]["serialized"] = serialize(record) logger.remove(0) logger = logger.patch(patching) logger.add(sys.stderr, format="{extra[serialized]}") logger.bind(user_id="USR-1243", doc_id="DOC-2348").debug("Processing document") {"timestamp": 1689601339.628792, "message": "Processing document", "level": "DEBUG", "file": "main.py", "context": {"user_id": "USR-1243", "doc_id": "DOC-2348"}}

Loguru 也完全支持上下文日志记录。您已经看到上面的 bind() 方法,它允许在 log point 添加上下文数据。

您还可以使用 bind() 方法来创建子记录器来记录共享相同上下文的记录:

child = logger.bind(user_id="USR-1243", doc_id="DOC-2348") child.debug("Processing document") child.warning("Invalid configuration detected. Falling back to defaults") child.success("Document processed successfully")

请注意,user_id 和 doc_id 字段出现在所有三条记录中:

{"timestamp": 1689601518.884659, "message": "Processing document", "level": "DEBUG", "file": "main.py", "context": {"user_id": "USR-1243", "doc_id": "DOC-2348"}} {"timestamp": 1689601518.884706, "message": "Invalid configuration detected. Falling back to defaults", "level": "WARNING", "file": "main.py", "context": {"user_id": "USR-1243", "doc_id": "DOC-2348"}} {"timestamp": 1689601518.884729, "message": "Document processed successfully", "level": "SUCCESS", "file": "main.py", "context": {"user_id": "USR-1243", "doc_id": "DOC-2348"}}

另一方面,它的 contextualize() 方法可以轻松地为特定范围或上下文中的所有日志记录添加上下文字段。例如,下面的代码段演示了将唯一的请求 ID 属性添加到因该请求而创建的所有日志中:

from loguru import logger import uuid def logging_middleware(get_response): def middleware(request): request_id = str(uuid.uuid4()) with logger.contextualize(request_id=request_id): response = get_response(request) response["X-Request-ID"] = request_id return response return middleware

Loguru 还支持优秀日志框架所应具备的所有功能,如通过自动旋转和压缩将日志记录到文件、自定义日志级别、异常处理、同时记录到多个目的地等。它还为来自标准logging模块的用户提供了迁移指南。

请参阅Loguru 官方文档和我们专用 Loguru 指南,了解有关使用 Loguru 为 Python 应用程序创建production-ready日志设置的更多信息。

3. Structlog

Structlog 是一个日志库,专门用于生成 JSON 或 Logfmt 格式的结构化输出。它支持为开发环境提供彩色、美观的控制台输出,也允许完全自定义日志格式,以满足不同需求。你可以使用下面的命令安装 Structlog 软件包:

$ pip install structlog

Structlog 最简单的用法是调用 get_logger()方法,然后在生成的logger上使用任何level方法:

import structlog logger = structlog.get_logger() logger.debug("Database query executed in 0.025 seconds") logger.info( "Processing file 'data.csv' completed. 1000 records were imported", file="data.csv", elapsed_ms=300, num_records=1000, ) logger.warning( "Unable to load configuration file 'config.ini'. Using default settings instead", file="config.ini", ) try: 1 / 0 except ZeroDivisionError as e: logger.exception( "Division by zero error occurred during calculation. Check the input values", exc_info=e, ) logger.critical("Application crashed due to an unhandled exception")

在这里插入图片描述

Structlog 日志记录器的默认配置对开发环境非常友好。输出是彩色的,任何包含的上下文数据都以 key=value 对的形式出现。此外,tracebacks的格式和organized都很整齐,因此更容易发现问题的原因。

Structlog 的独特之处在于,它不会按levels去过滤记录。这就是为什么上面所有的levels都被写入控制台的原因。不过,通过 configure() 方法配置默认级别也很简单,如下所示:

import structlog import logging structlog.configure(wrapper_class=structlog.make_filtering_bound_logger(logging.INFO))

Structlog 与标准logging模块中的日志级别兼容,因此可以使用上述 logging.INFO 常量。你也可以直接使用与级别相关的数字:

structlog.configure(wrapper_class=structlog.make_filtering_bound_logger(20))

get_logger()函数返回的日志记录器称为绑定日志记录器,因为您可以将上下文值与之绑定。一旦绑定了key/value pairs,它们将包含在日志记录器生成的每个后续日志条目中。

import structlog import platform logger = structlog.get_logger() logger = logger.bind(python_version=platform.python_version(), os="linux") . . . 2023-07-23 17:20:10 [debug] Database query executed in 0.025 seconds os=linux python_version=3.11.4 2023-07-23 17:20:10 [info] Processing file 'data.csv' completed. 1000 records were imported elapsed_ms=300 file=data.csv num_records=1000 os=linux python_version=3.11.4

绑定日志记录器还包括一系列处理器函数,可在日志记录通过日志记录管道时对日志记录进行转换和丰富。例如,您可以使用以下配置以 JSON 格式记录日志:

import structlog import platform structlog.configure( processors=[ structlog.processors.TimeStamper(fmt="iso"), structlog.processors.add_log_level, structlog.processors.JSONRenderer(), ] ) . . .

每个处理器都按照声明顺序执行,因此首先调用 TimeStamper() 为每个条目添加 ISO-8601 格式的时间戳,然后通过 add_log_level 添加严重级别,最后调用 JSONRenderer() 将整个记录序列化为 JSON 格式。对程序进行高亮显示的修改后,您将看到以下输出:

{"python_version": "3.11.4", "os": "linux", "event": "Database query executed in 0.025 seconds", "timestamp": "2023-07-23T15:32:21.590688Z", "level": "debug"} {"python_version": "3.11.4", "os": "linux", "file": "data.csv", "elapsed_ms": 300, "num_records": 1000, "event": "Processing file 'data.csv' completed. 1000 records were imported", "timestamp": "2023-07-23T15:32:21.590720Z", "level": "info"}

Structlog 能做的另一件很酷的事情是自动格式化tracebacks,使其也以 JSON 格式序列化。你只需要像这样使用 dict_tracebacks 处理器:

structlog.configure( processors=[ structlog.processors.TimeStamper(fmt="iso"), structlog.processors.add_log_level, structlog.processors.dict_tracebacks, structlog.processors.JSONRenderer(), ] )

每当记录异常情况时,你会发现记录中的异常情况信息格式非常丰富,便于在日志管理服务中进行分析。

{"python_version": "3.11.4", "os": "linux", "event": "Division by zero error occurred during calculation. Check the input values", "timestamp": "2023-07-23T16:07:50.127241Z", "level": "error", "exception": [{"exc_type": "ZeroDivisionError", "exc_value": "division by zero", "syntax_error": null, "is_cause": false, "frames": [{"filename": "/home/ayo/dev/betterstack/demo/python-logging/main.py", "lineno": 32, "name": "", "line": "", "locals": {"__name__": "__main__", "__doc__": "None", "__package__": "None", "__loader__": "", "__spec__": "None", "__annotations__": "{}", "__builtins__": "", "__file__": "/home/ayo/dev/betterstack/demo/python-logging/main.py", "__cached__": "None", "structlog": "\"


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有