Logging Refactoring

📆 Date: 2023-07-19

Rationale

The original logging infrastructure was mixing different approaches, due to the less streamlined configuration methods of previous ASP.NET versions. Now logging has been unified in API Program.cs, and removed from Startup.cs.

Affected Products

All the backend API projects.

Upgrade Path

(1) update your NuGet packages (Cadmus.Api.Controllers).

(2) in appsettings.json, remove the connection string from Serilog and add it to ConnectionStrings among the other connection strings:

"ConnectionStrings": {
  "Log": "mongodb://localhost:27017/cadmus-PRJ-log"
},

Note that while the other connection strings are rather templates, with a placeholder rpelacing the database name, this is a normal connection string ready to be used. Just replace PRJ with your project’s codename.

(3) in Program.cs in the Main method, use this code to configure the logger:

var host = await CreateHostBuilder(args)
    .UseSerilog((hostingContext, loggerConfiguration) =>
    {
        string cs = hostingContext.Configuration
            .GetConnectionString("Log")!;
        var maxSize = hostingContext.Configuration["Serilog:MaxMbSize"];

        loggerConfiguration
            .ReadFrom.Configuration(hostingContext.Configuration)
#if DEBUG
            .WriteTo.File("cadmus-log.txt", rollingInterval: RollingInterval.Day)
#endif
            .WriteTo.MongoDBCapped(cs,
                cappedMaxSizeMb: !string.IsNullOrEmpty(maxSize) &&
                    int.TryParse(maxSize, out int n) && n > 0 ? n : 10);
    })
    .Build()
    .SeedAsync(); // see Services/HostSeedExtension

If present, remove the static Serilog.Log.Logger configuration:

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
    .Enrich.FromLogContext()
    .WriteTo.Console()
#if DEBUG
    .WriteTo.File("cadmus-log.txt", rollingInterval: RollingInterval.Day)
#endif
    .CreateLogger();

(4) in Startup.cs, ConfigureServices, remove any logging configuration, which usually is like this:

// serilog
// Install-Package Serilog.Exceptions Serilog.Sinks.MongoDB
// https://github.com/RehanSaeed/Serilog.Exceptions
string maxSize = Configuration["Serilog:MaxMbSize"];
services.AddSingleton<ILogger>(_ => new LoggerConfiguration()
    .MinimumLevel.Information()
    .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
    .Enrich.WithExceptionDetails()
    .WriteTo.Console()
    .WriteTo.MongoDBCapped(Configuration["Serilog:ConnectionString"],
        cappedMaxSizeMb: !string.IsNullOrEmpty(maxSize) &&
            int.TryParse(maxSize, out int n) && n > 0 ? n : 10)
        .CreateLogger());

(5) in Startup.cs, GetPreviewer, replace the line which gets the logger from services (Serilog.ILogger? logger = provider.GetService<Serilog.ILogger>()) with this:

ILogger<Startup>? logger = provider.GetService<ILogger<Startup>>();

and change its method calls accordingly.

(6) if you have additional controllers which require an instance of ILogger, inject the Microsoft.Extensions.Logging.ILogger<T> where T is the target of the injection (i.e. the controller class). No reference to Serilog is required.

This type of injection has been applied also to the default controllers in Cadmus.Api.Controllers.