From 5c53976cfebf275adb042038278973283a82814c Mon Sep 17 00:00:00 2001 From: Cinka Date: Mon, 5 May 2025 20:43:28 +0300 Subject: [PATCH] - rework: Logging logic and file logging --- Nebula.Launcher/App.axaml.cs | 20 +++ Nebula.Launcher/MessageBox/MessageView.axaml | 17 +++ .../MessageBox/MessageView.axaml.cs | 25 ++++ .../MessageBox/MessageWindow.axaml | 12 ++ .../MessageBox/MessageWindow.axaml.cs | 12 ++ Nebula.Launcher/Program.cs | 14 +- Nebula.Launcher/Services/DecompilerService.cs | 15 +- Nebula.Launcher/ViewModels/MainViewModel.cs | 6 +- .../Pages/ContentBrowserViewModel.cs | 11 +- .../ViewModels/Popup/AddFavoriteViewModel.cs | 6 +- .../ViewModels/ServerEntryModelView.cs | 18 ++- Nebula.Shared/Services/AssemblyService.cs | 19 +-- Nebula.Shared/Services/AuthService.cs | 4 +- .../Services/ConfigurationService.cs | 25 ++-- .../Services/ContentService.Download.cs | 24 ++-- .../Services/ContentService.Migration.cs | 4 +- Nebula.Shared/Services/ContentService.cs | 2 + Nebula.Shared/Services/DebugService.cs | 135 +++++++++++++----- Nebula.Shared/Services/EngineService.cs | 62 ++++---- Nebula.Shared/Services/HubService.cs | 9 +- .../Services/Logging/ConsoleLogger.cs | 13 -- Nebula.Shared/Services/Logging/ILogger.cs | 2 +- Nebula.Shared/Services/RestService.cs | 7 +- Nebula.Shared/Services/RunnerService.cs | 7 +- Nebula.sln.DotSettings.user | 1 + 25 files changed, 332 insertions(+), 138 deletions(-) create mode 100644 Nebula.Launcher/MessageBox/MessageView.axaml create mode 100644 Nebula.Launcher/MessageBox/MessageView.axaml.cs create mode 100644 Nebula.Launcher/MessageBox/MessageWindow.axaml create mode 100644 Nebula.Launcher/MessageBox/MessageWindow.axaml.cs delete mode 100644 Nebula.Shared/Services/Logging/ConsoleLogger.cs diff --git a/Nebula.Launcher/App.axaml.cs b/Nebula.Launcher/App.axaml.cs index 7450309..47c203a 100644 --- a/Nebula.Launcher/App.axaml.cs +++ b/Nebula.Launcher/App.axaml.cs @@ -7,6 +7,7 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Data.Core.Plugins; using Avalonia.Markup.Xaml; using Microsoft.Extensions.DependencyInjection; +using Nebula.Launcher.MessageBox; using Nebula.Launcher.ViewModels.ContentView; using Nebula.Launcher.Views; using Nebula.Shared; @@ -22,6 +23,25 @@ public class App : Application public override void OnFrameworkInitializationCompleted() { + if (!Program.IsNewInstance) + { + IMessageContainerProvider? provider = null; + switch (ApplicationLifetime) + { + case IClassicDesktopStyleApplicationLifetime desktop: + DisableAvaloniaDataAnnotationValidation(); + desktop.MainWindow = new MessageWindow(out provider); + break; + case ISingleViewApplicationLifetime singleViewPlatform: + singleViewPlatform.MainView = new MessageView(out provider); + break; + } + + provider?.ShowMessage("Launcher is already running.","hey shithead!"); + + return; + } + if (Design.IsDesignMode) { switch (ApplicationLifetime) diff --git a/Nebula.Launcher/MessageBox/MessageView.axaml b/Nebula.Launcher/MessageBox/MessageView.axaml new file mode 100644 index 0000000..5c39b61 --- /dev/null +++ b/Nebula.Launcher/MessageBox/MessageView.axaml @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/Nebula.Launcher/MessageBox/MessageView.axaml.cs b/Nebula.Launcher/MessageBox/MessageView.axaml.cs new file mode 100644 index 0000000..85d80ae --- /dev/null +++ b/Nebula.Launcher/MessageBox/MessageView.axaml.cs @@ -0,0 +1,25 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace Nebula.Launcher.MessageBox; + +public partial class MessageView : UserControl, IMessageContainerProvider +{ + public MessageView(out IMessageContainerProvider provider) + { + InitializeComponent(); + provider = this; + } + + public void ShowMessage(string message, string title) + { + Title.Content = title; + Message.Content = message; + } +} + +public interface IMessageContainerProvider +{ + public void ShowMessage(string message, string title); +} \ No newline at end of file diff --git a/Nebula.Launcher/MessageBox/MessageWindow.axaml b/Nebula.Launcher/MessageBox/MessageWindow.axaml new file mode 100644 index 0000000..581b1f1 --- /dev/null +++ b/Nebula.Launcher/MessageBox/MessageWindow.axaml @@ -0,0 +1,12 @@ + + + diff --git a/Nebula.Launcher/MessageBox/MessageWindow.axaml.cs b/Nebula.Launcher/MessageBox/MessageWindow.axaml.cs new file mode 100644 index 0000000..d7c2b3b --- /dev/null +++ b/Nebula.Launcher/MessageBox/MessageWindow.axaml.cs @@ -0,0 +1,12 @@ +using Avalonia.Controls; + +namespace Nebula.Launcher.MessageBox; + +public partial class MessageWindow : Window +{ + public MessageWindow(out IMessageContainerProvider provider) + { + InitializeComponent(); + Content = new MessageView(out provider); + } +} \ No newline at end of file diff --git a/Nebula.Launcher/Program.cs b/Nebula.Launcher/Program.cs index db6eff8..92ebd9e 100644 --- a/Nebula.Launcher/Program.cs +++ b/Nebula.Launcher/Program.cs @@ -1,13 +1,22 @@ -using Avalonia; +using System; +using System.Threading; +using Avalonia; namespace Nebula.Launcher; public static class Program { + private static Mutex? _mutex; + public static bool IsNewInstance; + [STAThread] public static void Main(string[] args) { + _mutex = new Mutex(true, $"Global\\Nebula.Launcher", out IsNewInstance); BuildAvaloniaApp() .StartWithClassicDesktopLifetime(args); + + if (IsNewInstance) + _mutex.ReleaseMutex(); } // Avalonia configuration, don't remove; also used by visual designer. @@ -18,4 +27,5 @@ public static class Program .WithInterFont() .LogToTrace(); } -} \ No newline at end of file +} + diff --git a/Nebula.Launcher/Services/DecompilerService.cs b/Nebula.Launcher/Services/DecompilerService.cs index b921eac..c6ea2d2 100644 --- a/Nebula.Launcher/Services/DecompilerService.cs +++ b/Nebula.Launcher/Services/DecompilerService.cs @@ -13,6 +13,7 @@ using Nebula.Shared.FileApis; using Nebula.Shared.FileApis.Interfaces; using Nebula.Shared.Models; using Nebula.Shared.Services; +using Nebula.Shared.Services.Logging; namespace Nebula.Launcher.Services; @@ -25,10 +26,11 @@ public sealed partial class DecompilerService [GenerateProperty] private ContentService ContentService {get;} [GenerateProperty] private FileService FileService {get;} [GenerateProperty] private CancellationService CancellationService {get;} - [GenerateProperty] private EngineService engineService {get;} - [GenerateProperty] private DebugService debugService {get;} + [GenerateProperty] private EngineService EngineService {get;} + [GenerateProperty] private DebugService DebugService {get;} private HttpClient _httpClient = new HttpClient(); + private ILogger _logger; private static string fullPath = Path.Join(FileService.RootPath,"ILSpy"); private static string executePath = Path.Join(fullPath, "ILSpy.exe"); @@ -50,7 +52,7 @@ public sealed partial class DecompilerService var buildInfo = await ContentService.GetBuildInfo(url, CancellationService.Token); - var engine = await engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion); + var engine = await EngineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion); if (engine is null) throw new Exception("Engine version not found: " + buildInfo.BuildInfo.Build.EngineVersion); @@ -73,12 +75,15 @@ public sealed partial class DecompilerService ((IDisposable)loadingHandler).Dispose(); - debugService.Log("File extracted. " + tmpDir); + _logger.Log("File extracted. " + tmpDir); OpenDecompiler(string.Join(' ', myTempDir.AllFiles.Select(f=>Path.Join(tmpDir, f))) + " --newinstance"); } - private void Initialise(){} + private void Initialise() + { + _logger = DebugService.GetLogger(this); + } private void InitialiseInDesignMode(){} private async Task EnsureILSpy(){ diff --git a/Nebula.Launcher/ViewModels/MainViewModel.cs b/Nebula.Launcher/ViewModels/MainViewModel.cs index 4c907b8..3c9d6c2 100644 --- a/Nebula.Launcher/ViewModels/MainViewModel.cs +++ b/Nebula.Launcher/ViewModels/MainViewModel.cs @@ -10,6 +10,7 @@ using Nebula.Launcher.ViewModels.Popup; using Nebula.Launcher.Views; using Nebula.Shared.Models; using Nebula.Shared.Services; +using Nebula.Shared.Services.Logging; using Nebula.Shared.Utils; namespace Nebula.Launcher.ViewModels; @@ -43,6 +44,8 @@ public partial class MainViewModel : ViewModelBase [GenerateProperty, DesignConstruct] private ViewHelperService ViewHelperService { get; } = default!; [GenerateProperty] private FileService FileService { get; } = default!; + private ILogger _logger; + public ObservableCollection Items { get; private set; } protected override void InitialiseInDesignMode() @@ -53,6 +56,7 @@ public partial class MainViewModel : ViewModelBase protected override void Initialise() { + _logger = DebugService.GetLogger(this); InitialiseInDesignMode(); PopupMessageService.OnPopupRequired += OnPopupRequired; @@ -160,7 +164,7 @@ public partial class MainViewModel : ViewModelBase break; case Exception error: var err = ViewHelperService.GetViewModel(); - DebugService.Error(error); + _logger.Error(error); err.AppendError(error); PopupMessage(err); break; diff --git a/Nebula.Launcher/ViewModels/Pages/ContentBrowserViewModel.cs b/Nebula.Launcher/ViewModels/Pages/ContentBrowserViewModel.cs index ec6f532..62007c7 100644 --- a/Nebula.Launcher/ViewModels/Pages/ContentBrowserViewModel.cs +++ b/Nebula.Launcher/ViewModels/Pages/ContentBrowserViewModel.cs @@ -15,6 +15,7 @@ using Nebula.Launcher.Views.Pages; using Nebula.Shared.FileApis; using Nebula.Shared.Models; using Nebula.Shared.Services; +using Nebula.Shared.Services.Logging; using Nebula.Shared.Utils; namespace Nebula.Launcher.ViewModels.Pages; @@ -31,6 +32,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel [ObservableProperty] private string _searchText = ""; private ContentEntry? _selectedEntry; + private ILogger _logger; [ObservableProperty] private string _serverText = ""; [ObservableProperty] private ContentViewBase? _contentView; public bool IsCustomContenView => ContentView != null; @@ -69,7 +71,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel var myTempFile = Path.Combine(Path.GetTempPath(), "tempie" + ext); if(TryGetContentViewer(ext, out var contentViewBase)){ - DebugService.Debug($"Opening custom context:{item.Value.Path}"); + _logger.Debug($"Opening custom context:{item.Value.Path}"); contentViewBase.InitialiseWithData(value.GetPath(), stream, value); stream.Dispose(); ContentView = contentViewBase; @@ -88,7 +90,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel }; - DebugService.Log("Opening " + myTempFile); + _logger.Log("Opening " + myTempFile); Process.Start(startInfo); return; @@ -122,6 +124,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel protected override void Initialise() { + _logger = DebugService.GetLogger(this); FillRoot(HubService.ServerList); HubService.HubServerChangedEventArgs += HubServerChangedEventArgs; @@ -173,7 +176,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel if (ServerText != SelectedEntry?.ServerName) SelectedEntry = await CreateEntry(ServerText); - DebugService.Debug("Going to:" + path.Path); + _logger.Debug("Going to:" + path.Path); var oriPath = path.Clone(); try @@ -214,7 +217,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel FileName = "explorer.exe", Arguments = tmpDir, }; - DebugService.Log("Opening " + tmpDir); + _logger.Log("Opening " + tmpDir); Process.Start(startInfo); } diff --git a/Nebula.Launcher/ViewModels/Popup/AddFavoriteViewModel.cs b/Nebula.Launcher/ViewModels/Popup/AddFavoriteViewModel.cs index ec05790..e1824e7 100644 --- a/Nebula.Launcher/ViewModels/Popup/AddFavoriteViewModel.cs +++ b/Nebula.Launcher/ViewModels/Popup/AddFavoriteViewModel.cs @@ -3,6 +3,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using Nebula.Launcher.ViewModels.Pages; using Nebula.Launcher.Views.Pages; using Nebula.Shared.Services; +using Nebula.Shared.Services.Logging; using Nebula.Shared.Utils; using AddFavoriteView = Nebula.Launcher.Views.Popup.AddFavoriteView; @@ -12,12 +13,15 @@ namespace Nebula.Launcher.ViewModels.Popup; [ConstructGenerator] public partial class AddFavoriteViewModel : PopupViewModelBase { + private ILogger _logger; + protected override void InitialiseInDesignMode() { } protected override void Initialise() { + _logger = DebugService.GetLogger(this); } [GenerateProperty] @@ -41,7 +45,7 @@ public partial class AddFavoriteViewModel : PopupViewModelBase catch (Exception e) { Error = e.Message; - DebugService.Error(e); + _logger.Error(e); } } } \ No newline at end of file diff --git a/Nebula.Launcher/ViewModels/ServerEntryModelView.cs b/Nebula.Launcher/ViewModels/ServerEntryModelView.cs index 3ab1a2e..f654f65 100644 --- a/Nebula.Launcher/ViewModels/ServerEntryModelView.cs +++ b/Nebula.Launcher/ViewModels/ServerEntryModelView.cs @@ -16,6 +16,7 @@ using Nebula.Launcher.ViewModels.Popup; using Nebula.Launcher.Views; using Nebula.Shared.Models; using Nebula.Shared.Services; +using Nebula.Shared.Services.Logging; using Nebula.Shared.Utils; namespace Nebula.Launcher.ViewModels; @@ -31,6 +32,8 @@ public partial class ServerEntryModelView : ViewModelBase private string _lastError = ""; private Process? _p; + private ILogger _logger; + private ILogger? _processLogger; private ServerInfo? _serverInfo; [ObservableProperty] private bool _tagDataVisible; @@ -89,7 +92,7 @@ public partial class ServerEntryModelView : ViewModelBase catch (Exception e) { Description = e.Message; - DebugService.Error(e); + _logger.Error(e); } return _serverInfo; @@ -109,6 +112,7 @@ public partial class ServerEntryModelView : ViewModelBase protected override void Initialise() { + _logger = DebugService.GetLogger(this); CurrLog = ViewHelperService.GetViewModel(); } @@ -144,7 +148,7 @@ public partial class ServerEntryModelView : ViewModelBase } catch (Exception e) { - DebugService.Error(e); + _logger.Error(e); Status = new ServerStatus("ErrorLand", $"ERROR: {e.Message}", [], "", -1, -1, -1, false, DateTime.Now, -1); @@ -211,6 +215,8 @@ public partial class ServerEntryModelView : ViewModelBase } if (Process is null) return; + + _processLogger = DebugService.GetLogger(buildInfo.BuildInfo.Build.Hash); Process.EnableRaisingEvents = true; @@ -240,11 +246,13 @@ public partial class ServerEntryModelView : ViewModelBase Process.ErrorDataReceived -= OnErrorDataReceived; Process.Exited -= OnExited; - DebugService.Log("PROCESS EXIT WITH CODE " + Process.ExitCode); + _processLogger?.Log("PROCESS EXIT WITH CODE " + Process.ExitCode); if (Process.ExitCode != 0) PopupMessageService.Popup($"Game exit with code {Process.ExitCode}.\nReason: {_lastError}"); + _processLogger?.Dispose(); + Process.Dispose(); Process = null; } @@ -254,7 +262,7 @@ public partial class ServerEntryModelView : ViewModelBase if (e.Data != null) { _lastError = e.Data; - DebugService.Error(e.Data); + _processLogger?.Error(e.Data); CurrLog.Append(e.Data); } } @@ -263,7 +271,7 @@ public partial class ServerEntryModelView : ViewModelBase { if (e.Data != null) { - DebugService.Log(e.Data); + _processLogger?.Log(e.Data); CurrLog.Append(e.Data); } } diff --git a/Nebula.Shared/Services/AssemblyService.cs b/Nebula.Shared/Services/AssemblyService.cs index 4c77946..db534e9 100644 --- a/Nebula.Shared/Services/AssemblyService.cs +++ b/Nebula.Shared/Services/AssemblyService.cs @@ -3,6 +3,7 @@ using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Loader; using Nebula.Shared.FileApis; +using Nebula.Shared.Services.Logging; using Robust.LoaderApi; using SharpZstd.Interop; @@ -12,19 +13,19 @@ namespace Nebula.Shared.Services; public class AssemblyService { private readonly List _assemblies = new(); - private readonly DebugService _debugService; + private readonly ILogger _logger; private readonly HashSet _resolvingAssemblies = new(); public AssemblyService(DebugService debugService) { - _debugService = debugService; - + _logger = debugService.GetLogger(this); + ZstdImportResolver.ResolveLibrary += (name, assembly1, path) => { if (name.Equals("SharpZstd.Native")) { - _debugService.Debug("RESOLVING SHARPZSTD THINK: " + name + " " + path); + _logger.Debug("RESOLVING SHARPZSTD THINK: " + name + " " + path); GetRuntimeInfo(out var platform, out var architecture, out var extension); var fileName = GetDllName(platform, architecture, extension); @@ -77,7 +78,7 @@ public class AssemblyService } assembly = AssemblyLoadContext.Default.LoadFromStream(asm, pdb); - _debugService.Log("LOADED ASSEMBLY " + name); + _logger.Log("LOADED ASSEMBLY " + name); if (!_assemblies.Contains(assembly)) _assemblies.Add(assembly); @@ -104,14 +105,14 @@ public class AssemblyService { if (_resolvingAssemblies.Contains(name.FullName)) { - _debugService.Debug($"Already resolving {name.Name}, skipping."); + _logger.Debug($"Already resolving {name.Name}, skipping."); return null; // Prevent recursive resolution } try { _resolvingAssemblies.Add(name.FullName); - _debugService.Debug($"Resolving assembly from FileAPI: {name.Name}"); + _logger.Debug($"Resolving assembly from FileAPI: {name.Name}"); return TryOpenAssembly(name.Name!, assemblyApi, out var assembly) ? assembly : null; } finally @@ -125,12 +126,12 @@ public class AssemblyService var ourDir = Path.GetDirectoryName(typeof(AssemblyApi).Assembly.Location); var a = Path.Combine(ourDir!, unmanaged); - _debugService.Debug($"Loading dll lib: {a}"); + _logger.Debug($"Loading dll lib: {a}"); if (NativeLibrary.TryLoad(a, out var handle)) return handle; - _debugService.Error("Loading dll error! Not found"); + _logger.Error("Loading dll error! Not found"); return IntPtr.Zero; } diff --git a/Nebula.Shared/Services/AuthService.cs b/Nebula.Shared/Services/AuthService.cs index d6e2b7b..fb2b012 100644 --- a/Nebula.Shared/Services/AuthService.cs +++ b/Nebula.Shared/Services/AuthService.cs @@ -3,6 +3,7 @@ using System.Net.Http.Headers; using System.Text.Json; using System.Text.Json.Serialization; using Nebula.Shared.Models.Auth; +using Nebula.Shared.Services.Logging; using Nebula.Shared.Utils; namespace Nebula.Shared.Services; @@ -15,6 +16,7 @@ public class AuthService( { private readonly HttpClient _httpClient = new(); public CurrentAuthInfo? SelectedAuth { get; private set; } + private readonly ILogger _logger = debugService.GetLogger("AuthService"); public async Task Auth(AuthLoginPassword authLoginPassword, string? code = null) { @@ -22,7 +24,7 @@ public class AuthService( var login = authLoginPassword.Login; var password = authLoginPassword.Password; - debugService.Debug($"Auth to {authServer}api/auth/authenticate {login}"); + _logger.Debug($"Auth to {authServer}api/auth/authenticate {login}"); var authUrl = new Uri($"{authServer}api/auth/authenticate"); diff --git a/Nebula.Shared/Services/ConfigurationService.cs b/Nebula.Shared/Services/ConfigurationService.cs index 6bd4db6..7bbd5c8 100644 --- a/Nebula.Shared/Services/ConfigurationService.cs +++ b/Nebula.Shared/Services/ConfigurationService.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json; using Nebula.Shared.FileApis.Interfaces; +using Nebula.Shared.Services.Logging; using Robust.LoaderApi; namespace Nebula.Shared.Services; @@ -32,14 +33,12 @@ public static class ConVarBuilder [ServiceRegister] public class ConfigurationService { - private readonly DebugService _debugService; - public IReadWriteFileApi ConfigurationApi { get; init; } + private readonly ILogger _logger; public ConfigurationService(FileService fileService, DebugService debugService) { - _debugService = debugService ?? throw new ArgumentNullException(nameof(debugService)); - + _logger = debugService.GetLogger(this); ConfigurationApi = fileService.CreateFileApi("config"); } @@ -56,17 +55,17 @@ public class ConfigurationService var obj = JsonSerializer.Deserialize(stream); if (obj != null) { - _debugService.Log($"Successfully loaded config: {conVar.Name}"); + _logger.Log($"Successfully loaded config: {conVar.Name}"); return obj; } } } catch (Exception e) { - _debugService.Error($"Error loading config for {conVar.Name}: {e.Message}"); + _logger.Error($"Error loading config for {conVar.Name}: {e.Message}"); } - _debugService.Log($"Using default value for config: {conVar.Name}"); + _logger.Log($"Using default value for config: {conVar.Name}"); return conVar.DefaultValue; } @@ -83,7 +82,7 @@ public class ConfigurationService var obj = JsonSerializer.Deserialize(stream); if (obj != null) { - _debugService.Log($"Successfully loaded config: {conVar.Name}"); + _logger.Log($"Successfully loaded config: {conVar.Name}"); value = obj; return true; } @@ -91,10 +90,10 @@ public class ConfigurationService } catch (Exception e) { - _debugService.Error($"Error loading config for {conVar.Name}: {e.Message}"); + _logger.Error($"Error loading config for {conVar.Name}: {e.Message}"); } - _debugService.Log($"Using default value for config: {conVar.Name}"); + _logger.Log($"Using default value for config: {conVar.Name}"); return false; } @@ -105,14 +104,14 @@ public class ConfigurationService if (!conVar.Type.IsInstanceOfType(value)) { - _debugService.Error( + _logger.Error( $"Type mismatch for config {conVar.Name}. Expected {conVar.Type}, got {value.GetType()}."); return; } try { - _debugService.Log($"Saving config: {conVar.Name}"); + _logger.Log($"Saving config: {conVar.Name}"); var serializedData = JsonSerializer.Serialize(value); using var stream = new MemoryStream(); @@ -125,7 +124,7 @@ public class ConfigurationService } catch (Exception e) { - _debugService.Error($"Error saving config for {conVar.Name}: {e.Message}"); + _logger.Error($"Error saving config for {conVar.Name}: {e.Message}"); } } diff --git a/Nebula.Shared/Services/ContentService.Download.cs b/Nebula.Shared/Services/ContentService.Download.cs index 9d32d2a..e2fabb3 100644 --- a/Nebula.Shared/Services/ContentService.Download.cs +++ b/Nebula.Shared/Services/ContentService.Download.cs @@ -38,7 +38,7 @@ public partial class ContentService items = allItems.Where(a=> !hashApi.Has(a)).ToList(); - debugService.Log("Download Count:" + items.Count); + _logger.Log("Download Count:" + items.Count); await Download(downloadUri, items, hashApi, loadingHandler, cancellationToken); return hashApi; @@ -47,15 +47,15 @@ public partial class ContentService public async Task EnsureItems(RobustManifestInfo info, ILoadingHandler loadingHandler, CancellationToken cancellationToken) { - debugService.Log("Getting manifest: " + info.Hash); + _logger.Log("Getting manifest: " + info.Hash); if (ManifestFileApi.TryOpen(info.Hash, out var stream)) { - debugService.Log("Loading manifest from: " + info.Hash); + _logger.Log("Loading manifest from: " + info.Hash); return await EnsureItems(new ManifestReader(stream), info.DownloadUri, loadingHandler, cancellationToken); } - debugService.Log("Fetching manifest from: " + info.ManifestUri); + _logger.Log("Fetching manifest from: " + info.ManifestUri); var response = await _http.GetAsync(info.ManifestUri, cancellationToken); if (!response.IsSuccessStatusCode) throw new Exception(); @@ -69,7 +69,7 @@ public partial class ContentService public void Unpack(HashApi hashApi, IWriteFileApi otherApi, ILoadingHandler loadingHandler) { - debugService.Log("Unpack manifest files"); + _logger.Log("Unpack manifest files"); var items = hashApi.Manifest.Values.ToList(); loadingHandler.AppendJob(items.Count); @@ -82,13 +82,13 @@ public partial class ContentService { if (hashApi.TryOpen(item, out var stream)) { - debugService.Log($"Unpack {item.Hash} to: {item.Path}"); + _logger.Log($"Unpack {item.Hash} to: {item.Path}"); otherApi.Save(item.Hash, stream); stream.Close(); } else { - debugService.Error("OH FUCK!! " + item.Path); + _logger.Error("OH FUCK!! " + item.Path); } loadingHandler.AppendResolvedJob(); @@ -105,13 +105,13 @@ public partial class ContentService { if (toDownload.Count == 0 || cancellationToken.IsCancellationRequested) { - debugService.Log("Nothing to download! Fuck this!"); + _logger.Log("Nothing to download! Fuck this!"); return; } var downloadJobWatch = loadingHandler.GetQueryJob(); - debugService.Log("Downloading from: " + contentCdn); + _logger.Log("Downloading from: " + contentCdn); var requestBody = new byte[toDownload.Count * 4]; var reqI = 0; @@ -135,7 +135,7 @@ public partial class ContentService if (cancellationToken.IsCancellationRequested) { - debugService.Log("Downloading is cancelled!"); + _logger.Log("Downloading is cancelled!"); return; } @@ -185,7 +185,7 @@ public partial class ContentService { if (cancellationToken.IsCancellationRequested) { - debugService.Log("Downloading is cancelled!"); + _logger.Log("Downloading is cancelled!"); decompressContext?.Dispose(); compressContext?.Dispose(); return; @@ -230,7 +230,7 @@ public partial class ContentService using var fileStream = new MemoryStream(data.ToArray()); hashApi.Save(item, fileStream); - debugService.Log("file saved:" + item.Path); + _logger.Log("file saved:" + item.Path); loadingHandler.AppendResolvedJob(); i += 1; } diff --git a/Nebula.Shared/Services/ContentService.Migration.cs b/Nebula.Shared/Services/ContentService.Migration.cs index a20547a..b62013c 100644 --- a/Nebula.Shared/Services/ContentService.Migration.cs +++ b/Nebula.Shared/Services/ContentService.Migration.cs @@ -7,12 +7,12 @@ public partial class ContentService { public bool CheckMigration(ILoadingHandler loadingHandler) { - debugService.Log("Checking migration..."); + _logger.Log("Checking migration..."); var migrationList = ContentFileApi.AllFiles.Where(f => !f.Contains("\\")).ToList(); if(migrationList.Count == 0) return false; - debugService.Log($"Found {migrationList.Count} migration files. Starting migration..."); + _logger.Log($"Found {migrationList.Count} migration files. Starting migration..."); Task.Run(() => DoMigration(loadingHandler, migrationList)); return true; } diff --git a/Nebula.Shared/Services/ContentService.cs b/Nebula.Shared/Services/ContentService.cs index b5223af..b4a6447 100644 --- a/Nebula.Shared/Services/ContentService.cs +++ b/Nebula.Shared/Services/ContentService.cs @@ -1,5 +1,6 @@ using System.Data; using Nebula.Shared.Models; +using Nebula.Shared.Services.Logging; namespace Nebula.Shared.Services; @@ -11,6 +12,7 @@ public partial class ContentService( FileService fileService) { private readonly HttpClient _http = new(); + private readonly ILogger _logger = debugService.GetLogger("ContentService"); public async Task GetBuildInfo(RobustUrl url, CancellationToken cancellationToken) { diff --git a/Nebula.Shared/Services/DebugService.cs b/Nebula.Shared/Services/DebugService.cs index 742dfa3..00a7186 100644 --- a/Nebula.Shared/Services/DebugService.cs +++ b/Nebula.Shared/Services/DebugService.cs @@ -1,47 +1,33 @@ +using System.Reflection; +using Nebula.Shared.FileApis; using Nebula.Shared.Services.Logging; +using Robust.LoaderApi; namespace Nebula.Shared.Services; [ServiceRegister] public class DebugService : IDisposable { - public ILogger Logger; - - public DebugService(ILogger logger) + private ServiceLogger Root {get; set;} + + public DebugService() { - Logger = logger; + Root = new ServiceLogger("Root"); + } + + public ILogger GetLogger(string loggerName) + { + return Root.GetLogger(loggerName); + } + + public ILogger GetLogger(object objectToLog) + { + return Root.GetLogger(objectToLog.GetType().Name); } public void Dispose() { - - } - - public void Debug(string message) - { - Log(LoggerCategory.Debug, message); - } - - public void Error(string message) - { - Log(LoggerCategory.Error, message); - } - - public void Log(string message) - { - Log(LoggerCategory.Log, message); - } - - public void Error(Exception e) - { - Error(e.Message + "\r\n" + e.StackTrace); - if (e.InnerException != null) - Error(e.InnerException); - } - - private void Log(LoggerCategory category, string message) - { - Logger.Log(category, message); + Root.Dispose(); } } @@ -50,4 +36,89 @@ public enum LoggerCategory Log, Debug, Error +} + +internal class ServiceLogger : ILogger +{ + public ServiceLogger? Root { get; private set; } + public ServiceLogger(string category) + { + Category = category; + + var directory = Path.Combine(FileService.RootPath,"log", Assembly.GetEntryAssembly()?.GetName().Name ?? "App"); + if(!Directory.Exists(directory)) Directory.CreateDirectory(directory); + + _fileStream = File.Open(Path.Combine(directory,$"{Category}.log"), FileMode.Create, FileAccess.Write, FileShare.Read); + _streamWriter = new StreamWriter(_fileStream); + } + + public string Category { get; init; } + + private Dictionary Childs { get; init; } = new(); + + private readonly FileStream _fileStream; + private readonly StreamWriter _streamWriter; + + public ServiceLogger GetLogger(string category) + { + if (Childs.TryGetValue(category, out var logger)) + return logger; + + logger = new ServiceLogger(category); + logger.Root = this; + Childs.Add(category, logger); + return logger; + } + + public void Log(LoggerCategory loggerCategory, string message) + { + var output = + $"[{DateTime.Now.ToUniversalTime():yyyy-MM-dd HH:mm:ss}][{Enum.GetName(loggerCategory)}][{Category}]: {message}"; + Console.WriteLine(output); + + LogToFile(output); + } + + private void LogToFile(string output) + { + Root?.LogToFile(output); + _streamWriter.WriteLine(output); + _streamWriter.Flush(); + } + + public void Dispose() + { + _fileStream.Dispose(); + _streamWriter.Dispose(); + foreach (var (_, child) in Childs) + { + child.Dispose(); + } + Childs.Clear(); + } +} + +public static class LoggerExtensions +{ + public static void Debug(this ILogger logger,string message) + { + logger.Log(LoggerCategory.Debug, message); + } + + public static void Error(this ILogger logger,string message) + { + logger.Log(LoggerCategory.Error, message); + } + + public static void Log(this ILogger logger,string message) + { + logger.Log(LoggerCategory.Log, message); + } + + public static void Error(this ILogger logger,Exception e) + { + Error(logger,e.Message + "\r\n" + e.StackTrace); + if (e.InnerException != null) + Error(logger, e.InnerException); + } } \ No newline at end of file diff --git a/Nebula.Shared/Services/EngineService.cs b/Nebula.Shared/Services/EngineService.cs index f2622c2..8c1b61a 100644 --- a/Nebula.Shared/Services/EngineService.cs +++ b/Nebula.Shared/Services/EngineService.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using Nebula.Shared.FileApis; using Nebula.Shared.FileApis.Interfaces; using Nebula.Shared.Models; +using Nebula.Shared.Services.Logging; using Nebula.Shared.Utils; namespace Nebula.Shared.Services; @@ -10,34 +11,46 @@ namespace Nebula.Shared.Services; public sealed class EngineService { private readonly AssemblyService _assemblyService; - private readonly DebugService _debugService; private readonly FileService _fileService; private readonly RestService _restService; private readonly ConfigurationService _varService; private readonly Task _currInfoTask; private readonly IReadWriteFileApi _engineFileApi; + private readonly ILogger _logger; - private ModulesInfo _modulesInfo = default!; - private Dictionary _versionsInfo = default!; + private ModulesInfo? _modulesInfo; + private Dictionary? _versionsInfo; public EngineService(RestService restService, DebugService debugService, ConfigurationService varService, FileService fileService, AssemblyService assemblyService) { _restService = restService; - _debugService = debugService; + _logger = debugService.GetLogger(this); _varService = varService; _fileService = fileService; _assemblyService = assemblyService; _engineFileApi = fileService.CreateFileApi("engine"); - _currInfoTask = Task.Run(() => LoadEngineManifest(CancellationToken.None)); } + public void GetEngineInfo(out ModulesInfo modulesInfo, out Dictionary versionsInfo) + { + if(!_currInfoTask.IsCompleted) _currInfoTask.Wait(); + if(_currInfoTask.Exception != null) throw new Exception("Error while loading engine manifest:",_currInfoTask.Exception); + + if(_modulesInfo == null || _versionsInfo == null) throw new NullReferenceException("Engine manifest is null"); + + modulesInfo = _modulesInfo; + versionsInfo = _versionsInfo; + } + public async Task LoadEngineManifest(CancellationToken cancellationToken) { + _logger.Log("start fetching engine manifest"); _versionsInfo = await LoadExacManifest(CurrentConVar.EngineManifestUrl, CurrentConVar.EngineManifestBackup, cancellationToken); _modulesInfo = await LoadExacManifest(CurrentConVar.EngineModuleManifestUrl, CurrentConVar.ModuleManifestBackup, cancellationToken); + _logger.Log("fetched engine manifest successfully"); } private async Task LoadExacManifest(ConVar conVar,ConVar backup,CancellationToken cancellationToken) @@ -48,7 +61,7 @@ public sealed class EngineService { try { - _debugService.Log("Fetching engine manifest from: " + manifestUrl); + _logger.Log("Fetching engine manifest from: " + manifestUrl); var info = await _restService.GetAsync( new Uri(manifestUrl), cancellationToken); @@ -57,11 +70,11 @@ public sealed class EngineService } catch (Exception e) { - _debugService.Error($"error while attempt fetch engine manifest: {e.Message}"); + _logger.Error($"error while attempt fetch engine manifest: {e.Message}"); } } - _debugService.Debug("Trying fallback module manifest..."); + _logger.Debug("Trying fallback module manifest..."); if (!_varService.TryGetConfigValue(backup, out var moduleInfo)) { throw new Exception("No module info data available"); @@ -72,9 +85,9 @@ public sealed class EngineService public EngineBuildInfo? GetVersionInfo(string version) { - CheckAndWaitValidation(); + GetEngineInfo(out var modulesInfo, out var engineVersionInfo); - if (!_versionsInfo.TryGetValue(version, out var foundVersion)) + if (!engineVersionInfo.TryGetValue(version, out var foundVersion)) return null; if (foundVersion.RedirectVersion != null) @@ -83,7 +96,7 @@ public sealed class EngineService var bestRid = RidUtility.FindBestRid(foundVersion.Platforms.Keys); if (bestRid == null) bestRid = "linux-x64"; - _debugService.Log("Selecting RID" + bestRid); + _logger.Log("Selecting RID" + bestRid); return foundVersion.Platforms[bestRid]; } @@ -96,7 +109,7 @@ public sealed class EngineService public async Task EnsureEngine(string version) { - _debugService.Log("Ensure engine " + version); + _logger.Log("Ensure engine " + version); if (!TryOpen(version)) await DownloadEngine(version); @@ -119,7 +132,7 @@ public sealed class EngineService if (!TryGetVersionInfo(version, out var info)) return; - _debugService.Log("Downloading engine version " + version); + _logger.Log("Downloading engine version " + version); using var client = new HttpClient(); var s = await client.GetStreamAsync(info.Url); _engineFileApi.Save(version, s); @@ -140,9 +153,9 @@ public sealed class EngineService public EngineBuildInfo? GetModuleBuildInfo(string moduleName, string version) { - CheckAndWaitValidation(); + GetEngineInfo(out var modulesInfo, out var engineVersionInfo); - if (!_modulesInfo.Modules.TryGetValue(moduleName, out var module) || + if (!modulesInfo.Modules.TryGetValue(moduleName, out var module) || !module.Versions.TryGetValue(version, out var value)) return null; @@ -160,10 +173,10 @@ public sealed class EngineService public string ResolveModuleVersion(string moduleName, string engineVersion) { - CheckAndWaitValidation(); + GetEngineInfo(out var modulesInfo, out var engineVersionInfo); var engineVersionObj = Version.Parse(engineVersion); - var module = _modulesInfo.Modules[moduleName]; + var module = modulesInfo.Modules[moduleName]; var selectedVersion = module.Versions.Select(kv => new { Version = Version.Parse(kv.Key), kv.Key, kv }) .Where(kv => engineVersionObj >= kv.Version) .MaxBy(kv => kv.Version); @@ -185,7 +198,9 @@ public sealed class EngineService try { - return _assemblyService.Mount(_fileService.OpenZip(fileName, _engineFileApi) ?? throw new InvalidOperationException($"{fileName} is not exist!")); + return _assemblyService.Mount( + _fileService.OpenZip(fileName, _engineFileApi) ?? + throw new InvalidOperationException($"{fileName} is not exist!")); } catch (Exception) { @@ -199,7 +214,7 @@ public sealed class EngineService if (!TryGetModuleBuildInfo(moduleName, moduleVersion, out var info)) return; - _debugService.Log("Downloading engine module version " + moduleVersion); + _logger.Log("Downloading engine module version " + moduleVersion); using var client = new HttpClient(); var s = await client.GetStreamAsync(info.Url); _engineFileApi.Save(ConcatName(moduleName, moduleVersion), s); @@ -210,13 +225,4 @@ public sealed class EngineService { return moduleName + "" + moduleVersion; } - - private void CheckAndWaitValidation() - { - if (_currInfoTask.IsCompleted) - return; - - _debugService.Debug("thinks is not done yet, please wait"); - _currInfoTask.Wait(); - } } \ No newline at end of file diff --git a/Nebula.Shared/Services/HubService.cs b/Nebula.Shared/Services/HubService.cs index 63c756b..25c3ce1 100644 --- a/Nebula.Shared/Services/HubService.cs +++ b/Nebula.Shared/Services/HubService.cs @@ -1,4 +1,5 @@ using Nebula.Shared.Models; +using Nebula.Shared.Services.Logging; namespace Nebula.Shared.Services; @@ -7,7 +8,7 @@ public class HubService { private readonly ConfigurationService _configurationService; private readonly RestService _restService; - private readonly DebugService _debugService; + private readonly ILogger _logger; private readonly List _serverList = new(); @@ -19,7 +20,7 @@ public class HubService { _configurationService = configurationService; _restService = restService; - _debugService = debugService; + _logger = debugService.GetLogger(this); UpdateHub(); } @@ -54,8 +55,8 @@ public class HubService } catch (Exception e) { - _debugService.Error($"Failed to get servers for {uri}"); - _debugService.Error(e); + _logger.Error($"Failed to get servers for {uri}"); + _logger.Error(e); exception = e; } } diff --git a/Nebula.Shared/Services/Logging/ConsoleLogger.cs b/Nebula.Shared/Services/Logging/ConsoleLogger.cs deleted file mode 100644 index 7a35728..0000000 --- a/Nebula.Shared/Services/Logging/ConsoleLogger.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Nebula.Shared.Services.Logging; - -[ServiceRegister(typeof(ILogger))] -public class ConsoleLogger : ILogger -{ - public void Log(LoggerCategory loggerCategory, string message) - { - Console.ForegroundColor = ConsoleColor.DarkCyan; - Console.Write($"[{Enum.GetName(loggerCategory)}] "); - Console.ResetColor(); - Console.WriteLine(message); - } -} \ No newline at end of file diff --git a/Nebula.Shared/Services/Logging/ILogger.cs b/Nebula.Shared/Services/Logging/ILogger.cs index 51bb543..27d7e22 100644 --- a/Nebula.Shared/Services/Logging/ILogger.cs +++ b/Nebula.Shared/Services/Logging/ILogger.cs @@ -1,6 +1,6 @@ namespace Nebula.Shared.Services.Logging; -public interface ILogger +public interface ILogger : IDisposable { public void Log(LoggerCategory loggerCategory, string message); } \ No newline at end of file diff --git a/Nebula.Shared/Services/RestService.cs b/Nebula.Shared/Services/RestService.cs index 24bccdb..5cc2c25 100644 --- a/Nebula.Shared/Services/RestService.cs +++ b/Nebula.Shared/Services/RestService.cs @@ -2,6 +2,7 @@ using System.Globalization; using System.Net; using System.Text; using System.Text.Json; +using Nebula.Shared.Services.Logging; using Nebula.Shared.Utils; namespace Nebula.Shared.Services; @@ -10,7 +11,7 @@ namespace Nebula.Shared.Services; public class RestService { private readonly HttpClient _client = new(); - private readonly DebugService _debug; + private readonly ILogger _logger; private readonly JsonSerializerOptions _serializerOptions = new() { @@ -20,7 +21,7 @@ public class RestService public RestService(DebugService debug) { - _debug = debug; + _logger = debug.GetLogger(this); } public async Task GetAsync(Uri uri, CancellationToken cancellationToken) where T : notnull @@ -37,7 +38,7 @@ public class RestService } catch (Exception e) { - _debug.Error(e); + _logger.Error(e); return defaultValue; } } diff --git a/Nebula.Shared/Services/RunnerService.cs b/Nebula.Shared/Services/RunnerService.cs index 9f8465e..90c1343 100644 --- a/Nebula.Shared/Services/RunnerService.cs +++ b/Nebula.Shared/Services/RunnerService.cs @@ -1,4 +1,5 @@ using Nebula.Shared.Models; +using Nebula.Shared.Services.Logging; using Robust.LoaderApi; namespace Nebula.Shared.Services; @@ -11,10 +12,12 @@ public sealed class RunnerService( EngineService engineService, AssemblyService assemblyService) { + private ILogger _logger = debugService.GetLogger("RunnerService"); + public async Task PrepareRun(RobustBuildInfo buildInfo, ILoadingHandler loadingHandler, CancellationToken cancellationToken) { - debugService.Log("Prepare Content!"); + _logger.Log("Prepare Content!"); var engine = await engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion); @@ -29,7 +32,7 @@ public sealed class RunnerService( ILoadingHandler loadingHandler, CancellationToken cancellationToken) { - debugService.Log("Start Content!"); + _logger.Log("Start Content!"); var engine = await engineService.EnsureEngine(buildInfo.BuildInfo.Build.EngineVersion); diff --git a/Nebula.sln.DotSettings.user b/Nebula.sln.DotSettings.user index 244caef..ab59f99 100644 --- a/Nebula.sln.DotSettings.user +++ b/Nebula.sln.DotSettings.user @@ -1,4 +1,5 @@  + ForceIncluded ForceIncluded ForceIncluded ForceIncluded