Configure ELK stack in .NET Core 6

Every application needs a log centralized log system and for that, in this article, I will configure ElasticSearch and Kibana in .NET core 6 using Serilog.

The first step is to install the docker desktop application on our machine (for production purposes it is recommended to use a VM).

Now let’s create a yml file for Elastic search and Kibana and save it as elk.yml:

services:
  elasticsearch:
   container_name: elasticsearch
   image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2
   ports:
    - 9200:9200
   volumes:
    - elasticsearch-data:/usr/share/elasticsearch/data
   environment:
    - xpack.monitoring.enabled=true
    - xpack.watcher.enabled=false
    - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    - discovery.type=single-node
   networks:
    - customname
  kibana:
   container_name: kibana
   image: docker.elastic.co/kibana/kibana:7.6.2
   ports:
    - 5601:5601
   depends_on:
    - elasticsearch
   environment:
    - ELASTICSEARCH_URL=http://localhost:9200
   networks:
    - customname

Now let’s run it on docker, using docker-compose up -d, and in the docker app, we’ll have 2 containers, 1 for Elastic search and 1 for Kibana. If everything is ready then we should have access to localhost:9200 for Elastic search and localhost:5601 for Kibana

Now, let’s start coding 🙂

In our .NET Core 6 application, we need to install some NuGet packages:

  1. Serilog.AspNetCore
  2. Serilog.Enrichers.Environment
  3. Serilog.Sinks.Debug
  4. Serilog.Sinks.ElasticSearch

Now configure in our program.cs file, add 2 new methods and after that call:

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

void ConfigureLogging()
{
    var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
    var configuration = new ConfigurationBuilder()
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile(
            $"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json",
            optional: true)
        .Build();

    Log.Logger = new LoggerConfiguration()
        .Enrich.FromLogContext()
        .Enrich.WithEnvironmentName()
        .WriteTo.Debug()
        .WriteTo.Console()
        .WriteTo.Elasticsearch(ConfigureElasticSink(configuration, environment!))
        .Enrich.WithProperty("Environment", environment!)
        .ReadFrom.Configuration(configuration)
        .CreateLogger();
}

ElasticsearchSinkOptions ConfigureElasticSink(IConfigurationRoot configuration, string environment)
{
    return new ElasticsearchSinkOptions(new Uri(configuration["ElasticConfiguration:Uri"]))
    {
        AutoRegisterTemplate = true,
        IndexFormat = $"{Assembly.GetExecutingAssembly().GetName().Name?.ToLower().Replace(".", "-")}-{environment?.ToLower().Replace(".", "-")}-{DateTime.UtcNow:yyyy-MM}"
    };
}

Now in the appsettings.json change the Logging section with the code below and add the configuration for ElasticSearch:

  "Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Error",
        "System": "Warning"
      }
    }
  },
  "ElasticConfiguration": {
    "Uri": "http://localhost:9200"
  }

We are going to use Microsoft Error level because we want to define custom logs in our application. If you want to keep all the information from Microsoft you should keep Information.

Now, let’s create some logs in our application:

  private readonly ILogger<HomeController> _logger;

    public HomeController(ILogger<HomeController> logger)
    {
        _logger = logger;
    }

    public IActionResult Index()
    {
          _logger.LogInformation("This is a wow message to test the logs");

        return View();
    }

Now let’s check in Kibana, to see our log message.

In this article, I used docker desktop to configure Elastic search and Kibana and Serilog in a .NET Core application.