Sentry 配置实践:前后端项目划分、日志、追踪、Source Map 与 CLI 迁移

8月 11, 2026
DevOps, TypeScript, React, Observability, ByAI

AI 参与说明(Agent:Codex):本文以 Sentry 官方文档为主要依据,并用相关运行时的官方文档校验案例。本文给出的是前端与后端应用共用的 Sentry 配置原则;SDK、套餐能力与部署配置可能变化,实施前请按文末来源和当前版本复核,并审查数据采集范围。

先说结论:前端与后端通常建两个 Project#

对于绝大多数同时包含浏览器前端与服务端/后端的应用,推荐在同一个 Sentry Organization中创建两个 Project:一个接收浏览器数据,一个接收后端数据。这里的“后端”可以是 Node 服务、Serverless Function、Cloudflare Worker、BFF 或 SSR runtime;技术栈不改变这个边界。

Sentry Project接收什么DSN 放在哪里
myapp-web(浏览器前端,例如 React)浏览器错误、浏览器结构化日志、前端性能数据、可选的 Replay浏览器构建时注入的公开配置,例如 VITE_SENTRY_WEB_DSN
myapp-server(后端)API/SSR/后台任务异常、服务端日志、服务端 trace服务端运行时的环境变量或 binding,例如 SENTRY_SERVER_DSN

这不是“每个仓库必须有一个 Project”的规则。Sentry 将 Project 定义为错误归属、入站过滤、告警、所有权和 Issue 分组的边界,并明确建议:即使是单体代码库,也应把前端与后端分开;其 DSN 文档还直接以 React 前端与 Express 后端(即使同一 monorepo)为两个 Project、两个 DSN 的例子。Sentry Organization 指南 DSN 说明

flowchart LR
  B["浏览器\nSPA / hydration"] -->|"web DSN"| W["Sentry Project\nmyapp-web"]
  C["后端\nAPI · SSR · job"] -->|"server DSN"| S["Sentry Project\nmyapp-server"]
  B -->|"sentry-trace + baggage"| C
  W -. "同一 Organization\n同一 release / environment" .- S

两个 Project 不会妨碍浏览器请求与后端请求成为同一条分布式 trace。浏览器只应向自己的 API 域名传递 sentry-tracebaggage;跨域时还要在 CORS 中允许这两个 header。后端可以选择启用 strictTraceContinuation;该选项校验的是Organization ID而不是 Project ID,因此两个 Project 只要仍在同一 Organization 中即可安全续接 trace。Sentry 配置选项

何时可以先用一个 Project?#

小型个人项目、PoC,且浏览器和后端由同一人值守、使用同一套告警/脱敏/配额策略时,一个 Project 技术上可行。这是可运行的简化路径,不是项目划分的最佳默认值。

若这样做,至少给事件和日志设置稳定的 service/runtime 属性,分别标识 webserver。一旦需要不同的告警、值班人、PII 策略、Issue 分组或 source map 发布流程,就拆成两个 Project。

先配置这些 Sentry 通用边界#

1. DSN、环境与 release#

DSN 是事件上报地址,不是读取数据的凭据:浏览器的 VITE_SENTRY_WEB_DSN 可以公开;SENTRY_AUTH_TOKEN 则是上传 source map 或 release 的构建凭据,必须只存在于 CI/构建环境,不能进入浏览器 bundle 或后端 runtime。每个 Project 使用自己的 DSN。Sentry DSN 说明

不要为 productionstagingpreview 再创建一套 Project。它们应使用同一组 Project,并通过一致的 environment 值筛选和告警;浏览器与后端应写入相同的、不可变的 release(通常是 Git SHA 或 CI build ID),这样 Sentry 才能把同一版本的错误、trace 和 source map 对齐。Sentry Environments

2. 错误、日志、性能与隐私#

  • 错误事件通常保留完整采样;性能 trace、Replay 和日志应按流量、预算与关键路由独立采样,生产环境不要照抄 tracesSampleRate: 1
  • enableLogs: true 只启用 Sentry Logs。业务代码优先使用 Sentry.logger. 发送少量结构化日志;已有 console. 需要显式启用 consoleLoggingIntegration,通常仅保留 warn/error
  • 采集配置是隐私边界。需审查 user、cookie、header、URL query、HTTP body 与日志字段;在 beforeSend/beforeSendLog 中删除 token、密码和原始敏感数据。关闭某一类自动采集并不等于不再发送 PII。

