news 2026/9/11 13:38:19

Sentry 通知平台如何注册一个新的 Notification Action 并实现 fire 逻辑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Sentry 通知平台如何注册一个新的 Notification Action 并实现 fire 逻辑

Sentry 通知平台如何注册一个新的 Notification Action 并实现 fire 逻辑

【免费下载链接】sentryDeveloper-first error tracking and performance monitoring项目地址: https://gitcode.com/GitHub_Trending/sen/sentry

当你在 Sentry 中要接入一种新的通知行为——比如把审计日志推送到 Slack 频道,或为某种触发事件对接第三方服务——需要为NotificationAction模型注册一个ActionRegistration子类:用@NotificationAction.register_action声明 trigger/service/target 组合,并在子类中实现fire()方法承载与下游服务的实际通信逻辑。本文基于仓库内 notification_actions.md 的设计说明、notificationaction.py 的模型实现,以及 test_notificationaction.py 中的验证用例,给出从注册到验证 fire 逻辑的完整路径。

Notification Action 由什么组成

根据 notification_actions.md,Notification Action 依赖四个部分:

  1. Triggers——通知的来源,即 Sentry 中发生了什么导致通知触发;
  2. Services——投递机制(Slack、PagerDuty、MSTeams、Sentry Notifications 等);
  3. Targets——接收方类型(用户、团队,还是集成特有目标);
  4. Registrations——帮助 setup 新 action 的ActionRegistration子类。

注册键由三元组唯一确定,notificationaction.py 中get_registry_key的格式是"{trigger_type}:{service_type}:{target_type}"。同一组合只允许一个注册类。

前提:确认三个枚举中已存在你要用的值

register_action在注册时会对三个类型做校验(notificationaction.py):trigger_type必须在ActionTrigger.as_choices()中、service_type必须在ActionService.as_choices()中、target_type必须在ActionTarget.as_choices()中,否则直接抛出AttributeError,例如:

Trigger type of {trigger_type} is not registered. Modify ActionTrigger.

文档明确说明:这些枚举在保存新的 Notification Action 之前会被 Django 用于校验,所以如果需要新的 trigger/service/target,先扩展 notificationaction.py 中对应的枚举,这一步不能跳过。

当前枚举中已有的值供选择:

  • ActionServiceEMAIL=0PAGERDUTY=1SLACK=2MSTEAMS=3SENTRY_APP=4SENTRY_NOTIFICATION=5OPSGENIE=6DISCORD=7SLACK_STAGING=8
  • ActionTargetSPECIFIC=0USER=1TEAM=2SENTRY_APP=3ISSUE_OWNERS=4
  • ActionTriggerAUDIT_LOG=0audit-log)、GS_SPIKE_PROTECTION=100spike-protection)。

如果目标组合的三个值都已存在(比如AUDIT_LOG+SENTRY_NOTIFICATION+SPECIFIC),可以直接进入下一步。

编写注册类并实现 fire()

ActionRegistration基类定义在 notificationaction.py:

  • fire(self, data)是抽象方法,每个注册类必须实现,文档说明这里就是“与目标服务通信的逻辑”所在;
  • validate_action(data)在通过 API 校验新 action 时被调用,用于数据库完整性检查之外的自定义校验,不需要时可保留默认实现;
  • serialize_available(organization, integrations)用于把该 action 的可用性序列化给前端,默认返回空列表[]

另外注意ActionRegistration.__init__会保存self.action(触发本次 fire 的NotificationAction实例),模型自身的fire()通过registration(action=self).fire(*args, **kwargs)调用你的实现,因此你可以在fire()内通过self.action读取trigger_typeservice_type(即type字段)、target_identifiertarget_display等配置。

按照 notification_actions.md 给出的示例,注册写法如下(fire内部按你的服务对接逻辑填充):

from typing import Any from sentry.notifications.models.notificationaction import ( ActionRegistration, ActionService, ActionTarget, ActionTrigger, NotificationAction, ) @NotificationAction.register_action( trigger_type=ActionTrigger.AUDIT_LOG.value, service_type=ActionService.SENTRY_NOTIFICATION.value, target_type=ActionTarget.SPECIFIC.value, ) class SentryAuditLogRegistration(ActionRegistration): def fire(self, data: Any) -> None: # 与目标服务通信的逻辑写在这里 ... @classmethod def validate_action(cls, data) -> None: pass @classmethod def serialize_available(cls, organization, integrations=None) -> list[Any]: return []

两个注册时会立即失败的约束(来自 register_action 的源码):

  • 三个类型值任一不在对应枚举的as_choices()中,抛AttributeError,提示去修改相应枚举;
  • 组合键"{trigger}:{service}:{target}"已存在于_registry中时,抛AttributeErrorExisting registration found for trigger:..., service:..., target:....

