Two-stage logging

This commit is contained in:
2023-07-06 11:39:06 +02:00
parent 3e4a596741
commit 7afce07d21
5 changed files with 99 additions and 66 deletions

View File

@@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging;
namespace FileTime.GuiApp.App; namespace FileTime.GuiApp.App;
public partial class App : Application public class App : Application
{ {
static App() static App()
{ {
@@ -28,16 +28,12 @@ public partial class App : Application
.RegisterLogging() .RegisterLogging()
.RegisterServices() .RegisterServices()
.AddViewModels() .AddViewModels()
.BuildServiceProvider() .BuildServiceProvider();
.InitSerilog();
var logger = DI.ServiceProvider.GetRequiredService<ILogger<App>>(); var logger = DI.ServiceProvider.GetRequiredService<ILogger<App>>();
logger.LogInformation("App initialization completed"); logger.LogInformation("App initialization completed");
} }
public override void Initialize() public override void Initialize() => AvaloniaXamlLoader.Load(this);
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted() public override void OnFrameworkInitializationCompleted()
{ {

View File

@@ -33,6 +33,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="7.0.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" /> <PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="7.0.0" /> <PackageReference Include="Serilog.Settings.Configuration" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" /> <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />

View File

@@ -1,9 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using Avalonia; using Avalonia;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Serilog;
using Serilog.Debugging;
namespace FileTime.GuiApp.App; namespace FileTime.GuiApp.App;
@@ -19,51 +22,87 @@ public static class Program
#else #else
InitRelease(); InitRelease();
#endif #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<string>
{ {
EnvironmentName = "Development"; Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FileTime"),
Path.Combine(Assembly.GetEntryAssembly()?.Location ?? ".", "fallbackDataRoot")
};
AppDataRoot = Path.Combine(Environment.CurrentDirectory, "appdata"); string? appDataRoot = null;
} foreach (var possibleAppDataRoot in possibleDataRootsPaths)
void InitRelease()
{ {
EnvironmentName = "Release"; try
var possibleDataRootsPaths = new List<string>()
{ {
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FileTime"), var appDataRootDirectory = new DirectoryInfo(possibleAppDataRoot);
Path.Combine(Assembly.GetEntryAssembly()?.Location ?? ".", "fallbackDataRoot") if (!appDataRootDirectory.Exists) appDataRootDirectory.Create();
};
string? appDataRoot = null; //TODO write test
foreach (var possibleAppDataRoot in possibleDataRootsPaths) appDataRoot = possibleAppDataRoot;
{ break;
try }
{ catch
var appDataRootDirectory = new DirectoryInfo(possibleAppDataRoot); {
if (!appDataRootDirectory.Exists) appDataRootDirectory.Create();
//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 // Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break. // yet and stuff might break.
[STAThread] [STAThread]
public static void Main(string[] args) => BuildAvaloniaApp() public static void Main(string[] args)
.StartWithClassicDesktopLifetime(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. // Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp() public static AppBuilder BuildAvaloniaApp()

View File

@@ -84,9 +84,27 @@ public static class Startup
internal static IServiceCollection RegisterLogging(this IServiceCollection serviceCollection) internal static IServiceCollection RegisterLogging(this IServiceCollection serviceCollection)
{ {
return serviceCollection.AddLogging(loggingBuilder => serviceCollection.AddSerilog(
(serviceProvider, loggerConfiguration) =>
{
loggerConfiguration
.MinimumLevel.Verbose()
.ReadFrom.Configuration(serviceProvider.GetRequiredService<IConfiguration>())
.Enrich.FromLogContext()
.WriteTo.File(
Path.Combine(Program.AppDataRoot, "logs", "appLog.log"),
fileSizeLimitBytes: 10 * 1024 * 1024,
rollOnFileSizeLimit: true,
rollingInterval: RollingInterval.Day)
.WriteTo.Sink(serviceProvider.GetRequiredService<ToastMessageSink>());
}
);
serviceCollection.AddLogging(loggingBuilder =>
loggingBuilder.AddSerilog(dispose: true) loggingBuilder.AddSerilog(dispose: true)
); );
return serviceCollection;
} }
internal static IServiceCollection AddConfiguration(this IServiceCollection serviceCollection, IConfigurationRoot configuration) internal static IServiceCollection AddConfiguration(this IServiceCollection serviceCollection, IConfigurationRoot configuration)
@@ -96,27 +114,4 @@ public static class Startup
.Configure<KeyBindingConfiguration>(configuration.GetSection(SectionNames.KeybindingSectionName)) .Configure<KeyBindingConfiguration>(configuration.GetSection(SectionNames.KeybindingSectionName))
.AddSingleton<IConfiguration>(configuration); .AddSingleton<IConfiguration>(configuration);
} }
internal static IServiceProvider InitSerilog(this IServiceProvider serviceProvider)
{
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(serviceProvider.GetService<IConfiguration>())
.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<ToastMessageSink>());
}
} }

View File

@@ -1,4 +1,5 @@
using FileTime.GuiApp.Services; using FileTime.GuiApp.Services;
using Microsoft.Extensions.DependencyInjection;
using Serilog.Core; using Serilog.Core;
using Serilog.Events; using Serilog.Events;
@@ -6,11 +7,12 @@ namespace FileTime.GuiApp.Logging;
public class ToastMessageSink : ILogEventSink public class ToastMessageSink : ILogEventSink
{ {
private readonly IDialogService dialogService; private readonly Lazy<IDialogService> _dialogService;
public ToastMessageSink(IDialogService dialogService) public ToastMessageSink(
IServiceProvider serviceProvider)
{ {
this.dialogService = dialogService; _dialogService = new Lazy<IDialogService>(() => serviceProvider.GetRequiredService<IDialogService>());
} }
public void Emit(LogEvent logEvent) public void Emit(LogEvent logEvent)
@@ -20,7 +22,7 @@ public class ToastMessageSink : ILogEventSink
var message = logEvent.RenderMessage(); var message = logEvent.RenderMessage();
if (logEvent.Exception is not null) if (logEvent.Exception is not null)
message += $" {logEvent.Exception.Message}"; message += $" {logEvent.Exception.Message}";
dialogService.ShowToastMessage(message); _dialogService.Value.ShowToastMessage(message);
} }
} }
} }