3. Trace 与 source map#

浏览器的 tracePropagationTargets 仅匹配自有 API;后端 SDK 继续传入/传出的 trace。浏览器 bundle 的 source map 上传到 myapp-web,后端 bundle 的 source map 上传到 myapp-server,两边使用相同 release。Vite 等构建工具应生成 hidden map,并在上传后删除或拒绝公开访问 .map 文件。Sentry JavaScript Logs Sentry Vite source maps

案例:TanStack Start SSR 的后端部署在 Cloudflare Workers#

以下示例假设使用当前 TanStack Start 的 Cloudflare Workers 部署方式:@cloudflare/vite-pluginwrangler.jsoncnodejs_compat。TanStack Start 的官方 hosting 文档给出了该部署基线;Sentry 的 Cloudflare 专用指南要求将自定义 server entry 用 withSentry 包裹。TanStack Start Hosting Sentry Cloudflare + TanStack Start

1. 安装运行时 SDK#

pnpm add @sentry/cloudflare @sentry/tanstackstart-react

@sentry/tanstackstart-react 当前仍标注为 Beta,且其官方包声明 TanStack Start 最低兼容版本为 1.111.12;请将所有 @sentry/* 依赖保持在兼容的同一版本线上,而不是单独升级其中一个包。官方包说明

2. 让 Worker 使用自定义 server entry#

// wrangler.jsonc
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "myapp",
  "main": "src/server.ts",
  "compatibility_date": "2026-08-11",
  "compatibility_flags": ["nodejs_compat"],
  "vars": {
    "SENTRY_ENVIRONMENT": "production",
    "SENTRY_RELEASE": "myapp@<git-sha>"
  }
}

SENTRY_WORKER_DSN 作为部署 binding 设置,例如:

pnpm exec wrangler secret put SENTRY_WORKER_DSN

DSN 本身可以公开:它只能提交事件,不能读取 Sentry 数据。因此浏览器中使用 VITE_* 形式的 DSN 是正常做法;但 SENTRY_AUTH_TOKEN 是 source map/release 上传用的构建凭据,绝不能进入浏览器 bundle 或 Worker runtime。DSN 安全性 Vite source maps

3. Worker 侧:包住 TanStack Start 的 fetch handler#

// src/server.ts
import * as Sentry from "@sentry/cloudflare";
import { wrapFetchWithSentry } from "@sentry/tanstackstart-react";
import handler from "@tanstack/react-start/server-entry";

interface Env {
  SENTRY_WORKER_DSN: string;
  SENTRY_ENVIRONMENT: string;
  SENTRY_RELEASE: string;
}

export default Sentry.withSentry(
  (env: Env) => ({
    dsn: env.SENTRY_WORKER_DSN,
    environment: env.SENTRY_ENVIRONMENT,
    release: env.SENTRY_RELEASE,
    enableLogs: true,

    // 示例起点;应根据真实流量、预算和关键路由改成 sampler。
    tracesSampleRate: 0.1,

    // 这里只关闭两类自动采集;其余类别仍须按隐私策略显式审查。
    dataCollection: {
      userInfo: false,
      httpBodies: [],
    },
  }),
  // TanStack Start 与 Cloudflare handler 的类型不同;官方示例要求保留此注释。
  // @ts-expect-error - handler is not typed as a Cloudflare handler
  wrapFetchWithSentry(handler),
);

withSentry 负责 Worker 请求生命周期中的初始化、异常与 trace;wrapFetchWithSentry 为 TanStack Start 的 server 函数增加 tracing。不要改用 TanStack Start 的 Node --import 初始化教程:Sentry 明确标注那条路径不适用于 Cloudflare 部署。

dataCollection 是一个必须认真审查的隐私边界。当前 SDK 默认会收集丰富调试上下文;只写 userInfo: falsehttpBodies: [] 并不等于“没有 PII”。生产环境还应按数据分类显式检查 header、cookie、URL query 和业务日志,并在 beforeSend/beforeSendLog 中移除 token、密码、身份证明和原始请求体。dataCollection 选项

4. 框架级 Server Function / 请求中间件#

将 Sentry 中间件放在 TanStack Start 的数组首位:

// src/start.ts
import {
  sentryGlobalFunctionMiddleware,
  sentryGlobalRequestMiddleware,
} from "@sentry/tanstackstart-react";
import { createStart } from "@tanstack/react-start";

export const startInstance = createStart(() => ({
  requestMiddleware: [sentryGlobalRequestMiddleware],
  functionMiddleware: [sentryGlobalFunctionMiddleware],
}));

这里有一个容易漏掉的边界:上述中间件不会自动捕获 SSR 渲染异常。如果业务代码或自定义错误边界捕获了渲染异常,应在唯一的兜底点调用 Sentry.captureException(error),随后继续返回对应的错误响应或 fallback UI;不要在多个边界重复上报同一个错误。

5. 浏览器侧:尽早初始化,再挂上 router tracing#

浏览器使用 myapp-web 的 DSN,Worker 使用 myapp-worker 的 DSN。Cloudflare 专用页给出了在 router.tsx 内一次性 init 的简化方式;当前 TanStack Start 手动配置指南则推荐将初始化拆出来,并把它作为 client entry 的第一个 import。后者能捕获 hydration 前和其他模块初始化期间的错误,下面采用它。

// src/instrument.client.ts
import * as Sentry from "@sentry/tanstackstart-react";

Sentry.init({
  dsn: import.meta.env.VITE_SENTRY_WEB_DSN,
  environment: import.meta.env.VITE_SENTRY_ENVIRONMENT,
  release: import.meta.env.VITE_SENTRY_RELEASE,
  enableLogs: true,
  integrations: [
    // 可选:只采集高价值的旧 console 调用,避免无差别上传。
    Sentry.consoleLoggingIntegration({ levels: ["warn", "error"] }),
  ],
  tracesSampleRate: 0.1,

  // 仅把 trace header 发往本应用的 API / Worker;按实际域名调整。
  tracePropagationTargets: [/^\//, /^https:\/\/api\.example\.com/],
});
// src/client.tsx
// 必须是第一个 import。
import "./instrument.client";

import { StartClient } from "@tanstack/react-start/client";
import { StrictMode, startTransition } from "react";
import { hydrateRoot } from "react-dom/client";

startTransition(() => {
  hydrateRoot(
    document,
    <StrictMode>
      <StartClient />
    </StrictMode>,
  );
});
// src/router.tsx
import * as Sentry from "@sentry/tanstackstart-react";
import { createRouter } from "@tanstack/react-router";
import { routeTree } from "./routeTree.gen";

export const getRouter = () => {
  const router = createRouter({ routeTree });

  if (!router.isServer) {
    Sentry.addIntegration(
      Sentry.tanstackRouterBrowserTracingIntegration(router),
    );
  }

  return router;
};

VITE_SENTRY_WEB_DSNVITE_SENTRY_ENVIRONMENTVITE_SENTRY_RELEASE 是构建时注入的公开浏览器配置;Worker binding 不能在浏览器运行时直接读取。因此发布流水线应将同一个不可变 release 值(通常是 Git SHA 或 CI build ID)同时写给浏览器和 Worker,而环境值也要一致。TanStack Start 手动配置:浏览器初始化与 Router tracing

6. 日志不是自动收集全部 console#

enableLogs: true 只启用 Sentry Logs 能力;推荐业务代码显式发送少量、可查询的结构化日志:

Sentry.logger.info("checkout completed", {
  order_id: order.id,
  item_count: order.items.length,
  payment_provider: "stripe",
});

要将历史 console.* 调用作为 Sentry Logs 发送,需要像上例那样额外启用 consoleLoggingIntegration。生产环境通常只选 warn/error,并用 beforeSendLog 过滤 debug 噪声和敏感字段。日志会与活动 trace 关联,但日志、错误事件和浏览器 breadcrumbs 是不同的数据产品,不能把其中任意一种当作另外两种的替代。Sentry Logs

7. Source maps 与验证#

两个 Project 时,source map 也必须按运行时分流:浏览器 bundle 上传至 myapp-web,Worker bundle 上传至 myapp-worker;两边使用相同 release。不要让一个覆盖整个 SSR 构建的 Vite 上传步骤把所有 maps 错传到前端 Project。

  • 浏览器 Vite 构建可使用 @sentry/vite-plugin;它必须放在其他 Vite plugin 之后,生成 hidden source map,并在上传后删除 .map 文件。
  • Worker 侧遵循 Sentry 的 Cloudflare source map 流程;官方指南要求配置 upload_source_maps,并用 npx @sentry/wizard@latest -i sourcemaps 完成对应项目的上传设置。
  • Sentry Cloudflare Vite plugin 可提供额外的 Worker 依赖插桩,但当前标注为 experimental,且需要 nodejs_compat;它不是基础异常采集的前置条件。

发布后至少做三次验证:浏览器点击按钮抛出异常、请求一个会在 Worker 端抛错的 API 路由、发送一条 Sentry.logger.info。确认错误分别进入正确 Project、栈已还原到源文件,且一次浏览器到 API 的请求能在 Trace Explorer 中关联。Sentry 的官方 Cloudflare + TanStack Start 指南提供了相同的浏览器与 API 验证思路。Cloudflare source maps Cloudflare Vite plugin

案例:纯 Vite + TanStack Router SPA 只需要一个浏览器 Project#

这里没有 SSR、Worker server entry 或服务端函数。创建一个 React 类型的 myapp-web Project 即可,使用 @sentry/react,不要安装 @sentry/tanstackstart-react@sentry/cloudflare 来替代它。

pnpm add @sentry/react
pnpm add -D @sentry/vite-plugin

Sentry 的 TanStack Router 集成包含在 @sentry/react 中,兼容 @tanstack/react-router >= 1.64.0。它应在 router 创建之后、RouterProvider 渲染之前初始化,并使用专门的 tanstackRouterBrowserTracingIntegration,而不是通用的 browserTracingIntegrationSentry TanStack Router 集成

// src/router.tsx
import * as Sentry from "@sentry/react";
import { createRouter } from "@tanstack/react-router";
import { routeTree } from "./routeTree.gen";

export const router = createRouter({
  routeTree,
  // 路由 ErrorBoundary 捕获的错误在这里上报,并保留 React component stack。
  defaultOnCatch: (error, errorInfo) =>
    Sentry.captureReactException(error, errorInfo),
});

Sentry.init({
  dsn: import.meta.env.VITE_SENTRY_DSN,
  environment: import.meta.env.MODE,
  release: import.meta.env.VITE_SENTRY_RELEASE,
  enableLogs: true,
  integrations: [
    Sentry.tanstackRouterBrowserTracingIntegration(router),
    Sentry.consoleLoggingIntegration({ levels: ["warn", "error"] }),
  ],
  tracesSampleRate: import.meta.env.PROD ? 0.1 : 1.0,
  tracePropagationTargets: [/^\//],
});

declare module "@tanstack/react-router" {
  interface Register {
    router: typeof router;
  }
}

路由层已使用 defaultOnCatch 时,不要再用 React 19 的 createRoot({ onCaughtError: … }) 对同一类路由 ErrorBoundary 重复上报。未捕获错误仍由 SDK 自动采集;React 19 的 onUncaughtError/onRecoverableError 或局部 <Sentry.ErrorBoundary> 可以按需要作为额外 UI/诊断边界,但应明确每种错误只由一个路径报告。

若使用文件路由,Vite 配置还需兼顾三个 plugin 的顺序:TanStack Router 在 React 前,Sentry 在所有 plugin 后。SENTRY_AUTH_TOKEN 仅在 CI 或被 Git 忽略的构建环境文件中读取,不能加 VITE_ 前缀。

// vite.config.ts
import { defineConfig, loadEnv } from "vite";
import react from "@vitejs/plugin-react";
import { tanstackRouter } from "@tanstack/router-plugin/vite";
import { sentryVitePlugin } from "@sentry/vite-plugin";

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), "");

  return {
    build: { sourcemap: "hidden" },
    plugins: [
      tanstackRouter({ target: "react", autoCodeSplitting: true }),
      react(),
      sentryVitePlugin({
        org: env.SENTRY_ORG,
        project: env.SENTRY_PROJECT,
        authToken: env.SENTRY_AUTH_TOKEN,
        sourcemaps: {
          filesToDeleteAfterUpload: ["./dist/**/*.map"],
        },
      }),
    ],
  };
});

