.NET MAUI 崩溃报告与遥测完全指南 (2026):App Center 停服后迁移到 Sentry、Crashlytics 与 App Insights 实战

App Center 已于 2025 停服。这份 2026 迁移指南带你把 .NET MAUI 崩溃报告切到 Sentry、Firebase Crashlytics 或 Azure Application Insights,含 SDK 接入、CI/CD 符号上传与 GDPR 合规实战。

MAUI 崩溃报告迁移指南:Sentry (2026)

更新于:2026 年 8 月 24 日

如果你的 .NET MAUI 项目还在用 Visual Studio App Center 收集崩溃,答案很直接:你的数据管线已经死了 17 个月了。App Center 于 2025 年 3 月 31 日正式退役,Diagnostics(崩溃、错误、事件)与 Analytics API 已停止接收数据。2026 年在 .NET MAUI 里可靠的组合是 Sentry(跨平台一站式)、Firebase Crashlytics(Google 生态、免费无量级限制)与 Azure Application Insights(企业侧 APM)。这篇文章按 DevOps 的顺序把三条迁移路径讲清楚:SDK 接入、符号上传、CI/CD 自动化、合规。说实话,多数团队会跳过符号上传那步,然后线上堆栈全是 0x00007ff8a1c2。我在上一个项目里就掉进过这个坑,凌晨两点盯着一堆地址想哭。

  • App Center Diagnostics 于 2025-03-31 停止服务,2026 年继续使用相当于没有崩溃数据。
  • Sentry 是 MAUI 上最省事的选择:一个 NuGet 包同时覆盖 iOS、Android、Windows、Mac Catalyst,并原生支持 .NET NativeAOT 堆栈。
  • Firebase Crashlytics 免费且无事件量上限,但需要通过 xamarin.firebase.ios.crashlytics 与 Android Gradle 插件分平台接入,符号上传要跑两次。
  • Azure Application Insights 适合已经用 Azure Monitor 的团队,用 OpenTelemetry .NET SDK 直连,不再依赖 App Center 中间层。
  • 符号上传(iOS dSYMs、Android ProGuard mapping、.NET PDB)必须写进 CI/CD,否则线上堆栈无法反混淆。
  • GDPR/CCPA 要求崩溃 SDK 在获得用户同意前处于关闭态;三家 SDK 都提供延迟初始化 API。

App Center 停服后怎么办?

先把事实摆平。Microsoft 在 App Center 官方退役公告里明确列出:Build、Test、Distribute、Diagnostics、Analytics 五大服务于 2025-03-31 全部下线,数据导出窗口在 2025-06-30 关闭。也就是说,2026 年任何仍在 Microsoft.AppCenter.Crashes 上跑的 MAUI 项目,日志已经飘到虚空里了。SDK 不会报错,只是发不出去。

迁移的第一步不是选新 SDK,而是清点当前依赖。用下面的命令扫一遍解决方案。

dotnet list package --include-transitive | grep -i "AppCenter"
grep -rn "AppCenter\." --include="*.cs" --include="*.xaml.cs" .

把所有 Analytics.TrackEventCrashes.TrackError 调用位置抓出来,做成一张表。这张表就是你后面替换 SDK 时的"改造清单",也是 QA 回归测试的检查点。多数团队跳过这一步,然后新 SDK 上线后才发现漏掉了 30% 的埋点。App Center 的 Distribute 部分(in-app 更新)也没了,推荐用 Firebase App Distribution 或 TestFlight 顶上,不过那是另一篇文章的事,本文只谈崩溃与遥测。

Sentry、Crashlytics 与 Application Insights 哪个更适合 MAUI?

选型是这三个平台里差异最大的一步。下面这张表覆盖了大多数团队实际关心的维度。

