Net Core’da Serilog İle ElasticSearch Sink Özelleştirmesi-2
Bu makale serinin 2.ci yazısıdır. Seriye ait diğer yazıları okumak için altta bulunan linklerden diğer yazılara erişim sağlayabilirsiniz.
1- Docker Ortamlarının ayarlanması
2- Serilog Entegrasyonu ve Konfigürasyonu
3- ElasticSearch Detaylı Kullanım
4- Kibana ve Loglama için Custom Field oluşturma ve Kibana ortamlarında sorgulama
Development süreci boyunca karşılaştığımız hatalarda request-respons incelemeleri için bir çok olasılık ile farklı çözüm yöntemleri geliştiriyoruz. Bunların en çok kullanımı ‘curl gönderirmisin? debug edeyim’ söylemi oluyor. Bu yönteminde doğru bir yöntem olduğunu ama daha maliyetli (zaman) bir yöntem olduğunu tecrübe ederek öğreniyoruz. Merkezi log yöntemi ile aslında sorunlara çözüm üretme development tarafında asıl sorunun çözülme ve çözüm önerisi için daha net ve daha az efor ile çözüldüğünü öğreniyoruz. Anahtar kelimemiz Merkezi Loglama.
Merkezi loglama hemen her proje de olmazsa olmazımız haline geldi. Bir sorunumuz varsa gerek response durumu (HttpStatus Code), response model, response time, custom exceptionların detayları neden kaynaklandığı gibi farklı sorulara daha net cevaplar üretebiliriz.
Merkezi Loglama altyapı olarak çok farklı entegrasyonları ile farklı yoğurt yeme şekilleri vardır. Burada örnek olarak sadece bir yapı kullanacağız.
Biz loglama için Serilog kullancağız. Loglama ile request response ve diğer detayları ElasticSearch’e yazacağız (kurduğumuz Docker Container yardımı ile) ve ilerleyen yazımızda bahsedeceğim custom field oluşturup kibana da bu logları görüntüleme ve search etme işlemlerini yapacağız.
Özel parametreler oluşturmadan da log atılıyor olacak, burada belirttiğim özel durumlarda özel parametreleri log parametresi olarak ekleyip, kibana tarafında search işleminde bu parametreler ile filtreleme yapabiliyor olmamızdır.
Serilog
Serilog çeşitli platformlarında çalışan loglamayı basite indirgeyen bir kütüphanedir. Serilog kayıt gerçekleştirdiği ortamı ‘Sink’ ismi ile nitelendirmektedir. Sink çeşit olarak oldukça zengindir. İster dosya sink’ine ister bir veritabanı sink’ine, isterse de Elasticsearch sink’ine loglarımızı yazabiliyoruz. Serilog’un tüm sink’lerine buradan erişebilirsiniz. Yazılım geliştiricilere structured logging imkanı da sağladığı için logları okurken anlamlandırabilmek için oldukça faydalıdır.
Structured logging, bir veriyi nesne yapısında loglamak ve bu yapısal veriyi kolayca sorgulamaktır diyebiliriz.
Log.Debug(“O sene {busene}”, busene);
Bu şekilde loglanan data içerisinde ‘O sene’ olarak search (like) etmemiz gerekiyor.
var busene = new { Name = "Baba Hakkı Spor", Age = 1903 };
Log.Information("O sene {@busene}", busene);
Yukarıda gördüğümüz gibi busene parametremiz artık bir obje. İlk kullanımdan farklı olarak burada string içerisindeki süslü parantez ifadesini ’@busene’ şeklinde yazdık. Loglanan veriyi, hem de log sorgusu sonrasında Name=”Baba Hakkı Spor” olan bilgiye ulaştığımızı görebilirsiniz. Objeyi oluşturan property bazında sorgulama yapma kolaylığı sağlar.
PROJE
Daha önce oluşturduğumuz SerilogElasticKibana.Api projesine gerekli nuget paketlerini ekleyelim.
dotnet add package Serilog.AspNetCore --version 6.0.1
dotnet add package Serilog.Enrichers.CorrelationId --version 3.0.1
dotnet add package Serilog.Exceptions --version 8.4.0
dotnet add package Serilog.Sinks.Async --version 1.5.0
dotnet add package Serilog.Sinks.Elasticsearch --version 8.4.1
dotnet add package Elastic.CommonSchema.Serilog --version 1.5.3
dotnet add package Serilog.Settings.Configuration --version 3.3.0
Burada eklediğim nuget paketleri sadece serilog için değil ek olarak API UI tarafında endpoint leri daha hızlı hareket etmek adına Swagger entegrasyonunu da ekledi. Swagger hakkında bilgi almak için bu yazımı okuyabilirsiniz.
Konfigürasyon İşlemleri
Burada Serilog entegrasyon işlemlerini kod ile yapacağım, ancak bu entegrasyonu appsetting dosyasından okunur şekilde de düzenleyip ekleyeceğim.
Program.cs dosyası içerisinde altta bulunan satır kodlarını ekliyoruz.
var builder = WebApplication.CreateBuilder(args);
SerilogRegistration.ConfigureLogging();
builder.Host.UseSerilog();
Burada Serilog ile alakalı konfigürasyonu farklı bir .cs dosyasında yazacağız.
SerilogRegistration.ConfigureLogging()
Serilog ile neler yapabiliriz?
Serilog ile mesaj şablonları(message template) oluşturabilmekte ve loglama esnasında bu şablonları parametre ismi olarak kullanabilmekteyiz. Haliyle oluşturulan bu şablonlar bizlere filtreleme, sıralama, serialize etme vb. gibi işlemlerde inanılmaz yardımcı olmakta ve efektif bir loglama süreci geçirmemizi sağlamaktadır.
Static SerilogRegistration class içerisinde void geri dönüş tipinde ConfigureLogging metodu oluşturalım. Gerekli kod bloklarını yazalım.
SerilogExtension — AddSerilog
using System.Reflection;
using System.Runtime.Serialization;
using Elastic.CommonSchema.Serilog;
using Microsoft.AspNetCore.Http.Features;
using Serilog;
using Serilog.Events;
using Serilog.Exceptions;
using Serilog.Filters;
using Serilog.Formatting.Elasticsearch;
using Serilog.Formatting.Json;
using Serilog.Sinks.Elasticsearch;
using Serilog.Sinks.SystemConsole.Themes;
using SerilogElasticKibana.Api.Middlewares;
namespace SerilogElasticKibana.Api.StartupConfiguration;
public static class SerilogExtension
{
public static WebApplicationBuilder AddSerilog(this WebApplicationBuilder builder)
{
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment}.json", optional: true)
.Build();
if (environment == null)
return null;
var applicationName = Assembly.GetExecutingAssembly().GetName().Name;
if (applicationName == null)
return null;
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("System", LogEventLevel.Information)
.Enrich.FromLogContext()
.Enrich.WithProperty("ApplicationName", $"{applicationName} - {environment}")
.Enrich.WithProperty("Coder", "Adem Olguner")
.Enrich.WithCorrelationId()
.Enrich.WithExceptionDetails()
.Filter.ByExcluding(Matching.FromSource("Microsoft.AspNetCore.StaticFiles"))
.WriteTo.Async(writeTo => writeTo.Console(new JsonFormatter()))
.WriteTo.Async(writeTo=> writeTo.Debug(new RenderedCompactJsonFormatter()))
.WriteTo.Async(writeTo=> writeTo.File("log.txt",rollingInterval: RollingInterval.Day))
.WriteTo.Async(writeTo => writeTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri(configuration["ElasticConfiguration:uri"]))
{
TypeName = null,
AutoRegisterTemplate = true,
IndexFormat = $"{applicationName.ToLower()}-{DateTime.UtcNow:yyyy-MM}",
BatchAction = ElasticOpType.Create,
CustomFormatter = new ElasticsearchJsonFormatter(),
OverwriteTemplate = true,
DetectElasticsearchVersion = true,
AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7,
NumberOfReplicas = 1,
NumberOfShards = 2,
FailureCallback = e => Console.WriteLine("Unable to submit event " + e.MessageTemplate),
EmitEventFailure = EmitEventFailureHandling.WriteToSelfLog |
EmitEventFailureHandling.WriteToFailureSink |
EmitEventFailureHandling.RaiseCallback |
EmitEventFailureHandling.ThrowException
}))
.CreateLogger();
builder.Logging.ClearProviders();
builder.Host.UseSerilog(Log.Logger, true);
return builder;
}
Şimdi burada konfigürasyonunu yaptığımız alanları anlamaya çalışalım.
Sink Belirleme
Sink belirleyebilmek için aşağıdaki gibi ‘WriteTo…’ komutunun kullanılması yeterlidir.
Yukarıdaki örnek kod bloğunda birçok desteklenen Sink örneklendirdik.
- Console
Console platformunda log çıktılarını verir. Kullanabilmek için Serilog.Sinks.Console kütüphanesinin yüklü olması gerekmektedir. - Debug
Visual Studio ‘Output’ penceresin de log çıktılarını verir. Serilog.Sinks.Debug kütüphanesini gerektirir. - File
Bir metin dosyasına log çıktılarını verir. Serilog.Sinks.File kütüphanesini gerektirir.
‘rollingInterval’ parametresi ile log dosyasının yuvarlama aralığı belirtilebilir. Örneğin; ‘RollingInterval.Day’ değeri verildiğinde her gün için bir dosya üretecek ve o dosyaya loglama işlemini gerçekleştirecektir.
- ElasticSearch
Log çıktılarını ElasticSearch platformunda verir. Kullanabilmek için Serilog.Sinks.Elasticsearch kütüphanesinin yüklü olması gerekmektedir - Seq
Log çıktılarını Seq platformunda verir. Serilog.Sinks.Seq kütüphanesini gerektirir.
Burada Seq nedir diyip bir kaç cümle yazmakta fayda var. Seq Serilog içerisinde kullanılabilen farklı bir Sink yöntemidir. Loglanan verileri Seq ortamına kaydedilecek şekilde ayarlayabilir ve oluşan logların monitör edilmesi için kullanılabilir bir arayüzdür.
Ben burada logları Kibana kullanarak görüntüleyip monitör edeceğim için Seq kullanımı hakkında detaylı bilgiye giremiyorum, ama raştırmak öğrenmek isteyenler içinde not düşmek istedim.
.WriteTo.Async(writeTo=> writeTo.Seq(
serverUrl:"seq_server-url",
apiKey:"seq-api-keu"
)
)
Loglama Parametreleri ve Kullanımları
- Filter: Serilog’a uygulanacak filtreleri tanımlamaktadır. Loglama yapılırken hangi namespace’den gelen logları include veya exclude edeceğimizi belirleyebiliriz.
.Filter.ByExcluding(Matching.FromSource("System"))
.Filter.ByIncludingOnly(Matching.FromSource("Microsoft.AspNetCore.Hosting"))
- MinimumLevel: Log etkinlik seviyelerinden birini minimum seviye.
olarak uygulamamızı sağlar.
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
.MinimumLevel.Override("SerilogElasticCustomField.Behaviors.LoggingBehavior", LogEventLevel.Warning)
- Enrich: Atılacak log’a dair ekstradan propertyler aşağıdaki gibi tanımlanabilmektedir.
.Enrich.WithProperty("AppName", name)
.Enrich.WithProperty("Environment", environment)
.Enrich.WithProperty("Coder", "AdemOlguner")