TanStack 官方要求 Router Vite plugin 在 React plugin 前;Sentry 官方要求 Vite plugin 在全部其他 plugin 后,并提醒它不会在 watch/development 模式上传 source map。生产构建后用一次故意抛错来验证 source map,而不是只看构建成功。TanStack Router + Vite Sentry Vite source maps

Convex 有 Sentry 方案吗?有,但它是异常上报,不是完整 SDK 接入#

Convex 提供内建 Sentry Exception Reporting。它适合把 Convex 的 querymutationactionhttp_action 执行异常发到 Sentry:在 Convex Dashboard 的 Deployment Settings → Integrations → Sentry 中填写 DSN,并在 Sentry 创建一个Node.js 平台的 Project。该内建功能当前需要 Convex Pro 计划。Convex Exception Reporting

如果同一产品同时有浏览器、Cloudflare Worker 和 Convex,通常形成第三个 Project:

运行时推荐 Project说明
浏览器myapp-webReact / TanStack Router 或 TanStack Start 客户端
Cloudflare Workermyapp-workerSSR、HTTP、Worker 日志与 trace
Convexmyapp-convex(Node.js)Convex 函数异常

Convex 会附加不可覆盖的 funcfunc_typefunc_runtimerequest_idserver_nameenvironment 和认证用户标识等标签。这使它适合按函数和请求 ID 排查异常;事件可能需要一两分钟才显示在 Sentry。

