bootstrap.logging - grecosoft/NetFusion GitHub Wiki

Bootstrap Logging Configuration

NuGet Package NetFusion.Serilog
Types SerilogExtendedLogger
Sample Code View Code
dotnet add package NetFusion.Serilog

The last section showed a partial log written when the Web Application was bootstrapped. The first phase of the NetFusion bootstrap executes before the dependency-injection container is created so an instance of ILogger can't be injected. Therefore, the bootstrap process logs to an IExtendedLogger implementation passed to the CompositeContainer extension method. The last section specified the NullExtendedLogger logger as follows:

// Add Plugins to the Composite-Container:
builder.Services.CompositeContainer(builder.Configuration, new NullExtendedLogger()) 
    .AddPlugin<CorePlugin>()
    .AddPlugin<AppPlugin>()
    .AddPlugin<WebApiPlugin>()
    .Compose();

This implementation is never used within a production application since it simply writes the logs to the console. Also shown in the last section, these logs are incomplete since .net logger provides structured logging which NetFusion fully supports with help form Serilog. There are several benefits to structured logging:

  • Instead of flat text log messages, the parameter values contained within the message template become data that can be queried.
  • Additional data can be logged with each entry and can also be queried.
  • Reduces chatty related logs by allowing the details to be grouped under a single log entry.

Much effort was placed into NetFusion so clean and structured logs are written. As additional plugins are discussed, their relevant logs will be shown.

The following will be completed to integration Serilog and SEQ:

  1. Install the needed packages into the WebApiHost project.
  2. Replace the NullExtendedLogger with SerilogExtendedLogger.
  3. Add Serilog to the host by invoking UseSerilog() on ConfigureHostBuilder.
  4. Add request logging by invoking UseSerilogRequestLogging on the WebApplication.
  5. Configured Serilog and SEQ within the Program class.

Configuration

Execute the following to add the the required NuGet packages to the WebApiHost project:

dotnet add ./WebApiHost/WebApiHost.csproj package NetFusion.Serilog
dotnet add ./WebApiHost/WebApiHost.csproj package Serilog.AspNetCore
dotnet add ./WebApiHost/WebApiHost.csproj package Serilog.Sinks.Console
dotnet add ./WebApiHost/WebApiHost.csproj package Serilog.Sinks.Seq
nano ./WebApiHost/Program.cs

After completing the above 5 steps, the Program.cs file will be as follows:

using App.Component.Plugin;
using Core.Component.Plugin;
using NetFusion.Bootstrap.Container;
using NetFusion.Builder;
using NetFusion.Serilog;
using Serilog;
using Serilog.Events;
using Serilog.Sinks.SystemConsole.Themes;
using System.Diagnostics;
using WebApiHost.Plugin;

var builder = WebApplication.CreateBuilder(args);

builder.Host.ConfigureLogging(SetupLogging);
builder.Host.UseSerilog();

builder.Services.AddControllers();

// Add Plugins to the Composite-Container:
builder.Services.CompositeContainer(builder.Configuration, new SerilogExtendedLogger()) 
    .AddPlugin<CorePlugin>()
    .AddPlugin<AppPlugin>()
    .AddPlugin<WebApiPlugin>()
    .Compose();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseAuthorization();
app.UseSerilogRequestLogging();     

app.MapControllers();

// Reference the Composite-Application to start the plugins then
// start the web application.
var compositeApp = app.Services.GetRequiredService<ICompositeApp>();
var lifetime = app.Services.GetRequiredService<IHostApplicationLifetime>();

lifetime.ApplicationStopping.Register(() =>
{
    compositeApp.Stop();
    Log.CloseAndFlush();
});
                  
await compositeApp.StartAsync();
await app.RunAsync();

static void SetupLogging(HostBuilderContext context,
            ILoggingBuilder builder)
{
    var seqUrl = context.Configuration.GetValue<string>("logging:seqUrl");

    // Send any Serilog configuration issue logs to console.
    Serilog.Debugging.SelfLog.Enable(msg => Debug.WriteLine(msg));
    Serilog.Debugging.SelfLog.Enable(Console.Error);

    var logConfig = new LoggerConfiguration()
        .MinimumLevel.Debug()
        .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)

        .Enrich.FromLogContext()
        .Enrich.WithCorrelationId()
        .Enrich.WithHostIdentity(WebApiPlugin.HostId, WebApiPlugin.HostName);

    logConfig.WriteTo.Console(theme: AnsiConsoleTheme.Literate);

    if (!string.IsNullOrEmpty(seqUrl))
    {
        logConfig.WriteTo.Seq(seqUrl);
    }

    Log.Logger = logConfig.CreateLogger();

    builder.ClearProviders();
    builder.AddSerilog(Log.Logger);
}

Lastly, add the following configuration to the appsettings.json file:

nano ./WebApiHost/appsettings.json
{
  "logging": {
    "seqUrl": "http://localhost:5351"
  }
}

Notes

The following was added to the Program class:

  • SetupLogging method configures Serilog and specifies logs should be written to SEQ and the Console.
  • When the service is shutting down, the CloseAndFlush method is invoked.
  • The URL to the SEQ server has been added to the application settings file.

Run the Web Application again and the logs will be neatly displayed to the console:

cd ./WebApiHost
dotnet run

IMAGE

Unlike before, all messages have their template parameters populated and are cleanly written. However, the console does not allow addition structured event information to be viewed or searched. While the structured event data is present, it is not utilized by the console logger. The next section will show how to run SEQ which has already been configured as a log sink.

Running SEQ

SEQ can be installed locally or executed within a Docker container. The SEQ Web site has instructions on downloading and installing SEQ but this section will run the SEQ service within a Docker container.

Within the child Setup directory of the cloned NetFusion-Examples repository, execute the following to run the SEQ service within Docker:

Run the following if using Linux or MacOS:

cd NetFusion-Examples/Examples/Setup/seq
./install.sh

Otherwise on Windows, type the following:

cd NetFusion-Examples\Examples\Setup\seq
install.cmd

Once started, enter the following URL into a browser:

http://localhost:8051/

Run the WebApiHost project again and reload the browser to view the written bootstrap logs:

cd WebApiHost/
dotnet run

IMAGE

In comparison to the console log shown prior, the structured event logs are easy to read when written to SEQ and can be searched on specific event properties. When modules are added in the next section, the above plug-in logs will show details written by each module.

Next Steps

At this point, we have completed the following:

  • Created an ordinary WebApi project.
  • Added the needed NetFusion bootstrap NuGets
  • Added classes to each project indicating that the assembly is a Plug-in.
  • Added code to the Program class specifying the Plug-ins to use for building the composite-application.
  • Added code to the Program class to start/stop the built composite-application.
  • Configured Serilog and SEQ

The last step, discussed in the next topic, is to add Modules to each Plug-in to execute code as each Plug-in is bootstrapped.

⚠️ **GitHub.com Fallback** ⚠️