资讯动态

Sentry NotificationPlatform 开发者指南:从新增通知到自定义渲染器与 Provider

发布时间:2026/9/9 22:15:02 来源:尧图企业网站定制
Sentry NotificationPlatform 开发者指南从新增通知到自定义渲染器与 Provider【免费下载链接】sentryDeveloper-first error tracking and performance monitoring项目地址: https://gitcode.com/GitHub_Trending/sen/sentrySentry 的NotificationPlatform是一套基于 Provider 的多渠道通知系统屏蔽了 Email、Slack、Discord、MS Teams 各自的渲染与投递差异。本文以仓库内.agents/skills/notification-platform/SKILL.md为骨架结合src/sentry/notifications/platform/下的真实实现系统讲解如何新增一条通知、如何为现有 Provider 添加自定义渲染器、如何接入全新 Provider并覆盖 rollout 灰度、异步发送、测试与验收全流程。读完你可以照葫芦画瓢在 Sentry 代码库中完成一条数据定义 → 模板注册 → 灰度放量 → 发送的完整通知链路。平台定位一次定义多渠道投递NotificationPlatform 的核心思路是你只需要定义数据 模板平台负责按 Provider 完成渲染与投递。一条通知在发送时会经过模板渲染成与渠道无关的中间表示 → Provider 专属 Renderer 转成可投递对象HTML 邮件、Slack Block Kit JSON 等→ 渠道发送三层转换最终由EmailNotificationProvider、Slack / Discord / MS Teams Provider 分别落地。模块整体位于src/sentry/notifications/platform/并在 Django 应用 apps.py 的ready()中集中导入各 provider 与templates包以触发装饰器注册registry 填充。因此新增模板或 Provider 后必须保证对应 import 被执行否则运行期会查不到注册项。术语速览九个核心概念SKILL.md 给出的术语表可以在 types.py、provider.py、renderer.py、target.py、service.py 中一一找到对应实现概念职责关键实现点NotificationData单条通知的载荷声明唯一source基类继承pydantic.BaseModel并配置frozen True、use_enum_values True见 types.pyNotificationTemplate抽象类把NotificationData转成NotificationRenderedTemplate按NotificationSource注册render()为抽象方法附带example_data与render_example()types.pyNotificationRenderedTemplate与渠道无关的输出subject、body含 section 的 text block 列表、actions、chart、footer以及可选 email 模板路径types.pyNotificationProviderProtocol负责校验 target、挑选 renderer、投递最终 renderableprovider.pyNotificationRendererProtocol把NotificationRenderedTemplate转成 Provider 专属 renderablerenderer.pyNotificationTarget标识收件人邮箱、频道 ID 或 DM 用户 ID两个实体类为GenericNotificationTarget邮箱与IntegrationNotificationTargetSlack/Discord/MSTeamstarget.pyNotificationService统一入口编排查找/渲染/投递暴露has_access()、notify_target()、notify_async()、notify_sync()service.pytemplate_registry/provider_registry注册表后者额外提供get_all()与get_available()registry.pyNotificationRolloutService按组织 feature flag 速率 option 决定某 source 是否放行rollout.py第一步确定你要做的操作开始之前先对号入座来源SKILL.md Step 1目标路线新增一条通知最常见走 Step 2–5为已有 Provider 添加自定义渲染器走 Step 6接入一个全新 Provider走 Step 7无论哪种操作完成后都必须继续 Step 8测试与 Step 9验收。第二步定义 NotificationSource每条通知都必须有唯一的NotificationSource枚举值并映射到一个NotificationCategory。source会被埋点/metrics 追踪用于统计某条代码路径的发送量在 types.py 的 docstring 中有明确说明。在 types.py 中在对应分类注释下新增枚举值class NotificationSource(StrEnum): # MY_CATEGORY MY_NEW_SOURCE my-new-source把它加入NOTIFICATION_SOURCE_MAP对应分类的列表NOTIFICATION_SOURCE_MAP[NotificationCategory.MY_CATEGORY].append( NotificationSource.MY_NEW_SOURCE )如果现有NotificationCategoryDEBUG、DATA_EXPORT、DYNAMIC_SAMPLING、REPOSITORY、SEER、ISSUE、METRIC_ALERT、SENTRY_APP、DEPLOY、ACTIVITY都不合适先扩充NotificationCategory枚举再同步建立NOTIFICATION_SOURCE_MAP条目。NotificationCategory的 docstring 提醒这些分类对应用户可在设置中管理的宽泛分组DEBUG仅用于测试开发是例外types.py。从源码结构看仓库已注册约 40 个 source覆盖数据导出成功/失败、issue 状态流转resolved/regressed/escalating 等、Seer Autofix 系列、部署发布、sentry app webhook 失效等场景是很好的命名参照。第三步创建 NotificationData数据类承载模板渲染所需的全部字段。SKILL.md 以frozen dataclass形式给出说明而当前仓库中NotificationData基类实际是pydanticBaseModel通过Config.frozen True保证冻结、use_enum_values True让枚举以字符串参与序列化types.py。notify_async会把数据json.dumps(data.json())后投递给 Celery 任务再parse_obj还原service.py这就是避免 Django model 实例、只放原始类型/简单结构的原因。仓库实际写法参考 templates/data_export.pyfrom sentry.notifications.platform.types import NotificationData, NotificationSource class MyNotificationData(NotificationData): source: NotificationSource NotificationSource.MY_NEW_SOURCE # 可带默认值 title: str detail_url: str无论采用 dataclass 还是 pydantic 风格需要遵守的规则不变source标识这条通知属于哪个 enum不是模板渲染需要的业务字段SKILL.md 称之为 class variablepydantic 写法下为带默认值的字段声明冻结frozenTrue保证序列化安全只放模板render()真正需要的字段避免 Django model 实例优先原始类型或简单 dataclass以便异步任务安全反序列化。第四步创建 NotificationTemplate模板把数据转为与渠道无关的NotificationRenderedTemplate。SKILL.md 建议放在与 Step 3 同一文件templates/your_notification.py并通过装饰器按 source 注册from sentry.notifications.platform.registry import template_registry from sentry.notifications.platform.types import ( NotificationCategory, NotificationRenderedAction, NotificationRenderedTemplate, NotificationTemplate, ParagraphSection, PlainTextBlock, ) template_registry.register(MyNotificationData.source) class MyNotificationTemplate(NotificationTemplate[MyNotificationData]): category NotificationCategory.MY_CATEGORY example_data MyNotificationData( titleExample title, detail_urlhttps://example.com, ) def render(self, data: MyNotificationData) - NotificationRenderedTemplate: return NotificationRenderedTemplate( subjectdata.title, body[ ParagraphSection(blocks[PlainTextBlock(textSomething happened.)]) ], actions[ NotificationRenderedAction(labelView Details, linkdata.detail_url) ], )装饰器之所以能生效靠的是 templates/init.py 中必须导入你的模板类from .my_notification import MyNotificationTemplate该文件的注释明确写着All templates should be imported here so they are registered因为apps.py启动时会 import 整个templates包。可用的 Sections 与 Text Blocks一条通知由section与text block组合而成。text block 既出现在 body 的 section 内也可以直接出现在subject与footer字段中此时为list[NotificationTextBlock]而非纯str。Body 中使用的 sectionSection说明ParagraphSection一段文本之前有换行HTML 渲染为pCodeSection代码块之前有换行HTML 渲染为precodeBlockQuoteSection引文块渲染为 blockquote文本渲染为 ...text block用于 section 内也用于subject/footerBlock说明PlainTextBlock无格式文本BoldTextBlock加粗ItalicTextBlock斜体CodeTextBlock行内代码LinkTextBlock带text与url的超链接各 section/block 对应的枚举NotificationSectionType、NotificationTextBlockType与 dataclass 定义都在 types.py。此外NotificationRenderedTemplate还支持可选的chartNotificationRenderedImage含 url 与 alt_text与footer。所有可选渲染字段详见 types.py。邮件 Provider 的默认渲染行为见 email/provider.py可作为理解这些结构的直观样例段落转p、代码块转precode、blockquote 转blockquotetext block 映射为strong/em/code/a且内容会先escape再拼装以防范 XSS纯文本版本则输出**text**、text、text (url)等近似 markdown 形式。渲染链路如何串联NotificationService.render_template给出了数据 → 中间表示 → Provider renderable的完整调用链service.pyrendered_template template.render(datadata) renderer provider.get_renderer(datadata, categorytemplate.category) return renderer.render(datadata, rendered_templaterendered_template)先由你的模板产出NotificationRenderedTemplate再由 Provider 决策出的 Renderer 产出最终对象如EmailMultiAlternatives见 email/provider.py。第五步注册 Rollout 并发送灰度Rollout注册平台采用分层灰度任何新通知 source 在被真正投递前都必须配置到对应阶段的 rollout option 中。这些 option 在sentry-options-automator独立仓库中维护键名如下Rollout 阶段Option key内部测试notifications.platform-rollout.internal-testingSentry 组织notifications.platform-rollout.is-sentry早期试用notifications.platform-rollout.early-adopter全量开放notifications.platform-rollout.general-access每个 option 是Dict键为 source 字符串、值为 0.0–1.0 的灰度比例。例如{my-new-source: 1.0}四个 stage 的 option 已注册在 src/sentry/options/defaults.py形如register(notifications.platform-rollout.internal-testing, typeDict, default{}, flagsFLAG_AUTOMATOR_MODIFIABLE)。灰度判定逻辑在 rollout.py 中分层实现has_feature_flag_access()按优先级检查组织 feature flaginternal-testingis-sentryearly-adoptergeneral-access命中后返回对应 option key无命中返回None直接拒绝get_rollout_rate()从 option 中读取该 source 的速率未知 option 或未知 source 都记 warning 并返回0.0should_notify()掷随机数random.randint(0, 99) 100 * rate决定是否放行。发送模式标准发送代码SKILL.md Step 5from sentry.notifications.platform.service import NotificationService from sentry.notifications.platform.target import GenericNotificationTarget from sentry.notifications.platform.types import ( NotificationProviderKey, NotificationTargetResourceType, ) data MyNotificationData(titleExport ready, detail_urlhttps://...) # 用灰度检查做守卫 if NotificationService.has_access(organization, data.source): service NotificationService(datadata) target GenericNotificationTarget( provider_keyNotificationProviderKey.EMAIL, resource_typeNotificationTargetResourceType.EMAIL, resource_iduser.email, ) service.notify_async(targets[target])has_access静态方法把组织与 source 交给NotificationRolloutService.should_notify()service.py。务必先has_access再发送灰度由它把关。三种发送 API 的取舍参考references/targets-and-sending.md并对照 service.py方法行为适用场景notify_async(targets[...])经instrumented_task入队异步投递fire-and-forget默认选择绝大多数通知notify_sync(targets[...])同步逐 target 发送返回dict[ProviderKey, list[SendFailure]]需要把发送失败汇报给调用方notify_target(target...)同步发送单个 target忽略通知设置底层方法被上两者内部调用直接使用前需评估噪音风险notify_target同步路径与异步任务notify_target_async在 [service.py](https://link.gitcode.com/i/4e457b77e51771d604e747a91c110af2#L74-L164, L315-L408) 中执行几乎相同的五步流水线校验 target → 查 provider 并 validate → 按 source 查 template 并渲染 → 解析 thread可选→ provider.send() 并记录结果。二者都会先检查 killswitch optionnotifications.platform.killswitch.sources被 killswitch 的 source 直接 HALT。多收件人优先用 Strategynotify_async/notify_sync要求二选一提供strategy或targets都提供或都不提供都会抛NotificationServiceError。当一个通知面向多个收件人、或需要复杂查询才能构造 target 时优先实现NotificationStrategytypes.py并传入 serviceclass MyNotificationStrategy(NotificationStrategy): def get_targets(self) - list[NotificationTarget]: return [ ... ] # 查询相关用户/频道构造 target 列表 service.notify_async(strategyMyNotificationStrategy(org, project))Target 的两种实体类邮箱GenericNotificationTargetresource_id即收件人邮箱Slack/Discord/MSTeamsIntegrationNotificationTarget在基类字段之外还必须带integration_id与organization_idtarget.py。resource_type支持EMAIL、CHANNEL、DIRECT_MESSAGEtypes.py。各 Provider 支持能力由 provider 的target_resource_types声明例如 Email 只接受EMAILSlack/Discord/MS Teams 接受CHANNEL与DIRECT_MESSAGE。异步序列化时serialize_target/deserialize_target会按 target 类型打标与还原target.py。第六步为现有 Provider 添加自定义 Renderer当默认的 section/block 渲染不够用比如需要 Slack 交互式按钮、action ID、富卡片布局时可以为某 Provider 某 category定制渲染器绕过默认模板到 renderable 的转换。何时需要通知需要 Provider 专属交互元素带 action ID 的按钮、富文本块输出结构与 subjectbodyactions 差异很大同一 Provider 内不同类型的数据需要不同渲染。实现方式在 Provider 类上覆写get_renderer()按 category 返回自定义渲染器类# In the provider class classmethod def get_renderer( cls, *, data: NotificationData, category: NotificationCategory ) - type[NotificationRenderer[MyRenderable]]: if category NotificationCategory.MY_CATEGORY: return MyCustomRenderer return cls.default_renderer文件位置{provider}/renderers/{name}.py。从源码看仓库内已有若干真实样例Slack 的 renderers/seer.py对应 sourceSEER_AUTOFIX_*、renderers/seer_agent_write_approval.py审批类交互以及 Discord 的 issue.py、metric_alert.py。这些渲染器会让NotificationRenderedTemplate只承担极小工作有些 render 直接返回空 body见 templates/seer.py真正的输出由自定义 renderer 基于原始data构造——这正是NotificationRenderer.render会同时收到data与rendered_template的原因renderer.py。仅使用自定义 renderer 的模板通常应把hide_from_debugger True避免出现在内部调试器sentry.io/debug/notifications中types.py。第七步新增一个 Provider只有在新接入一个集成渠道时才需要新增 Provider。你需要实现NotificationProviderprotocol、一个默认NotificationRenderer并把两者注册创建{provider_name}/provider.py内含 Provider 与默认 Renderer 类用provider_registry.register(NotificationProviderKey.MY_PROVIDER)注册在 types.py 的NotificationProviderKey枚举中新增MY_PROVIDER在 apps.py 中 import 该 provider参考现有 Email/Slack/Discord/MSTeams 的写法在is_available()中把可用性放到 feature flag 后面做门控。Provider 协议要求声明key、default_renderer、target_class、target_resource_types并实现validate_target()、get_renderer()、is_available()、send()provider.py。validate_target会校验 target 类型、provider_key 匹配与 resource_type 支持范围send返回SendResult——成功返回带provider_message_id如 Slack 的ts的SendSuccessResult失败返回带HALT/FAILURE状态、错误码与详情字典的SendFailureprovider.py。若基于集成渠道可用共享的integration_error_result()把IntegrationError映射为对应的SendFailure。完整的 scaffold 要求参见references/provider-template.md位于.agents/skills/notification-platform/references/provider-template.md。第八步测试测试目录为tests/sentry/notifications/platform/含 templates、email、slack、discord、msteams、strategies、service、rollout、target、threading 等子目录。模板测试class TestMyNotificationTemplate: def test_render(self): data MyNotificationData(titleTest, detail_urlhttps://example.com) template MyNotificationTemplate() rendered template.render(data) assert rendered.subject Test assert len(rendered.body) 1 assert len(rendered.actions) 1 assert rendered.actions[0].link https://example.com def test_render_example(self): template MyNotificationTemplate() rendered template.render_example() assert rendered.subject # Verify example_data produces valid outputrender_example()的默认实现就是self.render(dataself.example_data)所以保证example_data能渲染出合法输出既服务调试器又服务测试。Service 集成测试from unittest.mock import patch from sentry.notifications.platform.service import NotificationService class TestMyNotificationService: patch(sentry.notifications.platform.email.provider.EmailNotificationProvider.send) def test_notify_target(self, mock_send): data MyNotificationData(titleTest, detail_urlhttps://example.com) service NotificationService(datadata) target GenericNotificationTarget( provider_keyNotificationProviderKey.EMAIL, resource_typeNotificationTargetResourceType.EMAIL, resource_iduserexample.com, ) service.notify_target(targettarget) assert mock_send.called通过 mock Provider 的send验证编排层查模板、渲染、投递调用链真实发生。自定义 Renderer 测试def test_get_renderer_returns_custom(): data MySpecialData(sourceNotificationSource.MY_SOURCE, ...) renderer MyProvider.get_renderer(datadata, categoryNotificationCategory.MY_CATEGORY) assert renderer is MyCustomRenderer第九步提交前验收清单对照 SKILL.md Step 9 逐项勾选NotificationSource枚举值已加入 types.pysource 已加入NOTIFICATION_SOURCE_MAP的正确分类数据类为冻结结构dataclassfrozenTrue或 pydantic frozen BaseModelsource不是业务字段模板已用template_registry.register(...)注册模板已在 templates/init.py 中 import模板example_data经render_example()能产出合法输出rollout option 已配置或为sentry-options-automator提交工单发送代码已用NotificationService.has_access()守卫测试通过pytest -svv --reuse-db tests/sentry/notifications/platform/所有改动文件通过 pre-commit附录一个完整的真实样例数据导出是理解全链路的最佳参照templates/data_export.py 同时定义了成功/失败两条通知。以DataExportSuccess为例它声明source DATA_EXPORT_SUCCESS、字段export_url与expiration_date对应模板注册后render()产出 subject 为 Your data is ready.、body 含一个ParagraphSection、action 为指向导出文件的按钮、footer 提示过期时间。失败通知则演示了更多结构能力CodeTextBlock内嵌错误信息、CodeSection展示orjson.dumps序列化后的原始请求载荷、两个文档类 action。这条通知即可通过 Email Provider 走默认EmailRenderer发送也可被自定义 renderer 拦截——source粒度上的一切复用与扩展都由此展开。更完整的示例、目标类型与收发策略、自定义渲染器架构、Provider scaffold 等延展阅读可继续查看.agents/skills/notification-platform/下references/data-and-templates.md、references/targets-and-sending.md、references/custom-renderers.md与references/provider-template.md四份参考文档。【免费下载链接】sentryDeveloper-first error tracking and performance monitoring项目地址: https://gitcode.com/GitHub_Trending/sen/sentry创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价