Two-stage logging
This commit is contained in:
@@ -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()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
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");
|
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()
|
||||||
|
|||||||
@@ -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>());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user