> ## Documentation Index
> Fetch the complete documentation index at: https://test-8ad8522e-feat-ai-sre.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 源码映射与异常追踪

> 本文档详细介绍如何使用 RUM 进行源码映射管理，以及如何通过源码映射进行异常追踪和调试。

Flashduty 支持多平台的符号文件上传与源码映射，帮助开发者将混淆或压缩后的错误堆栈还原为可读的原始源代码。

* **Web（JavaScript）**：通过 [Flashduty CLI](https://github.com/flashcatcloud/flashcat-cli) 上传 `sourcemap` 文件
* **微信小程序**：通过 Flashduty CLI 上传 `miniprogram-ci get-dev-source-map` 生成的 `sourcemap.zip`
* **HarmonyOS**：通过 `@flashcatcloud/hvigor-plugin` 上传 ArkTS `sourceMaps.map`、可选 `nameCache.json` 和 Native `.so` 符号文件
* **Android**：通过 Gradle 插件自动上传 ProGuard/R8 mapping 文件和 NDK 符号文件
* **iOS**：通过 Flashduty CLI 上传 dSYM 符号文件
* **Flutter**：通过 Flashduty CLI 上传 `--split-debug-info` 生成的 Dart AOT 符号文件（`.symbols`），还原 Android 上混淆后的 Dart 异常堆栈；iOS 原生崩溃与原生 iOS 应用一样走 dSYM
* **Electron**：通过 Flashduty CLI 上传 Breakpad `.sym` 符号文件，按模块 Debug ID 匹配原生崩溃堆栈；详见 [Electron 错误还原](/zh/rum/sdk/electron/error-symbolication)
* **React Native**：Android 通过 SDK 自带的 Gradle 脚本在 release 构建时自动上传 JS bundle sourcemap；iOS 通过 [Flashduty CLI](https://github.com/flashcatcloud/flashcat-cli)（≥ 0.4.0）上传 `main.jsbundle` 与 `main.jsbundle.map`（`sourcemaps upload-react-native`）。详见下文 [上传 React Native SourceMap](#上传-react-native-sourcemap)

用户可在「应用管理」-「源码管理」菜单查看已上传的符号文件，并通过上传面板生成脚本在本地执行上传操作。

## 为什么需要源码映射？

在现代应用开发中，代码通常会被压缩、混淆或编译，以优化加载速度和性能。无论是 Web 端的 JavaScript 压缩、微信小程序的发布包转换、HarmonyOS 的 ArkTS 构建产物、Android 的 ProGuard/R8 混淆，还是 iOS 的编译优化，这些处理都会导致错误堆栈中的代码位置信息无法直接映射到原始源代码，增加了调试难度。

<CardGroup cols={3}>
  <Card title="映射压缩代码" icon="map">
    `SourceMap` 记录了压缩代码与原始代码之间的映射关系，允许开发者在调试时查看未压缩的源代码
  </Card>

  <Card title="精确定位错误" icon="crosshairs">
    通过 `SourceMap` 可以在异常追踪中直接定位到原始源代码中的具体位置
  </Card>

  <Card title="提升调试效率" icon="gauge-high">
    开发者无需手动解码压缩文件，节省排查问题的时间
  </Card>
</CardGroup>

## 生成 SourceMap

大多数现代构建工具（如 Webpack、Rollup 或 Vite）都支持生成 `SourceMap`。

<Tabs>
  <Tab title="Webpack">
    在 `webpack.config.js` 中启用 `SourceMap` 生成：

    ```javascript theme={null}
    module.exports = {
      mode: "production",
      devtool: "source-map", // 生成独立的 .map 文件
      output: {
        filename: "bundle.js",
        path: path.resolve(__dirname, "dist"),
      },
    };
    ```

    构建后，`dist` 目录中会生成 `bundle.js` 和对应的 `bundle.js.map` 文件。
  </Tab>

  <Tab title="Vite">
    在 `vite.config.js` 中配置：

    ```javascript theme={null}
    export default {
      build: {
        sourcemap: true, // 生成 sourcemap
      },
    };
    ```
  </Tab>

  <Tab title="Rollup">
    在 `rollup.config.js` 中配置：

    ```javascript theme={null}
    export default {
      output: {
        sourcemap: true,
      },
    };
    ```
  </Tab>
</Tabs>

## 上传 SourceMap

使用 Flashduty CLI 将 `sourcemap` 文件上传至 Flashduty 服务器。

<Steps>
  <Step title="安装 Flashduty CLI">
    确保已安装 Node.js，然后通过 npm 安装：

    ```bash theme={null}
    npm install -g @flashcatcloud/flashcat-cli
    ```
  </Step>

  <Step title="配置上传参数">
    <Frame>
      <img src="https://docs-cdn.flashcat.cloud/imges/png/69c1c53e5df18d5241d8e0fa17e56198.png" alt="上传源码配置" />
    </Frame>

    在「应用管理」-「源码管理」菜单中，点击「上传源码」面板，填写以下信息：

    <ParamField path="API Key" type="string" required>
      用于认证您的身份
    </ParamField>

    <ParamField path="服务名" type="string" required>
      应用的服务名（例如 `my-service`）
    </ParamField>

    <ParamField path="版本号" type="string" required>
      应用的发布版本（例如 `1.0.0`）
    </ParamField>

    <ParamField path="压缩文件前缀" type="string" required>
      压缩文件的路径前缀（例如 `/assets`）
    </ParamField>

    <ParamField path="自定义上传 Endpoint" type="string">
      仅私有化部署需要填写。私有化部署时面板会自动填入部署下发的上报地址，可手动覆盖（协议 + 域名，不带路径，例如 `https://rum.example.com`）；留空则默认上传到 Flashcat SaaS。
    </ParamField>
  </Step>

  <Step title="执行上传">
    在项目根目录下运行生成的脚本：

    ```bash theme={null}
    flashcat-cli sourcemaps upload \
      --service my-service \
      --release-version 1.0.0 \
      --minified-path-prefix /assets \
      --api-key your-api-key \
      ./dist
    ```

    <Note>
      私有化部署时，在命令前加上 `FLASHCAT_SOURCEMAP_INTAKE_URL` 环境变量即可将符号文件上传至自定义入口，例如 `FLASHCAT_SOURCEMAP_INTAKE_URL=https://rum.example.com flashcat-cli sourcemaps upload ...`（协议 + 域名，不带路径）；未设置时默认上传到 Flashcat SaaS。填写「自定义上传 Endpoint」后，面板生成的命令会自动带上该变量。
    </Note>
  </Step>
</Steps>

<Warning>
  * 确保 `minified-path-prefix` 与实际部署的压缩文件路径一致
  * 上传成功后，可在「应用管理」-「源码管理」中查看已上传的 `sourcemap` 文件
</Warning>

## 上传微信小程序 SourceMap

微信小程序发布后，线上错误堆栈通常只包含转换后的文件路径和行列号。你可以先使用 `miniprogram-ci get-dev-source-map` 生成 `sourcemap.zip`，再通过 Flashduty CLI 上传。上传后，Flashduty 会按服务名、版本和文件路径匹配小程序错误堆栈，并在异常详情中展示还原后的源码位置。

<Steps>
  <Step title="生成小程序 SourceMap 压缩包">
    在小程序项目中运行 `miniprogram-ci get-dev-source-map`，生成可上传的 `sourcemap.zip` 文件。该文件路径会作为上传命令的 `--sourcemap-zip` 参数。

    `miniprogram-ci get-dev-source-map` 默认输出的 zip 结构即满足以下要求，通常无需手动调整：

    * 主包的 `.js.map` 条目放在 `__FULL__/` 目录下，例如 `__FULL__/app-service.js.map`
    * 分包的 `.js.map` 条目放在以分包名命名的目录下，例如 `subpkg-a/chunk_0.appservice.js.map`，目录名会作为分包标识写入元数据
    * 只有 `.js.map` 后缀的条目会被采纳，其他文件（README、source 文件等）会被忽略
    * 若 zip 中找不到任何 `.js.map` 条目，上传会返回错误 `No .js.map entries found in the sourcemap archive`

    <Note>
      单个 `.js.map` 解压后不超过 50 MB；整个 zip 解压后聚合不超过 500 MB；zip 内总条目数不超过 5000。超出限制时上传会返回 HTTP 413。
    </Note>
  </Step>

  <Step title="配置上传参数">
    在「应用管理」-「源码管理」菜单切换到「微信小程序」标签页，点击「上传源码」。上传面板会根据表单内容生成命令。

    <ParamField path="API Key" type="string" required>
      用于认证上传请求。页面会优先展示当前账户可访问的 API Key。
    </ParamField>

    <ParamField path="服务名" type="string" required>
      小程序应用的服务名，例如 `my-mp`。异常解析时会使用该值与错误事件中的 `service` 匹配。
    </ParamField>

    <ParamField path="发布版本" type="string" required>
      小程序发布版本，例如 `1.2.3`。异常解析时会使用该值与错误事件中的版本匹配。
    </ParamField>

    <ParamField path="SourceMap ZIP 文件路径" type="string" required>
      `miniprogram-ci get-dev-source-map` 输出的压缩包路径，例如 `./sourcemap.zip`。
    </ParamField>

    <ParamField path="AppID" type="string">
      微信小程序 appid，例如 `wxbad3e0a65782821c`。多小程序场景下用于区分不同小程序的同名 service + version 上传记录；只有一个小程序时可省略。
    </ParamField>

    <ParamField path="自定义上传 Endpoint" type="string">
      仅私有化部署显示。私有化部署时面板会自动填入部署下发的上报地址，可手动覆盖（协议 + 域名，不带路径，例如 `https://rum.example.com`）；留空则默认上传到 Flashcat SaaS。填写后，生成的上传命令会在开头带上 `FLASHCAT_SOURCEMAP_INTAKE_URL=<endpoint>` 环境变量。
    </ParamField>
  </Step>

  <Step title="执行上传">
    在项目根目录下运行生成的命令；`--appid` 为可选参数，未填写时上传命令中不会出现这一行：

    ```bash theme={null}
    FLASHCAT_API_KEY=your-api-key flashcat-cli sourcemaps upload-miniprogram \
      --service my-mp \
      --release-version 1.2.3 \
      --sourcemap-zip ./sourcemap.zip \
      --appid wxbad3e0a65782821c
    ```
  </Step>
</Steps>

## 上传 HarmonyOS 符号文件

HarmonyOS 崩溃栈可能同时包含 **ArkTS / JS 帧** 和 **Native `.so` 帧**。要在控制台中同时还原这两类堆栈，请上传以下构建产物：

* `sourceMaps.map`：ArkTS sourcemap 主文件
* `nameCache.json`：可选，用于还原混淆后的标识符名称
* 未 strip 的 Native `.so`：用于符号化 C/C++ 崩溃栈

控制台的 **应用管理 → 源码管理 → HarmonyOS** 上传面板会要求你填写 **Service**、**Version** 和 **API Key**，并生成对应的 `hvigor` 配置和上传命令。`service` 与 `version` 必须和应用实际上报的值保持一致，否则服务端无法匹配到对应符号文件。

<Steps>
  <Step title="安装 hvigor 插件">
    在工程的 `hvigor/hvigor-config.json5` 中声明依赖，由 hvigor 自行从 npm 拉取：

    ```json5 hvigor/hvigor-config.json5 theme={null}
    {
      "modelVersion": "5.0.0",
      "dependencies": {
        "@flashcatcloud/hvigor-plugin": "0.1.5"
      }
    }
    ```

    也可以用 `npm install -D @flashcatcloud/hvigor-plugin` 安装，但鸿蒙工程根目录默认没有 `package.json`，需先执行 `npm init -y`，否则会装到父目录的无关工程里。详见 [HarmonyOS SDK 高级配置](/zh/rum/sdk/harmony/advanced-config)。
  </Step>

  <Step title="在 hvigorfile.ts 中注册上传插件">
    把控制台面板生成的 `service`、`version` 与 `apiKey` 配置写入 `hvigorfile.ts`：

    ```ts hvigorfile.ts theme={null}
    import { hapTasks } from '@ohos/hvigor-ohos-plugin';
    import { flashcatSymbolUploadPlugin } from '@flashcatcloud/hvigor-plugin';

    export default {
      system: hapTasks,
      plugins: [
        flashcatSymbolUploadPlugin({
          apiKey: process.env.FLASHCAT_API_KEY ?? '',
          service: 'my-app',
          version: '1.0.0'
        })
      ]
    };
    ```
  </Step>

  <Step title="构建后执行上传">
    在构建产物生成后执行上传任务。**用环境变量配置时必须加 `--no-daemon`**，否则 hvigor 复用的守护进程会拿到陈旧的环境变量，导致 API Key 读不到、上传被静默跳过：

    ```bash theme={null}
    FLASHCAT_API_KEY=your-api-key \
      hvigorw uploadFlashcatSymbols --no-daemon \
      --mode module -p module=entry@default -p product=default
    ```
  </Step>
</Steps>

<Warning>
  * 请使用 **0.1.5 及以上**版本：0.1.3 注册任务时会声明不存在的 `assembleHar` 依赖，导致构建直接失败；0.1.4 已从 npm 撤回，装不到
  * 公有云省略 `endpoint` 时，**hvigor-plugin ≥ 0.1.3** 默认上传到 `https://ci.flashcat.cloud`。不要用 RUM 上报域名 `browser.flashcat.cloud`（会 404）。私有化设置 `FLASHCAT_SOURCEMAP_INTAKE_URL`（需 ≥ 0.1.3）或显式传 `endpoint`
  * Native `.so` 需要保留 GNU build-id；HarmonyOS NDK 默认开启，如你的构建链路关闭了它，请显式添加 `-Wl,--build-id`
  * 更完整的 HarmonyOS 接入、符号上传和兼容性说明，请继续阅读 [HarmonyOS SDK 高级配置](/zh/rum/sdk/harmony/advanced-config)
</Warning>

## 上传 Android 符号文件

Android 应用使用 ProGuard/R8 进行代码混淆后，错误堆栈中的类名和方法名会被替换为无意义的短名称。通过上传 mapping 文件，Flashduty 可以将混淆后的堆栈还原为原始代码。

对于包含 NDK 原生代码的应用，还需要上传 NDK 符号文件以还原 C/C++ 层的堆栈。

<Steps>
  <Step title="添加 Gradle 插件">
    在应用模块的 `build.gradle` 中添加 Flashcat Android Gradle 插件：

    ```groovy theme={null}
    plugins {
        id("cloud.flashcat.android-gradle-plugin") version "1.2.0"
    }
    ```

    Kotlin DSL 使用相同的插件 ID：

    ```kotlin theme={null}
    plugins {
        id("cloud.flashcat.android-gradle-plugin") version "1.2.0"
    }
    ```
  </Step>

  <Step title="配置 API Key">
    通过 Gradle 属性、环境变量或项目配置文件提供 API Key。发布流水线中通常使用环境变量：

    ```bash theme={null}
    # 二选一
    export FC_API_KEY=your-api-key
    export FLASHCAT_API_KEY=your-api-key

    # 可选：私有化部署时指定自定义上传入口
    export FLASHCAT_SOURCEMAP_INTAKE_URL=https://rum.example.com
    ```

    也可以在项目根目录创建 `flashcat-ci.json`：

    ```json theme={null}
    {
      "apiKey": "your-api-key",
      "flashcatSite": "ci.flashcat.cloud",
      "sourcemapEndpoint": "https://rum.example.com"
    }
    ```

    <Tip>
      API Key 可在控制台的「API Key 管理」页面创建和管理。
    </Tip>
  </Step>

  <Step title="配置上传参数">
    在应用模块的 `build.gradle` 中添加 `flashcat` 配置块。未显式配置时，插件会从 Android 构建配置读取 `versionName`、`versionCode` 和应用包名。

    ```groovy theme={null}
    flashcat {
        versionName = "1.3.0"              // 可选，默认读取 Android 配置中的 versionName
        serviceName = "my-service"         // 可选，默认使用应用包名
        site = "CN"                        // 可选，可选值：CN、STAGING，默认 CN
        sourcemapEndpoint = "https://rum.example.com" // 可选，私有化自定义上传入口；未带 /sourcemap/upload 时会自动补全
        checkProjectDependencies = "none"  // 可选：none、warn、fail；默认不检查
        mappingFilePath = "path/to/mapping.txt" // 可选，自定义 mapping 文件路径
        nonDefaultObfuscation = false      // 可选，使用 DexGuard 等非默认混淆工具时设为 true
        ignoreFlashcatCiFileConfig = false // 可选，是否忽略 flashcat-ci.json
        additionalSymbolFilesLocations = ["/path/to/location/obj"] // 可选，额外 NDK 符号目录
    }
    ```

    <Note>
      默认情况下插件会根据 `site` 选择上传入口：`CN` 对应 `ci.flashcat.cloud`，`STAGING` 对应 `ci-dev.flashcat.cloud`。私有化部署可通过 `sourcemapEndpoint` 指定自定义上传入口（自 `1.2.0` 起支持）；该值未带 `/sourcemap/upload` 路径时会自动补全。三种配置方式的优先级为：`flashcat {}` 扩展配置 > `flashcat-ci.json` > `FLASHCAT_SOURCEMAP_INTAKE_URL` 环境变量。配置自定义入口后，`site` 仅用于回退，不再影响实际上传地址。
    </Note>
  </Step>

  <Step title="运行上传任务">
    在构建完成后，执行 Gradle 任务上传符号文件：

    ```bash theme={null}
    # 上传 ProGuard/R8 mapping 文件
    ./gradlew uploadMappingRelease

    # 如果项目包含 NDK 原生代码，上传 NDK 符号文件
    ./gradlew uploadNdkSymbolFilesRelease
    ```

    <Note>
      插件会为启用混淆的 variant 创建 `uploadMapping<Variant>` 任务。NDK 符号上传任务会在项目存在 native build，或配置了 `additionalSymbolFilesLocations` 时创建。若项目使用多个 flavor，请为每个发布 variant 分别执行对应任务。
    </Note>

    <Warning>
      **NDK 符号文件必须是未剥离（unstripped）的 ELF 文件，且包含 GNU build-id 节。**

      上传服务器会读取每个 `.so` 文件的 `.note.gnu.build-id` ELF 节以提取 build-id，并以此作为符号化寻址的唯一键。若文件缺少该节，上传立即返回 HTTP 400，错误信息为：

      ```
      libfoo.so: .note.gnu.build-id section not found. Build the .so with -Wl,--build-id and upload the unstripped file
      ```

      **常见原因及修复：**

      * **CMake Release 构建默认剥离符号**：CMake 在 `CMAKE_BUILD_TYPE=Release` 时会在链接后执行 `strip`，移除 `.note.gnu.build-id` 节。需在 `CMakeLists.txt` 中显式添加链接标志：

        ```cmake theme={null}
        target_link_options(mylib PRIVATE -Wl,--build-id)
        ```

        或对整个项目生效：

        ```cmake theme={null}
        add_link_options(-Wl,--build-id)
        ```

      * **手动 strip 脚本**：如果 CI 流水线在上传前对 `.so` 运行了 `strip` 命令，需确保上传的是 `obj/` 目录下的原始未剥离文件，而不是 `libs/` 目录下已剥离的发布产物。`additionalSymbolFilesLocations` 参数用于指定此路径。

      NDK 原生符号文件在 API 中以 `event.type=ndk_symbol_file` 标识，区别于 ProGuard mapping 文件的 `event.type=jvm_mapping_file`。自定义上传集成（不使用 Gradle 插件）时需传入正确的类型字段。
    </Warning>
  </Step>
</Steps>

## 上传 iOS dSYM 文件

iOS 应用在编译时会生成 dSYM（Debug Symbol）文件，其中包含将内存地址映射回源代码位置所需的调试符号信息。上传 dSYM 文件后，Flashduty 可以将崩溃堆栈中的地址还原为可读的函数名、文件名和行号。

<Steps>
  <Step title="安装 Flashduty CLI">
    确保已安装 Node.js，然后通过 npm 安装：

    ```bash theme={null}
    npm install -g @flashcatcloud/flashcat-cli
    ```
  </Step>

  <Step title="获取 dSYM 文件">
    dSYM 文件可从以下位置获取：

    * **Xcode 本地构建**：在 Xcode 的 Build Products 目录中找到 `.dSYM` 文件
    * **Xcode Archive**：通过 Organizer 窗口导出 dSYMs
    * **App Store Connect**：从 App Store Connect 下载 dSYMs（需要在构建设置中启用符号上传）
  </Step>

  <Step title="执行上传">
    使用 Flashduty CLI 上传 dSYM 文件：

    ```bash theme={null}
    FLASHCAT_API_KEY=your-api-key flashcat-cli dsyms upload ./app.dSYM

    # 可选：私有化部署时指定自定义上传入口（协议 + 域名，不带路径）；未设置时默认上传到 Flashcat SaaS
    FLASHCAT_API_KEY=your-api-key FLASHCAT_SOURCEMAP_INTAKE_URL=https://rum.example.com flashcat-cli dsyms upload ./app.dSYM
    ```

    <Tip>
      您也可以在控制台的「源码管理」面板中，填写参数后自动生成上传命令。
    </Tip>
  </Step>
</Steps>

## 上传 Flutter 符号文件

Flutter 应用使用 `--obfuscate` 混淆构建后，Dart 异常堆栈中的符号会被剥离，只剩地址信息。通过上传 `--split-debug-info` 生成的 Dart AOT 符号文件（`app.<platform>-<arch>.symbols`），Flashduty 会读取符号文件的 ELF GNU build-id，与堆栈中携带的 build ID 匹配，还原 Android 上的 Dart 异常堆栈。Flutter 符号文件在上传 API 中以 `event.type=flutter_symbol_file` 标识。

<Steps>
  <Step title="混淆构建生成符号目录">
    Android 构建时开启混淆并指定符号输出目录：

    ```bash theme={null}
    flutter build apk --obfuscate --split-debug-info=./debug-symbols
    ```
  </Step>

  <Step title="配置上传参数">
    在「应用管理」-「源码管理」菜单切换到「Flutter」标签页，点击「上传源码」。上传面板会根据表单内容生成命令。

    <ParamField path="API Key" type="string" required>
      用于认证上传请求，对应命令中的 `FLASHCAT_API_KEY` 环境变量。
    </ParamField>

    <ParamField path="split-debug-info 目录" type="string" required>
      `--split-debug-info` 指定的符号目录，例如 `./debug-symbols`。
    </ParamField>

    <ParamField path="服务名" type="string" required>
      应用的服务名，例如 `my-app`。建议与 SDK 初始化时设置的 `service` 保持一致，便于在控制台按服务归类筛选。
    </ParamField>

    <ParamField path="发布版本" type="string" required>
      应用的发布版本，例如 `1.0.0`。建议与 SDK 初始化时设置的 `releaseVersion` 保持一致。
    </ParamField>

    <ParamField path="自定义上传 Endpoint" type="string">
      仅私有化部署显示。私有化部署时面板会自动填入部署下发的上报地址，可手动覆盖（协议 + 域名，不带路径，例如 `https://rum.example.com`）；留空则默认上传到 Flashcat SaaS。填写后，生成的上传命令会在开头带上 `FLASHCAT_SOURCEMAP_INTAKE_URL=<endpoint>` 环境变量。
    </ParamField>
  </Step>

  <Step title="执行上传">
    在项目根目录下运行生成的命令：

    ```bash theme={null}
    FLASHCAT_API_KEY=your-api-key flashcat-cli flutter-symbols upload ./debug-symbols \
      --service my-app \
      --release-version 1.0.0
    ```
  </Step>
</Steps>

<Warning>
  **iOS 的 Dart 堆栈暂不支持符号化。** Flutter 为 Apple 平台生成的符号文件是 Mach-O 格式，平台当前只能解析 Android 侧的 ELF 格式，因此 iOS 的 `.symbols` 上传会被拒绝。iOS 原生崩溃（Objective-C / Swift / C / C++）不受影响，与原生 iOS 应用一样上传 dSYM 即可符号化。

  如果你的应用同时发布 iOS 和 Android，`--obfuscate` 仍可开启：Android 的 Dart 堆栈会正常还原，iOS 的 Dart 堆栈则保持混淆状态。若 iOS 的可读堆栈更重要，则该端构建时不要开启 `--obfuscate`。
</Warning>

<Note>
  符号文件与崩溃事件通过 build ID 匹配，`service` 与 `release-version` 不参与解析，仅影响控制台列表的归类与筛选。符号文件支持 `arm`、`arm64`、`x64` 架构，由 `.symbols` 文件名自动识别。每次改动 Dart 代码都会生成新的 build ID，因此符号上传必须纳入每一次发布构建。更完整的 Flutter 符号化说明请参阅 [Flutter SDK 高级配置](/zh/rum/sdk/flutter/advanced-config)。
</Note>

## 上传 React Native SourceMap

React Native 的 JS 错误堆栈通过 sourcemap 还原。与 Flutter 类似，React Native 应用在同一份 JS 代码之上还有 Android/iOS 两端原生层：两端 sourcemap 按平台分开存储，同名的 bundle 不会互相覆盖——服务端按 `react-native` 类型 + 平台（iOS / Android）来匹配，因此 iOS 与 Android 可以使用相同的 bundle 文件名。

在「应用管理」-「源码管理」中选择「React Native」标签页，上传面板会按所选平台生成对应的脚本：

| 字段             | 说明                                                                                             |
| -------------- | ---------------------------------------------------------------------------------------------- |
| 平台             | `Android` 或 `iOS`                                                                              |
| API Key        | 上传凭证，对应环境变量 `FLASHCAT_API_KEY`                                                                 |
| 服务名            | 需与 SDK 初始化时设置的 `serviceName` 完全一致；不设置时服务端默认取 `applicationId`，与 SDK 上报的 service 对不上会存错归类        |
| 发布版本           | 仅 iOS：对应 `CFBundleShortVersionString`（如 `1.0.0`），是匹配 key 的一部分，必填；Android 从构建的 `versionName` 读取 |
| 构建号            | 仅 iOS，可留空：对应 `CFBundleVersion`（如 `42`），映射为 `--build-version`                                   |
| 自定义上传 Endpoint | 仅私有化部署：自定义上报地址，对应环境变量 `FLASHCAT_SOURCEMAP_INTAKE_URL`，留空表示 Flashcat SaaS                       |

### Android：构建时自动上传

Android 由 SDK 自带的 Gradle 脚本在 release 构建中完成上传（脚本会调用 flashcat-cli 上传 Metro 刚产出的 bundle）。在 `android/app/build.gradle` 固定服务名并应用脚本：

```groovy theme={null}
// android/app/build.gradle
// 必须与 SDK 初始化时传入的 serviceName 一致；未设置时默认取 applicationId
project.ext.flashcat = [serviceName: "com.example.shopping"]

apply from: "../../node_modules/@flashcatcloud/mobile-react-native/flashcat-sourcemaps.gradle"
```

然后带凭证执行 release 构建即可，上传发生在构建过程中：

```bash theme={null}
FLASHCAT_API_KEY=your-api-key ./gradlew assembleRelease
```

<Note>
  上传对象是 `index.android.bundle` 及对应 sourcemap。SDK 的 Metro 配置（Debug ID）也是 `withDatadogMetroConfig` 包装的，详见 [React Native SDK 接入](/zh/rum/sdk/react-native/sdk-integration)。
</Note>

### iOS：构建后手动上传

iOS 没有构建钩子，需要在 release 构建后手动上传。先让 `react-native-xcode.sh` 产出 sourcemap——它只有在 `SOURCEMAP_FILE` 已设置时才会生成，因此要在 Xcode 构建阶段「Bundle React Native code and images」中、`react-native-xcode.sh` 之前导出：

```bash theme={null}
# Xcode > Build Phases > "Bundle React Native code and images"
# 放在 react-native-xcode.sh 之前
export SOURCEMAP_FILE="$DERIVED_FILE_DIR/main.jsbundle.map"
```

再安装 CLI（≥ 0.4.0）并上传 bundle 与 sourcemap：

```bash theme={null}
npm i -g @flashcatcloud/flashcat-cli

FLASHCAT_API_KEY=your-api-key npx @flashcatcloud/flashcat-cli sourcemaps upload-react-native \
  --platform ios --service com.example.shopping \
  --release-version 1.0.0 --build-version 42 \
  --bundle main.jsbundle --sourcemap main.jsbundle.map
```

<Warning>
  * 上传的 bundle 文件名必须与应用运行时加载的名字一致（Android 为 `index.android.bundle`，iOS 为 `main.jsbundle`）。符号化按文件名匹配堆栈帧，CI 中用 `--bundle-output` 重命名过的 bundle 永远不会被匹配到。
  * `--release-version` 必须与上报的版本一致（SDK 的 `config.version` 覆盖或默认值），否则匹配不到。
  * 服务名如果写错，上传文件会归入错误的 service，问题列表与符号表两边都对不上。
  * 更完整的 React Native 接入说明（含 `@flashcatcloud/mobile-react-native` 包版本要求与私有化部署）请参阅 [React Native SDK 高级配置](/zh/rum/sdk/react-native/advanced-config)。
</Warning>

## 符号文件管理

在 Flashduty 平台上，符号文件的管理通过「应用管理」-「源码管理」菜单完成：

| 功能         | 说明                                                                                                                                                                                                                                                                                                                                                |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 查看已上传的符号文件 | 列出所有已上传的文件（SourceMap、小程序 SourceMap、ProGuard mapping 文件、dSYM、NDK 原生符号文件、Flutter 符号文件、Electron Breakpad 符号文件、React Native bundle sourcemap），包括路径、服务名、版本号、大小和上传时间。Android 标签页同时展示 ProGuard mapping 文件和 NDK 原生符号文件，NDK 行会显示 build\_id、arch 和 lib\_name 列；Electron 标签页每个原生模块一行，展示模块（lib\_name）、Debug ID（build\_id）、架构（arch）、服务、版本和大小列，并支持按 Debug ID 搜索 |
| 按平台筛选      | 在 Web、iOS、Android、微信小程序、HarmonyOS、Flutter、Electron 和 React Native 标签页之间切换，查看不同平台的符号文件                                                                                                                                                                                                                                                             |
| 版本管理       | 通过 `service` 和 `release-version` 参数为不同版本的应用分别管理                                                                                                                                                                                                                                                                                                   |
| 小程序维度      | 微信小程序列表会展示符号文件元数据中的 AppID（取自 `metadata.appid`）和分包（取自 `metadata.subpackage`）两列；主包没有分包标识时显示「主包」，AppID 未上传时显示 `-`                                                                                                                                                                                                                                    |
| 权限控制       | 通过 `API Key` 确保只有授权用户可以上传或管理                                                                                                                                                                                                                                                                                                                      |

## 在异常追踪中查看源码

RUM 异常追踪支持结合 Web `sourcemap`、微信小程序 SourceMap、Android mapping 文件和 iOS dSYM 还原错误堆栈，在异常详情中查看原始源码位置，精确定位问题。

<Steps>
  <Step title="捕获错误">
    RUM SDK 会自动捕获应用错误，并将错误堆栈信息发送至服务器。Web 场景通常包括 JavaScript 异常、Promise 拒绝和网络错误；Native 场景则包括崩溃、异常和符号化所需的堆栈信息。

    ```javascript theme={null}
    throw new Error("Something went wrong");
    ```
  </Step>

  <Step title="关联符号文件">
    当错误堆栈中的文件路径、行号或地址信息与已上传的 Web `sourcemap`、微信小程序 SourceMap、Android ProGuard mapping 文件、Android NDK 原生符号文件或 iOS dSYM 文件匹配时，Flashduty 会自动将压缩、混淆或编译后的错误位置映射到原始源代码。

    **压缩文件堆栈：**

    ```
    Error: Something went wrong
        at Object.<anonymous> (/assets/index-5e0391ac.js:1:123)
    ```

    **映射后的源代码：**

    ```
    Error: Something went wrong
        at App.render (src/components/App.js:45:10)
    ```
  </Step>

  <Step title="查看异常详情">
    在异常追踪模块中，点击具体的错误记录，可以查看：

    * **错误消息**：如 `Something went wrong`
    * **原始堆栈**：映射后的源代码文件路径、行号和列号
    * **上下文代码**：显示错误位置附近的源代码片段

    如果小程序错误堆栈尚未解析，异常详情中的提示会跳转到「源码管理」的小程序类型，并可直接打开上传面板补充对应版本的 `sourcemap.zip`。
  </Step>

  <Step title="调试与修复">
    根据映射后的源代码位置，直接在本地开发环境中找到对应代码，分析问题根因并修复。
  </Step>
</Steps>

## 最佳实践

<AccordionGroup>
  <Accordion title="规范化 SourceMap 上传">
    在 CI/CD 流水线中集成上传命令，确保每次发布时自动上传 Web `sourcemap` 或微信小程序 `sourcemap.zip`。

    **GitHub Actions 示例：**

    ```yaml theme={null}
    - name: Upload SourceMaps
      run: |
        flashcat-cli sourcemaps upload \
          --service my-service \
          --release-version ${{ github.sha }} \
          --minified-path-prefix /assets \
          --api-key ${{ secrets.FLASHCAT_API_KEY }} \
          ./dist
    ```
  </Accordion>

  <Accordion title="版本管理">
    使用 `--release-version` 参数与应用版本号保持一致，便于追踪特定版本的 `sourcemap`。
  </Accordion>

  <Accordion title="清理源码">
    在资源上传 CDN 之前删除 `sourcemap` 文件，避免将源码信息带入生产环境。
  </Accordion>

  <Accordion title="测试映射效果">
    上传 `sourcemap` 后，主动抛出测试错误，验证异常追踪模块是否能正确映射到源代码。
  </Accordion>
</AccordionGroup>

## 常见问题

<AccordionGroup>
  <Accordion title="为什么异常堆栈没有映射到源代码？">
    * 确认 `sourcemap` 是否成功上传，且 `minified-path-prefix` 与实际部署路径一致
    * 检查 `service` 和 `release-version` 是否与错误发生时的应用版本匹配
    * 如果是微信小程序，确认已上传对应版本的 `sourcemap.zip`
  </Accordion>

  <Accordion title="如何避免 SourceMap 泄露敏感信息？">
    * 确保 `sourcemap` 文件仅上传至 Flashduty 服务器，不直接暴露在公网
    * 在生产环境中，移除对 `sourcemap` 文件的直接访问（如通过 Nginx 配置）
  </Accordion>

  <Accordion title="上传 SourceMap 失败怎么办？">
    * 检查 `API Key` 是否有效
    * 确保网络连接正常，CLI 版本是最新的
  </Accordion>

  <Accordion title="小程序错误堆栈为空，但 message 中带有堆栈，能否参与 SourceMap 还原？">
    可以。对于早期版本 SDK 把堆栈塞进 message 字段的小程序错误，Flashduty 会自动从 message 中识别堆栈并参与 SourceMap 还原与异常分组。升级到新版 SDK 后，已存在的历史错误也会按这一规则重新归类，可能与升级前的分组不完全一致。
  </Accordion>
</AccordionGroup>

## 下一步

<CardGroup cols={2}>
  <Card title="异常聚合" icon="layer-group" href="./error-aggregation">
    了解异常聚合机制
  </Card>

  <Card title="Issue 状态" icon="circle-check" href="./issue-status">
    管理 Issue 状态流转
  </Card>
</CardGroup>
