@12-apps/observability-frontend
关于哨兵的浏览器错误报告——读出生产实际所说的内容 没人能把调试器附在线上.
它有一个服务器端的对应器, @12-apps/observability-backend它们是两个
软件包而不是一个,因为它们完全没有代码,只有环境
变量——一方是 @sentry/node 加号 winston-transport另一个是
@sentry/react 加上反应,一个包就可以装上温斯顿运输
每个 SPA 的依赖树和每个服务器的回放:
| 软件包 | 目录 |
|---|---|
@12-apps/observability-backend | a 温斯顿运输 error编号 :warn 打开 @sentry/node,一个PII 擦洗,在任意深度按键修改, 和冲出一个故意退出 |
@12-apps/observability-frontend | 拖到一个后面 已服务 DSN 打开 @sentry/react,使部署无法在打开标签时提交一期的噪声规则,更严格的PII刷新,路由错误边界,自查页面,Vite源图上传插件 |
它们是独立的——要么没有其他的——但它们是故意的
份额 SENTRY_ENVIRONMENT 财务报告和审定财务报表 SENTRY_RELEASE,所以浏览器事件和500
它的后方 在同一环境 在同一建筑之下.
两者均为主机不可知:没有取取包,没有路由器假设,没有出品 收到,没有租户词汇。 他们不知道的每件事 都是例行的API 错误看起来像,一个崩溃的页面应该渲染什么, 配置所在的地方—— 通过一个命名的接缝到达 东道主填写。 见 连接。 。 。 .
它们是从一个工作应用程序中提取的,而不是在其中设计的
抽象的,所以下面的推理引用了它们产生的失败. 留下来的
在消耗的回购中,是部署事实,而不是图书馆行为:
哨兵项目 每一个应用程序都报告,以及上传符号是如何到达的
docker build。 。 。 .
入境点
| 导入 | 这是什么 | 停车 |
|---|---|---|
@12-apps/observability-frontend | init, init前缓冲, 上下文标记, beforeSend, (中文(简体) ), reportRouteCrash 编号 : reportWarning | @sentry/react |
@12-apps/observability-frontend/react | createRouteErrorBoundary, (中文(简体) ), createObservabilityPage | +反应, react-router-dom |
@12-apps/observability-frontend/self-check | 自检查页本身 | + 键 @12-apps/ui |
@12-apps/observability-frontend/vite | 两个构建插件:源图上传和释放工人记者 | @sentry/vite-plugin (仅建) |
@12-apps/observability-frontend/service-worker | 作为经典剧本的工人记者 | 无——自足的IIFE |
分钱不是化妆品 根项是无框架的,所以一个工人
或者一个非反应主机可以报告 不反应通过它的枪管到达
不问。 页面只能通过自己的子路径或
动态 import() 路由工厂内部 react 枪管
每一个应用程序的输入块 都不会被打开.
其中三艘不同格式的舰只,没有一艘是首选. LOADS 代码决定代码必须是什么:
| 条目 | 装入 | 因此,它作为 |
|---|---|---|
. 编号 : /react 编号 : /self-check | 消费者的捆绑者 | 类型脚本源 |
/vite | 节点 (Vite 捆绑一个配置,但留下了赤色的外观) | 汇编的无害环境管理 |
/service-worker | importScripts() | 经典脚本( IIFE) |
节点拒绝剥去下面的类型 node_modules,以及 importScripts 无法
完全装入一个ES模块,这样这两个模块就会在每一个消费者中失败
出版后的第一个建筑,同时作为工作空间完美工作
在pnpm的同音链接将现实之路放出外面的地方 node_modules。 。 。 .
连接应用程序
1. 在第一次提交之前开始,不等待
// main.tsx
import { startObservability } from "@12-apps/observability-frontend";
void startObservability("storefront");
两个半音有相反的时空需要,单调都得到对分.
安装全局处理器( E)window.onerror, (中文(简体) ), unhandledrejection)为
同步且必须先于第一次渲染,或者在靴子中丢出一发子弹.
获取 DSN 是网络往返, 必须 没有 门先漆-一
等待错误报告来绘制的应用程序已经让报告中断 。 所以说
处理器立即进入并缓冲入内存;一旦
SDK已经上线了,如果报告被关闭,就会被撤销.
2. 配置错误边界一次, 随地挂起
// route-error-boundary.tsx
import { createRouteErrorBoundary } from "@12-apps/observability-frontend/react";
export const RouteErrorBoundary = createRouteErrorBoundary({
fallback: ({ error, reload }) => (
<ErrorState message={error.message} onRetry={reload} />
),
});
// the layout, around the routed outlet
<RouteErrorBoundary resetKey={location.key}>
<Outlet />
</RouteErrorBoundary>
为什么边界在可观察的包里. window.onerror
从来没有看到一个渲染崩溃——反应抓住了扔出并重新扔出
带宽——因此是一个带有全局处理器的应用程序,但没有边界报告 无 联 合 国
一个失败模式,将屏幕空出。 有两分之一
保证 把他们分开运输 才是失踪的原因.
有两件事要纠正:
resetKey是路由位置. 没有它, 边界带: 出错 状态是组件状态,而 React 继续使倒置状态持续到 所以以后每次导航都显示一个页面的故障 用户已离开。 故意这样做的 没有 在不相关的重递上清除 这将在循环中重挂一个决定性的刻板页.- 建于模块范围. 重建的边界是新组成部分 每次键入,所以每次重置时都会卸载并重载下方的一切 由父母决定.
倒计时 reload 财务报告和审定财务报表 reset. 偏爱 reload: 通常的事业
崩溃的页面是上次部署时已停止存在的块,并且
重交回同一树无法获取已丢失的文件 .
3. 自查路线
import { createObservabilityPage } from "@12-apps/observability-frontend/react";
const observabilityRoute = createObservabilityPage({
boundary: (children) => (
<RouteErrorBoundary resetKey="self-check">{children}</RouteErrorBoundary>
),
});
// …then interpolate it, do not render it:
<Routes>{observabilityRoute}</Routes>
它返回一个 <Route> 而不是导出一个组件,因为 <Routes>
阅读其儿童 createRoutesFromChildren 而不是让他们..
a 构成部分 返回时 (单位:千美元) <Route> 在沉默中被忽视.
通过应用程序 拥有 边界。 页面的渲染- 崩溃按钮只能证明 如果一个真实的页面崩溃击中的边界是捕获的 它; 提供包件的边界将测试包件.
4. 构建时上传源地图
// vite.config.ts
import { sentrySourcemaps } from "@12-apps/observability-frontend/vite";
plugins: [react(), sentrySourcemaps({ project: "my-frontend" })],
Falsy——所以Vite跳过——除非 SENTRY_AUTH_TOKEN 财务报告和审定财务报表 SENTRY_ORG 两者都是
设置,它让本地和公关建筑毫无秘密地运作。 上传然后
删除 .map 文件,所以 dist 到达网络服务器时没有.
5. 服务人员的报告
一个服务工作者是一个单独的全球范围: window.onerror页:1
unhandledrejection 处理器及其路线边界 无 一个工人
掷出. 一个应用程序可以把上面所有的东西都接通 并且仍然对它视而不见
代码,决定一个部署砖块 安装的PWA.
工人两行一插件:
// vite.config.ts
import { observabilityServiceWorkerAsset } from "@12-apps/observability-frontend/vite";
plugins: [react(), observabilityServiceWorkerAsset()],
// public/sw.js — the first two lines
importScripts("/observability-sw.js");
observability.installWorkerReporter({ app: "storefront" });
插件将记者发送到 dist 生产并服务于
路径从 node_modules 发展——404名工人在第一线
是个没人能调试的工人 public/ 由建筑逐字复制,所以
写下来就意味着要做出一件建筑文物.
电线传来两个全局处理器 处理器的投出 error,则a
承诺被拒绝 waitUntil编号 :respondWith 以
unhandledrejection工人在目的上的失败既达不到目标,也达不到目标
catch(() => undefined) 停止一个失败的安装离开一个没有的页面
工人,当他们有需要的时候,你当亲手报到:
caches.open(SHELL_CACHE)
.then((cache) => cache.add(SHELL_URL))
.catch((error) => observability.reportWorkerError(error, { handler: "install" }));
每个事件都有标记 source: service-worker因为工人的失败和
使坠机需要不同的人看.
它不会报告的: 请求失败 。 离线是一个工人的正常的
天气和每一次跳跃策略都是围绕 fetch 拒绝,所以
Load failed 编号 : Failed to fetch 编号 : NetworkError… 在任何I/O之前放弃.
此外,每个工人最多参加5次活动
循环点火数以百计的一秒钟—— 并声称槽 在此之前 页:1
第一个 await因为发送后反增量会让每个电话
在一分钱过支票, 并封顶什么都没有.
堆栈作为文本而不是帧到达. 工人的脚本按字面写
没有细微的,所以 sw.js:118 在原始堆栈中已经是答案; 手写
报告失败的代码内每浏览器堆栈解析器为秒
表面以获取错误.
自查页面
每个上载它的应用程序 /__observabilidade: 状态面板读取
现场直播 SDK( 初始化?, DSN 有密钥被遮蔽, 环境, 释放) 和
3个按钮,每个报告路径一个.
| 按钮 | 路径已执行 |
|---|---|
| Quebrar 不显示 | 在渲染过程中抛出 边界 reportRouteCrash |
| 没有操作器 | 扔回调 ~ window.onerror + 全球处理器 |
| 罪恶警告 | reportWarning 直接, 无错误 |
为什么一个页面而不是另一个测试. 管道的每个部分都经过单位测试 这一切仍然可能 死在生产, 因为什么打破从来不是 部分——这是其中两个部分的平面图,每半个部分看起来都是正确的. 其动机是: 两次部署一个SPA将源地图上传到 一个兄弟的哨兵项目。 构建为绿色, 上传成功, 配置 结束点回答200,象征被打破了整个时间. 没有 CI可以这么说.
像这样读取: 面板中空的 DSN 表示变量从未到达
进程, 没有按钮会发送任何信息; 否则事件应该出现
在秒内,并且 它的堆栈必须命名为 .tsx 文件和行。微调
框架表示地图没有与此发布对齐, 即失败
页面存在到表面.
它刻意地坐到认证门外——当你最想要遥测的时候
即当应用程序被打破,而"应用程序被打破"经常包括登录. 这是
安全:摄入的DSN只写字,每捆有船,所以任何人都想要
以燃烧配额,无需此页即可将配额添加到哨兵。 每个塔布3s
节流阀使一指一指一指不至被洪水淹没 每一起事件
生产被标记 self-check 因此,它们过滤和删除为一组.
缝合
6,每个都存在,因为弄错了就是沉默.
| 连接 | 主持人如何填充 | 跳过的代价 |
|---|---|---|
| 应用程序名称 | startObservability(app)键入为 string 在这里,缩小到一个联盟 在东道主 | 没什么,但工会是 做一个打字 main.tsx 编译错误 |
| 配置终点 | startObservability(app, { endpoint }); 默认为 /api/observability-config | —— 说 |
| 噪声分类器 | setErrorClassifiers({ isIgnorableResponse, isStaleChunk }) | 主机的常规 4xx 和它自己的块装填器错误成为问题 |
| 横跨文本规则 | setSpanTextScrubber(text => …),在 startObservability | 在租户的路径名称上,每个Web 维生符都带有租户的页面名称和 URL |
| 倒计时 | createRouteErrorBoundary({ fallback, onCrash }) | —— 说 |
| 自我检查边界 + 懒惰的包装 | createObservabilityPage({ boundary, lazyComponent }) | 崩溃按钮测试不使用真实页面的边界 |
一条线是故意的 没有 接缝:坚固的接缝 措辞。 。 。 .
克罗姆、萨法里和维特 每一句话都有不同的死块
列表横跨主机边界, 表示在报告时丢出一个浏览器的噪音
其它的 – 每名Safari 用户存档一次, 而每名用户从不存档一次
Chrome用户. 浏览器措辞是浏览器的知识,所以保留在这里
noise.ts; 东道主的 isStaleChunk 上层堆栈,用于投出
键入错误而不是可识别消息.
除非一个DSN到达,否则就关闭了
没有DSN,没有SDK,没有网络. Dev、CI和每一次测试都离线——即 也阻止了一个套房 充斥着问题追踪器 失败.
为什么DSN服务 而不是烘焙在
一个Vite SPA是一个静态的捆绑. import.meta.env 是在建设时间内嵌的 所以
VITE_SENTRY_DSN 冻结在 docker build 旋转变成了重建——
和一个带节点进程的 Web 服务器提供的捆绑, 什么都没有
在制作中改为 process.env 也代表委员会.
所以浏览器问 GET /api/observability-config?app=<app> 穿着靴子 阅读
嵌入通过 a 动态 服务器上的密钥报告运行时间配置
而不是建设时空。 终点是公开的
故意没有认证:食用DSN只写作,船装一包
每个访客下载, 它必须在会话存在之前回答, 因为
最值得注意的错误出现在登录屏幕和第一个油漆上.
成本是SDK开始来回迟到 同步 处理者加缓冲器 唯一未发现的窗口是浏览器 完全无法获取条目捆绑”,因此没有页面记者的渔获量.
字段Web Virtuals — 只有当配置服务于一个速率时
配置结束点也可能回答 tracesSampleRate,数字从0到1.
缺席表示0,所以一个更老的后端会保持错误和性能; a
存在但格式错误的值拒绝整个配置而不是猜测.
比率高于0:
- 每一片叶子穿过
beforeSendSpan。 。 。 .traceLifecycle: "stream"实值 客户选项,而不是每个合并选项。 流出跨度永远无法到达beforeSendTransaction因此,一个钩子,包裹在withStreamedSpan,为 在以下PII规则运行的跨度,例如beforeSend用于错误。 没错 安装在现场客户端选项上,与生命值一起,在 集成是跨度,所以洗涤也没有在关键道路上. - 紧要集成在后
load。 。 。 .webVitalsIntegration财务报告和审定财务报表spanStreamingIntegration一旦页面有动态导入, 请输入 装入。 测量出第一种颜料,决不能拖延. 主机的捆绑者必须 保持这个块懒: amanualChunks规则拉动每@sentry模块 进入一个急切块 把这些也拉入. - 横跨名称的一页 三种方式: 联合国
name,则sentry.transaction财务报告和审定财务报表sentry.segment.name属性,以及任何*.url属性 . URL通过scrubUrl()先说 然后是东道主setSpanTextScrubber规则 运行在名称和每个 URL 的 PATH 上( 绝不是其来源) 。 这个包 无法知道哪个路径段给租户起名, 因此它没有规则 .
噪音——绝不能成为问题
一个在每次部署上大喊大叫的记者 第一个星期就哑了,一个哑了
记者还是账单。 所有事情都发生在一个 beforeSend因此适用于
sDK本身的仪器 以及手取的仪器.
- 施特尔在部署后就散了. 每个散列文件名随部署而死亡 生产它,所以每一个标签 提前打开 要求块是 不见了。 丢弃到任何地方 除外 路由边界: 主机加载器 重新装入一次已经吞噬了第一次失败, 所以一个块错误, 即 到达边界后幸存下来,值得一看.
- 例行非5xx的回答. 无论主机的取地层投出什么
400看起来像是坠机.isIgnorableResponse是它怎么说的 否则;5xx仍然通过. - 密码不是我们的.
ResizeObserver loop …,不透明的交叉来源Script error.(没有堆栈,没有文件——它把所有无关的东西归在一个下面) ),和指向浏览器扩展的堆栈.
临 二
SPA的曝光是普通的而不是例外的:一个出柜屏幕持有 付款人的名字,电子邮件, 税证和卡片字段 在React州和DOM, 任何自动面包屑都可以到达 URL本身就是证据.
scrub()在任何深度下按关键符号编辑、规范大小写和分隔器taxId编号 :tax_id编号 :TaxID是一个条目。 按键而不是按值形状: a 巴西中央公积金为11个数字,许多身份证也值得保留. 定出身份证、充值身份证和数量,这是故意活下来的——一种吃这些身份证的刷子 让每个事件都无法判断.scrubUrl()保存路径并放下查询字符串和碎片。 这个 路径是使事件可以区分的原因; 查询字符串是符号和 (单位:千美元)?next=电子邮件最后.sendDefaultPii: false不管DSN是谁 都不许动 所以请求尸体, 饼干和IP从不随行.- 没有会话重播 . 复出崩溃的记录 某人的名片。 打开它是一个需要伪装的单独决定 配置为先 .
setObservabilityContext 取一个窄对象而不是用户,所以标记
保留到一个可以区分的事件而不识别任何人的集合中。 这个
只写实际通过的密钥——独立组件设置上下文
如果两者都写出每个密钥 以后的效果就会沉默
覆盖前一个 .
测试
pnpm --filter "@12-apps/observability-*" test
他们跑在jsdom与 @sentry/react 被嘲弄,所以没有到达网络.