它的边界也很重要:Convex 官方明确说明内建 Sentry 支持尚不提供 Sentry SDK 的高级自定义能力。因此不要把它当成 Convex 中已经有 Sentry.logger、完整 performance tracing 或浏览器 Replay 的承诺。Convex 另有 Log Streams,可将函数执行和 console.* 日志发到 Axiom、Datadog、PostHog 或自定义 webhook,但当前列出的目的地不包括直接的 Sentry sink;需要自建 webhook 转换器时,要另外处理认证、脱敏、重试、事件模型和去重。Convex Log Streams

sentrysentry-cli:前者是 v4,后者是旧 v3#

如果说的是开发、CI 中连接 Sentry SaaS 或 self-hosted 实例的命令行工具,答案是相反的sentry 是新版 v4 CLI,sentry-cli 是旧版 v3 CLI。v4 是一次重写:npm 包从 @sentry/cli 改为 sentry,二进制也从 sentry-cli 改为 sentry。它仍会读取原来的 SENTRY_AUTH_TOKENSENTRY_ORGSENTRY_PROJECTSENTRY_URL.sentryclirc,所以凭据和大部分项目配置可以沿用。Sentry CLI v3 → v4 迁移指南

目标v3(旧)v4(新)
安装包 / 可执行文件@sentry/cli / sentry-clisentry / sentry
登录sentry-cli loginsentry auth login
新建 releasesentry-cli releases new 1.0.0sentry release new 1.0.0
上传 source mapsentry-cli sourcemaps upload ./distsentry sourcemap upload ./distsourcemaps 仍是别名)
CI 认证tokentoken 仍可用;交互使用还可采用 OAuth device flow

