diff --git a/Needlework.Net/Needlework.Net.csproj b/Needlework.Net/Needlework.Net.csproj index 9ead956..f9b29b0 100644 --- a/Needlework.Net/Needlework.Net.csproj +++ b/Needlework.Net/Needlework.Net.csproj @@ -41,7 +41,6 @@ - diff --git a/Needlework.Net/Program.cs b/Needlework.Net/Program.cs index 8d4c623..0a1cc67 100644 --- a/Needlework.Net/Program.cs +++ b/Needlework.Net/Program.cs @@ -6,8 +6,10 @@ using Needlework.Net.ViewModels.MainWindow; using Needlework.Net.ViewModels.Pages; using Projektanker.Icons.Avalonia; using Projektanker.Icons.Avalonia.FontAwesome; +using Serilog; using System; using System.IO; +using System.Reflection; namespace Needlework.Net; @@ -44,9 +46,16 @@ class Program builder.AddSingleton(); builder.AddSingleton(); builder.AddSingletonsFromAssemblies(); - builder.AddHttpClient(); + var logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.File("Logs/NeedleworkDotNet.log", rollingInterval: RollingInterval.Day, shared: true) + .CreateLogger(); + logger.Debug("NeedleworkDotNet version: {Version}", Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "0.0.0.0"); + logger.Debug("OS description: {Description}", System.Runtime.InteropServices.RuntimeInformation.OSDescription); + builder.AddLogging(builder => builder.AddSerilog(logger)); + var services = builder.BuildServiceProvider(); return services; } diff --git a/Needlework.Net/ViewModels/MainWindow/MainWindowViewModel.cs b/Needlework.Net/ViewModels/MainWindow/MainWindowViewModel.cs index 2d473e6..30872d4 100644 --- a/Needlework.Net/ViewModels/MainWindow/MainWindowViewModel.cs +++ b/Needlework.Net/ViewModels/MainWindow/MainWindowViewModel.cs @@ -3,6 +3,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; using FluentAvalonia.UI.Controls; +using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; using Needlework.Net.Messages; using Needlework.Net.Models; @@ -42,8 +43,12 @@ public partial class MainWindowViewModel [ObservableProperty] private ObservableCollection _infoBarItems = []; - public MainWindowViewModel(IEnumerable pages, HttpClient httpClient, DialogService dialogService) + private readonly ILogger _logger; + + public MainWindowViewModel(IEnumerable pages, HttpClient httpClient, DialogService dialogService, ILogger logger) { + _logger = logger; + MenuItems = new AvaloniaList(pages .OrderBy(p => p.Index) .ThenBy(p => p.DisplayName) @@ -83,7 +88,11 @@ public partial class MainWindowViewModel var response = await HttpClient.SendAsync(request); var release = await response.Content.ReadFromJsonAsync(); - if (release == null) return; + if (release == null) + { + _logger.LogWarning("Release response is null"); + return; + } var currentVersion = int.Parse(Version.Replace(".", "")); @@ -101,18 +110,28 @@ public partial class MainWindowViewModel }); } } - catch (Exception) { } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to check for latest version"); + } } private async Task FetchDataAsync() { - var document = await Resources.GetOpenApiDocumentAsync(HttpClient); - HostDocument = document; - var handler = new OpenApiDocumentWrapper(document); - OpenApiDocumentWrapper = handler; + try + { + var document = await Resources.GetOpenApiDocumentAsync(HttpClient); + HostDocument = document; + var handler = new OpenApiDocumentWrapper(document); + OpenApiDocumentWrapper = handler; - WeakReferenceMessenger.Default.Send(new DataReadyMessage(handler)); - IsBusy = false; + WeakReferenceMessenger.Default.Send(new DataReadyMessage(handler)); + IsBusy = false; + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "Failed to fetch OpenAPI data"); + } } public void Receive(DataRequestMessage message) diff --git a/Needlework.Net/ViewModels/Pages/ConsoleViewModel.cs b/Needlework.Net/ViewModels/Pages/ConsoleViewModel.cs index f3be89c..59ae6e1 100644 --- a/Needlework.Net/ViewModels/Pages/ConsoleViewModel.cs +++ b/Needlework.Net/ViewModels/Pages/ConsoleViewModel.cs @@ -2,6 +2,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; +using Microsoft.Extensions.Logging; using Needlework.Net.Messages; using Needlework.Net.ViewModels.Shared; using System.Threading.Tasks; @@ -14,10 +15,11 @@ public partial class ConsoleViewModel : PageBase, IRecipient public IAvaloniaList RequestPaths { get; } = new AvaloniaList(); [ObservableProperty] private bool _isBusy = true; - [ObservableProperty] private LcuRequestViewModel _lcuRequest = new(); + [ObservableProperty] private LcuRequestViewModel _lcuRequest; - public ConsoleViewModel() : base("Console", "terminal", -200) + public ConsoleViewModel(ILogger lcuRequestViewModelLogger) : base("Console", "terminal", -200) { + _lcuRequest = new(lcuRequestViewModelLogger); WeakReferenceMessenger.Default.Register(this); } diff --git a/Needlework.Net/ViewModels/Pages/Endpoints/EndpointViewModel.cs b/Needlework.Net/ViewModels/Pages/Endpoints/EndpointViewModel.cs index 713ebdb..921ba54 100644 --- a/Needlework.Net/ViewModels/Pages/Endpoints/EndpointViewModel.cs +++ b/Needlework.Net/ViewModels/Pages/Endpoints/EndpointViewModel.cs @@ -1,7 +1,9 @@ using Avalonia.Collections; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Messaging; +using Microsoft.Extensions.Logging; using Needlework.Net.Messages; +using Needlework.Net.ViewModels.Shared; using System; using System.Linq; @@ -21,12 +23,12 @@ public partial class EndpointViewModel : ObservableObject public event EventHandler? PathOperationSelected; - public EndpointViewModel(string endpoint) + public EndpointViewModel(string endpoint, ILogger lcuRequestViewModelLogger) { Endpoint = endpoint; var handler = WeakReferenceMessenger.Default.Send().Response; - PathOperations = new AvaloniaList(handler.Plugins[endpoint].Select(x => new PathOperationViewModel(x))); + PathOperations = new AvaloniaList(handler.Plugins[endpoint].Select(x => new PathOperationViewModel(x, lcuRequestViewModelLogger))); FilteredPathOperations = new AvaloniaList(PathOperations); } diff --git a/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsNavigationViewModel.cs b/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsNavigationViewModel.cs index fe04b72..71a3d48 100644 --- a/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsNavigationViewModel.cs +++ b/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsNavigationViewModel.cs @@ -1,6 +1,8 @@ using Avalonia.Collections; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using Microsoft.Extensions.Logging; +using Needlework.Net.ViewModels.Shared; using System; namespace Needlework.Net.ViewModels.Pages.Endpoints; @@ -15,9 +17,9 @@ public partial class EndpointsNavigationViewModel : ObservableObject private readonly Action _onEndpointNavigation; - public EndpointsNavigationViewModel(IAvaloniaList plugins, Action onEndpointNavigation) + public EndpointsNavigationViewModel(IAvaloniaList plugins, Action onEndpointNavigation, ILogger lcuRequestViewModelLogger) { - _activeViewModel = _endpointsViewModel = new EndpointsViewModel(plugins, OnClicked); + _activeViewModel = _endpointsViewModel = new EndpointsViewModel(plugins, OnClicked, lcuRequestViewModelLogger); _onEndpointNavigation = onEndpointNavigation; } diff --git a/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsTabViewModel.cs b/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsTabViewModel.cs index 0b50f7e..cf9e6a8 100644 --- a/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsTabViewModel.cs +++ b/Needlework.Net/ViewModels/Pages/Endpoints/EndpointsTabViewModel.cs @@ -4,7 +4,9 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; using FluentAvalonia.UI.Controls; +using Microsoft.Extensions.Logging; using Needlework.Net.Messages; +using Needlework.Net.ViewModels.Shared; using System; namespace Needlework.Net.ViewModels.Pages.Endpoints; @@ -16,8 +18,11 @@ public partial class EndpointsTabViewModel : PageBase, IRecipient _lcuRequestViewModelLogger; + + public EndpointsTabViewModel(ILogger lcuRequestViewModelLogger) : base("Endpoints", "list-alt", -500) { + _lcuRequestViewModelLogger = lcuRequestViewModelLogger; WeakReferenceMessenger.Default.RegisterAll(this); } @@ -35,7 +40,7 @@ public partial class EndpointsTabViewModel : PageBase, IRecipient OnClicked { get; } - public EndpointsViewModel(IAvaloniaList plugins, Action onClicked) + private readonly ILogger _lcuRequestViewModelLogger; + + public EndpointsViewModel(IAvaloniaList plugins, Action onClicked, ILogger lcuRequestViewModelLogger) { Plugins = new AvaloniaList(plugins); Query = new AvaloniaList(plugins); OnClicked = onClicked; + _lcuRequestViewModelLogger = lcuRequestViewModelLogger; } partial void OnSearchChanged(string value) @@ -37,6 +42,6 @@ public partial class EndpointsViewModel : ObservableObject { if (string.IsNullOrEmpty(value)) return; - OnClicked.Invoke(new EndpointViewModel(value)); + OnClicked.Invoke(new EndpointViewModel(value, _lcuRequestViewModelLogger)); } } diff --git a/Needlework.Net/ViewModels/Pages/Endpoints/PathOperationViewModel.cs b/Needlework.Net/ViewModels/Pages/Endpoints/PathOperationViewModel.cs index 726828a..977cec6 100644 --- a/Needlework.Net/ViewModels/Pages/Endpoints/PathOperationViewModel.cs +++ b/Needlework.Net/ViewModels/Pages/Endpoints/PathOperationViewModel.cs @@ -1,5 +1,6 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using Microsoft.Extensions.Logging; using Needlework.Net.Models; using Needlework.Net.ViewModels.Shared; using System; @@ -16,11 +17,11 @@ public partial class PathOperationViewModel : ObservableObject [ObservableProperty] private bool _isBusy; [ObservableProperty] private Lazy _lcuRequest; - public PathOperationViewModel(PathOperation pathOperation) + public PathOperationViewModel(PathOperation pathOperation, ILogger lcuRequestViewModelLogger) { Path = pathOperation.Path; Operation = new OperationViewModel(pathOperation.Operation); - LcuRequest = new(() => new LcuRequestViewModel() + LcuRequest = new(() => new LcuRequestViewModel(lcuRequestViewModelLogger) { Method = pathOperation.Method.ToUpper() }); diff --git a/Needlework.Net/ViewModels/Pages/Websocket/WebsocketViewModel.cs b/Needlework.Net/ViewModels/Pages/Websocket/WebsocketViewModel.cs index 7f0fa4b..ea51159 100644 --- a/Needlework.Net/ViewModels/Pages/Websocket/WebsocketViewModel.cs +++ b/Needlework.Net/ViewModels/Pages/Websocket/WebsocketViewModel.cs @@ -3,11 +3,11 @@ using BlossomiShymae.GrrrLCU; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; +using Microsoft.Extensions.Logging; using Needlework.Net.Messages; using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics; using System.Linq; using System.Net.Http; using System.Text.Json; @@ -45,8 +45,11 @@ public partial class WebsocketViewModel : PageBase public IReadOnlyList FilteredEventLog => string.IsNullOrWhiteSpace(Search) ? EventLog : [.. EventLog.Where(x => x.Key.Contains(Search, StringComparison.InvariantCultureIgnoreCase))]; - public WebsocketViewModel(HttpClient httpClient) : base("Event Viewer", "plug", -100) + private readonly ILogger _logger; + + public WebsocketViewModel(HttpClient httpClient, ILogger logger) : base("Event Viewer", "plug", -100) { + _logger = logger; HttpClient = httpClient; EventLog.CollectionChanged += (s, e) => OnPropertyChanged(nameof(FilteredEventLog)); Task.Run(async () => @@ -66,6 +69,7 @@ public partial class WebsocketViewModel : PageBase } catch (HttpRequestException ex) { + _logger.LogError(ex, "Failed to get event types"); WeakReferenceMessenger.Default.Send(new InfoBarUpdateMessage(new("Failed to get event types", true, ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error, TimeSpan.FromSeconds(10)))); } } @@ -76,6 +80,7 @@ public partial class WebsocketViewModel : PageBase { if (Client != null) { + _logger.LogDebug("Disposing old connection"); foreach (var disposable in ClientDisposables) disposable.Dispose(); ClientDisposables.Clear(); @@ -105,6 +110,7 @@ public partial class WebsocketViewModel : PageBase }) { IsBackground = true }; thread.Start(); + _logger.LogDebug("Initialized new connection: {EventType}", EventType); TokenSource = tokenSource; } } @@ -129,12 +135,12 @@ public partial class WebsocketViewModel : PageBase private void OnReconnection(ReconnectionInfo info) { - Trace.WriteLine($"-- Reconnection --\nType{info.Type}"); + _logger.LogTrace("Reconnected: {Type}", info.Type); } private void OnDisconnection(DisconnectionInfo info) { - Trace.WriteLine($"-- Disconnection --\nType:{info.Type}\nSubProtocol:{info.SubProtocol}\nCloseStatus:{info.CloseStatus}\nCloseStatusDescription:{info.CloseStatusDescription}\nExceptionMessage:{info?.Exception?.Message}\n:InnerException:{info?.Exception?.InnerException}"); + _logger.LogTrace("Disconnected: {Type}", info.Type); InitializeWebsocket(); } diff --git a/Needlework.Net/ViewModels/Shared/LcuRequestViewModel.cs b/Needlework.Net/ViewModels/Shared/LcuRequestViewModel.cs index 52ceef8..fbe9c92 100644 --- a/Needlework.Net/ViewModels/Shared/LcuRequestViewModel.cs +++ b/Needlework.Net/ViewModels/Shared/LcuRequestViewModel.cs @@ -2,6 +2,7 @@ using BlossomiShymae.GrrrLCU; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Messaging; +using Microsoft.Extensions.Logging; using Needlework.Net.Messages; using Needlework.Net.ViewModels.MainWindow; using System; @@ -31,6 +32,13 @@ public partial class LcuRequestViewModel : ObservableObject public event EventHandler? RequestText; public event EventHandler? UpdateText; + private readonly ILogger _logger; + + public LcuRequestViewModel(ILogger logger) + { + _logger = logger; + } + partial void OnMethodChanged(string? oldValue, string? newValue) { if (newValue == null) return; @@ -59,6 +67,8 @@ public partial class LcuRequestViewModel : ObservableObject _ => throw new Exception("Method is not selected or missing."), }; + _logger.LogDebug("Sending request: {Tuple}", (Method, RequestPath)); + var processInfo = ProcessFinder.GetProcessInfo(); RequestText?.Invoke(this, this); var content = new StringContent(RequestBody ?? string.Empty, new System.Net.Http.Headers.MediaTypeHeaderValue("application/json")); @@ -88,6 +98,7 @@ public partial class LcuRequestViewModel : ObservableObject } catch (Exception ex) { + _logger.LogError(ex, "Request failed: {Tuple}", (Method, RequestPath)); WeakReferenceMessenger.Default.Send(new InfoBarUpdateMessage(new InfoBarViewModel("Request Failed", true, ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error, TimeSpan.FromSeconds(5)))); UpdateText?.Invoke(this, string.Empty);