更新于: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.TrackEvent 与 Crashes.TrackError 调用位置抓出来,做成一张表。这张表就是你后面替换 SDK 时的"改造清单",也是 QA 回归测试的检查点。多数团队跳过这一步,然后新 SDK 上线后才发现漏掉了 30% 的埋点。App Center 的 Distribute 部分(in-app 更新)也没了,推荐用 Firebase App Distribution 或 TestFlight 顶上,不过那是另一篇文章的事,本文只谈崩溃与遥测。
Sentry、Crashlytics 与 Application Insights 哪个更适合 MAUI?
选型是这三个平台里差异最大的一步。下面这张表覆盖了大多数团队实际关心的维度。
| 维度 | Sentry | Firebase Crashlytics | Application 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 Responding | Android + iOS | 仅 Android | 否 |
| 符号上传自动化 | sentry-cli | Fastlane 或 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/iOS,google-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.UnhandledException 与 TaskScheduler.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 字符串原样发送,需要在服务端跑 ilspycmd 或 dotnet-symbol 二次还原,是三家里最费劲的方案。
如果你的项目对启动速度和包体积都敏感,可以看看我们的 .NET MAUI 10 性能优化完全指南里关于 AOT 与 R2R 的取舍。别为了堆栈可读性放弃 AOT,正确做法是补齐符号管线。
GDPR 与用户同意流
崩溃 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 描述里对照。
- 用
grep 抓出所有 Microsoft.AppCenter 调用点,列表存档。
- 选定新 SDK(Sentry / Crashlytics / App Insights),走 POC 分支跑 48 小时观察吞吐。
- 把 DSN / API Key 放进 CI Secret,本地开发使用
DOTNET_ENVIRONMENT=Development 时禁用上报。
- 替换
Analytics.TrackEvent 到 SentrySdk.CaptureMessage / ActivitySource.StartActivity,逐文件 review。
- 接入
AppDomain.UnhandledException、TaskScheduler.UnobservedTaskException、平台层 UncaughtExceptionHandler。
- CI/CD 里增加符号上传步骤,构建 tag 与 release 一一对应。
- 手动触发一次
throw new Exception("smoke test"),验证控制台能看到带源码的堆栈。
- 把用户同意流上线,QA 用真机验证"拒绝同意"路径下网络面板无遥测请求。
- 灰度发布 1% 用户观察 24 小时,比对错误率与 App Center 历史基线。
- 全量后 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。