Asp.NET Core 3.0 – Logging

Merhaba,

Nasıl ki; bir kaya parçası tarihsel bir muammayı günümüze aydınlatıcı bir bilgi taşıyabiliyorsa, nasıl ki; insan vücudundaki bir reaksiyon sağlığına dair geçmişini gözler önüne serebiliyorsa yahut nasıl ki; beşeri hayatta devletler vatandaşları hakkında istediği zaman istediği -zamana dair- verilere erişebiliyorsa işte bunlara benzer bir şekilde herhangi bir yazılımda yarınlarına, geçmişine dair izler bırakabilmelidir… En nihayetinde yukarıda verilen bu örnekler nihai olarak tüm varoluşun dününe dair bizlere bıraktığı aydınlatıcı bilgilerdir. İşte biz yazılımcılar, nesnenin yahut olayların tabiatında olan doğal iz mekanizmalarını irademizle yazılımlarımıza işleyerek sistemin işleyişini adım adım kayıt altına almakta ve bu yapılanıda “Raporlama” ya da birbaşka deyişle “Loglama” şeklinde ifade ederek, yazılımın bugünündeki işleyişi hakkında yarınlara bilgiler taşımaktayız.

Aslında yukarıdaki girişin biraz derin olduğunu düşünebilirsiniz, hatta aranızdan böyle bir konuya abartılı bir giriş olduğunu dahi söyleyenler olabilir. Tüm bu fikirlere açık ve saygılı olduğum kadar bir o kadar da susamış olduğumu bilmenizi isterim. Lakin bunun yanında da şunu ifade etmek isterim ki, şahsen ben bir konunun ya kuramsal ya da kavramsal olarak altını doldurmadıkça yaptığım işin dayandığı kolonlardan birinin eksik olduğunu düşünmekteyim. “Evet” birazdan Asp.NET Core mimarisinde Logging mekanizmasına dair –nasıl uygulanır?– sorusunun cevabı için hususi didiniyor olacağız ama bunun yanında kavramsal açıdan raporlama yahut loglama terimlerini hiç bilmeyenler için mümkün mertebe olayı tabiatla ilişkilendirmenin eksiltici yanını görmemekteyim.

Her neyse…

Şunu unutmamak gerekiyor ki loglama bir stratejidir. Yani sistemin herhangi bir ‘T’ zamanında yaptığı işlemleri adım adım kayda almasıdır. Ve yine herhangi bir ‘T + n’ zamanda ise geçmişe dair -süreçte neler olmuş?- sorusuna karşılık metinsel ya da sayısal istatiksel bilgiler veren mekanizmayı inşa etmektir. İşte bizler bu stratejiyi birçok yöntemle icra edebileceğimiz gibi bu içeriğimizde bunlardan (iyi kötü) birini ele alacağız. Asp.NET Core mimarisinde Logging mekanizmasını…

Asp.NET Core İle Logging

Bir uygulama için olmazsa olmaz özelliklerinden birisi olan log mekanizması, Asp.NET Core mimarisinde Logging frameworkü ile modüler bir şekilde uygulamaya dahil edilebilmekte ve sisteme entegresyonu gerçekleştirilebilmektedir.

Asp.NET Core mimarisinde Logging araçlarına Microsoft.Extensions.Logging kütüphanesinden erişilmektedir.

Tabi devam etmeden önce şunu belirtmek isterim ki, Asp.NET Core mimarisindeki dahili Logging mekanizması hazır sistemler içerisinde genellikle kullanılan bir mekanizmaya sahip olmakla beraber olmazsa olmaz şeklinde düşünülecek işlevselliğide sahip değildir. Bizler bu içeriğimizde bir Asp.NET Core uygulamasında log işlemleri için dahili olan Logging kütüphanesini sadece inceleyecek ve nasıl entegrasyonun gerçekleştirileceği üzerine konuşacağız. Zira yazılım projelerinde loglama mekanizmalarını ilgili ekibin/gelişticinin ihtiyaca dönük bir şekilde kendilerinin tasarlaması ve inşa etmesi tarafımca tavsiye edilmektedir.