不要把已有 CI 的二进制名直接全局替换后就认为迁移完成。v4 将多数命令组从复数改为单数,且部分 flag(例如全局 --auth-token)改由环境变量提供;release deploy、source map 的部分参数也有语义变化。若现有流水线稳定,继续 pin 住 @sentry/cli / sentry-cli 是合理的;准备升级时,应逐条对照官方迁移表,并在 CI 中执行真实上传验证。官方也提供兼容 shim,使多数旧调用可在迁移期间转发给新版。

不要只用版本数字判断新旧#

迁移指南把新版称为 v4,但新版 sentry 可执行文件目前会显示 0.x 的版本号;例如 sentry --version 输出 0.41.0 并不表示它是旧版。判断依据应是二进制名与安装包sentry / sentry npm 包是新版客户端,sentry-cli / @sentry/cli 才是旧版。升级排查时先确认实际解析到哪个命令,而不是仅按数字猜测:

command -v sentry
sentry --version
command -v sentry-cli

CI 迁移时要验证什么#

旧版的 SENTRY_AUTH_TOKENSENTRY_ORGSENTRY_PROJECTSENTRY_URL.sentryclirc 仍可复用;不要把 token 写进命令行或提交到仓库。相反,原来位于命令前的 --auth-token、self-hosted 使用的 --url--header 已分别改用环境变量;--allow-failure 已移除。脚本若依赖旧版几乎总是 1 的退出码,或解析纯文本输出,也要重测:新版提供 --json,退出码按认证、输入、API 等错误类型分类。

