Raygun instead of AppCenter for .NET MAUI: Analytics and Crash Reporting made easy

In March 2024, Microsoft announced the retirement of Visual Studio AppCenter, effective March 31, 2025. While AppCenter has been a reliable platform for mobile app developers over the years, its upcoming shutdown means it’s time to explore new options.

In our recent blog post on the AppCenter retirement, we highlighted that there is no single solution available that encompasses all of AppCenter’s features—Build, Test, Distribute, Analytics, and Crash Diagnostics. Instead, developers must seek out specialized tools for each function.

Like many other developers, we have been impacted by this change with our custom .NET MAUI customer apps. Over the past few weeks, we’ve thoroughly evaluated various alternatives to AppCenter. In this blog post, we introduce Raygun, an outstanding alternative for analytics and crash diagnostics.

Content:

What is Raygun?

Raygun is a cloud-based platform for monitoring your applications that supports mobile apps as well as web and desktop applications. In addition to .NET, many other languages such as Go, Java, JavaScript, PHP, Python and Ruby as well as frameworks such as Android, iOS, Flutter, Node.js and React Native are supported. For .NET, Raygun offers everything a developer’s heart desires, including ASP.NET, Blazor, .NET MAUI, WinForms and WPF, each in the .NET 6+ and .NET Framework variants.

Raygun offers the following services depending on the language and framework:

  • Crash Reporting
  • Real User Monitoring
  • Application Performance Monitoring

Crash Reporting and Real User Monitoring are available for .NET MAUI. We will take a closer look at both services in this blog post.

Integrating Raygun into a .NET MAUI app

To use Raygun in your application, you first need to create an account at Raygun. You can register for a 14-day free trial. After the trial period, you’ll need to switch to a paid plan, as a free version is not available. The cheapest plan is currently USD 40 per month for Crash Reporting and USD 80 per month for Real User Monitoring, with annual payment required for these prices.

Once your account is set up, you can create a new app in the Raygun web portal by assigning a name and selecting the target platform — in our case, .NET MAUI. Unlike AppCenter, a notable feature is that you don’t need to create separate apps for each platform; you can gather all data in a single overview.

Next, add the Raygun NuGet package Raygun4Maui to your .NET MAUI application. We used the latest version 2.0.1 for our tests. In the MauiProgram.cs file, initialize Raygun for your application by calling the AddRaygun extension method in the Application Builder and passing your API key along with other required arguments.

builder
    .UseMauiApp()
    .AddRaygun(options =>
    {
        options.RaygunSettings = new RaygunSettings
        {
            ApiKey = "<Enter your Api Key here>"
        };
    })
    .ConfigureFonts(fonts =>
    {
        fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
        fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
        fonts.AddFont("MaterialSymbols-Rounded.ttf", "MaterialSymbolsRounded");
    });

Crash Reporting with Raygun in .NET MAUI

Nothing is more frustrating than learning about app crashes through bad reviews in the app store. Raygun’s Crash Reporting helps by automatically reporting app crashes to the Raygun portal. To enable this, set the CatchUnhandledExceptions value of RaygunSettings to true in the Raygun initialization within the MauiProgram.cs class.

 builder
     .UseMauiApp<App>()
     .AddRaygun(options =>
     {
         options.RaygunSettings = new RaygunSettings
         {
             ApiKey = "<Enter your API key here>",
             CatchUnhandledExceptions = true,  // transfer unhandled exceptions to Raygun

         };
      })
     .ConfigureFonts(fonts =>
     {
         fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
         fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
         fonts.AddFont("MaterialSymbols-Rounded.ttf", "MaterialSymbolsRounded");
     });

After integrating Raygun into your .NET MAUI app and enabling crash reporting, all crashes will be automatically recorded and reported to the Raygun platform. There, you will receive detailed reports about each crash, including the stack trace and affected environments. This not only makes it easier to diagnose and fix errors but also allows you to act proactively. The collected errors are displayed in a nice overview in Raygun.

Raygun Crash Reporting Overview Page

The data you receive in the detail view of a specific crash is similar to what App Center provides. In addition to the stack trace, you can see the app version, the timestamp, the exception name, and the error message.

Summary view of a Raygun crash report

More informative than App Center are the device details provided in the Environment tab. In addition to the device and operating system version, you also receive data about memory, processor, and resolution scaling.

Environment view of a Raygun crash report

The data becomes even more comprehensive when you track user interactions with your app using the Breadcrumb feature. In my sample app, I use the Shell for navigation and have overridden the OnNavigated method in the AppShell.xaml.cs file to log navigation through the app. As you can see from the code example, you can pass a message, a category, and custom data.

