using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Data.Core.Plugins; using Avalonia.Markup.Xaml; using AvaloniaSerilogDI.ViewModels; using AvaloniaSerilogDI.Views; using Common.Core.Extensions; using LogViewer.Avalonia; using LogViewer.Core; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using MsBox.Avalonia; using MsBox.Avalonia.Enums; using RandomLogging.Service; using Serilog; using Serilog.Sinks.LogView.Core; using System; using System.Globalization; using System.Linq; using System.Reflection; using System.Threading; using Icon = MsBox.Avalonia.Enums.Icon; namespace AvaloniaSerilogDI; public partial class App : Application { #region Constructors public override void Initialize() => AvaloniaXamlLoader.Load(this); #endregion private IHost? Host {get; set;} private CancellationTokenSource? CancellationTokenSource { get; set;} #region Methods public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { // Avoid duplicate validations from both Avalonia and the CommunityToolkit. // More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins DisableAvaloniaDataAnnotationValidation(); // catch all unhandled errors AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; HostApplicationBuilder builder = Microsoft.Extensions.Hosting.Host.CreateApplicationBuilder(); builder // Register the Random Logging Service .AddRandomBackgroundService() // visual debugging tools .AddLogViewer(); IServiceCollection services = builder.Services; // Serilog Logger // Azure: https://devblogs.microsoft.com/dotnet/asp-net-core-logging/ // ApplicationInsights: https://github.com/serilog-contrib/serilog-sinks-applicationinsights // AmazonCloudWatch: https://blog.ivankahl.com/logging-dotnet-to-aws-cloudwatch-using-serilog/ // video: https://www.youtube.com/watch?v=nVAkSBpsuTk (How Structured Logging With Serilog Can Make Your Life Easier) // video: https://www.youtube.com/watch?v=_iryZxv8Rxw (C# Logging with Serilog and Seq - Structured Logging Made Easy) // ps: docker run -d --restart unless-stopped --name seq -e ACCEPT_EULA=Y -v c:\WIP\LogData:/data -p 8081:80 datalust/seq:latest // docker rmi datalust/seq --force // ref: https://stackoverflow.com/questions/66304596/how-to-dependency-inject-serilog-into-the-rest-of-my-classes-in-net-console-app services.AddLogging(configure: cfg => { Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(builder.Configuration) .WriteTo.DataStoreLoggerSink( dataStoreProvider: () => Host!.Services.TryGetService()!,formatProvider: CultureInfo.InvariantCulture) .CreateLogger(); cfg.ClearProviders().AddSerilog(Log.Logger); }); services .AddSingleton() .AddSingleton(service => new MainWindow { DataContext = service.GetRequiredService() }); Host = builder.Build(); CancellationTokenSource = new(); try { LogStartingMode(); // set and show desktop.MainWindow = Host.Services.GetRequiredService(); desktop.ShutdownRequested += OnShutdownRequested; // startup background services _ = Host.StartAsync(CancellationTokenSource.Token); } catch (OperationCanceledException) { // skip } catch (Exception ex) { Log.Fatal(ex, "Application terminated unexpectedly"); ShowMessageBox("Unhandled Error", ex.Message); CleanUp(); return; } } base.OnFrameworkInitializationCompleted(); } private static void DisableAvaloniaDataAnnotationValidation() { // Get an array of plugins to remove var dataValidationPluginsToRemove = BindingPlugins.DataValidators.OfType().ToArray(); // remove each entry found foreach (var plugin in dataValidationPluginsToRemove) { BindingPlugins.DataValidators.Remove(plugin); } } private void OnShutdownRequested(object? sender, ShutdownRequestedEventArgs e) => CleanUp(); private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) { Exception exception = (Exception)e.ExceptionObject; Log.Fatal(exception, "Application terminated unexpectedly"); ShowMessageBox("Unhandled Error", exception.Message); CleanUp(); } private static void ShowMessageBox(string title, string message) { var box = MessageBoxManager.GetMessageBoxStandard( "Exception", text: message, ButtonEnum.Ok, Icon.Stop ); _ = box.ShowAsync().GetAwaiter(); } private void LogStartingMode() { // Get the Launch mode bool isDevelopment = string.Equals(Environment.GetEnvironmentVariable("DOTNET_MODIFIABLE_ASSEMBLIES"), "debug", StringComparison.OrdinalIgnoreCase); // initialize a logger & EventId ILogger logger = Host!.Services.GetRequiredService>(); EventId eventId = new(id: 0, name: Assembly.GetEntryAssembly()!.GetName().Name); // log a test pattern for each log level logger.TestPattern(eventId: eventId); // log that we have started... logger.Emit(eventId, LogLevel.Information, $"Running in {(isDevelopment ? "Debug" : "Release")} mode"); } private void CleanUp() { // tell the background services that we are shutting down _ = Host?.StopAsync(CancellationTokenSource?.Token ?? CancellationToken.None); // flush logs Log.CloseAndFlush(); } #endregion }