Aspire
Sends .NET Aspire crash reports to either a locally running Raygun portal or the Raygun cloud service.
Installation
Step 1 - Install the Raygun4Aspire NuGet package
You'll want to add the Raygun4Aspire NuGet package to both the Aspire orchestration project (AppHost), and any .NET projects within your Aspire app where you want to collect crash reports from. Either use the NuGet package management GUI in the IDE you use, OR use the below dotnet command.
dotnet add package Raygun4Aspire
Step 2 - Add Raygun to the orchestration project (AppHost)
In Program.cs
of the AppHost project, add a Raygun4Aspire using
statement, then call AddRaygun
on the builder (after the builder is initialized and before it is used to build and run).
using Raygun4Aspire;
// The distributed application builder is created here
builder.AddRaygun();
// The builder is used to build and run the app somewhere down here
Step 3 - Instrument your .NET projects
In each of the .NET projects in your Aspire app where you intend to log crash reports from, add a RaygunSettings
section to the appsettings
file.
"RaygunSettings": {
"ApiKey": "paste_your_api_key_here"
}
Then, in Program.cs
, add a using
statement, call AddRaygun
on the WebApplicationBuilder, followed by calling UseRaygun
on the created application.
using Raygun4Aspire;
// The WebApplicationBuilder is created somewhere here
builder.AddRaygun();
// The builder is used to create the application a little later on
app.UseRaygun();
// Then at the end of the file, the app is commanded to run
Manually sending exceptions
It's best practice to log all exceptions that occur within try/catch blocks (that aren't subsequently thrown to be caught by unhandled exception hooks). To do this with Raygun, inject the RaygunClient
into any place where you wish to manually send exceptions from. Then use the SendInBackground
method of that Raygun client.
Below is an example of doing this in a razor page:
@inject Raygun4Aspire.RaygunClient raygunClient
<!-- some html elements would typically be here -->
@code {
private void Function()
{
try
{
// whatever code this function is doing
}
catch (Exception ex)
{
raygunClient.SendInBackground(ex);
}
}
}
The SendInBackground method also contains optional parameters to send tags, custom data, user details and the HttpContext.
Tags is a list of strings that could be used to categorize crash reports.
Custom data is a dictionary of key value pairs for logging richer contextual information that can help further understand the cause of an exception specific to your code.
User details is a RaygunIdentifierMessage
object that can be used to capture details about a user impacted by the crash report. Make sure to abide by any privacy policies that your company follows when logging such information. The identifier you set here could instead be an internal database Id, or even just a unique guid to at least gauge how many users are impacted by an exception.
HttpContext will cause request and response information to be included in the crash report where applicable.
Custom user provider
By default Raygun4Aspire ships with a DefaultRaygunUserProvider
which will attempt to get the user information from the HttpContext.User object. This is Opt-In which can be added as follows:
// The WebApplicationBuilder is created somewhere here
// AddRaygun is called here
builder.AddRaygunUserProvider();
Alternatively, if you want to provide your own implementation of the IRaygunUserProvider
, you can do so by creating a class that implements that interface and then registering it via the generic AddRaygunUserProvider
overload as seen in the example below.
public class ExampleUserProvider : IRaygunUserProvider
{
private readonly IHttpContextAccessor _contextAccessor;
public ExampleUserProvider(IHttpContextAccessor httpContextAccessor)
{
_contextAccessor = httpContextAccessor;
}
public RaygunIdentifierMessage? GetUser()
{
var ctx = _contextAccessor.HttpContext;
if (ctx == null)
{
return null;
}
var identity = ctx.User.Identity as ClaimsIdentity;
if (identity?.IsAuthenticated == true)
{
return new RaygunIdentifierMessage(identity.Name)
{
IsAnonymous = false
};
}
return null;
}
}
This can be registered in the services during configuration like so:
// The WebApplicationBuilder is created somewhere here
// AddRaygun is called here
builder.AddRaygunUserProvider<ExampleUserProvider>();
Port
When running in the local development environment, crash reports are sent to the locally running Raygun portal via port 24605
.
Additional configuration options
The following options can be configured in an appsettings file or in code.
For example, in the appsettings.json
file:
{
"RaygunSettings": {
"ApiKey": "paste_your_api_key_here",
"ImaginaryOption": true,
...
}
}
The equivalent to doing this in C# is done in the Program.cs
file of an application where you are logging exceptions from. Amend the line where you AddRaygun
to the WebApplicationBuilder:
builder.AddRaygun(settings =>
{
settings.ApiKey = "paste_your_api_key_here";
settings.ImaginaryOption = true;
...
});
Examples below are shown in appsettings.json format.
Exclude errors by HTTP status code
You can exclude errors by their HTTP status code by providing an array of status codes to ignore in the configuration. For example if you wanted to exclude errors that return the I'm a teapot response code, you could use the configuration below.
"RaygunSettings": {
"ApiKey": "paste_your_api_key_here",
"ExcludedStatusCodes": [418]
}
Remove sensitive request fields
If you have sensitive data in an HTTP request that you wish to prevent being transmitted to Raygun, you can provide lists of possible keys (names) to remove. The available options are:
- IgnoreQueryParameterNames
- IgnoreFormFieldNames
- IgnoreHeaderNames
- IgnoreCookieNames
These can each be set to an array of keys to ignore. Setting an option as *
will indicate that all the keys will not be sent to Raygun. Placing *
before, after or at both ends of a key will perform an ends-with, starts-with or contains operation respectively. For example, IgnoreFormFieldNames: ["*password*"]
will cause Raygun to ignore all form fields that contain "password" anywhere in the name. These options are not case sensitive.
Note: There is also a special IgnoreSensitiveFieldNames
property which allows you to set common filter lists that apply to all query-parameters, form-fields, headers and cookies. (This setting is also used for filtering the raw request payload as explained further below).
Remove sensitive data logged from the raw request payload
By default, crash reports in Raygun will include the entire raw request payload where it has access to the HttpContext. If you want to avoid Raygun capturing the raw request payload entirely, then set the IsRawDataIgnored option to true:
"RaygunSettings": {
"ApiKey": "paste_your_api_key_here",
"IsRawDataIgnored": true
}
If you have any request payloads formatted as key-value pairs (e.g. key1=value1&key2=value2
), then you can set UseKeyValuePairRawDataFilter
to true and then any fields listed in the IgnoreSensitiveFieldNames
option will not be sent to Raygun.
"RaygunSettings": {
"ApiKey": "paste_your_api_key_here",
"UseKeyValuePairRawDataFilter": true,
"IgnoreSensitiveFieldNames": ["key1"]
}
Similarly, if you have any request payloads formatted as XML, then you can set UseXmlRawDataFilter
to true and then any element names listed in the IgnoreSensitiveFieldNames
option will not be sent to Raygun.
"RaygunSettings": {
"ApiKey": "paste_your_api_key_here",
"UseXmlRawDataFilter": true,
"IgnoreSensitiveFieldNames": ["Password"]
}
You can also implement your own IRaygunDataFilter
to suit your own situations and then register one or more of these custom filters on the RaygunSettings object (a code example for this can be seen after the below RaygunJsonDataFilter implementation example). If the filtering fails, e.g. due to an exception, then null should be returned to indicate this.
Here's an example of a custom raw request data filter for the JSON data structure:
using System;
using System.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Mindscape.Raygun4Net.Filters;
public class RaygunJsonDataFilter : IRaygunDataFilter
{
private const string FILTERED_VALUE = "[FILTERED]";
public bool CanParse(string data)
{
if (!string.IsNullOrEmpty(data))
{
int index = data.TakeWhile(c => char.IsWhiteSpace(c)).Count();
if (index < data.Length)
{
if (data.ElementAt(index).Equals('{'))
{
return true;
}
}
}
return false;
}
public string Filter(string data, IList<string> ignoredKeys)
{
try
{
JObject jObject = JObject.Parse(data);
FilterTokensRecursive(jObject.Children(), ignoredKeys);
return jObject.ToString(Formatting.None, null);
}
catch
{
return null;
}
}
private void FilterTokensRecursive(IEnumerable<JToken> tokens, IList<string> ignoredKeys)
{
foreach (JToken token in tokens)
{
if (token is JProperty)
{
var property = token as JProperty;
if (ShouldIgnore(property, ignoredKeys))
{
property.Value = FILTERED_VALUE;
}
else if (property.Value.Type == JTokenType.Object)
{
FilterTokensRecursive(property.Value.Children(), ignoredKeys);
}
}
}
}
private bool ShouldIgnore(JProperty property, IList<string> ignoredKeys)
{
bool hasValue = property.Value.Type != JTokenType.Null;
if (property.Value.Type == JTokenType.String)
{
hasValue = !string.IsNullOrEmpty(property.Value.ToString());
}
return hasValue && !string.IsNullOrEmpty(property.Name) && ignoredKeys.Any(f => f.Equals(property.Name, StringComparison.OrdinalIgnoreCase));
}
}
To get the RaygunClient to use a custom raw data filter, Change the AddRaygun statement to use the overload that takes a lambda where you can modify the RaygunSettings object:
builder.AddRaygun((settings) => settings.RawDataFilters.Add(new RaygunJsonDataFilter()));
In the case that the filter operation failed (i.e. the filter returned null), then the original raw request data will be included in the Raygun crash report by default. If you instead want a filter failure to cause the entire raw request payload to be excluded from the Raygun crash report, then you can set the IsRawDataIgnoredWhenFilteringFailed
option to true.
"RaygunSettings": {
"ApiKey": "paste_your_api_key_here",
"IsRawDataIgnoredWhenFilteringFailed": true
}
Application version
Raygun4Aspire will attempt to get the version of your application from the running assembly, and include that version number in each crash report. If this is not the version number you would like, you can overwrite it via the ApplicationVersion option:
"RaygunSettings": {
"ApiKey": "paste_your_api_key_here",
"ApplicationVersion": "Avacado"
}
Throw exceptions that occur within Raygun4Aspire
This option can help debug issues within Raygun4Aspire itself. It's highly recommended to not set this option in your production environment. By default, the Raygun4Aspire client will swallow any exceptions that it encounters. Setting ThrowOnError
to true will cause said errors to be rethrown instead, which can be useful for troubleshooting Raygun4Aspire.
"RaygunSettings": {
"ApiKey": "paste_your_api_key_here",
"ThrowOnError": true
}
Additional features
The following features are set by using the registered RaygunClient singleton. To do this, fetch the RaygunClient singleton from the Services list some time after the builder has been used to build the app.
For example, in Program.cs
of a .NET app where crash reports will be sent from:
// The WebApplicationBuilder is used to build the app somewhere up here
var raygunClient = app.Services.GetService<RaygunClient>();
// Use the raygunClient to set any features here
Modify Raygun crash reports
On the RaygunClient singleton, attach an event handler to the SendingMessage
event. This event handler will be called just before the RaygunClient sends any crash report to Raygun. The event arguments provide the RaygunMessage
object that is about to be sent. One use for this event handler is to add or modify any information on the RaygunMessage.
The following example uses the SendingMessage event to set a global tag that will be included on all crash reports.
var raygunClient = app.Services.GetService<RaygunClient>();
raygunClient.SendingMessage += (sender, eventArgs) =>
{
eventArgs.Message.Details.Tags.Add("Web API");
};
Cancel sending certain crash reports to Raygun
On the RaygunClient singleton, attach an event handler to the SendingMessage
event. This event handler will be called just before the RaygunClient sends any crash report to Raygun. Here you can analyze the crash report that's about to be sent to Raygun and use conditional logic to decide if that crash report is something you know that you don't need logged, so cancel the delivery. Set eventArgs.Cancel
to true to avoid sending that crash report to Raygun.
Below is a simple example to cancel sending specific exceptions. Be sure to add null checks for properties and bounds checks when looking into arrays.
var raygunClient = app.Services.GetService<RaygunClient>();
raygunClient.SendingMessage += (sender, eventArgs) =>
{
if (eventArgs.Message.Details.Error.Message.Contains("Test exception"))
{
eventArgs.Cancel = true;
}
};
Strip wrapper exceptions
If you have common outer exceptions that wrap a valuable inner exception, you can specify these by using the multi-parameter method:
var raygunClient = app.Services.GetService<RaygunClient>();
raygunClient.AddWrapperExceptions(typeof(CustomWrapperException));
In this case, if a CustomWrapperException occurs, it will be removed and replaced with the actual InnerException that was the cause. Note that TargetInvocationException
is already added to the wrapper exception list. This method is useful if you have your own custom wrapper exceptions, or a framework is throwing exceptions using its own wrapper.
Locally running Raygun portal
Integrating Raygun into the orchestration project (AppHost) of your Aspire application (as described in Step 2 of the setup instructions) will cause a Raygun resource to be listed as highlighted below.
Clicking on the Endpoint link of that Raygun resource will open up a locally running Raygun portal in a new tab. The home page displays a list of the most recently captured crash reports from your Aspire projects.
Click on a crash report of interest to view more details. The Summary tab shows information about the exception itself, including the stacktrace. Any custom data and tags you've set will be displayed here too. An Http tab will be available when an HttpContext was present when the exception was observed. There you will be able to view properties of the web request and response. The Raw data tab shows the json payload that was sent between your Aspire projects and the local Raygun portal.
The crash report data display in the local Raygun portal app is stored in a Docker volume called "raygun-data". This will store up to 1,000 crash report instances. When new crash reports are ingested beyond this threshold, then the oldest crash reports will be deleted to maintain that limit.
This client is open source and available at the Raygun4Aspire repository.