1
\$\begingroup\$

Here is a rundown of the situation

  • This is a .Net Core (2.1) application with a console and web front ends. For simplicity this question focuses only on the console end although the web end has the same issues.
  • Serilog is the logger of choice. We use 2 sinks (i.e. output sources): Console and MongoDB
  • Security is a concern - the MongoDB connection string contains the password so it needs to be fetched from a secure source. Here we are using an custom created library to retrieve any sensitive information.
  • My understanding is that Serilog configurations are not mutable (for example, add/configure another sink) after initialization, but can be modified after the fact to some extent with "enrichers". This is an aspect I am unfamiliar with.
  • This solution is using standard .NET dependency injection

What's a better way of setting up Serilog, where a sink has a dependency, in a solution using dependency injection? Examples, patterns, templates are welcome. Most examples I've come across of Serilog with .NET Core DI don't address this situation.

public class Program
{
    static int Main(string[] args)
    {
        var secrets = new ThirdPartySecretProvider("MyApp.Console");
        var secretWrapper = new ThirdPartySecretProviderWrapper(secrets);
        var loggingConnectionString = GetLoggingConnectionString(secretsWrapper);

        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Debug()
            .MinimumLevel.Override("Microsoft", LogEventLevel.Error)
            .Enrich.FromLogContext()
            .WriteTo.Console()
            .WriteTo.MongoDB(loggingConnectionString, "applicationLogs")
            .CreateLogger();

        SelfLog.Enable(System.Console.Error);

        var serviceCollection = new ServiceCollection();
        ConfigureServices(serviceCollection, secretServerWrapper);
        var serviceProvider = serviceCollection.BuildServiceProvider();

        try
        {
            var logger = serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger<Application>();
            var config = serviceProvider.GetRequiredService<IConfiguration>();
            Application app = new Application(logger, config);
            app.Run(args);
            return 0;
        }
        catch (Exception ex)
        {
            Log.Fatal(ex, "Host terminated unexpectedly");
            return 1;
        }
        finally
        {
            Log.CloseAndFlush();
        }

    }

    private static void ConfigureServices(IServiceCollection services, ISecretsWrapper secrets)
    {
        IConfiguration config = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json", true, true)
            .Build();

        var mongoUsernameRead = config["MongoDB:ReadUsername"];
        var mongoClientRead = new DataAccess.Mongo.MongoClient(mongoUsernameRead, secrets.MongoReadPassword);
        services.AddSingleton<DataAccess.Mongo.IMongoClient>(mongoClientRead);

        var hadoopRepository = new HadoopRepository(secretServer.HadoopApiKey);
        services.AddSingleton<IHadoopRepository>(hadoopRepository);

        services
            .AddLogging(configure => configure.AddSerilog(dispose: true))
            .AddSingleton(config)
            .AddSingleton<ISecretsWrapper>(secrets)
            .AddSingleton<IFileHelper, FileHelper>()
            .AddSingleton<IRepositoryMongo, RepositoryMongo>()
            .AddSingleton<IServiceMongo, ServiceMongo>()
            .AddSingleton<IBaseHttpClient, BaseHttpClient>()
            // ... additional dependencies omitted
    }

    private static string GetLoggingConnectionString(ISecretsWrapper secrets)
    {
        var mongoUsernameReadWrite = "app_myAppReadWrite";
        var mongoClientLog = new DataAccess.Mongo.MongoClient(mongoUsernameReadWrite, secrets.MongoReadWritePassword);
        return mongoClientLog.MongoConnectionString;
    }

}

Related examples:

One potential change is to create a simple logger (using Serilogger or other) initially to capture failures before the application proper gets started, then create the desired logger to use for the rest of the application. What are the considerations of this approach?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.