Evet… Şimdi konumuza dönersek eğer Logging kütüphanesinde kullanacağımız enstrünmanları inceleyerek başlayalım;

  • ILogger
    Loglama sorumluluğunu üstlenecek olan sınıfın imzasıdır.
  • Logger
    Loglama sorumluluğunu üstlenecek olan sınıftır.
  • ILoggerProvider
    Sağlayıcı sorumluluğunu üstlenecek olan sınıfın imzasıdır.
  • LoggerProvider
    Sağlayıcı sorumluluğunu üstlenecek olan sınıftır. Görevi “Logger” sınıfını üretmektir.
  • ILoggerFactory
    Log mekanizmasını uygulamaya dahil eden ve görev olarakta “LoggerProvider” sınıfını üreterek sisteme dahil eden arayüzdür. Esasında Asp.NET Core çekirdeği bu arayüzü implement eden “LoggerFactory” isimli bir sınıf barındırmaktadır. Runtime’da uygulama bu sınıftan oluşturulan bir instance üzerinden gerekli tanımlamaları sağlamaktadır.

Örnek Uygulama

Şimdi yukarıdaki yapılanmayı göz önüne alarak bir örnek icra edelim. İlgili içeriğimizde Asp.NET Core 3.0 çekirdeğinde bir uygulama ile birlikte MVC deseni kullanılacaktır.

  • Adım 1
    İlk olarak loglama sorumluluğunu üstlenecek olan “Logger” sınıfını oluşturalım.

        public class Logger : ILogger
        {
            IHostingEnvironment _hostingEnvironment;
            public Logger(IHostingEnvironment hostingEnvironment) => _hostingEnvironment = hostingEnvironment;
            public IDisposable BeginScope<TState>(TState state) => null;
            public bool IsEnabled(LogLevel logLevel) => true;
            public async void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
            {
                using (StreamWriter streamWriter = new StreamWriter($"{_hostingEnvironment.ContentRootPath}/log.txt", true))
                {
                    await streamWriter.WriteLineAsync($"Log Level : {logLevel.ToString()} | Event ID : {eventId.Id} | Event Name : {eventId.Name} | Formatter : {formatter(state, exception)}");
                    streamWriter.Close();
                    await streamWriter.DisposeAsync();
                }
            }
        }
    

    Dikkat ederseniz eğer “Logger” sınıfı “ILogger” arayüzünü implemente etmekte ve “Log” isimli generic metot içerisinde proje dizininde bulunan bir “.txt” uzantılı dosyaya gerekli loglamayı gerçekleştirmektedir.

  • Adım 2
    Sağlayıcı sorumluluğunu üstlenecek olan “LoggerProvider” sınıfını üretelim.

        public class LoggerProvider : ILoggerProvider
        {
            public IHostingEnvironment _hostingEnvironment;
            public LoggerProvider(IHostingEnvironment hostingEnvironment) => _hostingEnvironment = hostingEnvironment;
            public ILogger CreateLogger(string categoryName) => new Logger(_hostingEnvironment);
            public void Dispose() => throw new NotImplementedException();
        }
    

    Buradaki önemli nokta, “CreateLogger” metodu içerisinde yukarıda oluşturduğumuz “Logger” sınıfından bir nesne üretilip geriye döndürülmesidir.

  • Adım 3
    Oluşturduğumuz yapılar neticesinde yukarıdaki “LoggerProvider” isimli sağlayıcı sınıfı aracılığıyla “Logger” sınıfından bir instance’i uygulamaya dahil etmemiz gerekmektedir. Bunun için “Startup.cs” dosyasındaki “Configure” metodunda aşağıdaki çalışmayı yapmamız gerekmektedir.

        public class Startup
        {
            public Microsoft.AspNetCore.Hosting.IHostingEnvironment _hostingEnvironment { get; set; }
            public Startup(Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment) => _hostingEnvironment = hostingEnvironment;
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddControllersWithViews();
            }
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
            {
                loggerFactory.AddProvider(new LoggerProvider(_hostingEnvironment));
                if (env.IsDevelopment())
                    app.UseDeveloperExceptionPage();
                app.UseRouting();
                app.UseEndpoints(_ => _.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}"));
            }
        }
    

    Yukarıdaki kod bloğunu incelerseniz eğer “Configure” metoduna “ILoggerFactory” parametresi eklenmiştir. İlgili parametreye uygulama çekirdeğinden Runtime’da bir adet “LoggerFactory” instance’ı bağlanacak ve bu instance üzerinden sisteme “LoggerProvider” nesnesi ile log mekanizması entegre edilmiş olacaktır.

  • Adım 4
    Artık uygulamada hali hazırda kullanılmayı bekleyen bir log mekanizması mevcuttur. Şimdi yapmamız gereken ilgili Controller’da Dependency Injection ile “Logger” nesnesine talepte bulunmak ve istenilen noktada bu nesne üzerinden gerekli logları almaktır.

        public class HomeController : Controller
        {
            public ILogger<HomeController> _logger;
            public HomeController(ILogger<HomeController> logger) => _logger = logger;
            public IActionResult Information()
            {
                _logger.LogInformation("Information mesajı...");
                return View();
            }
            public IActionResult Error()
            {
                _logger.LogError("Error mesajı...");
                return View();
            }
            public IActionResult Warning()
            {
                _logger.LogWarning("Warning mesajı...");
                return View();
            }
        }
    

    Yukarıdaki kod bloğunda görüldüğü üzere “Home(Controller).cs” sınıfı içerisinde “Information”, “Error” ve “Warning” olmak üzere üç farklı action üzerinde farklı log fonksiyonları çağrılmıştır. Uygulamaya derlenip, çalıştırıldığı vakit yukarıdaki actionlara yapılan birer request neticesinde uygulama dizininde oluşturulan “log.txt” dosyasının içeriği aşağıdaki gibi doldurulmuş olacaktır.
    Asp.NET Core - Logging

