4

I was initially using one log file for Serilog, which I accomplished by doing this

var slc = new SerilogSubLoggerConfiguration();   
configuration.GetSection("Serilog:SubLogger").Bind(slc);

and then configuring the SubLogger in the Main method of Program.cs

 Log.Logger = new LoggerConfiguration()
            .ReadFrom.Configuration(configuration)
            .WriteTo.Logger(logger => logger.Filter.
                ByIncludingOnly(lvl => lvl.Level == slc.Level).WriteTo.RollingFile(slc..PathFormat))
            .CreateLogger();

I have moved to using three separate logs now defined in the appsettings file. This creates the class, with one property, a list of Serilog configurations

 public class SerilogSubLoggerConfigurations
{
    public List<SerilogSubLoggerConfiguration> SubLoggers { get; set; }
}

var slcs = configuration.GetSection("Serilog").Get<SerilogSubLoggerConfigurations>();

Now that I have my list of SubLogger configs, I need to create the logger, and add all these sub loggers. Each SubLogger will need its own

.WriteTo.Logger(logger => logger.Filter.
                ByIncludingOnly(lvl => lvl.Level == slc.Level).WriteTo.RollingFile(slc..PathFormat))

line in the Log.Logger call, therefore I need to iterate over the SubLoggers. My intention was to write a method to do so.

public static LoggerConfiguration GetLoggerConfiguration(IConfiguration config, SerilogSubLoggerConfigurations slcs)
    {
        var lc = new LoggerConfiguration();
        lc.ReadFrom.Configuration(config);

        foreach (var cfg in slcs.SubLoggers)
        {
            lc.WriteTo.Logger(logger => logger.Filter
                                       .ByIncludingOnly(lvl => lvl.Level == cfg.Level).WriteTo
                                       .RollingFile(cfg.PathFormat));
        }

        lc.CreateLogger();
        return lc;
    }

and then just use this in the Main method

Log.Logger = GetLoggerConfiguration(configuration, slcs);

But this errs,

Cannot implicitly convert type 'Serilog.LoggerConfiguration' to 'Serilog.ILogger'

I tried changing the method to an ILogger and returning an ILogger, but ILogger doesn't contain the WriteTo and ReadFrom methods, so that was a no go. I tried casting the returned LoggerConfiguration to an ILogger, but that didn't work either and a number of other iterations that also failed.

There must be a way to accomplish this. Any help appreciated.

Update:

 public static int Main(string[] args)
    {
        var currentEnv = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");

        var configuration = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .AddJsonFile($"appsettings.{currentEnv}.json", optional: true)
            .AddEnvironmentVariables()
            .Build();

        var slcs = configuration.GetSection("Serilog").Get<SerilogSubLoggerConfigurations>();
        Log.Logger = BuildLogger(configuration, slcs);
        Log.Information(Appname != null ? $"{Appname}{LogDelimeter}Logger created." : "Logger created.");

        try
        {
            Log.Information(Appname != null ? $"{Appname}{LogDelimeter}Starting web host" : "Starting web host.");
            BuildWebHost(args).Run();
            return 0;
        }
        catch (Exception ex)
        {
            Log.Fatal(ex,
                Appname != null
                    ? $"{Appname}{LogDelimeter}Web Host terminated unexpectedly"
                    : "Web Host terminated unexpectedly");
            return 1;
        }
        finally
        {

            Log.Information(Appname != null ? $"{Appname}{LogDelimeter}Flushing log and closing it." : "Flushing log and closing it.");
            Log.CloseAndFlush();
        }
    }

1 Answer 1

5

I tried changing the method to an ILogger and returning an ILogger, but ILogger doesn't contain the WriteTo and ReadFrom methods, so that was a no go.

The sequence of Serilog bootstraping is following:

  1. You create an instance of Serilog.LoggerConfiguration object and setup it with either code calls (WriteTo.RollingFile()) or from config files (ReadFrom.Configuration()).
  2. Then after Serilog configuration is built, you create a logger with LoggerConfiguration.CreateLogger() call.

So before you finished loading configuration you can't log any messages (you don't have Logger instance yet). But after you built a logger there is no point to call WriteTo or ReadFrom methods since logger is already built and changes to source configuration will not affect anything.

So you could change your GetLoggerConfiguration() method as:

public static Serilog.ILogger BuildLogger(IConfiguration config, SerilogSubLoggerConfigurations slcs)
{
    var lc = new LoggerConfiguration();
    lc.ReadFrom.Configuration(config);

    foreach (var cfg in slcs.SubLoggers)
    {
        lc.WriteTo.Logger(logger => logger.Filter
            .ByIncludingOnly(lvl => lvl.Level == cfg.Level).WriteTo
            .RollingFile(cfg.PathFormat));
    }

    //  Apply any additional changes to lc configuration here

    //  Finally build a logger
    return lc.CreateLogger();
}

static void Main(string[] args)
{
    //  ...
    var logger = BuildLogger(configuration, subLoggerConfigurations);

    //  No any changes could be made to logging configuration at this point.
    //  Just log what you need.

    logger.Information("Test info message");
}
Sign up to request clarification or add additional context in comments.

8 Comments

@CodeFuller...since I am not a professional programmer, I am not seeing why lc.CreateLogger(); return lc; is any different than return lc.CreateLogger(); because I had tried making my method a static ILogger and I was getting an error on return lc;
Also, since you are an expert in Serilog, why does this code section in Main try-catch-finally; finally { Log.Information(Appname != null ? $"{Appname}{LogDelimeter}Flushing log and closing it." : "Flushing log and closing it."); Log.CloseAndFlush(); } in my Main method, not produce a log message when the application is closed? It appears the logger is not disposed on application close?
Log.Information() method will work only if you set Log.Logger property: Log.Logger = BuildLogger(configuration, subLoggerConfigurations);
Log.Information() calls work everywhere in the application. I'm just wondering why when the application is closed I do not get the log message that the log is closed and flushed. any thoughts on my first comment on return lc;
Regarding first comment, lc.CreateLogger(); return lc; returns object of LoggerConfiguration type, you don't actually use Logger object created by CreateLogger() call.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.