protected override void OnNavigated(ShellNavigatedEventArgs args)
{
    RaygunBreadcrumbs.Record(new RaygunBreadcrumb
    {
        Message = $"Navigated to {args.Current.Location.OriginalString}",
        Category = "navigation",
        CustomData = new Dictionary<string, object>
        {
            { "Destination", args.Current.Location.OriginalString },
            { "Source", args.Previous?.Location.OriginalString }
        }
    });
    base.OnNavigated(args);
}

At the place where I throw the exception, I have also tracked the button click:

private void OnTriggerCrash()
{
    RaygunBreadcrumbs.Record(new RaygunBreadcrumb
    {
        Message = "Clicked the Unhandled Crash Button",
        Category = "click-event",
    });

    throw new InvalidOperationException("Raygun test exception");
}

In the Raygun web portal, you will then see a new Breadcrumbs tab in the detailed view of your exception.

Breadcrumb view of a Raygun crash report

The use of Breadcrumbs is reminiscent of tracking CustomEvents in AppCenter. It is a useful method for understanding the steps the user took before triggering the error.

Manually log errors to Raygun

So far, we have seen that unhandled exceptions that cause the app to crash are automatically reported to Raygun. However, you also have the option to manually send handled exceptions to Raygun.

There are two ways to do this. Firstly, you can use the Send or SendInBackground methods of the RaygunMauiClient class to transmit an exception.

private void OnTriggerHandledCrash()
{
    try
    {
      throw new InvalidOperationException("Raygun test handled exception");
    }
    catch (Exception e)
    {
        RaygunMauiClient.Current.SendInBackground(e);
    }
}

Another option is to use the RaygunLogProvider and log errors in your code via Microsoft.Extensions.Logging. To set this up, you first need to configure the RaygunLogger in the MauiProgram class:

  var builder = MauiApp.CreateBuilder();
  builder
      .UseMauiApp<App>()
      .AddRaygun(options =>
      {
          options.RaygunSettings = new RaygunSettings
          {
              ApiKey = "<Enter your api key here>",
              CatchUnhandledExceptions = true
          };

          // Configure the Raygun Logger as a provider for
          // Microsoft.Extensions.Logging 
          options.RaygunLoggerConfiguration = new RaygunLoggerConfiguration
          {
              SendDefaultTags = true,
              SendDefaultCustomData = true,
              MinLogLevel = LogLevel.Error,
              MaxLogLevel = LogLevel.Critical
          };
      })

After that, you can use Dependency Injection to inject a logger and log errors to Raygun. This approach has the advantage of minimizing direct dependencies on Raygun in your code.

public class SettingsViewModel : ViewModelBase
{
    private readonly ILogger<SettingsViewModel> _logger;

    // inject a Logger for the current class
    public SettingsViewModel(ILogger<SettingsViewModel> logger)  
    {
        _logger = logger;
         TriggerHandledCrashCommand = new RelayCommand(OnTriggerHandledCrash);
    }

    public RelayCommand TriggerHandledCrashCommand { get; }
    
    
    private void OnTriggerHandledCrash()
    {
        try
        {
            throw new InvalidOperationException("Raygun test handled exception");
        }
        catch (Exception e)
        {
            // Logging to Raygun
            _logger.LogError(e, "Exception sent via ILogger: {0}",e.Message);
        }
    }
}

Manage errors

In addition to logging errors, Raygun also offers other exciting features for error management. Similar to AppCenter, an error can be given a status and be assigned to a responsible person. Raygun also offers integration with many other tools in order to manage issues efficiently. Using the available integrations, which are only included in the more expensive Team Edition, you can, for example, create an issue in GitHub, a work item in Azure DevOps or a ticket in Jira or Zendesk.

Error management with Raygun

Real User Monitoring (RUM) for .NET MAUI Apps with Raygun

Knowing why an app has crashed and being able to act proactively significantly contributes to improving the quality of an app.

You can take it a step further by analyzing usage data in addition to crash data. This is where Raygun’s Real User Monitoring (RUM) comes in.

Overview of Raygun Real User Monitoring

Activating Real User Monitoring in your .NET MAUI app

To activate RUM in your app, set the EnableRealUserMonitoring property to true in the MauiProgram.cs file. You can then use the RumFeatureFlags to define what you want to log. In my case, I want to log both the performance of page views and the performance of network calls.

