拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Sentry Notification Platform 自定义渲染器(Custom Renderer)完整指南:架构、注册与实战

Sentry Notification Platform 自定义渲染器Custom Renderer完整指南架构、注册与实战【免费下载链接】sentryDeveloper-first error tracking and performance monitoring项目地址: https://gitcode.com/GitHub_Trending/sen/sentry本文围绕 Sentry 通知平台Notification Platform中的自定义渲染器机制展开讲解一条通知从NotificationData出发、经模板渲染与 Provider 分发最终落到 Slack/Discord/Email 等渠道专属可渲染对象上的完整链路。你将掌握NotificationRenderer协议的定义、Provider 侧get_renderer()的分发规则、真实项目Seer 通知系列的落地写法以及hide_from_debugger与内部调试器的配合方式可直接套用于在通知平台内接入带交互按钮、富文本块等 Provider 专属能力的全新通知。一、架构总览自定义渲染器替换哪一步Sentry 通知平台默认的数据流可以浓缩为下面一段流水线NotificationData → NotificationTemplate.render() → NotificationRenderedTemplate ↓ Provider.get_renderer(data, category) ├── default → DefaultRenderer.render(data, rendered_template) └── custom → CustomRenderer.render(data, rendered_template)也就是说模板Template把原始数据加工成与渠道无关的标准中间态NotificationRenderedTemplate渲染器Renderer再把中间态转换成特定 Provider 能够消费的可渲染对象。例如 Slack 期望 BlockKit JSONEmail 期望 HTML/纯文本。在 service.py 的render_template()类方法中可以看到这条链路的精确调用顺序rendered_template template.render(datadata) renderer provider.get_renderer(datadata, categorytemplate.category) return renderer.render(datadata, rendered_templaterendered_template)第一步按data.source从template_registry找到NotificationTemplate调用render()产出NotificationRenderedTemplate第二步Provider 通过get_renderer()决定使用默认渲染器还是自定义渲染器第三步渲染器把NotificationRenderedTemplate以及原始data翻译成 Provider 专属输出。自定义渲染器替换的就是最后一个步骤Provider 的get_renderer()依据通知类别category或数据类型data分发到自定义渲染器类绕过默认的 section/block 到 renderable 的通用转换逻辑。关键设计在于自定义渲染器仍然会收到rendered_template但它可以自由选择忽略它直接基于原始data构造输出——这正是 Seer 系列通知大量采用自定义渲染器的原因。两个注册表见 registry.py在整个平台中负责查找模板与 Providertemplate_registryNotificationSource → NotificationTemplate支持hide_from_debugger过滤provider_registryNotificationProviderKey → NotificationProvider提供get_all()与get_available()后者只返回已开放给全部客户的 Provider。二、NotificationRenderer协议所有渲染器的契约无论默认还是自定义所有渲染器都实现同一个协议定义在 renderer.pyclass NotificationRendererRenderableT: A protocol metaclass for all notification renderers. RenderableT is a type that matches the connected provider. provider_key: NotificationProviderKey classmethod def renderDataT: NotificationData - RenderableT: Convert a rendered template into a renderable object specific to the provider. For example, Slack might output BlockKit JSON, email might output HTML/txt. We pass in the data as well since custom renderers may use raw data to modify the output for the provider where the template cannot. For example, custom markdown formatting, provider-specific features like modals, etc. 协议有两个值得注意的要点泛型RenderableT与 Provider 绑定。例如 Slack Provider 的RenderableT是SlackRenderable——在 slack/provider.py 中定义的一个TypedDictclass SlackRenderable(TypedDict): blocks: list[Block] attachments: NotRequired[list[dict[str, Any]]] text: strrender()同时接收data与rendered_template。文档注释明确说明原因自定义渲染器可以用原始data输出模板无法表达的内容例如自定义 markdown 格式、Provider 专属能力modal、交互元素等。默认渲染器做了什么以 Slack 为例默认的 SlackRenderer 是标准布局的实现范本它忠实执行从通用中间态到 BlockKit 的映射subject→HeaderBlock因为 Slack 的 subject 不支持富文本所以取纯文本body中的 section 按类型映射PARAGRAPH→SectionBlock、CODE_BLOCK→ 用 包裹的代码块、BLOCK_QUOTE→ 前缀的引用块_render_bodyNotificationTextBlock子类型BOLD_TEXT、ITALIC_TEXT、CODE、LINK被分别映射为 markdown 的*text*、_text_、text、url|text_render_text_blocksactions→ 由url按钮组成的ActionsBlockchart→ImageBlockfooter→ContextBlock。默认渲染器统一了绝大多数通知的观感与样式而自定义渲染器正是在需要跳出这种统一布局时的逃生舱。三、什么时候该用、什么时候不该用自定义渲染器适用场景参考 custom-renderers.md需要交互元素例如带 action ID 的 Slack 按钮点击后回传给服务端做后续处理输出结构显著偏离标准的 subject/body/actions 三段式布局同一类别内的不同数据类型需要完全不同的渲染结果——这是最典型的信号Seer 类别即为此种情况需要 Provider 专属能力如富文本块rich text blocks、自适应卡片adaptive cards、嵌入embeds。不要使用的情形默认的 section/block 类型ParagraphSection、CodeSection、BlockQuoteSection、PlainTextBlock、BoldTextBlock、ItalicTextBlock、CodeTextBlock、LinkTextBlock已经够用时只是想微调样式——默认渲染器建立的通用样式是大多数通知应当遵循的基线。如果同类功能在某个 Provider 上走了自定义渲染器尽量把改动同步到其他 ProviderEmail/Discord/MSTeams避免同一通知在不同渠道差异过大。四、文件放置约定与仓库中的既有实现约定非常明确自定义渲染器放在{provider}/renderers/{name}.py。例如slack/renderers/seer.py。当前仓库中已有如下实现见 platform 目录Provider渲染器文件处理的类别Slackslack/renderers/issue.pyISSUESlackslack/renderers/metric_alert.pyMETRIC_ALERTSlackslack/renderers/seer.pySEERSlackslack/renderers/seer_agent_write_approval.pySEER按source再细分Discorddiscord/renderers/issue.py、discord/renderers/metric_alert.pyISSUE、METRIC_ALERT注意discord、email、msteams等目录与slack一样都实现了各自的provider.py说明该目录结构是跨渠道统一的{provider}/provider.py定义 Provider 与默认渲染器{provider}/renderers/存放该渠道的自定义渲染器。五、实战拆解SeerSlackRenderer——一个渲染器处理五类数据SeerSlackRenderer 是文档重点剖析的例子也是仓库中最完整的自定义渲染器范本。它负责把 SeerSentry 的 AI 辅助修 bug 引擎产生的多种完全不同形态的数据翻译成 Slack BlockKitclass SeerSlackRenderer(NotificationRenderer[SlackRenderable]): classmethod def renderDataT: NotificationData - SlackRenderable: if isinstance(data, SeerAutofixTrigger): autofix_button cls._render_autofix_button(data) return SlackRenderable( blocks[ActionsBlock(elements[autofix_button])], textSeer Autofix Trigger, ) elif isinstance(data, SeerAutofixError): return cls._render_autofix_error(data) elif isinstance(data, SeerAutofixUpdate): return cls._render_autofix_update(data) elif isinstance(data, SeerAgentError): return cls._render_agent_error(data) elif isinstance(data, SeerAgentResponse): return cls._render_agent_response(data) else: raise ValueError(fSeerSlackRenderer does not support {data.__class__.__name__})该方法对五种数据类型分发每一类的 Slack 输出完全不同末尾对未知类型抛出ValueError避免静默失败。数据类与模板注册共同定义在 templates/seer.py数据类型source场景Slack 输出要点SeerAutofixTrigger触发一次 autofix单个ActionsBlock 主按钮SeerAutofixErrorautofix 过程报错两个SectionBlock标题 引用形式的错误详情SeerAutofixUpdateautofix 阶段更新标题、摘要、reasoning/steps 有序列表、代码变更 diff、PR 查看按钮等复杂组合SeerAgentErrorAgent 出错与SeerAutofixError同构的错误展示SeerAgentResponseAgent 完成任务MarkdownBlock摘要、可选 Agent Trace 上下文与权限提示 footer5.1 交互按钮与 action ID 编码SeerAutofixTrigger是最能体现自定义渲染器 交互价值的例子。注意这类数据的用途有个重要限制源码 docstring 指出它仅用于渲染 autofix 运行本身要附带的触发按钮而非整条告警消息——这是为了在迁移到通知平台之前兼容既有告警渲染。按钮的构造_render_autofix_button涉及 action ID 的编码规则return ButtonElement( textAUTOFIX_CONFIG[data.stopping_point][label], styleprimary, valuedata.stopping_point, action_idencode_action_id( actionSlackAction.SEER_AUTOFIX_START.value, organization_iddata.organization_id, project_iddata.project_id, ), )encode_action_id来自sentry.integrations.slack.message_builder.routing会把 action 语义如SEER_AUTOFIX_START连同organization_id、project_id编码进action_idSlack 回调时据此还原上下文。源码中同时保留了SEER_AUTOFIX_HANDOFF交接给其他 coding agent、SEER_AUTOFIX_VIEW_PR查看 PR、SEER_AUTOFIX_VIEW_IN_SENTRY在 Sentry 查看 issue等 action完整 action 集见 seer_agent_write_approval.pySEER_AGENT_WRITE_APPROVE/SEER_AGENT_WRITE_REJECT等文件。另一个交互细节是create_first_block_idaction 处理器要求第一个 block 的block_id必须是携带 group 数据的 JSON 编码因此渲染时会用orjson.dumps({issue: group_id, run_id: run_id})生成并赋给首个SectionBlock。自定义渲染器需要了解这类消息结构约束才能保证下游交互可用——这无法由通用 section/block 中间态表达正是绕过默认渲染器的典型理由。5.2 复杂富文本SeerAutofixUpdate_render_autofix_update是文档中复杂渲染注解所指的完整实现其布局逻辑是阶段标题根据data.current_point从AUTOFIX_CONFIG查找对应阶段的 emoji 标题如 Root Cause、Solution、Code Changes、Pull Request该配置将AutofixStoppingPoint映射到 heading/label/working/completed 文案AUTOFIX_CONFIG标题 block 挂上第一步生成的 JSONblock_id摘要与 reasoning/steps摘要以SectionBlock输出reasoning 与 steps 各自渲染成有序富文本列表RichTextListElement(styleordered)且均被截断到MAX_STEPS 10代码变更 diff每个变更渲染为带repo_name、标题、描述、被包裹的 diff 文本的SectionBlock最多MAX_CHANGES 5条Pull Request 按钮每个 PR 生成一个LinkButtonElementView PR (#编号)action ID 追加::{pr_number}最多MAX_PRS 3个footer 收尾根据是否还有下一阶段显示working还是completed文案并在settings.DEBUG下追加 Run IDrender_footer_blocks。渲染器还包含下一步动作推导SeerAutofixTrigger.from_update()会根据当前阶段通过STOPPING_POINT_HIERARCHY反查下一 stopping point并在更新消息上顺带渲染继续下一步的按钮。这些逻辑体现了自定义渲染器可以作为平台与业务能力之间的适配层数据类与模板保持通用Provider 专属的交互编排沉淀在渲染器内。六、Provider 侧注册覆写get_renderer()渲染器本身不会自动生效需要 Provider 在get_renderer()中完成类别到渲染器类的映射。参考 slack/provider.py 的真实实现classmethod def get_renderer( cls, *, data: NotificationData, category: NotificationCategory ) - type[NotificationRenderer[SlackRenderable]]: from sentry.notifications.platform.slack.renderers.issue import IssueSlackRenderer from sentry.notifications.platform.slack.renderers.metric_alert import SlackMetricAlertRenderer from sentry.notifications.platform.slack.renderers.seer import SeerSlackRenderer from sentry.notifications.platform.slack.renderers.seer_agent_write_approval import ( SeerAgentWriteApprovalSlackRenderer, ) if category NotificationCategory.SEER: if data.source NotificationSource.SEER_AGENT_WRITE_APPROVAL: return SeerAgentWriteApprovalSlackRenderer return SeerSlackRenderer if category NotificationCategory.ISSUE: return IssueSlackRenderer if category NotificationCategory.METRIC_ALERT: return SlackMetricAlertRenderer return cls.default_renderer这个真实实现比参考文档的简化示例更进一步展示了两层分发逻辑按category分发SEER、ISSUE、METRIC_ALERT各有专属渲染器未命中的类别回落到cls.default_renderer即SlackRenderer。注意方法内使用了函数内导入lazy import避免渲染器模块与 provider 模块在加载期互相依赖。在同一SEER类别内再按data.source细分SEER_AGENT_WRITE_APPROVALAgent 写操作需人工审批被单独路由到SeerAgentWriteApprovalSlackRenderer其渲染Allow Seer to make changes? 权限范围列表 Approve/Reject 双按钮见 seer_agent_write_approval.py。这说明get_renderer()的入参同时包含data与category天然支持同一类别内按数据类型再分流。对应地Provider 的注册方式为slack/provider.pyprovider_registry.register(NotificationProviderKey.SLACK) class SlackNotificationProvider(NotificationProvider[SlackRenderable]): key NotificationProviderKey.SLACK default_renderer SlackRenderer # default for all categories target_class IntegrationNotificationTarget target_resource_types [ NotificationTargetResourceType.CHANNEL, NotificationTargetResourceType.DIRECT_MESSAGE, ]基类 NotificationProvider 的默认实现是直接return cls.default_renderer其 docstring 提醒覆写get_renderer()虽然允许不同通知使用不同渲染器但可能造成通知之间外观不一致需要权衡。SlackStagingNotificationProviderNotificationProviderKey.SLACK_STAGING则通过继承复用全部渲染逻辑仅换 key。NotificationCategory与NotificationSource的完整枚举定义在 types.py类别是用户在设置中管理是否接收的粗粒度分组如SEER、ISSUE、DEPLOY、ACTIVITY而 source 则唯一标识一次通知从哪个代码路径发出用于 metrics/analytics 追踪NOTIFICATION_SOURCE_MAP建立了两者的多对一映射。七、从零实现一个自定义渲染器四步走综合参考文档与源码接入一个自定义渲染器的完整步骤如下第 1 步创建{provider}/renderers/{name}.py。保持目录约定让所有 Provider 的自定义渲染器可被快速定位。第 2 步实现NotificationRenderer协议。参考模板如下from sentry.notifications.platform.renderer import NotificationRenderer class MyCustomRenderer(NotificationRenderer[ProviderRenderable]): provider_key NotificationProviderKey.MY_PROVIDER classmethod def renderDataT: NotificationData - ProviderRenderable: # Build provider-specific output from data # rendered_template is available but can be ignored ...实现要点用 Provider 的RenderableT参数化NotificationRenderer保证返回值与provider.send()的入参类型一致例如 Slack 是SlackRenderable类上声明provider_key方法内优先用isinstance(data, ...)分支处理自己支持的NotificationData子类型对不支持的类型抛ValueError可参考SeerSlackRenderer的收尾逻辑如果完全不需要模板中间态rendered_template直接忽略即可。第 3 步在 Provider 的get_renderer()中注册。在{provider}/provider.py覆写get_renderer()把相关的category/data.source映射到新渲染器类并把未覆盖的分支回落到cls.default_renderer。第 4 步为配套模板设置hide_from_debugger True。如果该通知离开自定义渲染器就无法表达见下一节在模板类上显式标记避免内部调试器尝试渲染无意义的标准模板输出。八、hide_from_debugger只依赖自定义渲染器的模板如何参与调试NotificationTemplate基类types.py默认hide_from_debugger: bool False其 docstring 解释了这一字段的用途Set true to omit these templates from the internal debugger (sentry.io/debug/notifications). This is useful for templates that only use custom renderers and bypass NotificationRenderedTemplates.当一个模板只有配合自定义渲染器才有意义时例如SeerAutofixUpdateTemplaterender()只需要返回一个最小化的NotificationRenderedTemplate因为自定义渲染器反正会忽略它。仓库中所有 Seer 模板都遵循了这一模式见 templates/seer.pytemplate_registry.register(NotificationSource.SEER_AUTOFIX_UPDATE) class SeerAutofixUpdateTemplate(NotificationTemplate[SeerAutofixUpdate]): category NotificationCategory.SEER hide_from_debugger True example_data SeerAutofixUpdate(...) def render(self, data: SeerAutofixUpdate) - NotificationRenderedTemplate: return NotificationRenderedTemplate(subjectSeer Autofix Update, body[])hide_from_debugger True实际作用于内部调试端点 internal_registered_templates.py该端点遍历template_registry凡hide_from_debugger为真的模板都会被跳过其余模板则会被序列化出 subject/body/actions/chart/footer 的标准示例并分别用 Email、MSTeams、Slack、Discord 渲染器产出渠道预览其中 serialize_slack_preview 特意通过SlackNotificationProvider.get_renderer()取渲染器——这意味着即便一个模板走自定义渲染器只要没有标记hide_from_debugger调试器也能展示其最终输出。目前标记了hide_from_debugger True的模板集中在 templates/seer.pySeerAutofixErrorTemplate、SeerAutofixUpdateTemplate、SeerAgentErrorTemplate、SeerAgentResponseTemplate、SeerAgentWriteApprovalTemplate它们与SeerSlackRenderer/SeerAgentWriteApprovalSlackRenderer一一对应。作为对照templates/issue.py 中的IssueNotificationTemplate未设置该标记说明它仍能产出有意义的通用中间态预览。九、实践建议与注意事项选择 Renderer 的粒度以数据形态差异为准同一类别内若干数据类型的输出结构差异越大越值得写一个以isinstance分发的多分支渲染器如SeerSlackRenderer而不是拆成多个几乎相同的默认模板。参考文档给出的get_renderer()契约本身就是datacategory双入参为这种按数据类型的再分发提供了原生支持。交互元素的 action ID 必须可回源Slack 等渠道的回调机制依赖编码进action_id/block_id的上下文organization、project、issue、run 等自定义渲染器需要自行保证这些约束参见encode_action_id与create_first_block_id的用法。保持跨渠道一致性基类get_renderer()的 docstring 已提示不同通知使用不同渲染器可能造成不一致。若为某个类别在 Slack 写了自定义渲染器应评估 Email、Discord、MSTeams 是否需要同步处理如slack与discord目录都实现了issue/metric_alert渲染器即是对应关系。未知数据类型要显式失败渲染器末尾对不支持的data.__class__.__name__抛出ValueError让类型扩展时第一时间暴露遗漏分支而不是发送一条残缺消息。模板中间态保持最小但合法被自定义渲染器完全接管时模板的render()仍须返回结构合法的NotificationRenderedTemplatesubject/body 至少占位同时配合hide_from_debugger True阻止调试器把它当普通模板渲染。通过本文涉及的协议定义renderer.py、Provider 分发实现slack/provider.py、完整范本slack/renderers/seer.py与调试机制types.py、internal_registered_templates.py读者既可以看懂 Seer 通知为什么长成那样也可以为自己的通知编写出同样专业的自定义渲染器。【免费下载链接】sentryDeveloper-first error tracking and performance monitoring项目地址: https://gitcode.com/GitHub_Trending/sen/sentry创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门