Sentry 配置实践:前后端项目划分、Logs、Tracing、Source Maps 与 CLI 迁移

This article is extracted from the chat log with AI. Please identify it with caution.

AI 参与说明(Agent:Codex):本文以 Sentry 官方 SDK、CLI、MCP 与 Agent Skills 文档为主要依据,并用本机 Sentry CLI 0.41.0 校验文中的命令结构。本文给出的是前端与后端应用共用的配置和排障原则;SDK、CLI、套餐能力与控制台入口可能变化,实施前请按文末来源和当前版本复核,并审查数据采集范围。

先说结论:前端与后端通常建两个 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 TD
  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、同一个 DSN,并通过一致的 environment 值筛选和告警;浏览器与后端应写入相同的、不可变的 release(通常是 Git SHA 或 CI build ID),这样 Sentry 才能把同一版本的错误、trace 和 source map 对齐。只有在数据权限、保留策略、配额或值班归属必须物理隔离时,才应另建 Project,此时才会产生新的 DSN。Sentry Environments

2. Errors、Logs、Tracing 与隐私#

  • 错误事件通常保留完整采样;性能 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. 先保留原始异常,再转换成用户文案#

错误监控最常见的失效方式,不是 SDK 没有初始化,而是业务代码先把原始异常转换成了通用错误:TypeError、浏览器原生消息、cause 和原始栈在进入 Sentry 前已经丢失。UI 可以显示稳定、可翻译的错误码,但 Sentry 应收到转换前的异常。

try {
  return await loadConversation();
} catch (error) {
  const originalError =
    error instanceof Error
      ? error
      : new Error("Non-Error value thrown", { cause: error });

  Sentry.withScope((scope) => {
    scope.setTags({
      operation: "conversation.history",
      boundary: "api-client",
    });
    Sentry.captureException(originalError);
  });

  throw toUserFacingError(originalError);
}

不要创建一个新的通用 Error(“Network request failed”),再手工复制旧 stack;这样 Sentry 的异常类型与消息仍然是假的。若确实要包装,使用 cause 保留异常链,并只在一个明确的失败边界上报,避免全局 SDK、Router ErrorBoundary 和业务 catch 重复创建三个 Issue。

异常消息也不应只允许少数精确字符串。正确策略是保留所有诊断消息,再对其中的秘密进行替换:Bearer/JWT、cookie、邮箱、URL query、认证 header 和高熵密钥替换为固定占位符;异常类型、清洗后的消息、栈和 cause 保留。清洗器必须集中维护,不能让各业务模块自己拼规则。

严格隐私场景可以直接删除自动附加的 requestextrauser,再显式上传少量安全上下文:

Sentry.init({
  beforeSend(event) {
    event.request = undefined;
    event.extra = undefined;
    event.user = undefined;

    for (const value of event.exception?.values ?? []) {
      value.value = sanitizeDiagnosticText(value.value);
    }

    if (event.logentry?.message) {
      event.logentry.message = sanitizeDiagnosticText(event.logentry.message);
    }

    return event;
  },
});

推荐的安全关联字段包括 operationboundary、稳定的 error_codehttp_status、Sentry release,以及服务端生成并返回的 request_id。不要上传 Authorization、cookie、请求/响应正文、聊天内容、prompt、附件内容或带 query 的完整 URL。浏览器事件中的 event.request 往往描述当前页面,并不必然等于失败的 fetch;API 关联信息应由 transport 层显式加入规范化 operation/path 与 request ID。

4. SDK 脱敏不是最后一道防线#

浏览器 DSN 可以公开,但仍可能被滥用制造垃圾事件;客户端清洗代码也可能因某次发布失效。因此还应在 Sentry 项目侧配置纵深防御:

  • Allowed Domains:限制浏览器事件允许的来源域名;通常位于 Project Settings 的 Client Keys(DSN)设置中。
  • Inbound Filters:过滤已知浏览器扩展、旧浏览器、爬虫或无价值错误。
  • Security & Privacy:启用 server-side data scrubbing,并按合规要求开启 Prevent Storing of IP Addresses。
  • Spike Protection 与配额告警:避免异常流量快速耗尽配额;同时在 Stats 中区分 accepted、filtered 与 rate-limited 事件。

