Python 脚本是保持许多应用程序及其基础架构运行的粘合剂,但是当您的一个脚本抛出异常时,您可能不会立即知道它,除非您有一个集中的地方来聚合错误。这就是添加 Sentrycan 解决这个分布式错误日志记录问题的地方。
在本教程中,我们将了解如何将 Sentry 快速添加到新的或现有的 Python 脚本,以将错误报告到集中位置以进行进一步调试。
开发环境搭建
确保安装了 Python 3。截至目前,Python 3.8.3 是最新的 Python 版本。
在本教程中,我们还将使用:
- sentry.io 上的托管 Sentry 实例,我们需要一个帐户才能访问
- Sentry Python 帮助程序库以将异常数据发送到我们的 Sentry 实例
使用以下命令将上述代码库安装到新的 Python 虚拟环境中:
python -m venv sentryscript
source sentryscript/bin/activate
pip install sentry-sdk>=0.14.4
我们的开发环境已经准备就绪,我们可以编写一些会抛出异常的代码来演示如何使用 Sentry。
请注意,本教程的所有代码都可以在 GitHub 上的 python-script-sentry 目录下的 blog-code-examplesGit 存储库中找到。
用于加载 Python 模块的示例脚本
我们将从编写一个小而有用的脚本开始,该脚本打印出 Python 包中所有模块的名称,然后在捕获异常显然是有用的添加时将 Sentry 添加到其中。
创建一个名为module_loader.py
的新文件,并在其中写入以下代码行,以便我们可以轻松地在命令行上执行它。
import argparse def import_submodules(package): return {} if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("package") args = parser.parse_args() package_to_load = args.package results = import_submodules(package_to_load) for r in results: print(str(r))
当从命令行调用脚本时,上面的代码接受一个参数,并将该值用作存根import_submodules
函数的输入,该函数将包含遍历包内模块树的代码.
接下来,添加以下突出显示的代码行以使用 importlib
和 pkgutil
递归地从包中导入模块(如果找到与发送的名称匹配的模块)作为 package
参数。
import argparse import importlib import pkgutil def import_submodules(package): """Import all submodules of a module, recursively, including subpackages. :param package: package (name or actual module) :type package: str | module :rtype: dict[str, types.ModuleType] """ if isinstance(package, str): package = importlib.import_module(package) results = {} for loader, name, is_pkg in pkgutil.walk_packages(package.__path__): full_name = package.__name__ + '.' + name try: results[full_name] = importlib.import_module(full_name) if is_pkg: results.update(import_submodules(full_name)) except ModuleNotFoundError as mnfe: print("module not found: {}".format(full_name)) except Exception as general_exception: print(general_exception) return results if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("package") args = parser.parse_args() package_to_load = args.package results = import_submodules(package_to_load) for r in results: print(str(r))
上面的新代码循环遍历所有包含 walk_package
标准库模块中的pkgutil
函数的包,并尝试使用 ## 导入它#
在包名上加上包作为字符串。如果结果成功,该函数将递归调用自身以在导入的包中导入子模块。如果未找到模块或出现其他问题,则会捕获异常,以便脚本不会失败,而是可以继续处理潜在的模块。
测试完整的脚本以查看它在命令行上使用任意包打印出的内容:
python module_loader.py importlib
上面的例子生成输出:
importlib._bootstrap importlib._bootstrap_external importlib.abc importlib.machinery importlib.resources importlib.util
尝试检查未安装的包会报错。将脚本与当前环境中未安装的包一起使用。
python module_loader.py flask
由于预期的ModuleNotFoundError
,上述命令产生了以下回溯。
Traceback (most recent call last): File "module_loader.py", line 35, in <module> results = import_submodules(package_to_load) File "module_loader.py", line 14, in import_submodules package = importlib.import_module(package) File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/importlib/__init__.py", line 127, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "<frozen importlib._bootstrap>", line 1006, in _gcd_import File "<frozen importlib._bootstrap>", line 983, in _find_and_load File "<frozen importlib._bootstrap>", line 965, in _find_and_load_unlocked ModuleNotFoundError: No module named 'flask'
如果将 Flask 安装到当前环境中,则会找到模块,应用程序将遍历模块和子模块列表。
我们的示例脚本是可用的,但是如果我们在一个或多个我们不经常检查的服务器上运行这段代码或类似的东西怎么办?这就是有一种方法可以将一个或多个脚本的异常输出聚集在一个地方的地方。 Sentry 可以帮助我们实现这一目标。
使用哨兵添加异常报告
Sentry 可以自托管,也可以通过 Sentry.io 用作云服务。在本教程中,我们将使用云托管版本,因为它比设置您自己的服务器更快,而且对于较小的项目是免费的。
转到 Sentry.io 的主页。
登录您的帐户或注册一个新的免费帐户。登录或完成 Sentry 注册过程后,您将进入主帐户仪表板。
我们的帐户仪表板上还没有记录任何错误,这符合预期,因为我们尚未将我们的帐户连接到 Python 脚本。
您需要为此应用程序创建一个新的 Sentry 项目,因此单击左侧栏中的“项目”以转到“项目”页面。
在“项目”页面上,单击页面右上角的“创建项目”按钮。
选择 Python,为您的新项目命名,然后按“创建项目”按钮。我们的新项目已准备好与我们的 Python 脚本集成。
我们需要我们的帐户和项目的唯一标识符来授权我们的 Python 代码将错误发送到此 Sentry 实例。获取所需内容的最简单方法是转到 Python 入门文档页面并向下滚动到“配置 SDK”部分。
复制 init
方法的字符串参数并将其设置为环境变量,而不是直接在您的应用程序代码中公开它。
export SENTRY_DSN='https://yourkeygoeshere.ingest.sentry.io/project-number'
确保将“yourkeygoeshere”替换为您自己的唯一标识符,并将“project-number”替换为与您刚刚创建的项目匹配的 ID。
使用 SENTRY_DSN
命令检查 echo
是否在您的 shell 中正确设置:
echo $SENTRY_DSN
修改应用程序以向 Sentry 发送异常信息,因为我们已经有了我们的唯一标识符。再次打开 module_loader.py
并更新以下突出显示的代码行。
import argparse import importlib import os import pkgutil import sentry_sdk from sentry_sdk import capture_exception # find on https://docs.sentry.io/error-reporting/quickstart/?platform=python sentry_sdk.init(dsn=os.getenv('SENTRY_DSN')) def import_submodules(package): """Import all submodules of a module, recursively, including subpackages. :param package: package (name or actual module) :type package: str | module :rtype: dict[str, types.ModuleType] """ if isinstance(package, str): package = importlib.import_module(package) results = {} for loader, name, is_pkg in pkgutil.walk_packages(package.__path__): full_name = package.__name__ + '.' + name try: results[full_name] = importlib.import_module(full_name) if is_pkg: results.update(import_submodules(full_name)) except ModuleNotFoundError as mnfe: print("module not found: {}".format(full_name)) capture_exception(mnfe) except Exception as general_exception: print(general_exception) capture_exception(general_exception) return results if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("package") args = parser.parse_args() package_to_load = args.package results = import_submodules(package_to_load) for r in results: print(str(r))
这些新代码行导入了 Sentry Python SDK 和 os
库(用于读取系统环境变量)。然后,应用程序使用在 SENTRY_DSN
环境变量中找到的字符串初始化 Sentry SDK。在 import_submodules
函数中,每当 capture_exception
被抛出或另一个将被捕获的异常时,调用 ModuleNotFoundException
SDK 函数更广泛的 Exception
桶。
现在我们的代码已经准备就绪,让我们测试新的 Sentry 集成。
测试脚本并查看异常
测试哨兵代码是否正常工作的最简单方法是尝试导入一个不存在的模块。假设您在命令中输入错误并尝试在 importliba
而不是 importlib
上运行脚本(可能是因为您使用的是糟糕的 Macbook Pro“蝴蝶”键盘而不是耐用的键盘)。尝试一下,看看会发生什么:
python module_loader.py importliba
脚本将运行并完成,但会出现错误,因为该模块不存在。感谢我们的新代码,我们可以在 Sentry 中查看错误。
检查 Sentry 仪表板以查看错误。
我们还可以点击进入错误以了解更多关于发生的事情。
您还可以收到有关发生的错误的电子邮件报告,这样您就不必始终保持登录到仪表板。
所有配置完成后,随着我们的 Python 应用程序变得更加复杂,我们现在有了一个很好的基础来扩展脚本并使用 Sentry 构建更好的错误处理。
下一步是什么?
我们刚刚创建了一个示例脚本,它输出一个包中的所有模块和子模块,然后将 Sentry 添加到其中,以便它将任何异常报告回我们的中央托管实例。
这只是对 Sentry 的简单介绍,所以接下来您需要阅读以下文章之一以使用它做更多事情:
- Python Sentry 文档
- 如何在 Flask 中使用 Sentry
- 将 Sentry 集成到 Celery 任务队列中
您还可以通过阅读 Full Stack Python 目录页面了解在您的 Python 项目中接下来要编写什么代码。
有问题吗?通过 Twitter@fullstackpython 或@mattmakai 与我联系。我也在 GitHub 上,用户名是 mattmakai。
这篇文章有问题吗?在 GitHub 上创建此页面的源代码并提交拉取请求。