对于基本的健康跟踪,Sentry 接受包含会话更新事件的 envelopes。这些会话更新事件可用于通知 Sentry 有关 release 和 project 相关 project 健康状况的信息。
注意: 在本地使用 session 时,请确保使用必要的环境变量更新配置文件 ~/.sentry/sentry.config.py:
SENTRY_EVENTSTREAM = 'sentry.eventstream.kafka.KafkaEventStream'
基本操作
- Session 完全是 client 驱动的。client 确定 session 何时开始、结束或转变为不健康状态。
- client 可以明确结束 session 以记录时间或退出条件(崩溃等)。
- 如果需要,client 应在重新启动时明确结束 session,但 session 不结束是可以接受的。
- Session 通过保存整个 session state 的会话更改事件进行更新。
- Session 从发送的事件中更新。最近的事件保存整个 session state。发送到服务器的初始 session event 被显式标记。
- Session 更新在实现时不得更改属性或数据损坏。请参阅下面关于属性不变性的部分。
- Session 只能更新 5 天。如果一个会话在 5 天内没有收到第二个事件,则永远是好的。
- Session 不一定要启动才崩溃。仅仅报告一次崩溃就足够了。
服务器模型
目前,Sentry 的 session system 已针对易于扩展性和操作成本进行了优化。这意味着该协议非常适合实现这一目标。其中一些优化显示在协议中,client 准确地遵循协议以避免在服务器上创建 bad data 非常重要。
服务器每小时都有预先物化(pre-materialized)的 session 数据。当会话更新事件到来时,服务器将立即将数据具体化(materialize)到正确的存储桶中。这意味着该协议仅限于“附加”。这也意味着 client 需要在其一侧存储会话的整个状态。
会话更新负载
一个 session update 是名为 session 的 envelope 中的一项。它包含一个大致如下所示的 JSON 负载:
- {
- "sid": "7c7b6585-f901-4351-bf8d-02711b721929",
- "did": "optional distinct user id",
- "init": true,
- "started": "2020-02-07T14:16:00Z",
- "duration": 60,
- "status": "exited",
- "attrs": {
- "release": "my-project-name@1.0.0",
- "environment": "environment name",
- "ip_address": "optional user ip address for filtering",
- "user_agent": "optional user agent for filtering"
- }
- }
请注意,这必须包含在 envelope 中。所以完整的事件看起来像这样:
- {}
- {"type":"session"}
- {"sid":"..."}
存在以下字段:
sid
- String, optional. 会话 ID (唯一的并且由客户端生成的)。
如果初始会话状态为 exited,则允许客户端跳过它。
did
- String, optional. Distinct ID. 应该是设备或用户 ID。
系统会在存储此 ID 之前自动对其进行哈希处理。
seq
- Number, optional. 一个逻辑时钟。默认为摄取期间的当前 UNIX 时间戳(以毫秒为单位)。
值 0 是保留的,因为 init 设置为 true 的会话将自动将 seq 强制为 0。
timestamp
- String, optional. 会话更改事件发生时的时间戳。
必须是 ISO 日期时间字符串。如果未发送,服务器将采用当前的 UTC 时间戳。在数据模型中,这称为 received。
started
- String, required. 会话开始时的时间戳。
必须是 ISO 日期时间字符串。
init
- Boolean, optional, 默认为 false。
如果将其设置为 true,则表示这是会话的第一个事件。这让服务器优化会话计数,因为不需要重复数据删除(客户端无论如何都是权威的)。在内部设置此标志时,处理时 seq 更改为0。
duration
- Number, optional. 一个可选字段,可以在接收到事件时传输会话持续时间。这可以由客户端控制,例如,可以减去非活动时间(以浮点数表示的秒数)。
status
- String, optional, 默认是ok。会话的当前状态。
一个 session 只能有效地处于两种状态:ok,这意味着会话处于活动状态或终止状态之一。当会话从 ok 移开时,它不能再被更新。
- ok: 会话当前正在进行中,但运行良好。这可以是会话的终止状态。
- exited: 会话正常终止。
- crashed: 会话因崩溃而终止。
- abnormal: 会话遇到非崩溃相关的异常退出。
errors
- _Number, optional, 默认为 0 _。此会话正在进行时遇到的错误的运行计数器。
重要的是,当会话进入 crashed 时,此计数器也会增加。(例如:crash 本身也始终是一个 error)。如果未设置或为 0,摄取应强制 errors 为 1。
attrs
- Object, required 除了 release 之外的所有 key 都是可选的。具有以下属性的对象:
- release: Sentry Release ID (release),建议格式为 my-project-name@1.0.0。
- environment: Sentry 环境 (environment)。
- ip_address: 要考虑的主要 IP 地址。这通常是用户的 IP。此数据不会持久化,而是用于过滤。如果未设置,则自动填写 IP。
- user_agent: 要考虑的 user agent。这通常是导致会话的用户的用户代理。此数据不会持久化,而是用于过滤。
Session Aggregates Payload(会话聚合有效负载)
特别是对于 request-mode(请求模式) 会话(见下文),通常每秒有数千个请求和会话。
假设这些 session 将是短时间的,并且不希望跟踪它们的持续时间, 那么在它们被发送到 Sentry 之前,可以在 SDK 端将这些 session 聚合在一起。
SDK 应聚合关闭的 session,并按 started 时间、distinct_id 和 attrs 对它们进行分组。这些组将作为 sessions envelope 项发送。它包含一个大致如下所示的 JSON 负载:
- {
- "aggregates": [
- {
- "started": "2020-02-07T14:16:00Z",
- "exited": 123
- },
- {
- "started": "2020-02-07T14:16:00Z",
- "did": "optional distinct user id",
- "exited": 12,
- "errored": 3
- }
- ],
- "attrs": {
- "release": "my-project-name@1.0.0",
- "environment": "development"
- }
- }
请注意,这必须包含在 envelope 中。所以完整的 envelope 看起来像这样:
- {}
- {"type": "sessions"}
- {"aggregates": [...], "attrs": {...}}
aggregates
- Array, required. 按 started 时间戳和 distinct id (did) 分组的聚合数组。
- started: Required. 组的时间戳,四舍五入到分钟。必须是 ISO 日期时间字符串。
- did: Optional. 组的 distinct user id。
- exited: Optional. 状态为 "exited" 且没有任何错误的 session 数。
- abnormal: Optional. 状态为 "abnormal" 的 session 数。
- crashed: Optional. 状态为 "crashed" 的 session 数。
- errored: Optional. 状态为 "exited" 且 errors 计数不为零的 session 数。
attrs
- Object, required. 见上文。
崩溃与会话
会话(Session)和错误(error)事件是 Sentry 中两个不同的系统。 Session 更新可以在不发送错误事件的情况下完成,同样,可以在没有 session 更新的情况下发送 error。
这使 client 可以完全控制应如何执行 session 更新。激励因素是服务器可以在某些情况下自由拒绝 error 事件,在这种情况下记录 session 信息仍然很有趣。例如,如果项目对 error 事件应用了 rate limit,则它们的 session 数据仍然可以绕过此 rate limit 路由到项目。
但是,强烈建议在与 crash 事件相同的 envelope 中发送 session 更新,以防 session 转换到 crashed 状态。如果网络不可靠,这将确保事件同时到达系统。
重要客户端行为
这些是 client 必须遵守的重要规则:
属性不可变性
当前不允许 session 更改后续更新中的任何属性,包括 did、started 或其他属性。唯一允许更改的属性是 session 状态、持续时间或错误计数。如果一开始不知道 user,则应该延迟 session 开始,或者一旦知道 user 就应该重新启动 session。
会话计数/初始化
发送到系统的初始 session 更新必须将 init 设置为 true。这是必要的,因为服务器当前不会将总 session 计数作为优化进行重复数据删除。如果初始的 init: true flag 丢失,则 Sentry 可能无法正确摄取 session。
终止会话状态
Session 可以存在两种状态:进行中(progress)或终止(terminated)。终止的 session 不得接收进一步的更新。退出(exited)、崩溃(crashed)和异常(abnormal)都是终止状态。当 session 达到此状态时,client 不得再报告任何 session 更新或启动新 session。
鼓励 SDK 区分结束 session 的不同方式:
- exited: 这意味着 session 干净地结束了。从成功报告的角度来看,这与保持 ok 的 session 没有任何不同。但是,只有以 exited 结尾的 session 才会被考虑用于 session 持续时间。即使发生 error,session 也可以进入 exited 状态。
- crashed: 在以下情况下,session 应报告为 crashed:
- 发生未处理的错误(unhandled error)并且 session 自然结束(例如:HTTP 请求结束)
- 应用程序完全崩溃(崩溃到桌面,终止)
- 用户反馈(feedback)对话框显示给用户。在此之后,SDK 必须启动一个新 session,就像它完全崩溃一样。
- abnormal: 如果可以的话,鼓励 SDK 始终将 session 转换为 exited 或 crashed。对于能够始终结束 session 的 SDK,如果无法检测到应用程序正确关闭, 则应以 abnormal 结束 session。异常 session 示例:
异常的 session 结束通常会在应用程序重新启动时被记录下来。
- 计算机被关闭/断电
- 用户通过 kill -9 或任务管理器强制关闭应用程序
崩溃、异常与错误
Session 应该在遇到未处理的错误(例如应用程序完全崩溃)时转换为 crashed。对于无法完全崩溃的应用程序(例如网站),如果用户遇到错误对话框,则转换到 crashed 状态是可以接受的。对于我们为每个传入请求创建 session 的 server 环境,crashed 基本上就像状态代码 500 内部服务器错误。因此,如果在请求期间发生未处理的错误(unhandled error),会话应该 crashed。
Abnormal 是其命运未知的 session。例如,对于桌面应用程序,如果 session 被存储但未观察到应用程序退出但也没有崩溃, 则将 session 转换为 abnormal 是有意义的。在这些情况下,用户通过任务管理器强制关闭应用程序、机器断电或其他情况。可以通过将 session 持久保存到磁盘来存储 session。这个保存的文件可以在应用程序重启时检测到,以关闭 abnormal 的会话。
错误的 session 由大于零的 errors 计数器确定。 client 需要对被视为 error 的事件进行计数,并将计数与 session 更新一起发送。正常且错误计数大于零的 session 被视为错误 session。所有崩溃和异常 session 也始终被视为错误,但会从最终错误 session 计数中减去。
退出
Session 可以转换为 exited,这与 ok 状态完全相同, 但有一个区别:转换为 exited 的 session 的 session 持续时间是平均的。这让 Sentry 向您显示非崩溃 session 的持续时间。
警报
当 issue 影响到指定百分比的会话时触发警报。创建新的 issue alert 并选择 "When" 条件 An issue affects more than {X} percent of sessions(问题影响超过 {X}% 的会话)。在 Issue Alert Configuration 文档中查看更多问题警报选项。
- https://docs.sentry.io/product/alerts/alert-types/#issue-alerts
- https://docs.sentry.io/product/alerts/create-alerts/issue-alert-config/
SDK 注意事项
一般来说,SDK 可以使用两种独立的健康报告模式。一个是非常短暂的 session,另一个是用户参与的 session。
短时会话 (server-mode / request-mode)
这些 session 大致对应于服务器设置中的 HTTP 请求或 RPC 调用。
- 海量请求, 通常每个请求一个会话
- Session 数通常高于 Sentry 事件数
- Session 附加到单个 hub/concurrency unit
- 计时信息通常是无用的,因为 session 时间以毫秒为单位
用户参与的会话 (user-mode / application-mode)
这些 session 更对应于实际的用户 session 或应用程序运行。这就是您在 Web 浏览器、移动世界、命令行应用程序或类似应用程序中会看到的内容。
- 通常只是从应用程序开始到退出的单个 session
- 如果适用,一旦应用程序置于后台超过 30 秒(移动 SDK),session 就可以结束
- session 次数通常少于 Sentry 事件
- session 跨越多个 hub/thread
- session 持续时间通常以分钟为单位,计时信息很有用
从 API 的角度来看,这两种情况看起来很相似,但对于 SDK 的建议不同。
选择会话模式
虽然理论上可以在单个应用程序中使用两种会话模式,但建议 SDK 默认使用最适合语言生态系统主要用例的单一模式。这类似于某些 SDK 支持的全局 Hub 模式,并且可以以相同的方式使用。
当 SDK 配置为使用 user-mode 会话或全局 Hub 模式时,应在应用程序启动时启动单个会话,并应在应用程序的运行时持续存在。根据 SDK 内部结构,此单个 session 可以在所有应用程序线程和线程本地 Hub 之间共享。
使用 server-mode 会话时,不会启动应用程序范围的 session, 并且由集成或用户在收到请求时启动 session 并在返回响应时结束 session。
统一 API 的含义
SDK 应该遵循的统一 API 定义了 Hub、Scope 和 Client 的概念。
从概念上讲,session 是 Hub 的关注点,与 scope 不同,session 不应嵌套。当任何一种事件发生时,应该只有一个明确的 session 来跟踪错误计数。
在考虑通过 SDK 的事件流时,从静态 capture_event 函数,通过线程本地 Hub,进入 Client::capture_event(event, scope) 方法;根据 SDK 的内部实现细节,将 session 附加到 Scope 可能是有意义的, 这将使 Client 可以将 event 和 session 更新捆绑到单个 envelope 中以发送到 Sentry。
会话更新以及何时向上游发送更新
对于所有 SDK,只要在调用 apply_to_scope 的类似位置捕获数据以增加 error 计数, 或根据 distinct ID / user ID 更新 session,将自动更新当前 session。
SDK 通常应旨在减少向上游发送的 envelope 数量。
跟踪大量会话的 server-mode SDK 应考虑使用定期 session 刷新器(每 60 秒), 将会话预聚合到单个 session_aggregates envelope 项中。
User-Mode SDK 可能会选择在同一 envelope 中发送 session 更新以及捕获的事件。关闭 session 的最终 session 更新可以类似于 Server-Mode session 进行批处理。
在任何一种情况下,必须为 session 的第一次传输正确设置 init 标志, 并且 session 元数据(例如 distinct ID)在初始传输后必须是不可变的。
会话的预聚合
如果 SDK 配置为使用 server-mode session,则应在将 session 计数发送到 Sentry 之前对其进行分组和预聚合。每当 session 关闭(转换到 terminal 状态),并且之前没有向上游发送(其 init 标志为 true)时,它就有资格进行聚合,其执行方式如下:
- Session 的 started 时间戳应四舍五入到分钟。
- 然后必须将 Session 聚合到由该舍入时间戳标识的存储 bucket 中,以及会话的 distinct id (did)。
- 在适当的 bucket 中,根据 session 状态增加 session 计数。与单个 session 更新相反,"errored" 状态用于标记具有 "exited" 状态和非零 errors 计数的会话。
公开 API
公开的最基本的 API 位于 hub 级别,可让您启动和停止 session 记录:
API:
Hub.start_session()
在当前 scope 上存储一个 session 并开始跟踪它。这通常会将一个全新的 session 附加到 scope,并隐式地结束任何已经存在的 session。
Hub.end_session()
结束 session,设置适当的 status 和 duration,并将其加入队列以发送到 Sentry。
Hub.start_auto_session_tracking() / Hub.stop_auto_session_tracking()
停止并重新激活自动 session 跟踪。
初始化选项:
auto_session_tracking
这通过集成 启用/禁用 自动 session 跟踪。
SDK 实现指南
在开始在 SDK 中实现此功能之前,请与团队联系。
- https://github.com/getsentry/develop/pull/323
我们通过从 SDK 发送 session 有效负载来跟踪 Sentry 中每个项目 release 的健康状况。 Session 有效负载提供诸如 session 持续时间以及是否存在错误/崩溃等数据。
SDK 以两种模式之一跟踪 session:
- 单个 Session
- Session 聚合
单个 session 是一般情况,非常适合通常只涉及单个用户的(相对短暂的)应用程序。例子:
- 命令行实用程序,如 craft;craft 子命令的每次执行都会向 Sentry 报告一个 session
- 用户与 mobile app 交互
- 用户使用他们最喜欢的浏览器加载网站
当发送单个 session 不受欢迎或不切实际时,将使用 session 聚合。为了限制资源使用(即内存和网络),SDK 会跟踪有关最近发生的一批会话的摘要信息, 实际上不必处理代表构成聚合的各个 session 的 session 对象。此模式适用于运行任意长时间并为潜在的多个用户处理更大吞吐量的应用程序,例如 Web Server、后台 Job Worker 等。请注意,对于这些类型的应用程序,session 的更好定义与执行匹配单个 HTTP 请求或任务,而不是整个应用程序进程的单个执行。
在任何一种情况下,SDK 都应默认创建和报告 session,根据应用程序类型选择单独报告或作为聚合报告。
如果 SDK 可以检测到 session 聚合更好地为应用程序提供服务,则它不得报告应用程序范围的 session。应用程序范围的 session 可能仍会在 SDK 初始化期间创建,但必须中止并且永远不会发送到 Sentry。例如,在 Node.js SDK 中,如果应用程序使用提供的 requestHandler 集成,我们可以检测到它可能是一个 web server。
单个会话功能
配置
- 全局/静态 API 默认开启;
如果用户不想跟踪 session,他们应该能够禁用 session。
报告 session 和确定 Sentry 中项目的 Release Health 的先决条件,例如 release 应由 SDK 自动检测,例如通过查找 env 变量。
(也许,需要讨论)如果我的先决条件不能被检测到(例如,没有好的方法来确定 release 版本), 那么我们设置一些默认值,以便我们总是可以默认报告 session (取决于讨论,这可能不会是 SDK 代码的更改,但在 Relay 中,基本上删除了 session 有效负载中的硬要求)。
会话的生命周期
默认情况下,会话应该只为由 Sentry.init 初始化的全局 hub/client 启用,并默认为任何其他手动创建的 client 禁用。 Session 在 SDK 初始化时开始(理想情况下,当默认 client 绑定到全局 hub 时)并在以下情况之一发生时结束:显式调用 Hub.endSession() 方法;或程序无错误终止;或程序以未处理的异常终止;或程序以未处理的 promise rejection 而终止。
必须注意永远不要尝试为已经结束的 session 向 Sentry 发送新的 session 负载。例如,如果用户使用 Hub.endSession() 手动结束 session,则程序终止时不应有任何新的 session 更新。
会话属性和可变性
向 Sentry 发送会话
Session 最初在一定的(最初是硬编码的,配置越少越好)延迟(大约 1 到 30 秒 TBD)后发送, 然后在程序终止时更新持续时间、最终状态和错误计数。请注意,作为一种优化,short lived 程序不会向 Relay 发送 2 个 session 请求, 而只会向 Relay 发送最后一个带有状态和持续时间的请求。
会话聚合功能
配置
默认情况下应启用 session,session 在 web server 收到请求后立即启动,并在响应完全发回后立即结束。
会话的生命周期
Session 从不被跟踪或单独发送,相反,它们被聚合,聚合每 30 秒发送一次,最后一次当 web server 终止。作为对上述点的实现提示,当 "Client" 关闭或刷新时,相关联的 "Session Flusher" 也应被刷新并在传输被刷新/关闭之前提交当前聚合。确保这对于 Serverless 来说是合理的 — 我们不会使用 "request mode" 和 SessionFlusher,因为我们不能在 request-response 流之外进行任何工作。提供一种与现有 Node 框架(Express、Next.js、Koa)集成的简单方法。