Adding logging to a .NET Core console application

In .NET Core projects logging is managged via dependency injection. Whilst this works fine for ASP.NET projects where this is all automatically created upon starting a new project in Startup.cs, in console applications it takes a bit of configuration to get it up and running.

This was mostly pieced together from a blog article here but which I modified to use Serilog to log to a table in Azure storage.

This project was created in Visual Studio 2017 and required the following packages (found in the csproj file).

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Configuration" Version="1.1.2" />
    <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.2" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.2" />
    <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.2" />
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.1.1" />
    <PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.2" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.2" />
    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.2" />
    <PackageReference Include="Microsoft.Extensions.Options" Version="1.1.2" />
    <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.2" />
    <PackageReference Include="Serilog" Version="2.4.0" />
    <PackageReference Include="Serilog.Extensions.Logging" Version="1.4.0" />
    <PackageReference Include="Serilog.Sinks.AzureTableStorage" Version="3.0.0" />
    <PackageReference Include="WindowsAzure.Storage" Version="8.1.3" />
  </ItemGroup>

Business logic in this project is taken care of in the services and Program.cs is used for bootstrapping everything needed to run the application. The AzureTableStorage sink is used in this project, to connect to it a “LoggingStorage” entry is needed in the “ConnectionStrings” section of appsettings.json which can be added to a console application like this.

An example of this can be seen in this GitHub repository.

Program.cs


using System.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using AzureBackup.Services;
using Serilog;
using Microsoft.WindowsAzure.Storage;

namespace AzureBackup
{
    public class Program
    {
        public static void Main(string[] args)
        {
            // Create service collection
            var serviceCollection = new ServiceCollection();
            ConfigureServices(serviceCollection);

            // Create service provider
            var serviceProvider = serviceCollection.BuildServiceProvider();
            
            // Run app
            serviceProvider.GetService().Run();
        }

        private static void ConfigureServices(IServiceCollection serviceCollection)
        {
            // Add logging
            serviceCollection.AddSingleton(new LoggerFactory()
                .AddConsole()
                .AddSerilog()
                .AddDebug());
            serviceCollection.AddLogging();

            // Build configuration
            var configuration = new ConfigurationBuilder()
                .SetBasePath(AppContext.BaseDirectory)
                .AddJsonFile("appsettings.json", false)
                .Build();

            // Get table storage connection string for serilog
            CloudStorageAccount logStorageAccount = CloudStorageAccount.Parse(configuration.GetConnectionString("LoggingStorage"));

            // Initialize serilog logger
            Log.Logger = new LoggerConfiguration()
                 .WriteTo.AzureTableStorage(logStorageAccount)
                 .MinimumLevel.Debug()
                 .Enrich.FromLogContext()
                 .CreateLogger();

            // Add access to generic IConfigurationRoot
            serviceCollection.AddSingleton(configuration);

            // Add services
            serviceCollection.AddTransient<IBackupService, BackupService>();

            // Add app
            serviceCollection.AddTransient();
        }
    }
}

App.cs


using AzureBackup.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace AzureBackup
{
    public class App
    {
        private readonly IBackupService _backupService;
        private readonly ILogger _logger;
        private readonly IConfigurationRoot _config;

        public App(IBackupService backupService, IConfigurationRoot config, ILogger logger)
        {
            _backupService = backupService;
            _logger = logger;
            _config = config;
        }

        public void Run()
        {
            _logger.LogInformation($"Running application.");
            _backupService.Run();
            System.Console.ReadKey();
        }
    }
}

BackupService.cs


using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace AzureBackup.Services
{
    public interface IBackupService
    {
        void Run();
    }

    class BackupService : IBackupService
    {
        private readonly ILogger _logger;
        private readonly IConfigurationRoot _config;

        public BackupService(ILogger logger, IConfigurationRoot config)
        {
            _logger = logger;
            _config = config;
        }

        public void Run()
        {
            _logger.LogDebug($"Running backup service.");
        }
    }
}

Scott Walton · 21st February 2018 at 5:01 pm

Thank you for simplifying the code and listing all of the required packages. I have been pulling out what’s left of my hair trying to get my head around this.

Leave a Reply

Your email address will not be published. Required fields are marked *