先做不上传文件的连通性检查,再在 staging 或一次真实 production build 中验证 source map 和 release:

# 以下命令不创建 release,也不上传 source map
sentry --version
sentry auth status
sentry release list --json
sentry sourcemap upload --help

release list 需要已有的组织、项目和认证配置;若失败,先修正 CI 注入的环境变量,而不是在脚本中回退到明文 token。实际构建验证通过、Sentry 中能看到正确 release 且故意制造的错误能还原到源码后,再卸载旧 sentry-cli。如果仓库代码直接 import 了 @sentry/cliSentryCli 类,不能只替换命令:v4 改为 sentry 包的 createSentrySDK(),需要按官方迁移说明改写调用。

对本文的 Vite 场景,优先让 @sentry/vite-plugin 在 production build 中完成 source map 上传,通常无需手写任一 CLI 命令。只有自建 release/source-map 脚本、调试 source map 或管理 Sentry 资源时,才直接选择 CLI;无论选择哪一代,都要固定版本以保证 CI 可复现。

若你在self-hosted Sentry 服务容器内看到同名的 sentry,先用 command -v sentrysentry --version 确认来源:那可能是服务端的 Python 管理命令,并不是这里的 v4 客户端 CLI。本文这节讨论的是开发者/CI 客户端;v4 客户端本身也可通过 SENTRY_URL 连到 self-hosted 实例。新版 CLI 安装与 self-hosted 配置

发布前清单#

  • 同一产品的浏览器、Worker、Convex 后端放在同一个 Sentry Organization;按运行时拆 Project,而不是按环境拆 Project。
  • 每个 Project 用自己的 DSN;浏览器只持有 public DSN,绝不持有 SENTRY_AUTH_TOKEN
  • 浏览器、Worker(以及有需要时 Convex 的发布记录)使用可追溯且一致的 release 约定;environment 使用稳定值。
  • 只对本应用 API 设置 tracePropagationTargets;跨域时 CORS 允许 sentry-tracebaggage
  • Logs 使用结构化字段,避免 token、cookie、原始 body 和过大的对象;先配置 beforeSend/beforeSendLog 再放量。
  • browser 与 Worker 的 source map 分别上传到正确 Project,并在生产 build 后验证一条还原后的栈。
  • 显式测试浏览器错误、Worker 错误、SSR 渲染兜底、TanStack Server Function、结构化日志和跨端 trace。

参考资料(整理于 2026-08-11)#

本文共 6931 字,上次修改于 Aug 11, 2026,以 CC 署名-非商业性使用-禁止演绎 4.0 国际 协议进行许可。

相关文章

» Expo 技术原理与交付:从 React Native 项目到 EAS 发布

» Expo、React Native 与 Flutter:概念、架构、上架与选型

» React Native 技术原理:从 TypeScript 到原生界面、Fabric 与 Hermes

» Prometheus 和 ServiceMonitor 介绍

» React Native UI 方案横评:Expo UI、HeroUI Native、Paper、Tamagui 与 gluestack-ui