MAUI

Raygun4Maui will get you set up with Raygun Crash Reporting for your .NET MAUI application in no time.


The best way to install Raygun is to use the NuGet package manager. Right-click on your project and select "Manage NuGet Packages....". Navigate to the Browse tab, then use the search box to find Raygun4Maui and install it.

To install the latest version:

dotnet add package Raygun4Maui

Alternatively, you can specify a version tag to install a specific version of the package. See Raygun4Maui NuGet Gallery page for information on available versions.

dotnet add package Raygun4Maui --version 2.0.1

Import the module by:

using Raygun4Maui;

To add the ability to send crash reports to Raygun, you need to initialize the client during the builder stage. Open your main MauiProgram class (MauiProgram.cs) and change the CreateMauiApp method by adding the AddRaygun extension method:

var builder = MauiApp.CreateBuilder();
builder
    .UseMauiApp<App>()
    ...
    .AddRaygun();

The default method uses the configuration service to pull in your configuration and create the Raygun client.

We provide three ways to set up Raygun4Maui. We recommend using Lambda options for their simplicity and flexibility, but we also offer configuration via appsettings.json and a Raygun4MauiSettings overload. Choose the method that best fits your application's setup process.

We provide Lambda options that you can use to make in-code changes to the configuration, e.g.

.AddRaygun(options => {
    options.RaygunSettings.ApiKey = "paste_your_api_key_here";
    options.RaygunSettings.CatchUnhandledExceptions = true;
})

To activate automatic sending of unhandled exceptions to Raygun, you must set CatchUnhandledExceptions to true in the RaygunSettings portion of the configuration.

Configuration settings can be added via an appsettings.json file. To add appsettings.json to the bundled app, you should add it as an embedded resource (consult IDE specific instructions). If you do not provide one, we create a default Raygun4MauiSettings object which you can change using a Lambda to change the options. This must be added to the configuration before you call the .AddRaygun() method.

 var a = Assembly.GetExecutingAssembly();
 using var stream = a.GetManifestResourceStream("Raygun4Maui.SampleApp.appsettings.json");
        
 builder.Configuration.AddJsonStream(stream!);

Below is an example appsettings.json file. You need to use Raygun4MauiSettings for the root as the configuration will not pull it in otherwise.

{
  "Raygun4MauiSettings": {
    "RaygunSettings": {
      "ApiKey": "paste_your_api_key_here",
      "ApplicationVersion": "1.0.0",
      "CatchUnhandledExceptions": true
    },
    "RaygunLoggerConfiguration": {
      "SendDefaultTags": true, 
      "SendDefaultCustomData": true,
      "MinLogLevel": "Debug",
      "MaxLogLevel": "Critical" 
    }
  }
}

The AddRaygun extension method contains an overloaded method that takes a Raygun4MauiSettings options object which can be used instead of the configuration service. This contains RaygunSettings from Raygun4Net.

  • RaygunSettings
    • Any configuration available in the Raygun4Net RaygunSettings, such as ApiKey.
  • RaygunLoggerConfiguration
    • SendDefaultTags (defaulted to true) adds the Log Level (e.g., Severe, Warning, etc.) and the Build Platform (e.g., Windows, Android, iOS, etc.) to reports and logs sent to Raygun.
    • SendDefaultCustomData (defaulted to true) adds all available information in the uncaught exception as custom data on the crash report sent to Raygun.
    • MinLogLevel and MaxLogLevel specify the range of logging levels to be sent to Raygun.
  • IgnoredViews a list of views to ignore when tracking.
  • IgnoredUrls a list of URLs to ignore when tracking.

To use these additional configurations, create and initialize a new Raygun4MauiSettings object as follows:

