Konfiguracja Serilog dla całej solucji

0

Witam.
Potrzebuje nakierowania jak uwspólnić jedną konfigurację Serilog w obrębie wszystkich aplikacji w solucji. Mam w solucji 3 projekty - worker service (usługa windows), class library (logika biznesowa), winforms (okno do konfiguracji uslugi).

Po stronie usługi mam konfigurację serilog z użyciem Seq

.UseSerilog((ctx, lc) => lc.WriteTo.Console().WriteTo.Seq("http://localhost:5341"))

Po stronie okna mam

services.AddLogging(logging =>
{
  logging.AddSerilog();
});

Kiedy zadanie wykona usługa to dane są logowane w Seq, ale kiedy zrobimy to samo zadanie oknem, to już nie ma tego wpisu. Moim zdaniem dzieje się tak, ponieważ "okno" nie korzysta z tej samej konfiguracji Seriloga. Metoda, która jest wykonywana jest ta sama metoda i oba projekty korzystają z tego samego co jest wpisane w trzecim projekcie (class library).

W WinForms nie mam opcji UseSerilog(), mam tylko AddLogging() oraz AddSerilog(). Jak skonfigurować to raz, aby oba programy poprawnie logowały błędy.

0

Kiedy zadanie wykona usługa to dane są logowane w Seq, ale kiedy zrobimy to samo zadanie oknem, to już nie ma tego wpisu. Moim zdaniem dzieje się tak, ponieważ "okno" nie korzysta z tej samej konfiguracji Seriloga. Metoda, która jest wykonywana jest ta sama metoda i oba projekty korzystają z tego samego co jest wpisane w trzecim projekcie (class library).

potrzebna jest jeszcze informacja w jaki sposób to coś czego używa zarówno usługa jak i okno, loguje informacje czyli jak ma dostarczony interfejs logowania, który interfejs logowania (ILogger<?>) i jak go używa.

0

A więc tak... Konfiguracja po stronie usługi jest taka sama jak w ASP. Problemem jest całe depedency injection po stronie winforms.

USŁUGA - plik Program.cs

using HotelSync;
using HotelSyncLib.Jobs;
using HotelSyncLib.Models;
using HotelSyncLib.Services.Enova;
using HotelSyncLib.Services.Enova.Enova;
using HotelSyncLib.Services.Wubook;
using Quartz;
using Serilog;

IHost host = Host.CreateDefaultBuilder(args)
    .UseWindowsService(options =>
    {
        options.ServiceName = "HotelSync";
    })
    .ConfigureServices((host, services) =>
    {
        services.AddScoped<EnovaService>();
        services.AddScoped<HotelReader>();
        services.AddScoped<SqlService>();

        services.Configure<SyncConfiguration>(host.Configuration.GetSection("ServiceSettings"));

        services.AddQuartz(q =>
        {
            q.UseMicrosoftDependencyInjectionJobFactory();
            var reservationsJobKey = new JobKey("Reservations");
            //var runNowReservations = new JobKey("RunNow");

            q.AddJob<ReservationsJob>(x => x.WithIdentity(reservationsJobKey));
            //q.AddJob<RunNowReservationJob>(x=>x.WithIdentity(runNowReservations));

            q.AddTrigger(x => x.ForJob(reservationsJobKey).WithCronSchedule(host.Configuration["ServiceSettings:Cron"]));
        });

        services.AddQuartzHostedService(options =>
        {
            options.WaitForJobsToComplete = false;
        });

        services.AddHostedService<Worker>();
    })
    .UseSerilog((ctx, lc) => lc.WriteTo.Console().WriteTo.Seq("http://localhost:5341"))
    .Build();

await host.RunAsync();

OKNO - plik Program.cs

using DevExpress.LookAndFeel;
using DevExpress.Skins;
using DevExpress.UserSkins;
using DevExpress.Xpo.Logger.Transport;
using HotelSyncLib.Models;
using HotelSyncLib.Services.Enova;
using HotelSyncLib.Services.Enova.Enova;
using HotelSyncLib.Services.Wubook;
using Microsoft.Extensions.Configuration;
using Serilog.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Versioning;
using System.Windows.Forms;

namespace HotelSyncConfiguration
{
    [SupportedOSPlatform("windows")]
    internal static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetHighDpiMode(HighDpiMode.SystemAware);
            Application.SetCompatibleTextRenderingDefault(false);

            var services = new ServiceCollection();
        
            ConfigureServices(services);

