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;
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<ILogger<App>>();
logger.LogInformation("App initialization completed");
}
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void Initialize() => AvaloniaXamlLoader.Load(this);
public override void OnFrameworkInitializationCompleted()
{

View File

@@ -33,6 +33,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Json" 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="Serilog.Extensions.Hosting" 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.Sinks.File" Version="5.0.0" />

View File

@@ -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();
void InitDevelopment()
Log.Logger.Information("Early app starting...");
}
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");
}
void InitRelease()
string? appDataRoot = null;
foreach (var possibleAppDataRoot in possibleDataRootsPaths)
{
EnvironmentName = "Release";
var possibleDataRootsPaths = new List<string>()
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()

View File

@@ -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<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)
);
return serviceCollection;
}
internal static IServiceCollection AddConfiguration(this IServiceCollection serviceCollection, IConfigurationRoot configuration)
@@ -96,27 +114,4 @@ public static class Startup
.Configure<KeyBindingConfiguration>(configuration.GetSection(SectionNames.KeybindingSectionName))
.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 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<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)
@@ -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);
}
}
}