forked from sim0n00ps/OF-DL
Use a logging sync to ensure GUI users are aware of any errors that occur while downloading
This commit is contained in:
parent
49cddd0608
commit
f1d3ac7ea3
@ -939,7 +939,8 @@ public class DownloadService(
|
|||||||
using HttpClient client = new();
|
using HttpClient client = new();
|
||||||
HttpRequestMessage request = new() { Method = HttpMethod.Get, RequestUri = new Uri(url) };
|
HttpRequestMessage request = new() { Method = HttpMethod.Get, RequestUri = new Uri(url) };
|
||||||
|
|
||||||
using HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, progressReporter.CancellationToken);
|
using HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead,
|
||||||
|
progressReporter.CancellationToken);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
Stream body = await response.Content.ReadAsStreamAsync(progressReporter.CancellationToken);
|
Stream body = await response.Content.ReadAsStreamAsync(progressReporter.CancellationToken);
|
||||||
|
|
||||||
|
|||||||
@ -7,8 +7,11 @@ namespace OF_DL.Services;
|
|||||||
|
|
||||||
public class LoggingService : ILoggingService
|
public class LoggingService : ILoggingService
|
||||||
{
|
{
|
||||||
public LoggingService()
|
private readonly ILogEventSink? _optionalErrorSink;
|
||||||
|
|
||||||
|
public LoggingService(ILogEventSink? optionalErrorSink = null)
|
||||||
{
|
{
|
||||||
|
_optionalErrorSink = optionalErrorSink;
|
||||||
LevelSwitch = new LoggingLevelSwitch();
|
LevelSwitch = new LoggingLevelSwitch();
|
||||||
InitializeLogger();
|
InitializeLogger();
|
||||||
}
|
}
|
||||||
@ -38,10 +41,17 @@ public class LoggingService : ILoggingService
|
|||||||
// Set the initial level to Error (until we've read from config)
|
// Set the initial level to Error (until we've read from config)
|
||||||
LevelSwitch.MinimumLevel = LogEventLevel.Error;
|
LevelSwitch.MinimumLevel = LogEventLevel.Error;
|
||||||
|
|
||||||
Log.Logger = new LoggerConfiguration()
|
LoggerConfiguration loggerConfiguration = new LoggerConfiguration()
|
||||||
.MinimumLevel.ControlledBy(LevelSwitch)
|
.MinimumLevel.ControlledBy(LevelSwitch)
|
||||||
.WriteTo.File("logs/OFDL.txt", rollingInterval: RollingInterval.Day)
|
.WriteTo.File("logs/OFDL.txt", rollingInterval: RollingInterval.Day);
|
||||||
.CreateLogger();
|
|
||||||
|
if (_optionalErrorSink != null)
|
||||||
|
{
|
||||||
|
loggerConfiguration = loggerConfiguration.WriteTo.Sink(_optionalErrorSink,
|
||||||
|
LogEventLevel.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Logger = loggerConfiguration.CreateLogger();
|
||||||
|
|
||||||
Log.Debug("Logging service initialized");
|
Log.Debug("Logging service initialized");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using OF_DL.Helpers;
|
using OF_DL.Helpers;
|
||||||
using OF_DL.Services;
|
using OF_DL.Services;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
@ -20,13 +19,10 @@ public static class Program
|
|||||||
// Parse command line arguments
|
// Parse command line arguments
|
||||||
HidePrivateInfo = args.Contains("--hide-private-info", StringComparer.OrdinalIgnoreCase);
|
HidePrivateInfo = args.Contains("--hide-private-info", StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
ServiceCollection services = new();
|
// Initialize the logging service to ensure that logs are written before Avalonia starts (with a new logging service)
|
||||||
services.AddSingleton<ILoggingService, LoggingService>();
|
LoggingService _ = new();
|
||||||
ServiceProvider tempProvider = services.BuildServiceProvider();
|
|
||||||
ILoggingService loggingService = tempProvider.GetRequiredService<ILoggingService>();
|
|
||||||
|
|
||||||
RegisterGlobalExceptionHandlers();
|
RegisterGlobalExceptionHandlers();
|
||||||
Log.Information("Starting OF DL GUI");
|
|
||||||
|
|
||||||
// Check if running in Docker and print a message
|
// Check if running in Docker and print a message
|
||||||
if (EnvironmentHelper.IsRunningInDocker())
|
if (EnvironmentHelper.IsRunningInDocker())
|
||||||
@ -40,7 +36,7 @@ public static class Program
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
HandleUnhandledException(ex, "Program.Main", isTerminating: true);
|
HandleUnhandledException(ex, "Program.Main", true);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -65,7 +61,7 @@ public static class Program
|
|||||||
TaskScheduler.UnobservedTaskException += (_, eventArgs) =>
|
TaskScheduler.UnobservedTaskException += (_, eventArgs) =>
|
||||||
{
|
{
|
||||||
HandleUnhandledException(eventArgs.Exception, "TaskScheduler.UnobservedTaskException",
|
HandleUnhandledException(eventArgs.Exception, "TaskScheduler.UnobservedTaskException",
|
||||||
isTerminating: false);
|
false);
|
||||||
eventArgs.SetObserved();
|
eventArgs.SetObserved();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
45
OF DL.Gui/Services/DownloadErrorLogTracking.cs
Normal file
45
OF DL.Gui/Services/DownloadErrorLogTracking.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using Serilog.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
|
namespace OF_DL.Gui.Services;
|
||||||
|
|
||||||
|
public sealed class DownloadErrorLogTracker
|
||||||
|
{
|
||||||
|
private int _sessionActive;
|
||||||
|
private int _errorLoggedInSession;
|
||||||
|
|
||||||
|
public bool IsSessionActive => Volatile.Read(ref _sessionActive) == 1;
|
||||||
|
|
||||||
|
public void StartSession()
|
||||||
|
{
|
||||||
|
Interlocked.Exchange(ref _errorLoggedInSession, 0);
|
||||||
|
Interlocked.Exchange(ref _sessionActive, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool StopSession()
|
||||||
|
{
|
||||||
|
Interlocked.Exchange(ref _sessionActive, 0);
|
||||||
|
return Volatile.Read(ref _errorLoggedInSession) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecordError()
|
||||||
|
{
|
||||||
|
if (!IsSessionActive)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Interlocked.Exchange(ref _errorLoggedInSession, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class DownloadErrorTrackingSink(DownloadErrorLogTracker tracker) : ILogEventSink
|
||||||
|
{
|
||||||
|
public void Emit(LogEvent logEvent)
|
||||||
|
{
|
||||||
|
if (logEvent.Level >= LogEventLevel.Error)
|
||||||
|
{
|
||||||
|
tracker.RecordError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using OF_DL.Gui.ViewModels;
|
using OF_DL.Gui.ViewModels;
|
||||||
using OF_DL.Gui.Views;
|
using OF_DL.Gui.Views;
|
||||||
using OF_DL.Services;
|
using OF_DL.Services;
|
||||||
|
using Serilog.Core;
|
||||||
|
|
||||||
namespace OF_DL.Gui.Services;
|
namespace OF_DL.Gui.Services;
|
||||||
|
|
||||||
@ -11,6 +12,8 @@ internal static class ServiceCollectionFactory
|
|||||||
{
|
{
|
||||||
IServiceCollection services = new ServiceCollection();
|
IServiceCollection services = new ServiceCollection();
|
||||||
|
|
||||||
|
services.AddSingleton<DownloadErrorLogTracker>();
|
||||||
|
services.AddSingleton<ILogEventSink, DownloadErrorTrackingSink>();
|
||||||
services.AddSingleton<ILoggingService, LoggingService>();
|
services.AddSingleton<ILoggingService, LoggingService>();
|
||||||
services.AddSingleton<IConfigService, ConfigService>();
|
services.AddSingleton<IConfigService, ConfigService>();
|
||||||
services.AddSingleton<IAuthService, AuthService>();
|
services.AddSingleton<IAuthService, AuthService>();
|
||||||
|
|||||||
@ -26,7 +26,8 @@ public partial class MainWindowViewModel(
|
|||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
IAuthService authService,
|
IAuthService authService,
|
||||||
IStartupService startupService,
|
IStartupService startupService,
|
||||||
IDownloadOrchestrationService downloadOrchestrationService) : ViewModelBase
|
IDownloadOrchestrationService downloadOrchestrationService,
|
||||||
|
DownloadErrorLogTracker downloadErrorLogTracker) : ViewModelBase
|
||||||
{
|
{
|
||||||
private enum SingleDownloadType
|
private enum SingleDownloadType
|
||||||
{
|
{
|
||||||
@ -946,6 +947,7 @@ public partial class MainWindowViewModel(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downloadErrorLogTracker.StartSession();
|
||||||
IsDownloading = true;
|
IsDownloading = true;
|
||||||
_workCancellationSource?.Dispose();
|
_workCancellationSource?.Dispose();
|
||||||
_workCancellationSource = new CancellationTokenSource();
|
_workCancellationSource = new CancellationTokenSource();
|
||||||
@ -1000,18 +1002,39 @@ public partial class MainWindowViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ThrowIfStopRequested();
|
ThrowIfStopRequested();
|
||||||
|
if (downloadErrorLogTracker.StopSession())
|
||||||
|
{
|
||||||
|
AppendLog(
|
||||||
|
"Errors were encountered during the download. Check the logs saved to the logs folder for details.");
|
||||||
|
}
|
||||||
|
|
||||||
eventHandler.OnScrapeComplete(DateTime.Now - start);
|
eventHandler.OnScrapeComplete(DateTime.Now - start);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
|
if (downloadErrorLogTracker.IsSessionActive)
|
||||||
|
{
|
||||||
|
downloadErrorLogTracker.StopSession();
|
||||||
|
}
|
||||||
|
|
||||||
AppendLog("Operation canceled.");
|
AppendLog("Operation canceled.");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
if (downloadErrorLogTracker.IsSessionActive)
|
||||||
|
{
|
||||||
|
downloadErrorLogTracker.StopSession();
|
||||||
|
}
|
||||||
|
|
||||||
AppendLog($"Download failed: {ex.Message}");
|
AppendLog($"Download failed: {ex.Message}");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
if (downloadErrorLogTracker.IsSessionActive)
|
||||||
|
{
|
||||||
|
downloadErrorLogTracker.StopSession();
|
||||||
|
}
|
||||||
|
|
||||||
IsDownloading = false;
|
IsDownloading = false;
|
||||||
_workCancellationSource?.Dispose();
|
_workCancellationSource?.Dispose();
|
||||||
_workCancellationSource = null;
|
_workCancellationSource = null;
|
||||||
@ -1228,6 +1251,7 @@ public partial class MainWindowViewModel(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downloadErrorLogTracker.StartSession();
|
||||||
IsDownloading = true;
|
IsDownloading = true;
|
||||||
_workCancellationSource?.Dispose();
|
_workCancellationSource?.Dispose();
|
||||||
_workCancellationSource = new CancellationTokenSource();
|
_workCancellationSource = new CancellationTokenSource();
|
||||||
@ -1306,18 +1330,39 @@ public partial class MainWindowViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ThrowIfStopRequested();
|
ThrowIfStopRequested();
|
||||||
|
if (downloadErrorLogTracker.StopSession())
|
||||||
|
{
|
||||||
|
AppendLog(
|
||||||
|
"Errors were encountered during the download. Check the logs saved to the logs folder for details.");
|
||||||
|
}
|
||||||
|
|
||||||
eventHandler.OnScrapeComplete(DateTime.Now - start);
|
eventHandler.OnScrapeComplete(DateTime.Now - start);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
|
if (downloadErrorLogTracker.IsSessionActive)
|
||||||
|
{
|
||||||
|
downloadErrorLogTracker.StopSession();
|
||||||
|
}
|
||||||
|
|
||||||
AppendLog("Operation canceled.");
|
AppendLog("Operation canceled.");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
if (downloadErrorLogTracker.IsSessionActive)
|
||||||
|
{
|
||||||
|
downloadErrorLogTracker.StopSession();
|
||||||
|
}
|
||||||
|
|
||||||
AppendLog($"Download failed: {ex.Message}");
|
AppendLog($"Download failed: {ex.Message}");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
if (downloadErrorLogTracker.IsSessionActive)
|
||||||
|
{
|
||||||
|
downloadErrorLogTracker.StopSession();
|
||||||
|
}
|
||||||
|
|
||||||
IsDownloading = false;
|
IsDownloading = false;
|
||||||
_workCancellationSource?.Dispose();
|
_workCancellationSource?.Dispose();
|
||||||
_workCancellationSource = null;
|
_workCancellationSource = null;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user