Raygun4MauiSettings raygunMauiSettings = new Raygun4MauiSettings {
    RaygunSettings = new RaygunSettings() {
        ApiKey = "paste_your_api_key_here",
        CatchUnhandledExceptions = true
    },
    RaygunLoggerConfiguration = new RaygunLoggerConfiguration {
        SendDefaultTags = true, // defaults to true
        SendDefaultCustomData = true, // defaults to true
        MinLogLevel = LogLevel.Debug, // defaults to Debug
        MaxLogLevel = LogLevel.Critical // defaults to Critical
    }
};

The Raygun4MauiSettings are added to the service provider so that any DI dependent service can pull in the Raygun4MauiSettings and make changes. For example, the application version may be obtained from an endpoint, so this can be assigned later rather than at app startup.


Raygun4Maui stores an instance of a RaygunMauiClient object that is initialized by the Maui builder, this can be accessed through the following code:

RaygunMauiClient.Current

This client extends the Raygun4Net.NetCore RaygunClient, as a result any features supported by the Raygun4Net.NetCore Client are supported here.

For manual sending, use Send or SendInBackground methods, as shown below:

try {
    // Code that may fail
} catch (Exception e) {
    RaygunMauiClient.Current.SendInBackground(e);
//or
    RaygunMauiClient.Current.Send(e);
}

An exception needs to be thrown in order for its stack trace to be populated. If the exception is created manually no stack trace data is collected.

For additional examples on how to use the RaygunMauiClient object refer to the Raygun4Net.NetCore documentation

Unhandled exceptions can be automatically sent by setting CatchUnhandledExceptions to true in the RaygunSettings portion of the configuration. This will capture exceptions before your app crashes, so you can get information on what happened.

Example of where to enable this in the appsettings configuration:

{
  "Raygun4MauiSettings": {
    "RaygunSettings": {
      "CatchUnhandledExceptions": true
    }
  }
}

Raygun4Maui will automatically send any logger logs to Raygun.

To make a log entry, obtain the reference to the ILogger services that your MAUI app maintains:

ILogger logger = Handler.MauiContext.Services.GetService<ILogger<MainPage>>();

You may now use the appropriate ILogger log method from the logger object. This uses the same RaygunMauiClient object accessible from RaygunMauiClient.Current

