diff --git a/StabilityMatrix/App.xaml.cs b/StabilityMatrix/App.xaml.cs index e4a158c5..2b741921 100644 --- a/StabilityMatrix/App.xaml.cs +++ b/StabilityMatrix/App.xaml.cs @@ -5,6 +5,7 @@ using System.IO; using System.Text.Json; using System.Text.Json.Serialization; using System.Windows; +using AsyncAwaitBestPractices; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -15,6 +16,7 @@ using Polly.Contrib.WaitAndRetry; using Polly.Extensions.Http; using Polly.Timeout; using Refit; +using Sentry; using StabilityMatrix.Api; using StabilityMatrix.Helper; using StabilityMatrix.Helper.Cache; @@ -37,7 +39,11 @@ namespace StabilityMatrix public partial class App : Application { private ServiceProvider? serviceProvider; - + public static bool IsSentryEnabled => !Debugger.IsAttached || Environment + .GetEnvironmentVariable("DEBUG_SENTRY")?.ToLowerInvariant() == "true"; + public static bool IsExceptionWindowEnabled => !Debugger.IsAttached || Environment + .GetEnvironmentVariable("DEBUG_EXCEPTION_WINDOW")?.ToLowerInvariant() == "true"; + public static IConfiguration Config { get; set; } public App() @@ -47,16 +53,36 @@ namespace StabilityMatrix .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true) .Build(); + // This needs to be done before OnStartup + // Or Sentry will not be initialized correctly + ConfigureErrorHandling(); } - private void App_OnStartup(object sender, StartupEventArgs e) + private void ConfigureErrorHandling() { - // Configure window exception handler when not in debug mode - if (!Debugger.IsAttached) + if (IsSentryEnabled) + { + SentrySdk.Init(o => + { + o.Dsn = "https://eac7a5ea065d44cf9a8565e0f1817da2@o4505314753380352.ingest.sentry.io/4505314756067328"; + o.StackTraceMode = StackTraceMode.Enhanced; + o.TracesSampleRate = 1.0; + o.IsGlobalModeEnabled = true; + // Enables Sentry's "Release Health" feature. + o.AutoSessionTracking = true; + // 1.0 to capture 100% of transactions for performance monitoring. + o.TracesSampleRate = 1.0; + }); + } + + if (IsSentryEnabled || IsExceptionWindowEnabled) { DispatcherUnhandledException += App_DispatcherUnhandledException; } + } + private void App_OnStartup(object sender, StartupEventArgs e) + { var serviceCollection = new ServiceCollection(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -164,6 +190,14 @@ namespace StabilityMatrix log.SetMinimumLevel(LogLevel.Trace); log.AddNLog(logConfig); }); + + // Default error handling for 'SafeFireAndForget' + SafeFireAndForgetExtensions.Initialize(); + SafeFireAndForgetExtensions.SetDefaultExceptionHandling(ex => + { + var logger = serviceProvider?.GetRequiredService>(); + logger?.LogError(ex, "Background Task failed: {ExceptionMessage}", ex.Message); + }); serviceProvider = serviceCollection.BuildServiceProvider(); var window = serviceProvider.GetRequiredService(); @@ -178,18 +212,27 @@ namespace StabilityMatrix [DoesNotReturn] private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) { + if (SentrySdk.IsEnabled) + { + SentrySdk.CaptureException(e.Exception); + } + var logger = serviceProvider?.GetRequiredService>(); logger?.LogCritical(e.Exception, "Unhandled Exception: {ExceptionMessage}", e.Exception.Message); - var vm = new ExceptionWindowViewModel - { - Exception = e.Exception - }; - var exceptionWindow = new ExceptionWindow + + if (IsExceptionWindowEnabled) { - DataContext = vm, - Owner = MainWindow - }; - exceptionWindow.ShowDialog(); + var vm = new ExceptionWindowViewModel + { + Exception = e.Exception + }; + var exceptionWindow = new ExceptionWindow + { + DataContext = vm, + Owner = MainWindow + }; + exceptionWindow.ShowDialog(); + } e.Handled = true; Environment.Exit(1); } diff --git a/StabilityMatrix/Services/NotificationBarService.cs b/StabilityMatrix/Services/NotificationBarService.cs index 6d64700a..8ab70584 100644 --- a/StabilityMatrix/Services/NotificationBarService.cs +++ b/StabilityMatrix/Services/NotificationBarService.cs @@ -1,5 +1,8 @@ -using System.Threading.Tasks; +using AsyncAwaitBestPractices; +using Wpf.Ui.Common; using Wpf.Ui.Contracts; +using Wpf.Ui.Controls; +using Wpf.Ui.Controls.IconElements; using Wpf.Ui.Services; namespace StabilityMatrix.Services; @@ -13,5 +16,11 @@ public class NotificationBarService : SnackbarService, INotificationBarService { public void ShowStartupNotifications() { + Timeout = 10000; + var linkIcon = new SymbolIcon(SymbolRegular.Link24); + var snackbar = ShowAsync( + "Welcome to StabilityMatrix!", + "You can join our Discord server for support and feedback.", linkIcon, ControlAppearance.Info); + snackbar.SafeFireAndForget(); } } diff --git a/StabilityMatrix/StabilityMatrix.csproj b/StabilityMatrix/StabilityMatrix.csproj index 54bf5803..f7075560 100644 --- a/StabilityMatrix/StabilityMatrix.csproj +++ b/StabilityMatrix/StabilityMatrix.csproj @@ -14,6 +14,7 @@ + @@ -32,6 +33,7 @@ +