维度SentryFirebase CrashlyticsApplication Insights
官方 MAUI 支持是(Sentry.Maui否(走 Xamarin.Firebase 绑定)否(走 OpenTelemetry .NET)
免费额度5,000 error/月无限5 GB/月
付费起价(2026)$26/月 team免费按 GB 计费 $2.30/GB
Session Replay是(含移动端)
ANR/App Not RespondingAndroid + iOS仅 Android
符号上传自动化sentry-cliFastlane 或 Gradle 插件手动 or Azure DevOps 任务
数据驻留区域US/EU/自托管US(可选多区域)18 个 Azure 区域
NativeAOT 兼容是(8.0+)不受影响(原生层)是(OTel 支持)

一句话选型:没有硬性生态要求就选 Sentry,一个包搞定四个平台,Session Replay 对移动 UX 团队是杀手锏;已经深度绑 Google Cloud 或需要免费无限量选 Crashlytics;已经在 Azure Monitor 里跑后端遥测选 Application Insights,前后端能拼在同一张 trace 里。

如何在 .NET MAUI 中集成 Sentry?

Sentry 是三家里唯一提供一等 MAUI 支持的。安装两个包。

dotnet add package Sentry.Maui --version 5.5.1
dotnet add package Sentry.Profiling --version 5.5.1  # 可选:CPU profiling

MauiProgram.cs 里挂上 UseSentry,注意 DSN 一定要走环境变量或 appsettings.json,别硬编码进源码。这是 App Center 时代最常被扫到的 secret 泄漏点。

using Sentry.Maui;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .UseSentry(options =>
            {
                options.Dsn = Environment.GetEnvironmentVariable("SENTRY_DSN")
                              ?? throw new InvalidOperationException("SENTRY_DSN missing");
                options.Debug = false;
                options.TracesSampleRate = 0.2;      // 20% 事务采样
                options.ProfilesSampleRate = 0.1;    // 需 Sentry.Profiling
                options.IncludeTextInBreadcrumbs = false;   // 隐私:不采集输入框内容
                options.CacheDirectoryPath = FileSystem.CacheDirectory;
                options.AutoSessionTracking = true;
                options.AttachScreenshot = true;
                options.Release = $"com.example.app@{AppInfo.Current.VersionString}+{AppInfo.Current.BuildString}";
            })
            .ConfigureFonts(fonts => fonts.AddFont("OpenSans-Regular.ttf", "OpenSans"));

        return builder.Build();
    }
}

Release 字段必须匹配符号文件的 bundle_id@version+build,否则 Sentry 服务端找不到对应 dSYM,堆栈会显示为地址。这一点参考 Sentry .NET MAUI 官方文档,里面的 release 命名规则和 Xcode Archive 输出一致,不要自造格式。

手动抛出异常与面包屑

把关键业务分支做成面包屑,崩溃时能看到用户到底点了什么。

SentrySdk.AddBreadcrumb("User tapped Checkout", category: "ui.action");
try
{
    await _paymentService.ChargeAsync(orderId);
}
catch (PaymentDeclinedException ex)
{
    SentrySdk.CaptureException(ex, scope => scope.SetTag("order_id", orderId));
    throw;
}

Firebase Crashlytics 在 MAUI 双平台接入

Crashlytics 没有官方 MAUI 包,需要走原生绑定。2026 年社区维护的两个 NuGet 是 Plugin.Firebase.Crashlytics(推荐,跨平台外观)和分别的 Xamarin.Firebase.iOS.Crashlytics / Xamarin.Firebase.Crashlytics(Android)。

dotnet add package Plugin.Firebase.Crashlytics --version 3.1.4
dotnet add package Plugin.Firebase --version 3.1.4

GoogleService-Info.plist 放进 Platforms/iOSgoogle-services.json 放进 Platforms/Android,并在 .csproj 里声明 BundleResource 与 GoogleServicesJson。iOS 端在 AppDelegate.cs 里调用。

using Firebase.Core;

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    App.Configure();   // Firebase Core
    CrossFirebaseCrashlytics.Current.SetCrashlyticsCollectionEnabled(true);
    return base.FinishedLaunching(app, options);
}

Android 端在 MainApplication.cs

public override void OnCreate()
{
    base.OnCreate();
    FirebaseApp.InitializeApp(this);
    FirebaseCrashlytics.Instance.SetCrashlyticsCollectionEnabled(true);
    AppDomain.CurrentDomain.UnhandledException += (s, e) =>
        CrossFirebaseCrashlytics.Current.RecordException(e.ExceptionObject as Exception);
}

对比 Sentry,Crashlytics 的痛点在符号上传要跑两遍:iOS 需要 Fastlane upload_symbols_to_crashlytics 上传 dSYMs,Android 需要 Gradle 插件 com.google.firebase.crashlytics 自动上传 ProGuard mapping。Windows 与 Mac Catalyst 完全不支持,这三家里 Crashlytics 是唯一的双平台方案。参考 Firebase Crashlytics 官方入门验证符号上传状态。

Azure Application Insights + OpenTelemetry

App Center Analytics 的老团队最自然的过渡是 Application Insights,但 2026 年推荐的接入方式不再是 Microsoft.ApplicationInsights SDK,而是 OpenTelemetry .NET + Azure Monitor Exporter。后者是 Microsoft 自家钦定的方向,也是唯一支持 NativeAOT 的路径。相关规范可参考 OpenTelemetry .NET 官方文档

dotnet add package OpenTelemetry.Extensions.Hosting --version 1.10.0
dotnet add package Azure.Monitor.OpenTelemetry.Exporter --version 1.4.0

