Sentry集成

Sentry 是一个错误跟踪和性能监控服务。每当发生错误时,应用程序会向它发送一份报告。Sentry 会将相同的错误归并为一个 issue,显示堆栈跟踪和上下文,并在出现新 issue 时提醒团队。你可以使用 sentry.io 上的托管服务,也可以自行部署。借助 Sentry,你可以在一个地方找到所有站点的生产环境错误,而不必逐个搜索每个站点的错误日志。

概述

当你设置一个环境变量并且站点选择启用遥测后,Frappe 会将意外的服务器错误和 desk JavaScript 错误发送到 Sentry 项目。可选变量可以添加性能跟踪、性能分析和 SQL span。

  • 每个 bench 一个 DSN。 DSN 来自进程环境,而不是来自 site_config.json。由同一组 web 和 worker 进程服务的所有站点都会报告到同一个 Sentry 项目。
  • 两个开关。 DSN 为进程开启 Sentry SDK。系统设置中的复选框允许发送使用数据以改进应用程序(enable_telemetry)决定每个站点是否发送任何内容。
  • 错误跟随错误日志。 在服务器上,当 Frappe 写入一条错误日志时,事件会发送到 Sentry。如果你看到一条错误日志,Sentry 应该也有。
  • 站点通过名称区分。 每个事件都会将 Sentry 用户 ID 设置为站点名称,因此你可以在 Sentry 中按站点筛选。

设置

为 web 和 worker 进程设置 FRAPPE_SENTRY_DSN,重启它们,然后在每个应报告的站点上启用遥测。

变量 值 效果
FRAPPE_SENTRY_DSN 你项目的 DSN 必需。在 web 服务器和后台 worker 中启动 Sentry SDK,并在 desk 中加载浏览器 SDK。
SENTRY_TRACING_SAMPLE_RATE 0.0 到 1.0 之间的浮点数,例如 0.1 可选。为该比例的 web 请求和作业发送性能跟踪。
SENTRY_PROFILING_SAMPLE_RATE 0.0 到 1.0 之间的浮点数 可选。为该比例的被跟踪事务发送 CPU 性能分析。需要启用跟踪。
ENABLE_SENTRY_DB_MONITORING 任意非空值,例如 1 可选。将每个 SQL 查询记录为一个 span,并将每次数据库连接记录为一个面包屑。
  1. 在 Sentry 中创建一个项目(平台:Python)并复制其 DSN。
  2. 将这些变量添加到 Frappe web 服务器(gunicorn)以及每个 bench worker 进程的环境中。使用 supervisor 时,使用每个程序的 environment= 行。使用 Docker 时,在后端和 worker 容器上设置它们。
  3. 重启进程。Frappe 仅在进程启动时读取这些变量。
  4. 在每个站点上,打开系统设置并勾选允许发送使用数据以改进应用程序。从 CLI 执行:bench --site <site> execute frappe.db.set_single_value --args "['System Settings', 'enable_telemetry', 1]"</site>。
  5. 在浏览器中重新加载 desk,以便加载浏览器 SDK。

如果设置了 DSN 但站点的 enable_telemetry 处于关闭状态,该站点不会发送任何内容:没有服务器事件,没有浏览器事件,并且 desk 不会加载 SDK。

捕获的内容

Sentry 会获取开发者可以在代码中修复的错误。Frappe 有意向用户显示的错误则被排除在外。

来源 发送到 Sentry 不发送
Web 请求 以 HTTP 5xx 结束的未捕获异常。在开发者模式下,Frappe 记录的每个错误。 frappe.ValidationError、frappe.PermissionError 及其子类(例如 frappe.throw() 消息、必填字段错误)。正常的 4xx 响应。
后台作业 失败并写入错误日志的作业。Worker 在每个作业后刷新事件。 与 web 请求相同的异常过滤器。
frappe.log_error() 来自应用代码的每次调用,如果有活动异常则作为异常事件,否则作为带有标题和回溯的消息。 错误日志插入本身失败的调用。
Desk(浏览器) /desk 上的未捕获 JavaScript 错误和未处理的 promise 拒绝。 JS 中由 frappe.throw() 引发的错误。门户和网站页面:SDK 仅在 desk 中加载。会话跟踪已关闭。

服务器 SDK 运行时关闭了 Sentry 的默认集成。它不会挂钩 Python logging,因此 frappe.logger().error(...) 不会创建 Sentry 事件。来自 CSSUTILS 日志记录器的事件会被丢弃。