控制台名称可能随版本变化,最终以项目设置页和官方 Project API 返回的 allowedDomainsdataScrubberscrubIPAddresses 等字段为准。Allowed Domains 不是认证机制,也不能替代服务端清洗;四层配置需要同时存在。Sentry Project API Spike Protection API Sentry Stats

5. Tracing 与 Source Maps#

浏览器的 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. Sentry Logs 不会自动收集全部 console#

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

Sentry.logger.info("checkout completed", {
  operation: "checkout.complete",
  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 create my-org/1.0.0release new 是别名)
上传 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(),需要按官方迁移说明改写调用。

本地开发不需要长期配置 SENTRY_AUTH_TOKEN。执行一次 sentry auth login 后,日常查询和手工上传复用 CLI 的登录 session;CI 才从 secret store 注入组织级 SENTRY_AUTH_TOKEN。若本地构建必须调用只接受 token 的 Vite plugin,可由可信构建脚本在当前进程中读取 sentry auth token 并立即传给 plugin,但不得打印、写入 .env、提交仓库或注入 Cloudflare runtime。更简单的选择是让本地构建跳过上传,需要验证 source map 时再用已登录的 CLI 执行 sentry sourcemap upload

对本文的 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 配置

SDK、CLI、Agent Skill 与 MCP 如何分工#

这四者不是四种互相替代的接入方案,而是四个不同层次:

能力主要职责不负责什么
Sentry SDK在浏览器、Worker、服务端运行时采集异常、日志、trace 和 release 上下文不负责让 Agent 查询 Sentry,也不替代 source map 上传
Sentry CLI确定性地查询 Issue/Event/Release,验证 source map,执行构建上传和 API 操作不在应用运行时自动捕获异常
Sentry Agent Skill告诉 Coding Agent 何时、如何安全调用 CLI;复用现有 CLI 登录态Skill 本身没有 Sentry 账号权限,也不传输事件
Sentry MCP为人机协作式排障提供面向 Agent 的 Sentry 工具与语义化工作流不是通用管理 API,也不能替代 SDK、CLI 或人工审核
flowchart TD
  R["应用运行时"] -->|"SDK"| P["Sentry Project"]
  CI["CI / 构建"] -->|"CLI 或构建插件\nrelease + source map"| P
  A["Coding Agent"] --> SK["Sentry Skill"]
  SK -->|"调用已登录 CLI"| CLI["Sentry CLI"]
  CLI -->|"Issue · Event · Release · API"| P
  A -->|"交互式排障"| MCP["Sentry MCP"]
  MCP -->|"OAuth + 受限工具"| P

Agent Skill:让 Agent 正确使用 CLI#

当前 Sentry CLI 的 sentry cli setup 会为检测到的 Coding Agent 安装内置 Skill;不希望安装时可传 --no-agent-skills。也可以手工安装:

sentry cli setup
npx skills add https://cli.sentry.dev

Skill 会指导 Agent 优先使用专用命令,其次用 sentry schema 探索当前 API,只有没有专用命令时才使用 sentry api。它复用 sentry auth login 已建立的本地认证,因此无需把 token 复制给 Agent prompt、仓库或 shell 命令。Sentry CLI Agentic Usage

MCP:适合交互式定位,不适合作为运行时依赖#

Sentry 官方远程 MCP 地址是 https://mcp.sentry.dev/mcp。优先让支持 Remote MCP 的客户端连接该地址并完成 OAuth;这样上游 Sentry token 由服务端处理,客户端拿到的是 MCP access token。官方将它定位为 human-in-the-loop 的开发与调试中间层,而非覆盖全部 Sentry 功能的通用 MCP。

若客户端允许固定 MCP URL,可按最小权限暴露能力,例如连接 https://mcp.sentry.dev/mcp?skills=inspect,triage;自托管 Sentry 或不支持 Remote MCP 的客户端再考虑官方 @sentry/mcp-server stdio transport,并从客户端 secret store 注入 User Auth Token,绝不能把 token 写进 MCP 配置文件或命令行历史。MCP 返回的根因推断仍要由原始 event、源码和可复现测试验证。Sentry 官方 MCP Sentry MCP 服务

一套可重复的 CLI 排障流程#

先确认身份与目标,再看 Issue 的最新原始 Event;不要从 UI 的二次包装文案猜根因:

# 本地一次登录,之后复用 session。
sentry auth login
sentry auth status

# 明确指定项目,避免 monorepo 自动检测到错误 Project。
sentry issue list my-org/my-project \
  --query "is:unresolved environment:develop" \
  --period 24h \
  --limit 10 \
  --json \
  --fields shortId,title,lastSeen,level,status

# 最新 Event、异常链、tags 与 trace 一次取回。
sentry issue view PROJ-123 \
  --spans all \
  --json \
  --fields shortId,title,event,trace

# 查看同一 Issue 的多次 Event,判断是否只影响特定 release 或浏览器。
sentry event list PROJ-123 --period 24h --full

# 验证部署版本和 source map。
sentry release list my-org/my-project \
  --environment develop \
  --limit 10
sentry sourcemap resolve ./dist

sentry sourcemap resolve 是只读检查:它会说明每个 bundle 如何找到 map、是否包含 Sentry Debug ID,不会修改文件。若要手工上传,顺序通常是 sentry sourcemap inject ./distsentry sourcemap upload ./dist;生产流水线已经由官方 Vite plugin 完成注入与上传时,不要重复执行。

专用命令没有覆盖某个资源时,先让 CLI 告诉你当前 schema,而不是凭记忆拼 endpoint:

sentry schema --search release
sentry schema issues list
# 最后才使用 sentry api,并先确认方法和是否会修改状态。

CLI 的 --json --fields 很适合脚本和 Agent:输出字段稳定,也能减少把无关上下文送入模型。涉及 resolve、archive、delete、bulk update 等写操作时,必须先显示目标并取得人工确认。

本地复现还可使用 Spotlight,不消耗远端 Sentry 配额:

sentry local run -- pnpm dev

该命令为子进程注入 SENTRY_SPOTLIGHT 和常见前端前缀变量,并在本地接收 SDK envelope;它适合验证 SDK 是否初始化、异常是否保真和清洗器是否生效。最终仍需在 develop 环境制造一条可控错误,确认远端 Issue、environment、release、原始异常链与 source map 全部正确。

发布前清单#

  • 同一产品的浏览器、Worker、Convex 后端放在同一个 Sentry Organization;按运行时拆 Project,而不是按环境拆 Project。
  • 每个 Project 用自己的 DSN;同一 Project 的 develop/production 复用该 DSN,仅用 environment 区分。
  • 浏览器只持有 public DSN;SENTRY_AUTH_TOKEN 只放 CI secret store,不进入浏览器、Cloudflare runtime 或仓库。本地使用 CLI session。
  • 浏览器、Worker(以及有需要时 Convex 的发布记录)使用可追溯且一致的 release 约定;environment 使用稳定值。
  • 只对本应用 API 设置 tracePropagationTargets;跨域时 CORS 允许 sentry-tracebaggage
  • 在错误被 UI/transport 转换前捕获原始 Error,保留清洗后的 message、stack 和 cause;每类异常只有一个报告边界。
  • Logs 使用结构化字段,避免 token、cookie、原始 body、prompt 和过大的对象;先配置 beforeSend/beforeSendLog 再放量。
  • Project 侧启用 Allowed Domains、Inbound Filters、server-side data scrubbing、IP 隐私策略和 Spike Protection。
  • browser 与 Worker 的 source map 分别上传到正确 Project,部署产物不公开 .map,并在 production build 后验证一条还原后的栈。
  • 显式测试浏览器错误、Worker 错误、SSR 渲染兜底、TanStack Server Function、结构化日志和跨端 trace。
  • 用 CLI 核对原始 Event、environment、release 和 source map;MCP/Agent 结论必须回到原始事件与可复现测试验证。

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

本文共 10278 字,创建于 Aug 11, 2026

相关标签: DevOps, TypeScript, React, Observability, CLI, ByAI