注册代码是这样。

builder.Services.AddOpenTelemetry()
    .WithTracing(t => t
        .AddSource("MyMauiApp")
        .AddHttpClientInstrumentation()
        .AddAzureMonitorTraceExporter(o =>
            o.ConnectionString = Environment.GetEnvironmentVariable("APPINSIGHTS_CONNECTION_STRING")))
    .WithMetrics(m => m
        .AddMeter("MyMauiApp")
        .AddAzureMonitorMetricExporter(o =>
            o.ConnectionString = Environment.GetEnvironmentVariable("APPINSIGHTS_CONNECTION_STRING")));

var activitySource = new ActivitySource("MyMauiApp");
using var activity = activitySource.StartActivity("LoadDashboard");
activity?.SetTag("user.id", currentUser.Id);

Crash 部分需要自己接 AppDomain.UnhandledExceptionTaskScheduler.UnobservedTaskException,转成 ExceptionTelemetry 发送。这是三家里最"手工"的方案,但换来的是与后端 Distributed Tracing 的天然拼合能力。这里跟我们的 .NET MAUI CI/CD 自动化部署指南里的 release 号可以复用,不用维护两套版本策略。

CI/CD 自动上传 dSYMs 与 mapping.txt

符号文件是崩溃报告的地基,DevOps 视角下必须做到本地开发不上传、CI 每次构建都上传。下面是 GitHub Actions 的完整片段,覆盖 Sentry(iOS + Android + .NET PDB 一次搞定)。

name: mobile-release
on:
  push:
    tags: ['v*']

jobs:
  build-ios:
    runs-on: macos-14
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with: { dotnet-version: '9.0.x' }
      - name: Build iOS Archive
        run: |
          dotnet publish -f net9.0-ios -c Release \
            -p:ArchiveOnBuild=true \
            -p:RuntimeIdentifier=ios-arm64 \
            -o ./artifacts/ios
      - name: Install sentry-cli
        run: curl -sL https://sentry.io/get-cli/ | bash
      - name: Upload dSYMs + PDBs
        env:
          SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
          SENTRY_ORG: my-org
          SENTRY_PROJECT: mobile
        run: |
          sentry-cli debug-files upload --include-sources ./artifacts/ios
          sentry-cli releases new "com.example.app@${GITHUB_REF_NAME}"
          sentry-cli releases set-commits --auto "com.example.app@${GITHUB_REF_NAME}"
          sentry-cli releases finalize "com.example.app@${GITHUB_REF_NAME}"

  build-android:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with: { dotnet-version: '9.0.x' }
      - name: Build Android AAB
        run: |
          dotnet publish -f net9.0-android -c Release \
            -p:AndroidPackageFormat=aab \
            -p:AndroidLinkTool=r8 \
            -o ./artifacts/android
      - name: Upload ProGuard mapping
        env: { SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} }
        run: |
          sentry-cli debug-files upload \
            --type proguard \
            ./artifacts/android/mapping.txt

关键点:--include-sources 会把 .cs 源码作为 source bundle 一起上传,Sentry 就能显示带上下文的堆栈行;sentry-cli debug-files upload 对同一路径的 iOS dSYM、macOS Mach-O、PE PDB 都能自动识别,不用分三条命令。对 Crashlytics,改成 fastlane run upload_symbols_to_crashlytics dsym_path:...;对 App Insights,走 PublishSymbols@2 Azure DevOps 任务。

.NET AOT 下的崩溃堆栈如何还原?

MAUI 9 及以上默认在 iOS Release 构建启用 NativeAOT,Android 也在向 AOT 迁移。AOT 编译后的方法名会被替换成 rvaXXXXXXXX,堆栈直接看不出业务代码。修复方式取决于所选 SDK。

  • Sentry:上传 *.pdb(Portable PDB)到 debug-files,加上 <DebugType>portable</DebugType><DebugSymbols>true</DebugSymbols>。Sentry 5.4+ 会用 PDB 做符号化。
  • Crashlytics:AOT 崩溃发生在原生层,会自然带上 iOS dSYM/Android NDK 符号,无需额外操作;但 managed 层的方法名会缺失。
  • Application Insights:OpenTelemetry Exporter 会把 Exception.StackTrace 字符串原样发送,需要在服务端跑 ilspycmddotnet-symbol 二次还原,是三家里最费劲的方案。

如果你的项目对启动速度和包体积都敏感,可以看看我们的 .NET MAUI 10 性能优化完全指南里关于 AOT 与 R2R 的取舍。别为了堆栈可读性放弃 AOT,正确做法是补齐符号管线。

