Sentry is great for crashes!

|

Update 27th September: Added information about uploading symbols for symbolication of exception

This blog post is not sponsored or paid. It is just me being a bit excited about a product I’ve tried and reflects the as of writing point of view about the product. Also I had written a section about Metrics in Sentry, which was a preview feature. However, they pulled this support to provide a better version of it later based on Spans, so I will omit this here. It shows though that new features in Sentry are thought through.

With the AppCenter announcement that it is retiring (March 31st 2025), I have been looking around a bit for a good replacement for it. Currently, I use AppCenter for crashes, analytics and App distribution and it would be great if there was a single product out there, which could replace it fully.

It seems like Firebase is the closest to a 100% replacement of AppCenter. However, there is a catch. Crashes on iOS when running on .NET iOS do not work. You will only ever end up with a native stack trace without any of the managed C# stacks. This is not great, and there doesn’t seem to be any traction on getting this to work. Hence, the search for a solution that works excellent for crashes for both .NET Android and iOS, which I primarily target in the Apps I work on.

Having looked at Sentry among other competitors a couple of times before and played a bit around with Sentry too, then now revisiting it again, it seems like a very solid choice for crashes and some of its other features.

What is cool about Sentry?

What I really like in Sentry, is that it really excels as a product for crash reporting. If you have used AppCenter, you will know that when you get a crash, you end up with a stack trace, with limited information about the device it crashed on. If you want extra information, you can enrich it yourself when the App restarts and upload file attachments to the crash. In my Apps, I’ve been using this to upload logs from the App on the crash, to help understand how the user ended up in the situation where it lead to a crash.

This is where Sentry, by far, beats AppCenter! Where Sentry excels is a feature called Breadcrumbs. This is a way to enrich the crash with events, log entries, navigation and much more. It essentially leaves little traces as the App runs from different sources. This feature is really powerful as it can help you much better understand what happened before the crash, so you can better understand what when on and reproduce.

Some of the things I’ve tried using breadcrumbs for are:

  • Adding log entries
  • Adding key events, such as user logged in
  • Navigation, such as user navigated to a specific screen
  • Button presses
  • App lifecycle
  • HTTP requests
  • Android ANRs (AppCenter doesn’t support these!)

Out of the box you also get a plethora of device information, such as storage, memory, ABI, display size, density and so much more. With this extra information, it helps a lot understanding what happened before the App crashed and in which state it was in.

Getting started

Depending on your App you might want to consume Sentry differently. I will cover .NET Android and iOS Apps here, but Sentry supports so many platforms and frameworks, so you will most definitely be able to add support for it in your code.

MvvmCross Apps

Currently MvvmCross still uses its own IoC container, so you cannot use some of the handy extension methods for Microsoft.Extensions.DependencyInjection. However, don’t fear, it is still possible to get it working!

The way I do it is to use the two NuGet packages Sentry and Sentry.Serilog. So I get the out of the box support for Android and iOS that the Sentry package provides. To get logs as breadcrumbs and to automatically report errors when you use LogError(exception, "message"), I use the Sentry.Serilog NuGet package.

<PackageReference Include="Sentry" Version="4.11.0" />
<PackageReference Include="Sentry.Serilog" Version="4.11.0" />

In your MainApplication.OnCreate() on Android and in AppDelegate.FinishedLaunching() (make sure to do this before base.FinishedLaunching() if you are inheriting from MvxAppDelegate) on iOS you add your Sentry setup like:

SentrySdk.Init(options =>
{
    options.Dsn = "<your DSN here>";
    options.ProfilesSampleRate = 1.0;
    options.TracesSampleRate = 1.0;
#if RELEASE
    options.Environment = "prod";
#else
    options.Environment = "dev";
#endif
});

On Android and iOS you can customize some of the platform specifics by enabling some of the stuff in the options.Native property. For instance, if your Application has permission to read logcat you could enable it to pull that and add to crashes with:

options.Android.LogCatIntegration = LogCatIntegrationType.Errors;

Explore that property, because there might be a bunch of interesting things in there for you.

To get the logs integration, using the handy extension method from Sentry.Serilog you can add a few lines to your Serilog configuration:

.WriteTo.Async(l => l.Sentry(options =>
{
    options.InitializeSdk = false;
    options.MinimumBreadcrumbLevel = LogEventLevel.Information;
}))

Since I am initializing Sentry myself, I’ve chosen to tell it to not do it here. MinimumBreadcrumbLevel and MinimumEventLevel helps you control what is added as breadcrumbs and as events to Sentry. If you don’t want logging errors as events in Sentry, you can control that with MinimumEventLevel, which defaults to Error, while MinimumBreadcrumbLevel defaults to Verbose.

What about MAUI Apps?

For the MAUI Framework, there is a dedicated Sentry.Maui package, which you can add by calling AddSentry() on your service collection, which makes it much easier than the setup above. It also adds Logging Providers to hook into HttpClient requests and Microsoft.Extensions.Logging out of the box. So consider using packages that match closely the framework you are using.

There are many more for ASP.NET, Google Cloud, Hangfire, Log4Net and EntityFramework.

Uploading symbols for symbolication

To get nice symbolicated stack traces you can use the sentry-cli commandline tool upload symbols. I add it to my pipeline like so:

- script: |
    sentry-cli debug-files upload --org $(sentryOrg) --project $(sentryProject) --auth-token $(sentryToken) MyProject/bin/Release/net8.0-android
  displayName: Upload Debug Symbols to Sentry

You will need to run sentry-cli login to get a token, once you do that, you are good to go!

Differences from AppCenter

Some few differences in usage compared to AppCenter are how extra information is annotated on events. In AppCenter, when you would report an error or analytics event, you would provide a Dictionary<string, string> with extra information you want added.

This is different in Sentry, where you instead create a scope. It supports both sync and async scope creation. So something like:

// local scope only for this exception
SentrySdk.CaptureException(exception, scope =>
{
    // adding key/value like in AppCenter
    foreach (var (key, value) in properties)
    {
        scope.SetExtra(key, value);
    }
});

This will create a local scope for the specific exception. You can also set global values with:

SentrySdk.ConfigureScope(scope =>
{
    scope.SetTag("my-tag", "my value");
    scope.User = new SentryUser { Id = userId };
});

I find this much nicer, especially that you can set global things for the session.

Overall, compared to AppCenter. Sentry, beats it hands down and goes way beyond what AppCenter crashes supports. Looking forward to some of the other App features the Sentry team comes out with.