From 7afce07d2169e343bd57ed1e4b39fc20ad5e8937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Kov=C3=A1cs?= Date: Thu, 6 Jul 2023 11:39:06 +0200 Subject: [PATCH] Two-stage logging --- .../Avalonia/FileTime.GuiApp.App/App.axaml.cs | 10 +- .../FileTime.GuiApp.App.csproj | 1 + .../Avalonia/FileTime.GuiApp.App/Program.cs | 101 ++++++++++++------ .../Avalonia/FileTime.GuiApp.App/Startup.cs | 43 ++++---- .../Logging/ToastMessageSink.cs | 10 +- 5 files changed, 99 insertions(+), 66 deletions(-) diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.App/App.axaml.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.App/App.axaml.cs index cd8b998..b0c8c66 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.App/App.axaml.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.App/App.axaml.cs @@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging; namespace FileTime.GuiApp.App; -public partial class App : Application +public class App : Application { static App() { @@ -28,16 +28,12 @@ public partial class App : Application .RegisterLogging() .RegisterServices() .AddViewModels() - .BuildServiceProvider() - .InitSerilog(); + .BuildServiceProvider(); var logger = DI.ServiceProvider.GetRequiredService>(); logger.LogInformation("App initialization completed"); } - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); - } + public override void Initialize() => AvaloniaXamlLoader.Load(this); public override void OnFrameworkInitializationCompleted() { diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.App/FileTime.GuiApp.App.csproj b/src/GuiApp/Avalonia/FileTime.GuiApp.App/FileTime.GuiApp.App.csproj index b149d2f..3389591 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.App/FileTime.GuiApp.App.csproj +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.App/FileTime.GuiApp.App.csproj @@ -33,6 +33,7 @@ + diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.App/Program.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.App/Program.cs index 32740af..5a15adf 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.App/Program.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.App/Program.cs @@ -1,9 +1,12 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Reflection; using Avalonia; using Avalonia.ReactiveUI; +using Serilog; +using Serilog.Debugging; namespace FileTime.GuiApp.App; @@ -19,51 +22,87 @@ public static class Program #else InitRelease(); #endif + InitLogging(); + + Log.Logger.Information("Early app starting..."); + } - void InitDevelopment() + private static void InitDevelopment() + { + EnvironmentName = "Development"; + + AppDataRoot = Path.Combine(Environment.CurrentDirectory, "appdata"); + } + + private static void InitRelease() + { + EnvironmentName = "Release"; + + var possibleDataRootsPaths = new List { - EnvironmentName = "Development"; + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FileTime"), + Path.Combine(Assembly.GetEntryAssembly()?.Location ?? ".", "fallbackDataRoot") + }; - AppDataRoot = Path.Combine(Environment.CurrentDirectory, "appdata"); - } - - void InitRelease() + string? appDataRoot = null; + foreach (var possibleAppDataRoot in possibleDataRootsPaths) { - EnvironmentName = "Release"; - - var possibleDataRootsPaths = new List() + try { - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FileTime"), - Path.Combine(Assembly.GetEntryAssembly()?.Location ?? ".", "fallbackDataRoot") - }; + var appDataRootDirectory = new DirectoryInfo(possibleAppDataRoot); + if (!appDataRootDirectory.Exists) appDataRootDirectory.Create(); - string? appDataRoot = null; - foreach (var possibleAppDataRoot in possibleDataRootsPaths) - { - try - { - var appDataRootDirectory = new DirectoryInfo(possibleAppDataRoot); - if (!appDataRootDirectory.Exists) appDataRootDirectory.Create(); - - //TODO write test - appDataRoot = possibleAppDataRoot; - break; - } - catch - { - } + //TODO write test + appDataRoot = possibleAppDataRoot; + break; + } + catch + { } - - AppDataRoot = appDataRoot ?? throw new UnauthorizedAccessException(); } + + AppDataRoot = appDataRoot ?? throw new UnauthorizedAccessException(); + } + + private static void InitLogging() + { + SelfLog.Enable(l => Debug.WriteLine(l)); + + var logFolder = Path.Combine(AppDataRoot, "logs", "bootstrap"); + + if (!Directory.Exists(logFolder)) Directory.CreateDirectory(logFolder); + + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.FromLogContext() + .WriteTo.File( + Path.Combine(logFolder, "appLog.log"), + fileSizeLimitBytes: 10 * 1024 * 1024, + rollOnFileSizeLimit: true, + rollingInterval: RollingInterval.Day) + .CreateBootstrapLogger(); } // Initialization code. Don't use any Avalonia, third-party APIs or any // SynchronizationContext-reliant code before AppMain is called: things aren't initialized // yet and stuff might break. [STAThread] - public static void Main(string[] args) => BuildAvaloniaApp() - .StartWithClassicDesktopLifetime(args); + public static void Main(string[] args) + { + try + { + BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + } + catch (Exception ex) + { + Log.Fatal(ex, "An unhandled exception occured during bootstrapping"); + } + finally + { + Log.CloseAndFlush(); + } + } // Avalonia configuration, don't remove; also used by visual designer. public static AppBuilder BuildAvaloniaApp() diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.App/Startup.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.App/Startup.cs index c00e427..d8d1c0b 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.App/Startup.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.App/Startup.cs @@ -84,9 +84,27 @@ public static class Startup internal static IServiceCollection RegisterLogging(this IServiceCollection serviceCollection) { - return serviceCollection.AddLogging(loggingBuilder => + serviceCollection.AddSerilog( + (serviceProvider, loggerConfiguration) => + { + loggerConfiguration + .MinimumLevel.Verbose() + .ReadFrom.Configuration(serviceProvider.GetRequiredService()) + .Enrich.FromLogContext() + .WriteTo.File( + Path.Combine(Program.AppDataRoot, "logs", "appLog.log"), + fileSizeLimitBytes: 10 * 1024 * 1024, + rollOnFileSizeLimit: true, + rollingInterval: RollingInterval.Day) + .WriteTo.Sink(serviceProvider.GetRequiredService()); + } + ); + + serviceCollection.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(dispose: true) ); + + return serviceCollection; } internal static IServiceCollection AddConfiguration(this IServiceCollection serviceCollection, IConfigurationRoot configuration) @@ -96,27 +114,4 @@ public static class Startup .Configure(configuration.GetSection(SectionNames.KeybindingSectionName)) .AddSingleton(configuration); } - - internal static IServiceProvider InitSerilog(this IServiceProvider serviceProvider) - { - Log.Logger = new LoggerConfiguration() - .ReadFrom.Configuration(serviceProvider.GetService()) - .Enrich.FromLogContext() - .WriteTo.File( - Path.Combine(Program.AppDataRoot, "logs", "appLog.log"), - fileSizeLimitBytes: 10 * 1024 * 1024, - rollOnFileSizeLimit: true, - rollingInterval: RollingInterval.Day) - .WriteTo.MessageBoxSink(serviceProvider) - .CreateLogger(); - - return serviceProvider; - } - - private static LoggerConfiguration MessageBoxSink( - this LoggerSinkConfiguration loggerConfiguration, - IServiceProvider serviceProvider) - { - return loggerConfiguration.Sink(serviceProvider.GetRequiredService()); - } } \ No newline at end of file diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Logging/ToastMessageSink.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Logging/ToastMessageSink.cs index e89e6e0..c8649dc 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Logging/ToastMessageSink.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Logging/ToastMessageSink.cs @@ -1,4 +1,5 @@ using FileTime.GuiApp.Services; +using Microsoft.Extensions.DependencyInjection; using Serilog.Core; using Serilog.Events; @@ -6,11 +7,12 @@ namespace FileTime.GuiApp.Logging; public class ToastMessageSink : ILogEventSink { - private readonly IDialogService dialogService; + private readonly Lazy _dialogService; - public ToastMessageSink(IDialogService dialogService) + public ToastMessageSink( + IServiceProvider serviceProvider) { - this.dialogService = dialogService; + _dialogService = new Lazy(() => serviceProvider.GetRequiredService()); } public void Emit(LogEvent logEvent) @@ -20,7 +22,7 @@ public class ToastMessageSink : ILogEventSink var message = logEvent.RenderMessage(); if (logEvent.Exception is not null) message += $" {logEvent.Exception.Message}"; - dialogService.ShowToastMessage(message); + _dialogService.Value.ShowToastMessage(message); } } } \ No newline at end of file