崩溃 SDK 收集的数据(设备型号、OS 版本、IP、堆栈变量)在欧盟 GDPR 与美国加州 CCPA 下算个人数据。App Center 时代默认自动上报,2026 年这已经不合规了。三家 SDK 都提供延迟初始化 / 数据擦除 API。

// Sentry:初始化后关闭数据发送
SentrySdk.Init(o => o.Dsn = "...");
if (!userConsent.HasAccepted) {
    SentrySdk.CurrentHub.GetClient()?.Options.SendDefaultPii = false;
    SentrySdk.CurrentHub.PauseSession();
}

// Crashlytics:完全禁用
CrossFirebaseCrashlytics.Current.SetCrashlyticsCollectionEnabled(userConsent.HasAccepted);

// Application Insights:过滤器
services.AddSingleton<ITelemetryInitializer, ConsentTelemetryInitializer>();

更完整的合规策略,包括同意流的 UI 呈现、隐私政策文案与 iOS App Tracking Transparency 弹窗,可以参考我们之前发布的 .NET MAUI 应用安全与身份验证完全指南。核心原则只有一条:用户点"同意"之前,SDK 只能初始化、不能发送任何遥测

迁移检查表:从 App Center 到 2026 栈

最后把整个流程收成一张 DevOps 视角的 checklist,直接扔到 PR 描述里对照。

  1. grep 抓出所有 Microsoft.AppCenter 调用点,列表存档。
  2. 选定新 SDK(Sentry / Crashlytics / App Insights),走 POC 分支跑 48 小时观察吞吐。
  3. 把 DSN / API Key 放进 CI Secret,本地开发使用 DOTNET_ENVIRONMENT=Development 时禁用上报。
  4. 替换 Analytics.TrackEventSentrySdk.CaptureMessage / ActivitySource.StartActivity,逐文件 review。
  5. 接入 AppDomain.UnhandledExceptionTaskScheduler.UnobservedTaskException、平台层 UncaughtExceptionHandler
  6. CI/CD 里增加符号上传步骤,构建 tag 与 release 一一对应。
  7. 手动触发一次 throw new Exception("smoke test"),验证控制台能看到带源码的堆栈。
  8. 把用户同意流上线,QA 用真机验证"拒绝同意"路径下网络面板无遥测请求。
  9. 灰度发布 1% 用户观察 24 小时,比对错误率与 App Center 历史基线。
  10. 全量后 7 天,从 .csproj 移除 Microsoft.AppCenter.* 包,减少构建时间。

崩溃管线看起来只是"接个 SDK",但真到线上出事的时候,符号缺失、AOT 堆栈不可读、GDPR 投诉这三样任何一个都会让你半夜起来处理。按这份清单从上到下走一遍,比等出事再补救便宜得多。想再深挖预发布验证,可以看我们的 .NET MAUI 测试完全指南,把崩溃 SDK 的初始化路径写进 Appium 冒烟用例,能挡掉八成的低级问题。

常见问题

App Center 停服后老数据还能导出吗?

不能。Microsoft 的数据导出窗口在 2025-06-30 关闭,之后所有历史崩溃、事件与分析数据均已删除。2026 年只能从新 SDK 开始重建数据集。

Sentry 免费额度用完后会怎样?

超出 5,000 error/月后新事件会被丢弃,SDK 本地不会报错。建议在 Sentry 后台设置 spike protection 告警,或升级到 team 计划($26/月起,含 50k events)。

Firebase Crashlytics 支持 Windows 或 Mac Catalyst 吗?

不支持。Crashlytics 官方只覆盖 iOS 与 Android。如果你的 MAUI 项目发布到 Windows Store 或 Mac App Store,必须再选一家(Sentry 或 App Insights)补齐这两个平台。

如何在 MAUI 中上传 dSYMs?

推荐用 sentry-cli debug-files upload(Sentry)或 fastlane upload_symbols_to_crashlytics(Crashlytics)在 CI 里自动化。本地手动上传容易漏、也不利于审计。dSYM 路径通常在 ./bin/Release/net9.0-ios/ios-arm64/*.app.dSYM

MAUI 崩溃报告 GDPR 合规怎么做?

三步:初始化 SDK 但不发送任何数据;展示同意 UI;用户接受后再调用 SetCrashlyticsCollectionEnabled(true) 或恢复 Sentry Session。iOS 还需在 Info.plist 里配 NSUserTrackingUsageDescription

Sofia Rodriguez
关于作者 Sofia Rodriguez

Mobile DevOps engineer focused on the unglamorous stuff: build pipelines, signing, store releases, and the tooling that keeps teams shipping.