var builder = MauiApp.CreateBuilder();
builder
    .UseMauiApp<App>()
    .UseMauiCommunityToolkit()
    .UseMauiCameraView()
    .AddRaygun(options =>
    {
        options.RaygunSettings = new RaygunSettings
        {
            ApiKey = "<Enter your API Key here>",
            CatchUnhandledExceptions = true                
        };

        // Real User Monitoring aktivieren
        options.EnableRealUserMonitoring = true;
        options.RumFeatureFlags = RumFeatures.AppleNativeTimings 
                                  | RumFeatures.Network 
                                  | RumFeatures.Page;

Let’s now take a look at the individual tabs in Real User Monitoring.

Versions

In the Versions tab, you get an overview of which versions of your app are actively in use and how the usage trend of different versions has developed over the selected period. If you have also enabled crash reporting in your app, you can see the error rates for the various versions.

This data allows you to understand your users' update behavior and verify if the latest version is more stable than the previous one.

Raygun Real User Monitoring: Versions view

Views

In the Views tab, you can see a list of the most frequently and recently displayed screens. For each screen, you see the number of users, sessions, views, and the average load time. This list helps you quickly identify which pages are frequently accessed in your app and which pages are slow. Clicking on a page opens the detail view, where you can see the distribution of load times.

For example, in the case of the ItemsPage, which according to the screenshot has an average load time of 5.77s, we could see in the detailed view that the median load time was 13ms and there was only one outlier at 2:32 minutes (likely because I was on a breakpoint in the debugger).

Raygun Real User Monitoring: Views

In our tests, we also noticed that not only our XAML-Pages but also some internal components, such as .UICursorAccessoryViewController and Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer, appeared in the list. This cluttered the list and reduced its readability.

We solved this problem by adding the unwanted views to the ignore list in the Raygun configuration within the MauiProgram.cs file.

var builder = MauiApp.CreateBuilder();
builder
    .UseMauiApp<App>()
    .AddRaygun(options =>
    {
        options.RaygunSettings = new RaygunSettings
        {
            ApiKey = "<Enter your API Key here>",
            CatchUnhandledExceptions = true,                
        };

        options.RaygunLoggerConfiguration = new RaygunLoggerConfiguration
        {
            SendDefaultTags = true,
            SendDefaultCustomData = true,
            MinLogLevel = LogLevel.Error,
            MaxLogLevel = LogLevel.Critical
        };

        // enter the list of view to be ignored here.                 
        options.IgnoredViews = [".UICursorAccessoryViewController",
                  "Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer",
                  "Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRootRenderer",
                  "Microsoft.Maui.Controls.Platform.Compatibility.ShellItemRenderer",
                  "Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRenderer",
                  "Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer",
                  "Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutContentRenderer",
                  "Microsoft.Maui.Controls.Platform.Compatibility.ShellTableViewController",
                  "Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer",
                  "Microsoft.Maui.Platform.PageViewController",
                  "UICompatibilityInputViewController",
                  "UIPredictionViewController",
                  "UISystemKeyboardDockController",
                  "UIInputWindowController"];

        options.EnableRealUserMonitoring = true;
        options.RumFeatureFlags = RumFeatures.AppleNativeTimings 
                                  | RumFeatures.Network 
                                  | RumFeatures.Page;

    })

Sessions

In the Sessions tab, you get an overview of how users are interacting with your app. Initially, you see a table with recent sessions, including the user, the duration of each session, the number of pages visited, and the last page visited.

Raygun Real User Monitoring: Session Overview

Clicking the magnifying glass in the last column of the table opens the detailed view of a session. In this view, you can follow the user’s path through the app using a timeline. Tracking the user journey is extremely helpful as it provides insights into user behavior. You can see how users interact with your app, which pages they visit, and in what order. This allows you to identify potential bottlenecks or issues in the user flow and optimize the app accordingly. Additionally, you can understand which features are particularly popular and how effective your navigation is. This enables you to improve the user experience and make your app more successful.

Raygun Real User Monitoring: Session Details

Users

In the Users tab, you can see the total number of users for your app over the selected period, as well as a breakdown of new and returning users. We find the “Crash Free Users” metric particularly interesting, which you get if you have both Real User Monitoring and Crash Reporting enabled.

Below the metrics at the top, there is a list of new and active users.

By default, Raygun shows a GUID instead of a username, which cannot be used to uniquely trace a user. However, you can assign your own username, for example, after a successful login, using the SetUser method of the RaygunMauiUserProvider.

The following lines show an example of how this works. The prerequisite is that you have registered the provider in the ServiceCollection:

builder.Services.AddTransient<IRaygunMauiUserProvicer, RaygunMauiUserProvider>();
public partial class App : Application
{
    public App(IRaygunMauiUserProvider userProvider)
    {
        InitializeComponent();
        MainPage = new AppShell();
        
        // provide a custom user name here
        userProvider.SetUser(new RaygunIdentifierMessage("Quality Bytes GmbH"));
    }
}

In our example, we have decided to simply identify the customer and not the specific user of the customer. However, you have the option of add further information, as the following code example shows:

var user = new RaygunIdentifierMessage("quality-bytes-ak")
{
    FullName = "André Krämer",
    FirstName = "André",
    Email = "info@qualitybytes.de",
    IsAnonymous = false,
    UUID = "qb-ak-iphone"
};
userProvider.SetUser(user);

Attention!

Since you can trace all a user’s sessions, including their navigation paths through the app, I strongly recommend consulting with your data protection officer before tracking users at this level of detail!

Raygun Real User Monitoring: Users

Devices

In the Devices tab, you can see the different types of devices that users are using to access your app. This information is particularly useful if you discover that a bug occurs only on a specific device type. You can use the device list to see how common that device is among your users, helping you prioritize the bug fix accordingly. Additionally, this view can indicate whether you might need to acquire additional test devices.

Raygun Real User Monitoring: Devices

Operating Systems

In the Operating Systems tab, you can see a distribution of the operating systems and their versions used by your app’s users. This data helps you make informed decisions about which operating system versions you want to continue supporting and which you can phase out.

Raygun Real User Monitoring: Operating Systems

Geo

On the Geo tab, you can see the distribution of your users across continents and countries. This data can help you make decisions about localizing your app.

Attention!

Location data is considered personal data under GDPR! Therefore, I strongly recommend consulting with your data protection officer before collecting location data!

Raygun Real User Monitoring: Geo

Live

The final view of Real User Monitoring is the Live view, which provides a nice overview of current usage and recent crash statistics.

Raygun Real User Monitoring: Live View

Data Protection

Whenever you collect data about your users, the question of data protection compliance inevitably arises.

According to Raygun’s statement, they are GDPR-compliant. In a detailed blog post, Raygun describes how they implement GDPR on their platform. A key element for compliant implementation is disabling the transmission of IP addresses and location data to Raygun.

Raygun Application Settings: Disable Personal User Information

Attention!

This is not legal advice on data protection! Be sure to discuss further details about the compliant use of Raygun with your data protection officer!

Conclusion

Raygun is an impressive platform for application monitoring. Both Crash Reporting and Real User Monitoring offer significantly more features than AppCenter. As you’ve seen from the code examples in this article, integrating it into a .NET MAUI app is very straightforward. A major advantage is that, unlike AppCenter, you don’t need to create separate apps for Android, iOS, and Windows — all data is collected in a single app. Additionally, Raygun supports various backend technologies alongside mobile platforms, allowing you to monitor your web services with Raygun and manage everything on a single platform.

However, it’s important to note that Raygun is a paid service. While AppCenter offered free crash reporting and analytics, with Raygun you must switch to a paid plan after a 14-day trial period. Prices start at $40 per month for Crash Reporting and $80 per month for Real User Monitoring, provided you pay annually. These additional costs can impact your budget depending on your project’s requirements.

For a hobby project, this price might be a significant hurdle. However, for commercially successful apps, the budget for Raygun is typically manageable. Ultimately, each team must evaluate the value that Raygun’s additional features provide. In my assessment, the extended functionality and comprehensive monitoring capabilities justify the cost in a commercial environment, especially if they lead to better app quality and faster issue resolution.

Need Assistance?

Are you interested in Raygun and would like to use it in your .NET MAUI app, but lack the time or know-how to integrate it?

No problem: Schedule a free .NET MAUI consultation. We are happy to help!


About the author

André Krämer

Dipl.-Inform. (FH) André Krämer, Msc. is a co-founder and the CEO of Quality Bytes GmbH. The multi-awarded Microsoft MVP has been active in software development since 1997 and specialized in Microsoft .NET development in 2002.

His expertise includes app development with .NET MAUI, Azure DevOps, document automation, and reporting with TX Text Control, as well as the analysis of memory leaks and performance issues. He is a regular author forlinkedIn Learning, where he already published more than thirty German video courses.

He is also a TX Text Control MVP and a Microsoft MVP. He has written a German book on Xamarin.Forms and another on .NET MAUI for the German publisher Hanser Verlag.