            using (ServiceProvider serviceProvider = services.BuildServiceProvider())
            {
                var mainForm = serviceProvider.GetRequiredService<MainForm>();
                Application.Run(mainForm);
            }
        }

        private static void ConfigureServices(ServiceCollection services) 
        {
            services.AddLogging(logging =>
            {
                logging.AddSerilog();
            });
            Configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
            services.Configure<SyncConfiguration>(Configuration.GetSection("ServiceSettings"));
            services.AddSingleton<IConfiguration>(Configuration);
            services.AddScoped<SqlService>();
            services.AddScoped<EnovaService>();
            services.AddScoped<HotelReader>();
            services.AddScoped<MainForm>();
        }

        public static IConfigurationRoot Configuration { get; set; }
    }
}

W bibliotece mam całą logikę i tam jest AddDocument(), z którego korzysta usługa jak wczytuje rezerwacje do Enova365 oraz okno gdy wczytujemy rezerwację "ręcznie".

0

NIe chodziło o okno i o usługę, tylko o to miejsce które ostatecznie ma coś zalogować. Chcę popatrzeć co i jak tam jest wstrzykiwane.

0

ILogger<EnovaService> jest wstrzykiwane

    public class EnovaService
    {
        readonly ILogger<EnovaService> _logger;
        readonly SyncConfiguration _configuration;

        public EnovaService(ILogger<EnovaService> logger, IOptions<SyncConfiguration> options)
        {
            _logger = logger;
            _configuration = options.Value;
        }

        public void AddDocument(Reservation reservation, Session session, Booker booker, Rooms rooms, string company)
        {
            _logger.LogInformation($"DODAJE DOKUMENT | REZERWACJA: {reservation.IdHuman} | FIRMA: {company}");
            // ...
        }
    }
1

Mi to wygląda tak że nigdzie nie ma konfiguracji seriloga (podpięcia sinków). W usłudze robi to .UseSerilog, w aplikacji desktopowej trzeba ręcznie


        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetHighDpiMode(HighDpiMode.SystemAware);
            Application.SetCompatibleTextRenderingDefault(false);

            var services = new ServiceCollection();        
            ConfigureServices(services);

            // to musi być dodane
            Log.Logger = new LoggerConfiguration()
                  .MinimumLevel.Debug()
                  .WriteTo.Console().WriteTo.Seq("http://localhost:5341")
                  .CreateLogger();
            
            using (ServiceProvider serviceProvider = services.BuildServiceProvider())
            {
                var mainForm = serviceProvider.GetRequiredService<MainForm>();
                Application.Run(mainForm);
            }
        }
        
0

No właśnie nie byłem pewny jak mam to skonfigurować. Sprawdzam rozwiązanie

EDIT

Niestety, ale nie poszło. Żaden sposób nie chce logować błędów w projekcie WinForms.

OPCJA 1 - dorzuciłem do projektu okienkowego wszystko co potrzebnę, aby stworzyć IHostBuilder, nie zadziałało.

IHost host = Host.CreateDefaultBuilder(args)
    .UseSerilog((ctx, lc) => lc.WriteTo.Console().WriteTo.Seq("http://localhost:5341"))
    .Build()

OPCJA 2 - dopisałem stworzony logger do konfiguracji w AddSerilog(), też nic.

services.AddLogging(logging =>
{
  logging.AddSerilog(Log.Logger, false);
});

Tak jakby konfiguracja dependency injection seriloga działała tylko w obrębie usługi z jakiś powodów. Czyli jeśli metoda AddDocument() wywoła się w z usługi do DI działa, jeśli ta sama metoda zostanie uruchomiona z okna, to już nie działa i nie loguje nic.

0

Niestety, ale nie poszło. Żaden sposób nie chce logować błędów w projekcie WinForms.

To znaczy że coś innego jest na rzeczy, na minimalnej aplikacji winforms sprawdziłem i to działa, z okienka loguje (co prawda podpiąłem logger plikowy ale to detal).

https://filebin.net/2njgb9t9zky94rnv/WinFormsApp1.zip

0

No tak, sink nie ma znaczenia. Moim zdaniem problemem jest dependency injection. Konfiguracja AddLogging() w winformsach narzuca domyślne logowanie jakie zapewnia Microsoft w .NET 6. Nie nadpisuje tego żadna inna konfiguracja, nawet AddSerilog()

0
AdamWox napisał(a):

No tak, sink nie ma znaczenia. Moim zdaniem problemem jest dependency injection. Konfiguracja AddLogging() w winformsach narzuca domyślne logowanie jakie zapewnia Microsoft w .NET 6. Nie nadpisuje tego żadna inna konfiguracja, nawet AddSerilog()

