Serilog .net core 6 Kullanımı

25 Eyl

Microsoft .net core 6 bazı yenilikleri de beraberinde getirdi. Yeni bir proje oluşturulduğunda karşımıza çıkan ilk yenilik minimal asp.net uygulaması olacaktır. Asp.net core 6 ile Program ve Starup class sınıfları kaldırılarak minimal api dünyasına giriş yapılmıştır.

  • Minimal şablonda Program ve Startup class’lar bulunmaz. Program.cs içerisinde class bulunmayan düz bir yapı bulunur.
  • Konfigurasyonun oluşturulabilmesi için yeni bir WebApplicationBuilder API bulunu.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");
app.Run();

Uygulamaya ait bütün ayarlar bu dosya içerisinde yapılandırıldığından Serilog ayarları da burada konumlandırılır. (.Net Core 6 konfigürasyon ayarları.)

Neden Serilog Kullanılır?

Bir .net 6 uygulamasında üretilen log’ların bir çok farklı yere akması (sink) istenebilir. Örneğin konsol, metin dosyası, SqlServer, Oracle, Elasticsearch, Kafka, Redis, vs. gibi. Serilog altyapısı, geliştiricilere sadece konfigürasyon ayarları ile, kodlama yapısına hiç dokunmadan, log bilgilerini farklı kaynaklara aktarabilme imkanı sağlar.

Uygulamaya Serilog eklenebilmesi için Serilog.AspNetCore paketinin eklenmesi gerekir.

dotnet add package Serilog.AspNetCore

veya

NuGet\Install-Package Serilog.AspNetCor

Örneğin Apache Kafka’ya log yazabilmek için Serilog.Sinks.Kafka nuget paketinin uygulamaya eklenmesi gerekir.

.Net Core 6 da Serilog Kullanımı

Bir önceki .Net 5 versiyonunda serilog implementasyonu aşağıdaki gibidir.

using Serilog;

var builder = WebApplication.CreateBuilder(args)
    .UseSerilog(...);

// Error CS1929 : 'WebApplicationBuilder' does not contain a definition for 'UseSerilog' and
// the best extension method overload 'SerilogWebHostBuilderExtensions.UseSerilog(
// IWebHostBuilder, ILogger, bool, LoggerProviderCollection)' requires a receiver of type
// 'IWebHostBuilder'

Bu uygulanış şekli .net 6 versiyonunda değişmiştir. Bu nedenle yukarıdaki hata mesajı alınacaktır. Doğru uygulama şekli aşağıdaki gibidir.

IHostBuilder içerisinde bulunan builder.Host kullanılır.

var builder = WebApplication.CreateBuilder(args);

builder.Host.UseSerilog((builderContext, loggerConfiguration) => loggerConfiguration
    .WriteTo.Console()
    .WriteTo.Kafka());

var app = builder.Build();

Bu şekilde yapılandırma ile hem konsol hem de Kafka’ya log akması sağlanabilir. Bu şekilde düzenlenen Serilog yapılandırmasında, ayarlar kod tarafında verilir. Örneğin Kafka sunu ayarları değiştiğinde aşağıdaki gibi düzenleme yapılır.

var builder = WebApplication.CreateBuilder(args);

builder.Host.UseSerilog((builderContext, loggerConfiguration) => loggerConfiguration
    .WriteTo.Console()
    .WriteTo.Kafka("MyTopicName", "172.15.45.3:9092"));

var app = builder.Build();

Konfigürasyonun appsettings.json üzerinde yapılması

Serilog ayarları appsettings.json dosyası içerisinde Serilog section içerisinde yapılandırıldığında, kod tarafında bir değişiklik yapmaya gerek kalmaz.

{
  "Serilog": {
    "MinimumLevel": {
      "Default": "Debug",
      "Override": {
        "Microsoft": "Information",
        "System": "Information"
      }
    },
    "WriteTo": [
      {
        "Name": "Kafka",
        "Args": {
          "batchSizeLimit": "50",
          "period": "5",
          "bootstrapServers": "localhost:9092",
          "topic": "logs"
        }
      }
    ]
  }
}

