Geliştirdiğimiz uygulamaların durumlarını kontrol eden, bu kontrollere göre çeşitli alarmlar üreten monitör araçları eminim bir çoğumuza tanıdık gelecektir. Uygulamaların belli durumlarına göre tedbirler alıp, bu durumlarda müdahale edip gerekli düzenlemeleri yapabilmek adına uygulama monitör araçları ve yöntemleri oldukça önemli bir konu. Dağıtık bir yapıya sahip uygulamalarda ise olmazsa olmaz; hatta bence uygulamanın sağlığı için zorunlu bir yaklaşım. ASP.NET Core tarafında bu tarz ihtiyaçları nasıl gerçekleştirebiliriz buna bakalım…

Ama önce kavramı ve konuyu daha iyi anlamak için, bir çoğumuza tanıdık gelebilecek bir senaryoyu hatırlayalım. Bir web uygulamasının monitör edilmesini sağlamak için http://minepla.net/Status.aspx tarzı bir sayfa(url) geliştirdiğimizi düşünelim; hatta uygulamanın içinde belki bir web servis. Bu URL’de basit belli işlemler gerçekleştirerek uygulamanın durumu hakkında monitör araçlarına çeşitli bilgiler paslayabiliriz. Bu sayfaya erişim olmazsa, sayfanın yüklenmesi uzun sürerse, veri tabanı işlemlerinde yavaşlık, sayfanın içinde basit bir iş kuralından beklenen sonuç dönmezse gibi gibi farklı durumları, ilgili sistemlere iletip alarm yaratılmasını sağlayabiliyoruz. Piyasadaki bir çok monitör araçının bu tarz kaynaklardan alarm üretmesi standart artık…

ASP.NET Core tarafında, monitör araçlarının bu tarz ihtiyaçları için v2.2 Preview 2 ile gelen HealthChecks kavramına bir bakalım hızlıca.

HealthChecks kavramı ASP.NET Core’un önceki versiyonları için de bir şekilde vardı. Ama Preview 2’deki son durumundan farklı ve ek paketler ile kullanılıyordu. Şimdi Preview olmasına rağmen direkt ASP.NET Core’un içinde ve daha olgun bir durumda.

ASP.NET Core’da artık uygulamaların sağlıkları ile ilgili çeşitli durumları paylaşmak için servis olarak çeşitli kontrolleri ekleyebiliyoruz. Bunun için ConfigureServices() metodunun içerisine aşağıdaki gibi .AddHealthChecks() şeklinde kendi ihtiyaçlarımıza göre geliştirdiğimiz servisimizi ekliyoruz. Burada ihtiyaça göre metotun diğer versiyonlarını da bir göz atın derim. Şimdi onlara çok girmeyeceğim.

        public void ConfigureServices(IServiceCollection services)
        {
            .
            ..
            ...
            ....
            
            services.AddHealthChecks()
                    .AddCheck<CustomCheck>();
            ....
            ...
            ..
            .
        }

Bununla beraber, Startup.cs içindeki Configure() methodu ile uygulamamızda da HealthCheck servisinin aktif olacağını belirtmemiz gerekiyor. Burada URL olarak path’i de belirtebiliyoruz. Bu sayede http://localhost:1234/IsHealthy şeklinde uygulamanın çeşitli durumlarını paylaşabileceğimiz URL path’ini tanımlıyoruz.

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            .
            ..
            ...
            app.UseHealthChecks("/IsHealthy");
            
            ...
            ..
            .
        }

Microsoft.AspNetCore.Diagnostics.HealthChecks namespace’i ile gelen bir kaç arayüz ile kendi kontrol yöntemimizi geliştirip, uygulamamızın durumlarını kontrol edebiliyoruz.

IHealthCheck arayüzünden yarattığımız sınıfımız ile örnek bir kontrol yapısı oluşturalım. Bu arayüzün Name özelliğini ve Task<HealthCheckResult> CheckHealthAsync() metodunu bir şekilde tanımlamamız gerekiyor. Mesela bir tabloya son 5 dakikadır hiç kayıt atılmıyorsa uygulamada bir sorun olabilir gibi bir yaklaşımımız olsun. Uygulamanın kalite özelliklerine göre çok farklı kontrol ihtiyaçları ve yöntemleri olabilir tabi ki, ben şimdi çok uğraşmamayım diye sıktım. 🙂

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Diagnostics.HealthChecks;

namespace NotifyMe.Services
{
    class CustomCheck : IHealthCheck
    {
        public string Name =&amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;quot;Custom Check&amp;amp;amp;amp;quot;;

        public async  Task&amp;amp;amp;amp;amp;lt;HealthCheckResult&amp;amp;amp;amp;amp;gt; CheckHealthAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            try
            {
                DateTime lastDate = DateTime.Now.AddMinutes(-10);
                TimeSpan difference = DateTime.Now - lastDate;
                int minutes = (int)difference.Minutes;

                if (minutes &amp;amp;amp;amp;amp;gt; 5)
                {
                    var data = new Dictionary&amp;amp;amp;amp;amp;lt;string,object&amp;amp;amp;amp;amp;gt;();
                    data.Add(&amp;amp;amp;amp;quot;LastUpdateDate&amp;amp;amp;amp;quot;,lastDate.ToString());
                    data.Add(&amp;amp;amp;amp;quot;DifferenceAsMinutes&amp;amp;amp;amp;quot;,minutes.ToString());

                    return HealthCheckResult.Unhealthy(&amp;amp;amp;amp;quot;No data is inserted&amp;amp;amp;amp;quot;,data);
                }

                return HealthCheckResult.Healthy(&amp;amp;amp;amp;quot;Ok&amp;amp;amp;amp;quot;);
            }
            catch (System.Exception ex)
            {
                return HealthCheckResult.Unhealthy(ex);
            }

        }
    }
}

Tanımladığımız metodun dönüş değeri HealtCheckResult sınıfı ile belirlenebiliyor. Burada .Healthy() ve .Unhealthy() metotları ile uygulamamızın durumu belirtebiliyoruz.

Şimdi bütün bunları yaptık ve uygulamamızı çalıştırdığımızda, 5 dakika kontrolü olduğu için; direkt olarak tarayıcıda Unhealthy ifadesini göreceğiz. Dönen response’u biraz daha ayrıntılı incelersek, 503 statü kodu ve text/plain tipinde içeriğin döndüğünü de görürüz.

Örnek olarak yaptığımız metod içeriğinde .Unhealthy() içinde belli bilgileri de koymuştuk. Bunlara ne oldu? IHealthCheck arayüzünden türeyen sınıfımızın çıktıları, sınıfı uygulamıza eklediğimizde dönüş değeri, “Plain Text” olarak gösterilir. ASP.NET Core tarafında varsayılan çalışma şekli bu şekilde. Ama tabi ki bu varsayılan dönüş tipini değiştirebiliyoruz. Kodumuzu aşağıdaki gibi değiştirip, HealthCheckOptions sınıfının ResponseWriter özelliğini kendi yazdığımız metot ile tanımlayabiliriz.

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            .
            ..
            ...
            //app.UseHealthChecks(&amp;amp;amp;amp;quot;/IsHealthy&amp;amp;amp;amp;quot;);
            app.UseHealthChecks(&amp;amp;amp;amp;quot;/IsHealthy&amp;amp;amp;amp;quot; ,new HealthCheckOptions()
            {
                ResponseWriter = WriteAsJson
            });
            ...
            ..
            .
        }

        private static Task WriteAsJson(HttpContext httpContext,CompositeHealthCheckResult result)
        {
            httpContext.Response.ContentType = &amp;amp;amp;amp;quot;application/json&amp;amp;amp;amp;quot;;
            var json = JsonConvert.SerializeObject(result,Formatting.Indented);

            return httpContext.Response.WriteAsync(json);
        }

Yukarıda basitçe response değerinin JSON formatında olması için bir metot yazıp, bunu belirtiyoruz. Bu değişiklik ile uygulamamızı tekrar çalıştırdığımızda, uygulamamız ile ilgili daha fazla durumu JSON olarak dönebiliyoruz.

JSON formatı ile çok daha fazla bilgiyi response olarak dönebiliyor olmamız, monitör araçlarına daha fazla bilgiyi paslayabilmemizi sağlıyor. Bu bilgiler ile de farklı aksiyonlar ya da alarmlar tetiklenebilir tabi ki. Aynı yaklaşımla uygulamada her şey yolunda ise bunun bilgilerini de aktarmak mümkün.

Umarım faydalı olmuş ve ASP.NET Core uygulamalarınıza bu tarz özellikleri eklemek için fikir vermiştir. Şimdilik bu kadar, bir sonraki yazıda görüşmek üzere 🙂