W tej przykładowej aplikacji którą podlinkowałem jest tak jak tu: do formularza trafia wstrzyknięte ILogger<T> który konfigurację czerpie z tego co się ustawi wcześniej.

Ja bym spróbował podpiąć tam jakiś logger plikowy, w mojej wersji zdaje się tak było i plik z logiem się tworzył.

Z resztą, w tym miejscu kodu w którym jest _logger.LogInformation można się zatrzymać pułapką na tym, zawisnąć myszą na _logger i rozwijać w debuggerze "Non-public members" tak długo aż się dokopie do listy sinks i wtedy widać że jest podpięty jeden sink plikowy i do jakiego pliku zapisuje (ścieżkę widać).

Jeżeli to działa w tej przykładowej aplikacji a nie działa w faktycznej, to znaczy że jeszcze jakiś inny czynnik jest, którego w tym "Minimal-reproducible-example" nie uwzględniono.

0

Debug pokazuje, że faktycznie Serilog jest załadowany poprawnie do ILogger, ale problem jest z sink'iem. Pokazuje Serilog.Core.Logger. Czy nie powinno być Serilog.Sinks.Seq?

logger.png

Doleciałem głębiej. ConsoleSink

logger2.png

0
AdamWox napisał(a):

Debug pokazuje, że faktycznie Serilog jest załadowany poprawnie do ILogger, ale problem jest z sink'iem. Pokazuje Serilog.Core.Logger. Czy nie powinno być Serilog.Sinks.Seq?

Na pierwszy rzut oka wygląda dobrze. Jak jest podpięty plikowy to zapisuje do pliku?

0

Nie jest plikowy, ponieważ jest wybrane ConsoleSink. Nawet jeśli byłby plikowy na co debug nie wskazuje, to żadnego pliku nie ma.

0

Hmm, ja mówię "proszę spróbować plikowy" i "w mojej załączonej przykładowej aplikacji działa" a zwrotnie mam że jest podpięty Console. Console w aplikacji okienkowej przecież ma niewielki sens, tam domyślnie nie ma żadnej konsoli przecież.

Trzeba go wymienić na plikowy i spróbować.

Skoro w załączonej przeze mnie aplikacji działa, to wydaje się że to jest wystarczające żeby porównać jedno z drugim.

1

Skonfigurowałem do pliku i działa, zapisuje wszystko. Poszukałem w internetach o co chodzi z tym Seq. I sugerują, żeby ręcznie zrobić Serilog.Log.CloseAndFlush();. Wrzuciłem to do finally, faktycznie zaczął logować błędy i to co powinien. Usunąłem linię Serilog.Log.CloseAndFlush(); z finally i działa... Nic z tego już nie rozumiem 🤷‍♂️

Może się Seq "zawiesiło"? Może w przypadku UseSerilog() to całe CloseAndFlush() działa inaczej. Teraz pytanie czy to zadziała u klienta. Dzięki za pomoc 🤘

1
AdamWox napisał(a):

Skonfigurowałem do pliku i działa, zapisuje wszystko. Poszukałem w internetach o co chodzi z tym Seq. I sugerują, żeby ręcznie zrobić Serilog.Log.CloseAndFlush();. Wrzuciłem to do finally, faktycznie zaczął logować błędy i to co powinien. Usunąłem linię Serilog.Log.CloseAndFlush(); z finally i działa... Nic z tego już nie rozumiem 🤷‍♂️

Może się Seq "zawiesiło"? Może w przypadku UseSerilog() to całe CloseAndFlush() działa inaczej. Teraz pytanie czy to zadziała u klienta. Dzięki za pomoc 🤘

Plikowy jest jakiś dziwny tam, bo ma domyślnie flush nie natychmiastowy tylko z opóźnieniem. Na pewno taki

   ....WriteTo.File("foo.log", flushToDiskInterval: TimeSpan.FromSeconds(1))

działa tak jakby człowiek się spodziewał, czyli flushuje dość często.

Nie używam serilog na codzień i jak dla mnie to jest jakiś quirk, dokumentacja mówi że jak się tego nie ustawi to zapisuje natychmiast, a obserwacyjnie wygląda na to że jednak nie i trzeba nadpisać interval żeby zapisywał. Może ktoś kto się zna na serilogu lepiej potrafi to jakoś objaśnić.

1 użytkowników online, w tym zalogowanych: 0, gości: 1