Bu konfigurasyonun okunabilmesi için kod tarafında bir değişiklik gerekir.

var builder = WebApplication.CreateBuilder(args);

builder.Host.UseSerilog((builderContext, loggerConfiguration) => loggerConfiguration
    .ReadFrom.Configuration(builderContext.Configuration));

var app = builder.Build();

Burada dikkat edilmesi gereken noktalardan biri, eğer MinimumLevel bölümündeki Override bölümünde bulunan Microsoft veya System kategorileri aşağıdaki gibi Warning veya Error gibi seviyelere çekilirse, geliştirme aşamasında üretilen Information logları akmaz. Bu durumda uygulamanın çalışmadığı hissine kapılmamak gerekir.

    "MinimumLevel": {
      "Default": "Debug",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning"
      }
    },

Bu ayarlamaya göre, MinimumLevel olarak Debug seviyesinde oluşan log bilgileri akar. Override kısmında hangi kategoriye göre hangi seviyede log akışı olacağı bilgileri yer almaktadır. Bilindiği gibi .net core ile sınıflar içerisinde log bilgilerini yazmak için ILogget<TCategoryName> interface kullanılır. Örneğin:

 public CategoryController(ILogger<CategoryController> logger)
 {
    logger.LogInformation("CategoryController started.");
    logger.LogError("Error!");
 }

TCategryName log kullanan ilgili sınıfın (CategoryController) ismidir. Fakat log işlemlerinde arka planda bu sınıfın tam ismi kullanılır. Örneğin Shopping.WebApi.Controllers.CategoryController şeklinde tam isim alınır. Nemespace bilgisi Shopping ile başlayan kategori loglarını information seviyesinde akıtmak için Override bölümü aşağıdaki şeklilde düzenlenebilir.

  "MinimumLevel": {
     "Default": "Debug",
     "Override": {
       "Microsoft": "Warning",
       "System": "Warning",
       "Shopping": "Information"
    }
  },

Extension method yöntemi

Extension method yöntemi kullanılarak Program.cs dosyası içeriği sadeleştirilebilir.

public static class LoggingExtensions
{
   public static void AddSerilogConfiguration(this IHostBuilder builder)
   {
       Action<HostBuilderContext, IServiceProvider, LoggerConfiguration> ConfigureLogger =
                (builderContext, serviceProvider, loggerConfiguration) =>
                            loggerConfiguration.ReadFrom.Configuration(builderContext.Configuration)
                                       .Enrich.FromLogContext();
            
       builder.UseSerilog(ConfigureLogger);
   }
}
var builder = WebApplication.CreateBuilder(args);

builder.Host.AddSerilogConfiguration();

var app = builder.Build();

İkinci Alternatif olarak Serilog uygulamasında aşağıdaki yöntem kullanılabilir.

public static class LoggingExtensions
{
    public static void AddSerilogConfiguration(this ILoggingBuilder loggingBuilder, IConfiguration configuration)
    {
        var logger = new LoggerConfiguration()
            .ReadFrom.Configuration(configuration)
            .Enrich.FromLogContext()
            .CreateLogger();

        Log.Logger = logger;

        loggingBuilder.ClearProviders();
        loggingBuilder.AddSerilog(logger);
    }
}

Not: Program.cs içerisinde uygulama başlatma bilgileri için Serilog namespace altındaki Log static class kullanılabilir.

var builder = WebApplication.CreateBuilder(args);

Log.Information("test"); // builder.Build() olmadan bu çalışmaz.

builder.Host.AddSerilogConfiguration();

var app = builder.Build();

try 
{
     Log.Information("Application starting.";
     app.Run();
} 
catch(Exception exception) 
{
    Log.Fatal("Application terminated unexpectedly.");
} 
finally 
{
    Log.CloseAndFlush();
}