发布版本是 Frappe 版本(服务器上为 frappe.__version__,浏览器中为 frappe.boot.versions.frappe),因此你可以看到是哪个版本引入了错误。

性能监控

设置 SENTRY_TRACING_SAMPLE_RATE 以在 Sentry 中查看缓慢的端点和作业。在繁忙的站点上从低值开始(0.01 到 0.1),因为每个被跟踪的请求都会作为事务计费。

  • Web 请求。 Frappe 将 WSGI 应用包装在 Sentry 的中间件中。事务按端点命名:RPC 调用为 /api/method/<dotted.path></dotted.path>,否则为 URL 路径。
  • 后台任务。事务以任务的方法命名。计划任务则使用调度器任务类型。任务上下文包含任务 ID、其参数、是否为计划任务,以及它在队列中的等待时间(秒)。
  • SQL span。启用 ENABLE_SENTRY_DB_MONITORING 后,每次 frappe.db.sql() 调用都会变成一个带有查询文本的 span,并且每次数据库连接都会添加一条面包屑。这就是你查找 N+1 查询的方法。它也会给每次查询增加开销,所以在繁忙的生产环境中启用前请先测量。
  • 性能分析。SENTRY_PROFILING_SAMPLE_RATE 是被追踪事务的一部分,因此 0.5 在追踪率为 0.1 时会分析 5% 的请求。

当这三个变量中的任意一个被设置时,Frappe 还会在每个请求和任务开始时标记作用域(事务名称、站点、用户、追踪 ID)。这仅在启用了遥测的站点上发生。

数据与隐私

错误事件可能包含业务和个人数据。请像对待生产数据库一样对待你的 Sentry 项目,并限制可以读取它的人员。

Sentry 中的字段 内容
用户 ID 站点名称,例如 “erp.example.com”
标签 frappe_user 已登录用户,例如 “jane@example.com”,或 “Guest”
标签 frappe_trace_id X-Frappe-Request-ID 标头(如果请求带有的话)
请求 URL、方法、查询字符串和标头。SDK 会剥离 Cookie、Authorization 和其他敏感标头。不包含客户端 IP。
上下文 “JSON Body” / “Form Data” 完整的请求负载,未经过滤。这可能包含文档数据和个人数据。
额外 job 任务 ID、方法、所有任务参数、队列等待时间
堆栈跟踪 带有局部变量的代码帧(Sentry SDK 默认)
消息 对于 frappe.log_error() 调用:Error Log 标题和回溯
模块、argv 已安装的 Python 包和进程命令行
SQL span 仅查询文本。查询值不会被发送。

遥测复选框不涵盖性能数据。选择性加入检查针对错误事件和作用域标签运行。追踪、性能分析和 SQL span 会为进程中的每个请求和任务启动。如果一个 bench 托管了未选择加入的站点,请不要在其上设置这三个性能变量。

对于其他你绝不能存储的内容(例如包含国民身份证号或银行详细信息的字段),请使用 Sentry 的服务器端数据擦除功能。

故障排除

症状 检查项
完全没有事件 FRAPPE_SENTRY_DSN 是否在运行进程的环境中,而不仅仅是你的 shell 中?设置后你是否重启了 Web 和工作进程?站点的 enable_telemetry 是否开启?
存在 Error Log,但 Sentry 中没有任何内容 该异常是否是 ValidationError 或 PermissionError 的子类?这些会被有意跳过。
Sentry 中有 500 错误,但没有 Error Log 几秒后再检查。请求错误会以延迟插入的方式写入 Error Log。
缺少任务错误 检查工作进程是否具有 DSN。工作进程在启动时会启动自己的 SDK。
没有浏览器错误 打开 desk 并检查浏览器控制台中 frappe.boot.sentry_dsn 是否有值。如果为空,则站点的遥测已关闭。Content-Security-Policy 规则必须允许向你的 Sentry 主机发起请求。
事务过多或 Sentry 账单过高 降低 SENTRY_TRACING_SAMPLE_RATE 或将其移除,然后重启。
缺少来自脚本或 bench console 的错误 只有 Web 服务器和 bench worker 会启动 SDK。其他 CLI 命令不会向 Sentry 报告。

代码位于 frappe/utils/sentry.py、frappe/app.py(Web)、frappe/utils/background_jobs.py(工作进程)、frappe/utils/error.py(log_error 钩子)和 frappe/public/js/sentry.bundle.js(浏览器)中。