logger.LogInformation("Raygun4Maui.SampleApp.TestLoggerErrorsSent: {MethodName} @ {Timestamp}", "TestLogInformation", DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"));
logger.LogCritical("Raygun4Maui.SampleApp.TestLoggerErrorsSent: {MethodName} @ {Timestamp}", "TestLogCritical", DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"));

This functionality also allows you to manually catch and log exceptions as shown below:

try {
    // Code that throws exception
} catch (Exception e) {
    // Example ILogger call. You can use the ILogger method and arguments of your choice.
    logger.Log(LogLevel.Error, e, "Exception caught at {Timestamp}", DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"));
}

User information enables the Raygun dashboard to track unique users impacted by each error or crash and for real user monitoring. While you can input any useful data, remember to respect your company's privacy policies. At minimum, input a unique GUID for user impact visibility per error. A database ID, if available, could help in resolving issues. Providing names and contact details allows for direct communication about issue resolution.

We have an interface of IRaygunUserProvider. This offers a GetUser function that our Crash Reporting product can use to get the current user. Only having GetUser makes sense for the .NET provider, but since MAUI supports Real User Monitoring we need a way of notifying the Real User Monitoring implementation that a user has changed.

We therefore, provide an IRaygunMauiUserProvider interface which adds a SetUser method. With this we can notify the Real User Monitoring module. There is a default implementation of this class for this provider which takes in a user with SetUser and provides this user through GetUser.

You can obtain an instance of this provider through dependency injection using IRaygunMauiUserProvider, then you can set the user by calling SetUser.

public MainPage(IRaygunMauiUserProvider userProvider) {
    userProivder.SetUser(new RaygunIdentifierMessage("anonymous");    
}

You can implement your own custom user provider if the default does not fit your needs. This can be done by implementing the IRaygunMauiUserProvider, specifically GetUser and SetUser.

Please note, if you create a custom implementation you must send a user changed event to the RaygunAppEventPublisher for our Real User Monitoring module to be notified.

RaygunAppEventPublisher.Publish(new RaygunUserChanged
{
    User = _user
});

As mentioned, we obtain this user provider by using dependency injection, so to add your custom instance of the user provider to the DI container we provide an extension on the app builder.

builder.AddRaygunUserProvider<CustomRaygunMauiUserProvider>();

note: The IRaygunUserProvider was introduced in Raygun4Net v10.0.0. The previous User and UserInfo properties are marked as obsolete and are not supported in the MAUI provider.

Here are all the available RaygunIdentifierMessage properties:

  • Identifier (passed into the constructor) is the unique identifier from your system for this user.
  • IsAnonymous is a flag indicating whether the user is logged in (or identifiable) or if they are anonymous. An anonymous user can still have a unique identifier.
  • Email The user's email address. If you use email addresses to identify your users, feel free to set the identifier to their email and leave this blank. We will use the identifier as the email address if it looks like one, and if no email address is specified in this field.
  • FullName The user's full name.
  • FirstName The user's first (or preferred) name.
  • UUID A device identifier. Could be used to identify users across devices, or machines that are breaking for many users.

note: The string properties on a User have a maximum length of 255 characters. Users that have fields that exceed this amount will not be processed.


We support the inclusion of environment variables such as PATH which will be displayed under the "Environment" tab of your crash report. In RaygunSettings provide a list of strings that will match the keys you would like to record from the environment variables. These queries are case-insensitive, so if you query for PATH we can still obtain Path for you.

This works on Windows, MacCatalyst, iOS and Android. However, the range of environment variables on iOS and Android may vary depending on whether you are running on a simulated device or a real device.

For usage information see the .NET 6+ documentation.


Portable PDB (Program Database) files in .NET are a modern, cross-platform alternative to traditional Windows PDB files used for debugging. They store debugging information, such as source code line numbers, variable names, and metadata, in a more compact and efficient format. Portable PDBs are designed to work across different operating systems and platforms, making them ideal for cross-platform .NET applications.

You can upload your application's Portable PDB files directly to Raygun. We will automatically detect any stack traces that can be symbolicated using the uploaded PDB's. This process enhances the stack trace information, providing you with more detailed insights into your application's runtime behavior and making it easier to debug issues.

It's important to note that every time you build a release of your application, you'll need to upload the newly generated PDB file if you want more information in the stack traces from that build. Raygun is most valuable when your application is in production and being used by customers, rather than while debugging. So you generally only need to upload PDB files for builds of your application that get released to your customers.

The recommended way of uploading the PDB files to Raygun is by automating a POST to the API endpoint. Below are examples of uploading a PDB file using cURL and different authentication methods. Each needs to be substituted (including the angle brackets) with your own value as described below each code example.

Basic authentication

curl -L
  -H "Authorization: Basic <BASIC_AUTH_TOKEN>"
  -F "SymbolFile=@<PATH_TO_PDB_FILE>"
  https://app.raygun.com/upload/pdbsymbols/<RAYGUN_APPLICATION_ID>

BASIC_AUTH_TOKEN can be generated by taking your Raygun account credentials in the format username:password and then using the base 64 encoded result.
PATH_TO_PDB_FILE is an absolute or relative path to your PDB file including the file name and .pdb extension.
RAYGUN_APPLICATION_ID is available in the URL of Raygun when viewing your application data.

External access token

curl -L
  -F "SymbolFile=@<PATH_TO_PDB_FILE>"
  https://app.raygun.com/upload/pdbsymbols/<RAYGUN_APPLICATION_ID>?authToken=<EXTERNAL_ACCESS_TOKEN>

PATH_TO_PDB_FILE is an absolute or relative path to your PDB file including the file name and .pdb extension.
RAYGUN_APPLICATION_ID is available in the URL of Raygun when viewing your application data.
EXTERNAL_ACCESS_TOKEN can be obtained from Raygun by clicking your name in the top right corner, select "My settings" and then hit "Generate external access token" or copy it if you already have one.

Additional options

If the upload is clearly not working, add the verbose -v option to get more information about what's wrong.

curl -v
  -L
  -F "SymbolFile=@<PATH_TO_PDB_FILE>"
  https://app.raygun.com/upload/pdbsymbols/<RAYGUN_APPLICATION_ID>?authToken=<EXTERNAL_ACCESS_TOKEN>

Possible results

200 if the upload was successful.

302 if you don't provide any form of authentication.

401 if credentials are incorrect, or you don't have access to the application.

404 if the URL or app id is incorrect.

curl (26) if the path to the PDB file is incorrect.

A drag and drop zone is available in the Raygun web application for manually uploading PDB files. This can be found by first visiting the desired application in your Raygun account, selecting "Application settings" from the side menu, and then clicking "Symbol center".

Regardless of how you upload your PDB files, they will all be listed in the Symbol Center as seen in the image above. Each listed PDB file includes a button to delete the file which is useful if you upload the wrong PDB or want to tidy up old files.


Raygun4Maui will automatically collect information specific to the environment the application is being run in. However, there are inconsistencies between certain values across platforms.

  • On iOS, Raygun4Maui cannot obtain the device's name. This is a privacy restriction put in place by Apple. If you would like this information to be collected and sent with crash reports you will have to request for permission from apple.
  • The Total physical memory and Available physical memory properties mean different things across platforms. Below is a table explaining the differences for each platform.
Platform Total physical memory Available physical memory
Mac Total installed ram Total memory available for user-level processes
iOS Total installed ram Total memory available for user-level processes
Windows Total installed ram Total amount of private memory used by the process at the time of the measurement. For a number of reasons this might not be the actual total memory usage
Android Total amount of memory that the JVM has allocated for the application Total amount of free memory that the JVM has available for the application to use

You can optionally specify an Offline Store for crash reports when creating your Raygun4MauiClient.

When an offline store is specified, if there are any issues sending an exception to the Raygun API, a copy of the exception may be stored locally to be retried at a later date.

An exception is stored offline when one of the following conditions are met:

  • There was a network connectivity issue, e.g. no active internet connection on a mobile device
  • The Raygun API responded with an HTTP 5xx, indicating an unexpected server error
// This will initialize Raygun with the default Application Data Store
mauiAppBuilder.AddRaygun(options => 
{
  options.UseOfflineStorage();
});

You can also define the background send strategy and store separately

// Attempt to send any offline crash reports every 30 seconds
var sendStrategy = new TimerBasedSendStrategy(TimeSpan.FromSeconds(30));

// Store crash reports in directory relative to the Application (`FileSystem.AppDataDirectory`)
var offlineStore = new RaygunMauiOfflineStore(sendStrategy);

mauiAppBuilder.AddRaygun(options => 
{
  options.OfflineStore = offlineStore;
});

You may extend and create your own custom implementations of OfflineStoreBase and IBackgroundSendStrategy to further customize where errors are stored, and when they are sent.


.NET MAUI Blazor Hybrid Apps are special in the sense that they contain a combination of both the .NET runtime and the JavaScript runtime. This creates difficulties with integrating crash reporting as errors can happen on either runtime which don't always flow through to each other. In addition to this, there is no unhandled exception hook, which means we cannot capture unhandled exceptions for you automatically.

To get around this you can follow the steps on our Blazor documentation page for JavaScript runtime errors.

For errors originating from .NET you can use error boundaries to invoke the Raygun4Maui client when an error occurs. Due to the difference in the way every app is set up, it is best to consult the error boundary documentation to find the best way this can fit your application. This can act as "automatic" unhandled exception reporting if set up throughout the app.


The provider is open source and available at the Raygun4Maui repository.