Nihai olarak; hangi platform yahut proje olursa olsun bir uygulamanın olmazsa olmaz ana organlarından biri olan log mekanizması sisteme uygun eşlik edebilecek şekilde tasarlanmalı ve yukarıda belirttiğim gibi tekrar ifade ediyorum ki mümkünse ilgili proje ihtiyaçlarına özel bir log mekanizması ortaya koyulmalıdır. Tabi isterseniz de bu içeriğimizde olduğu gibi hazır olan Logging mekanizmasıyla birlikte piyasada bulunan birçok hazır log mekanizmasını uygulamalarınıza entegre ederek kullanabilirsiniz.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

2 Cevaplar

  1. Mustafa.Ü dedi ki:

    Selamlar, Streamwriter sürekli kardeş bu dosya zaten kullanımda hatasına düşüyor. bunun yerine FileStream kullanmak daha mantıklı gibi geldi ama orada da Async olduğu için bazı mesajlar eziliyor mu ondan da emin olamadım.

    using (var fileStream = File.Open(filePath, FileMode.Append, FileAccess.Write, FileShare.Write))
                {
                    var logMessage = $"Log Level : {logLevel.ToString()} | Event ID : {eventId.Id} | Event Name : {eventId.Name} | Formatter : {formatter(state, exception)} {Environment.NewLine}";
                    byte[] logMessageByteArray = Encoding.UTF8.GetBytes(logMessage);
                    await fileStream.WriteAsync(logMessageByteArray);
                    fileStream.Close();
                    fileStream.Dispose();
                }
    
    • Gençay dedi ki:

      Stream ile işin bittiğinde Close ve Dispose etmeyi unutmamak lazım. Aksi taktirde başka kaynak tarafından kullanıldığına dair hatayla karşılaşacaksınız. Demek ki ilgili dosya bir başka sonlandırılmayan stream tarafından kullanılmaktadır. Süreci iyi takip etmek gerekmekte.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir