# @12-apps/observability-frontend 中文文档

规范地址：https://npmcn.com/package/%4012-apps/observability-frontend/

版本：2\.4\.2
npm 元数据更新时间：2026-09-30T22:49:58.185Z
中文译文更新时间：2026-11-05T19:52:12.885Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%4012-apps/observability-frontend
原文对照：https://npmcn.com/package/%4012-apps/observability-frontend/?lang=original
项目主页：https://github.com/12-apps/shared-packages#readme
源码仓库：https://github.com/12-apps/shared-packages

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：158 kB，27 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=22\.0\.0
- 运行依赖：3 个
- peer 依赖：vite \>=6\.0\.0，react \>=19\.0\.0，react-dom \>=19\.0\.0，react-router-dom \>=7\.0\.0
- 首次发布：2026-08-07T15:49:43.499Z
- 最新版本发布：2026-09-30T22:49:57.791Z
- 近 30 天下载量：32985（2026-10-04 至 2026-11-02，npm 官方下载统计）
- 已发布版本数：21（近 12 个月 21 个）

## 中文简介

浏览器错误报告 React SPA,关于哨兵: 延迟在已送达的 DSN 后面输入, 噪声规则使部署无法在打开的标签上存档一次, PII 刷新, 路由错误边界和自查页面 证明链条在 de

## 安装

```sh
npm install @12-apps/observability-frontend
```

也可以使用：`pnpm add @12-apps/observability-frontend`、`yarn add @12-apps/observability-frontend`、`bun add @12-apps/observability-frontend`

## README

# `@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 错误看起来像,一个崩溃的页面应该渲染什么, 配置所在的地方—— 通过一个命名的接缝到达 东道主填写。 见 [连接](https://npmcn.com/package/%4012-apps/observability-frontend/#readme-the-seams)。 。 。 .

它们是从一个工作应用程序中提取的,而不是在其中设计的 抽象的,所以下面的推理引用了它们产生的失败. 留下来的 在消耗的回购中,是部署事实,而不是图书馆行为: 哨兵项目 每一个应用程序都报告,以及上传符号是如何到达的 `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\. 在第一次提交之前开始,不等待

```ts
// main.tsx
import { startObservability } from "@12-apps/observability-frontend";

void startObservability("storefront");
```

两个半音有相反的时空需要,单调都得到对分. 安装全局处理器( E)`window.onerror`, (中文(简体) ), `unhandledrejection`)为 同步且必须先于第一次渲染,或者在靴子中丢出一发子弹. 获取 DSN 是网络往返, 必须 **没有** 门先漆-一 等待错误报告来绘制的应用程序已经让报告中断 。 所以说 处理器立即进入并缓冲入内存;一旦 SDK已经上线了,如果报告被关闭,就会被撤销.

### 2\. 配置错误边界一次, 随地挂起

```tsx
// 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} />
  ),
});
```

```tsx
// the layout, around the routed outlet
<RouteErrorBoundary resetKey={location.key}>
  <Outlet />
</RouteErrorBoundary>
```

**为什么边界在可观察的包里.** `window.onerror` 从来没有看到一个渲染崩溃——反应抓住了扔出并重新扔出 带宽——因此是一个带有全局处理器的应用程序,但没有边界报告 _无_ 联 合 国 一个失败模式,将屏幕空出。 有两分之一 保证 把他们分开运输 才是失踪的原因.

有两件事要纠正:

-   **`resetKey` 是路由位置.** 没有它, 边界带: 出错 状态是组件状态,而 React 继续使倒置状态持续到 所以以后每次导航都显示一个页面的故障 用户已离开。 故意这样做的 _没有_ 在不相关的重递上清除 这将在循环中重挂一个决定性的刻板页.
-   **建于模块范围.** 重建的边界是新组成部分 每次键入,所以每次重置时都会卸载并重载下方的一切 由父母决定.

倒计时 `reload` 财务报告和审定财务报表 `reset`. 偏爱 `reload`: 通常的事业 崩溃的页面是上次部署时已停止存在的块,并且 重交回同一树无法获取已丢失的文件 .

### 3\. 自查路线

```tsx
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\. 构建时上传源地图

```ts
// 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.

工人两行一插件:

```ts
// vite.config.ts
import { observabilityServiceWorkerAsset } from "@12-apps/observability-frontend/vite";

plugins: [react(), observabilityServiceWorkerAsset()],
```

```js
// 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)` 停止一个失败的安装离开一个没有的页面 工人,当他们有需要的时候,你当亲手报到:

```js
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` 一旦页面有动态导入, 请输入 装入。 测量出第一种颜料,决不能拖延. 主机的捆绑者必须 保持这个块懒: a `manualChunks` 规则拉动每 `@sentry` 模块 进入一个急切块 把这些也拉入.
-   **横跨名称的一页 三种方式:** 联合国 `name`,则 `sentry.transaction` 财务报告和审定财务报表 `sentry.segment.name` 属性,以及任何 `*.url` 属性 . URL通过 `scrubUrl()` 先说 然后是东道主 `setSpanTextScrubber` 规则 运行在名称和每个 URL 的 PATH 上( 绝不是其来源) 。 这个包 无法知道哪个路径段给租户起名, 因此它没有规则 .

## 噪音——绝不能成为问题

一个在每次部署上大喊大叫的记者 第一个星期就哑了,一个哑了 记者还是账单。 所有事情都发生在一个 `beforeSend`因此适用于 sDK本身的仪器 以及手取的仪器.

1.  **施特尔在部署后就散了.** 每个散列文件名随部署而死亡 生产它,所以每一个标签 提前打开 要求块是 不见了。 丢弃到任何地方 _除外_ 路由边界: 主机加载器 重新装入一次已经吞噬了第一次失败, 所以一个块错误, 即 到达边界后幸存下来,值得一看.
2.  **例行非5xx的回答.** 无论主机的取地层投出什么 `400` 看起来像是坠机. `isIgnorableResponse` 是它怎么说的 否则;5xx仍然通过.
3.  **密码不是我们的.** `ResizeObserver loop …`,不透明的交叉来源 `Script error.` (没有堆栈,没有文件——它把所有无关的东西归在一个下面) ),和指向浏览器扩展的堆栈.

## 临 二

SPA的曝光是普通的而不是例外的:一个出柜屏幕持有 付款人的名字,电子邮件, 税证和卡片字段 在React州和DOM, 任何自动面包屑都可以到达 URL本身就是证据.

1.  **`scrub()`** 在任何深度下按关键符号编辑、规范大小写和分隔器 `taxId` 编号 : `tax_id` 编号 : `TaxID` 是一个条目。 按键而不是按值形状: a 巴西中央公积金为11个数字,许多身份证也值得保留. 定出身份证、充值身份证和数量,这是故意活下来的——一种吃这些身份证的刷子 让每个事件都无法判断.
2.  **`scrubUrl()`** 保存路径并放下查询字符串和碎片。 这个 路径是使事件可以区分的原因; 查询字符串是符号和 (单位:千美元) `?next=` 电子邮件最后.
3.  **`sendDefaultPii: false`**不管DSN是谁 都不许动 所以请求尸体, 饼干和IP从不随行.
4.  **没有会话重播 .** 复出崩溃的记录 某人的名片。 打开它是一个需要伪装的单独决定 配置为先 .

`setObservabilityContext` 取一个窄对象而不是用户,所以标记 保留到一个可以区分的事件而不识别任何人的集合中。 这个 只写实际通过的密钥——独立组件设置上下文 如果两者都写出每个密钥 以后的效果就会沉默 覆盖前一个 .

## 测试

```bash
pnpm --filter "@12-apps/observability-*" test
```

他们跑在jsdom与 `@sentry/react` 被嘲弄,所以没有到达网络.

## 常见问题

### @12-apps/observability-frontend 是什么？

浏览器错误报告 React SPA,关于哨兵: 延迟在已送达的 DSN 后面输入, 噪声规则使部署无法在打开的标签上存档一次, PII 刷新, 路由错误边界和自查页面 证明链条在 de

### 如何安装 @12-apps/observability-frontend？

在项目目录运行 npm install @12-apps/observability-frontend，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：react（\>=19\.0\.0）、react-dom（\>=19\.0\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @12-apps/observability-frontend 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @12-apps/observability-frontend 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @12-apps/observability-frontend 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22\.0\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @12-apps/observability-frontend 可以商用吗？

@12-apps/observability-frontend 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @12-apps/observability-frontend 的下载量有多少？

根据 npm 官方下载统计，@12-apps/observability-frontend 在 2026/10/04 – 2026/11/02 这 30 天内被下载 32,985 次，日均约 1,100 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @12-apps/observability-frontend 还在维护吗？

最新版本 2\.4\.2 发布于 2026/10/01（1 个月前）；至今共发布 21 个版本，其中过去 12 个月发布了 21 个；首次发布于 2026/08/07。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