装饰器在类定义被 import 时执行注册,因此定义注册类的模块必须在应用启动时被导入,否则运行时_registry中查不到该组合。

验证注册是否生效、fire 是否被调用

仓库中的 test_notificationaction.py 给出了可复用的验证模式,核心是用@patch.dict(NotificationAction._registry, {})清空注册表后注册一个 handler,再触发fire()

from unittest.mock import MagicMock, patch from sentry.notifications.models.notificationaction import ( NotificationAction, ) from sentry.notifications.models.notificationaction import logger as NotificationActionLogger from sentry.testutils.cases import TestCase @patch.dict(NotificationAction._registry, {}) class NotificationActionTest(TestCase): def setUp(self) -> None: self.organization = self.create_organization(name="night city") self.projects = [ self.create_project(name="netrunner", organization=self.organization), self.create_project(name="edgerunner", organization=self.organization), ] self.notif_action = self.create_notification_action( organization=self.organization, projects=self.projects ) @patch.object(NotificationActionLogger, "error") def test_register_action_for_fire(self, mock_error_logger: MagicMock) -> None: mock_handler = MagicMock() NotificationAction.register_action( trigger_type=self.notif_action.trigger_type, service_type=self.notif_action.service_type, target_type=self.notif_action.target_type, )(mock_handler) self.notif_action.fire() assert not mock_error_logger.called assert mock_handler.called

成功条件是两条断言:mock_handler.calledfire()被正确路由到注册的 handler)且mock_error_logger未被调用(没有走到错误分支)。

同一文件还覆盖了两条失败路径,写新注册类时都应注意:

  • 重复注册:对同一组合第二次调用register_action会抛AttributeError(见test_register_action_for_overlap);
  • 组合未注册fire()不会抛异常,而是静默失败,只记录一条missing_registration错误日志(见test_fire_fails_silently和 NotificationAction.fire 的实现)。反之,注册命中时记录的是fire_action日志,其中包含action_idtriggerservicetarget四个字段,可用于线上核对。

如果你的 action 还要通过 API 创建,notification_action_request.py 中的validate_with_registry会再查一次注册表:组合未注册时返回错误Combination of trigger_type, service_type and target_type has not been registered.,随后调用你注册类上的validate_action(data)。另外当service_typePAGERDUTYSLACKSLACK_STAGINGMSTEAMSOPSGENIE之一时,API 层还要求提供该组织下已安装的对应integration_id,否则保存直接被拒绝。

限制与注意事项

  • fire()是抽象方法,不能省略;validate_actionserialize_available有默认实现,可按需覆盖。若需要前端在可用 action 列表里看到你的组合,必须实现serialize_available,默认返回空列表意味着不会出现在前端。
  • 组合未注册时fire()不抛异常、只记错误日志,排查“通知没发出去”时应先查missing_registration日志,确认触发时该组合是否已完成注册。
  • 枚举扩展是保存 Notification Action 的硬前提,只写注册类而不动枚举,在需要新触发类型时无法通过校验。

完成上述步骤后,你的注册类会进入NotificationAction._registry,模型fire()能路由到它,API 校验也能接受对应组合的 action 创建请求——这三个验证点分别对应运行时投递、API 保存和前端可见性,覆盖一个新 Notification Action 从注册到 fire 的完整链路。

【免费下载链接】sentryDeveloper-first error tracking and performance monitoring项目地址: https://gitcode.com/GitHub_Trending/sen/sentry

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 13:38:13

用公园叙事破解商业空间同质化:STAYREAL PARK设计启示

做了这么多年商业空间设计,我越来越觉得这个行业走到一个尴尬的关口:商场越建越多,但走进任何一座新开业的购物中心,你看到的东西几乎没有区别。同样的网红花砖、同样的金属冲孔板、同样的水磨石地面、同样的大面积绿植墙&#xf…

作者头像 李华
网站建设 2026/9/11 13:32:36

AI应用开发实战路线图:3个月交付可商用AI工具

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 13:31:24

Java EE初阶--多线程

一.为何使用线程?在过去年代的计算机,大部分采用了并发编程,一个服务器可以服务多个客户端,每给一个客户端服务就会创建一个进程,服务完之后就会销毁进程。一个进程的创建和销毁开销比较大,频繁的创建和销毁严重浪费资…

作者头像 李华
网站建设 2026/9/11 13:27:34

Q(V)-特征控制在新能源配电网中的Matlab仿真与实践

1. 项目背景与核心问题在新能源高比例接入的现代配电网中,变流器作为分布式电源与电网的接口设备,其控制策略的稳定性直接影响整个电力系统的安全运行。传统基于P-Q控制的变流器在弱电网条件下容易出现稳定性问题,而Q(V)-特征控制通过引入电压…

作者头像 李华