Öncelikle “telemetry” ne demek bununla başlayalım. Genel kullanım olarak telemetre şeklinde dilimizde yer edinen bu kavram, dilimizde de aslında “uzaktan ölçüm” anlamı ile tanımlanmakta. Sürdürülebilir yazılım çözümleri için önemli olan “ölçüm” ya da “ölçmek” konuları; günümüzün hızla “değişen” ve “büyüyen” ihtiyaçlarına sağlanan çözümler için daha da farkında olunması gereken ve önemi artan konuları oldu. Sunulan çözümlerin ölçülebilir olması ve bu ölçüm değerlerinin monitör edilebilir olması çözümlerin kalite gereksinimlerini karşılamak ve çözümlere değer katabilmek için oldukça kritik bir nokta oynuyor. Malum ölçemediğimiz bir çözümü geliştirmek ve ileri götürmek biraz zor

Bir yazılım çözümünün gereksinim duyduğu hafıza, işlem gücü, çalışma süreleri, çalışma anındaki kayıtlar(log’lar) ya da iletişim süreleri gibi gibi başlıklar, çözümlerin davranışlarına ve gelişmelerine büyük katkı sağlıyor.  

Özellikle artık dağıtık mimarilerin bir norm olduğu zamanda yaşıyoruz ve bu dağıttığımız bileşenlerin hayat döngülerini monitör edebilmemiz oldukça önemli. Yazılım teknolojileri için bu alanda da tahmin edebileceğimiz gibi birçok araç var. Sürekli gelişen ve büyüyen yazılım dünyası için yine tahmin edebileceğiniz gibi bu araçların sayısı ve olgunlukları her geçen gün değişiyor. Bu araçların uygulama yöntemleri de değişiklik gösterdiği için yazılım çözümleri içerisinde genel bir yaklaşım ile kullanmak biraz zor, -ki zaten standart yaklaşımların yazılım dünyasında oluşması(?) biraz zor.  

OpenTelemetry nedir?

İşte bu noktada OpenTelemetry projesi hayatımıza giriyor. Farklı firmaların ya da organizasyonların, farklı yöntemler ile geliştirdiği ve sunduğu araçlara direkt bağlı kalmadan, firma/organizasyon bağımsız API’ların, araçların, SDK’ların sunulduğu, kısmen yeni bir proje, OpenTelemetry. Açık kaynak olarak geliştirilen ve birçok popüler benzer araçlar tarafından da desteklenen bir proje olması ile de bir noktada hepimizin karşılaşacağı bir proje olacak.  

Bulut platformlarındaki teknolojilerin olgunluk ve tercih edilme kriterleri için kılavuz olabilecek “Cloud Native Computing Foundation” içerisinde şu an kuluçka döneminde gelişimini sürdüren bir proje statüsünde. 1-2 yıl içinde de mezuniyeti bekliyorum şahsen.🎓 😀

 Başlıktan direkt sadece .NET platformu ile ilgili bir proje olduğu algısı oluşmuş olabilir. Ama şimdiden onu da belirtim ki, direkt bir alakası yok. Python, Go, Java, JavaScript gibi gibi birçok yazılım dili teknolojileri için de geçerli bir proje ama tabi ki olgunlukları ne yazık ki aynı değil. Açık kaynak geliştirme dünyasının bir ürünü olduğu için gelişme ve olgunlaşma durumları aynı değil. Bu yüzden dikkatli olmak ve “neden-sonuç” ilişkisini iyi tartarak bu projeye yaklaşmak önemli. 

“Madem direkt alakalı değil, niye .NET de yazdın?” diyecek olanlar için biraz daha sabırlı olmalarını isteyeceğim. OpenTelemetry‘nin yazılım çözümlerini ölçerken baz aldığı kavramlar ve bu kavramların.NET platformundaki karşılıklarından bahsederek yazının başlığındaki .NET kısmına geleceğim. 

OpenTelemetry, yazılım çözümlerinin ölçümünü için 3 ana başlık altında topluyor. Bu arada OpenTelemetry‘nin bu başlıkların hepsi için de ayrı ayrı spesifikasyon da sunuyor ki, bu şartlara göre bu ölçüm değerlerinin ve ölçüm işlerinin yapılmasını hedefliyor. 

En basit anlatım olarak, OpenTelemetry‘nin sağladığı API’lar ve SDK’ler ile bu başlıklar altındaki verileri toplayıp gereken araçlara aktarmak mümkün, -ki birazdan bunlarla ilgili bir örnek ile daha iyi anlayacağız. Ama bir başlıktan daha bahsetmek istiyorum; 

OpenTelemetry Collector

OpenTelemetry projesinden ilk bahsederken, “…API’ların, araçların, SDK’ların sunulduğu…” demiştim hatırlarsınız. OpenTelemetry Collector, buradaki “araçlar” kısmı. OpenTelemetry Collector, yukarıdaki başlıklar altındaki ölçüm verileri toplayıp, konfigürasyonla başka istenen araçlara aktarabiliyor. Mesela Trace verilerini ya ister Jaeger‘e ya da ister Zipkin‘e uygulama tarafında bir değişiklik yapmadan görüntülemek mümkün.  

OpenTelemetry Collector‘ın önemli olduğu ve fayda sağladığı nokta bu uçtan uca akışı sadece konfigürasyon yönetimi ile sağlıyor olması. Aşağıdaki görsel üzerinden yapıyı biraz daha anlamaya çalışalım. Görsel OpenTelemetry‘nin ölçüm değerlerini nasıl toplandığı biraz olsun anlamak için oldukça iyi. 

OpenTelemetry Collector

Sol tarafta dikkat edersiniz ki; receiver(alıcı) bileşenleri var. Bu Kafka, PostgreSql, OTLP, RabbitMQ, Redis, MySql… gibi gibi birçok aracı destekleyen OpenTelemetry bileşenleri olabilir. Hepsi açık kaynak bir şekilde geliştirilmekte. Buradan ulaşabilirsiniz ve hatta isterseniz daha derinlere dalıp kendi alıcınızı da yazabilirsiniz. Bu alıcılar ilgili araçların çeşitli API’larını ya da parçalarını dinleyip ya da sorgulayıp ilgili verileri alıyorlar. Mesela, RabbitMQ alıcısı; RabbitMQ’nun Management API’larını dinleyip ondan gelen verileri iletiyor. 

Görselde dikkatinizi OTLP diye bir kutucuk dikkatinizi çekecektir. Bu OpenTelemetry Protocol (OTLP)’üne denk gelen bir gösterim. Yani; ölçüm verilerinin kaynak ve alıcılar arasında standart bir şekilde olmasını sağlayacak bir protokol. Bu protokol ile verilerin sıkıştırılması, iletişimdeki re-try mekanizmaları, şifreleme gibi aktarım yöntemleri sağlanmakta. Log, Metric ve Trace kavramları için aynı olgunlukta değil ama standartlaşma(?) açısından ışık veren bir protokol.   

OpenTelemetry ne değildir? 

Açıkcası burada OpenTelemetry’nin uygulamalardaki ölçüm verilerinin oluşturulması ve dağıtılması ile ilgili ara bir bileşen olduğunun altınızı çizmek isterim. Bu verilerin saklanması, sorgulanması ya da gösterilmesi gibi görevleri yok. Çok fazla veri üretilmesine yol gösteren bir proje olduğundan ister istemez böyle bir beklenti içinde de olabiliyor insan. Özellikle belirtmek istedim… 

Çok konuştun biraz da kod göster… 

Daha değil ama az kaldı. Önce hızlıca yazının başlığında .NET tarafına yaklaşalım. OpenTelemetry’nin yukarda bahsettiğim 3 ana başlığı, .NET platformundaki OpenTelemetry API’ları için nasıl işletiliyor önce bunu anlamaya çalışalım. 

Takip etme (a.k.a Tracing) 

OpenTelemetry, Tracing verileri için .NET platformunda System.Diagnostics.* namespace’ine bel bağlamış durumda, -ki aslında zaten System.Diagnostics.* .NET Framework zamanından beri belli “trace” verilerinin oluşmasını zaten built-in olarak sağlıyordu. Yeni .NET platformu ile de biraz daha güncel ihtiyaçlara uygun hale geldiğini söyleyebilirim 

Uygulama kayıtları(a.k.a Logging) 

OpenTelemetry, loglama ölçüm verileri için de .NET’in Microsoft.Extensions.Logging.Abstractions namespace‘ine bel bağlamış durumda. Yani standart olarak .NET platformunun sağladığı Logging ara yüzleri ve metotları ile geliştirdiğimiz uygulama loglama alt yapılarında üretilen veriler OpenTelemetry ile diğer ölçüm değerleri ile ilişkilendirilebilmekte. Ama eğer uygulama loglama alt yapımızı .NET’in standart sağladığı yapıların üzerine kurmadıysak uygulama logları ile ölçüm değerleri ilişkilendirmek pek mümkün değil. Bu arada ne demek istiyorum biraz daha açsam iyi olacak sanırım; uygulama içinde yazdığımız log’ların hangi talep ile ilişkili olduğunun tutulması gibi diyebilirim. Sorguların CorelationID ya da TraceID‘leri ile o sorgu sırasında yazılan uygulama loglarının ilişkilendirilmesi gibi. 

Ölçü sistemi (a.k.a Metrics) 

Metric ölçüm verileri için yine System.Diagnostics.* namespace‘i kullanılmakta. Meter, Counter gibi sınıflar ile uygulamamızda ölçmek isteğimiz metrik değerlerini oluşturabiliyor ve ölçebiliyoruz. Mesela bir metotun, hangi parametre ile kaç kere çağrıldığı ya da bir metot kaç kere hata fırlatmış bunları ölçmek için metrikleri yine System.Diagnostics.* ile yapabiliyoruz. OpenTelemetry‘nin bileşenleri de bunlar üstünden bu metrik verilerini dışa aktarabiliyor. Bu arada eğer zaten dotnet counters aracına aşinaysanız ne demek istediğimi anlamışınızdır.  

Birazdan göstermeye çalışacağım örnekte uygulama metrikleri ile ilgili bir gösterim olmayacak. Yazı fazla uzadığından onunla alakalı ayrı bir yazı yakında paylaşacağım.

Dikkat edersiniz ki, herhangi bir OpenTelemetry ya da başka bir bileşene sahip olmadan zaten var olan .NET platform bileşenleri ile ölçüm değerleri oluşturulabiliyor. Bu noktada OpenTelemetry bunları bir araya getirip anlamlı bir hale sokuyor diyebiliriz. 

Ve sonunda kod… 

Burada anlatmaya çalıştığım konuların ve bu örneğin kodlarına ardacetinkaya/opentelemetry-aspnet-demo (github.com) adresinden ulaşabilirsiniz. 💻

.NET platformunda geliştirdiğimiz çözümlerde OpenTelemetry projesini kullanarak uygulamalarımızı nasıl ölçülebilir hale getireceğiz hızlıca buna bakmaya çalışalım. Bunu OpenTelemetry‘nin sağladığı API’lar ile yapacağız. Aşağıdaki gibi bir örnek sistemimiz olsun. 

Sistemdeki örnek uygulama belirtilen gün adeti kadar hava tahmini yapmak için bir API çağrısı yapsın, bu API’da sonuçları dönsün ve veri tabanına da bir kayıt atsın. Oldukça basit bir senaryo. 😀🌧

Örnek bir web uygulamasına yaptığımız sorgunun, ilişkili olarak uçtan uça izlediği yolun takibi ve bu yoldaki yarattığımız uygulama loglarını nasıl oluşturabiliriz bunları görmüş olacağız. Bu örneğimizdeki “trace” verileri için Jaeger‘i kullanacağız. Jaeger‘in geliştirme ortamları için kullanımı olan “all-in-one” versiyonunu Docker imajı olarak kolay bir şekilde kullanabiliriz.

Daha kolay geliştirme yapabilmek için Microsoft tarafından açık kaynak bir şekilde geliştirilen Project Tye‘ı kullanıyorum, bu örneği kolay geliştirmek için ve ayağa kaldırmak için de kullandım. Burak Selim Şenyurt‘un Project Tye ile ilgili oldukça geniş bir örneği var, eğer Project Tye‘ı ilk defa duyduysanız şiddetle o yazıya da bakmanızı tavsiye ederim. Buradan ulaşabilirsiniz.

Örnekte kullanacağım OpenTelemetry API’ları aşağıdaki gibi olacak, bu şekilde paylaşıyorum ki kabaca kullanılan kütüphanelerden genel yapıyı anlamak biraz daha kolay olsun. 

    <PackageReference Include="OpenTelemetry" Version="1.2.0-rc4" />
    <PackageReference Include="OpenTelemetry.Api" Version="1.2.0-rc4" />
    <PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.1.0" />
    <PackageReference Include="OpenTelemetry.Exporter.Jaeger" Version="1.2.0-rc4" />
    <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.2.0-rc4" />
    <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol.Logs" Version="1.0.0-rc9.1" />
    <PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.0.0-rc9.1" />
    <PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.0.0-rc9.1" />
    <PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.0.0-rc9.1" />
    <PackageReference Include="Npgsql.OpenTelemetry" Version="6.0.0" />

Öncelikle standart bir şekilde uygulamalara (Web ve API) OpenTelemetry Tracing desteğini ekliyoruz. 

............
.......................
builder.Services.AddTracingSupport(builder.Configuration);

var app = builder.Build();
..................
..........
....
using Microsoft.Extensions.DependencyInjection;
using OpenTelemetry.Resources;
using System.Reflection;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
using Microsoft.Extensions.Configuration;
using System.Diagnostics;
using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;
using Microsoft.AspNetCore.Http;
using Microsoft.Data.SqlClient;
using Npgsql;

namespace SomeWebApp.Telemetry;
public static class TracingSupportExtensions
{
    public static IServiceCollection AddTracingSupport(this IServiceCollection services, IConfiguration configuration)
    {
        //Konfigürasyon dosyamızı okuyoruz.
       var telemetryConfiguration = TelemetryConfiguration.Init(configuration);

        // OpenTelemetry'nin Tracing API'larını kullanarak, izleme verilerinin oluşmasını sağlıyoruz.
        services.AddOpenTelemetryTracing((options) =>
        {
            options.SetResourceBuilder(telemetryConfiguration.ResourceBuilder)
                .AddHttpClientInstrumentation(options =>
                {
                    options.RecordException = true;
                    options.Enrich = HttpActivityEnrichment;
                })
                .AddAspNetCoreInstrumentation(options =>
                {
                    options.RecordException = true;
                    options.Enrich = AspNetCoreActivityEnrichment;
                })
                .AddNpgsql(); //PostgreSql iletişimlerini de ölçümlemek için tanım yapıyoruz

            switch (telemetryConfiguration.Exporter)
            {
                case "jaeger":  //TracingExporter ayarı "jaeger" olduğu için Jaeger'e aktarıyoruz.
                    options.AddJaegerExporter();
                    //Burada Jaeger Agent'ının varsayılan endpoint ve port bilgilerini kullanıyoruz
                    // Konfigurasyon tarafı ile özelleştirmek mümkün
                    break;
                case "otlp":
                    options.AddOtlpExporter(otlpOptions =>
                    {
                        otlpOptions.Endpoint = new Uri(configuration.GetValue<string>("Otlp:Endpoint"));
                    });
                    break;
                default:
                    options.AddConsoleExporter();
                    break;
            }
        });

        return services;
    }
}

internal class TelemetryConfiguration
{
    public string? AppName { get; set; }
    public string? Exporter { get; set; }
    public string? AssemblyVersion { get; set; }

    public ResourceBuilder? ResourceBuilder { get; set; }

    public static TelemetryConfiguration Init(IConfiguration configuration)
    {
        var telemetryConfiguration = new TelemetryConfiguration();
        telemetryConfiguration.AppName = configuration.GetValue<string>("AppName").ToLowerInvariant();
        telemetryConfiguration.Exporter = configuration.GetValue<string>("TracingExporter").ToLowerInvariant();
        telemetryConfiguration.AssemblyVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "unknown";
        telemetryConfiguration.ResourceBuilder = ResourceBuilder.CreateDefault().AddService(telemetryConfiguration.AppName, serviceVersion: telemetryConfiguration.AssemblyVersion, serviceInstanceId: Environment.MachineName);

        return telemetryConfiguration;
    }
}

Yukarıda fark etmiş olduğunuz gibi OpenTelemetry Tracing için ilgili tanımları yapıyoruz. Burada görmüş olduğunuz gibi AddHttpClientInstrumentation(), AddAspNetCoreInstrumentation() ve AddNpgsql() şeklinde uygulamamızın hangi kısımlarının Trace verileri için ele alınacağını belirtiyoruz. HTTP sorguları, PostgreSQL sorguları da uygulamamızın çalışması ile izlenebilir durumda olacak. 

Burada dikkatinizi .Enrich özellikleri dikkatinizi çekecektir. Bunlar ölçümlerin yapılmasını biraz daha zenginleştirmek için ekleyebileceğimiz Action<Activity,string,string> şeklindeki aktivite tanımları. 

private static void HttpActivityEnrichment(Activity activity, string eventName, object rawObject)
{
    if (eventName.Equals("OnStartActivity") &amp;&amp; rawObject is HttpRequestMessage httpRequest)
    {
        var request = "EMPTY!";
        if (httpRequest.Content != null)
        {
            request = httpRequest.Content.ReadAsStringAsync().Result;

        }
        activity.SetTag("http.request_content", request);

    }

    if (eventName.Equals("OnStopActivity") &amp;&amp; rawObject is HttpResponseMessage httpResponse)
    {
        var response = "EMPTY!";
        if (httpResponse.Content != null)
        {
            response = httpResponse.Content.ReadAsStringAsync().Result;
        }

        activity.SetTag("http.response_content", response);

    }

    if (eventName.Equals("OnException") &amp;&amp; rawObject is Exception exception)
    {
        activity.SetTag("http.exception", exception.Message);
    }

    SetTraceId(activity);
}

private static void AspNetCoreActivityEnrichment(Activity activity, string eventName, object rawObject)
{
    if ((activity.GetTagItem("http.target") ?? "/") == "/")
        activity.DisplayName = "Home";
    else
        activity.DisplayName = activity.GetTagItem("http.target")?.ToString();


    if (eventName.Equals("OnStartActivity"))
    {
        if (rawObject is HttpRequest httpRequest)
        {
            if (httpRequest.Query.ContainsKey("fordays"))
            {
                activity.SetTag("data.ForDays", httpRequest.Query["fordays"].ToString());
            }
        }
    }
    else if (eventName.Equals("OnStopActivity"))
    {
        if (rawObject is HttpResponse httpResponse)
        {
            activity.SetTag("data.ResponseType", httpResponse.ContentType);
        }
    }

    SetTraceId(activity);
}

private static void SetTraceId(Activity activity)
{
    activity.AddTag("trc.TraceId", activity.TraceId.ToString());
    activity.AddTag("trc.TraceSpanId", activity.SpanId.ToString());
    activity.AddTag("trc.TraceParentSpanId", activity.Parent?.SpanId.ToString());
}

Burada yaptığımız zenginleştirmeler ile “trace” verilerine bizim için anlamlı olacak değerleri ekleyebiliyoruz. Mesela “request” ve “response” verilerini ya da iş ihtiyaçlarımıza göre başka özelleştirilmiş değerleri “trace” verilerine eklemek mümkün. (“data.ForDays” gibi)

Burada önemli olan bir diğer konu da, dikkat edersiniz ki, System.Diagnostics.Activity sınıfı üzerinden, TraceId ve SpanId gibi genel “trace” konsepti ile ilgili değerlere ulaşabilmemiz. Çok ayrıntısına giremeyeceğim ama .NET içindeki bu sınıfın ayrıntılarına bakmanızı da tavsiye ederim.

Eeee peki nerede bu “trace” verileri?

Uygulamaları çalıştırdığımız ve bir kaç işlem yaptıktan sonra, http://localhost:16686/ adresinden Jaeger ön yüzüne erişip uygulamamızdaki trace adımlarını ve detaylarını görüntüleyebiliriz. 

Her hangi bir “trace” detayına girip, ilgili “request”in kırılımlarını ve eklenmiş ekstra bilgileri görebiliriz.

Aşağıdaki çıktılardan, “somewebapp-ui” uygulamasının “somewebapp-api” uygulamasının /WeatherForecast adresine HTTP GET çağrısı yaptığını, “somewebapp-api” uygulamasının da 2 kere veri tabanına(postgresql) gittiğini görebiliyoruz.

Bu adımların her birine tek tek de bakmak mümkün. Mesela /WeatherForecast uç noktasının “trace” ayrıntılarına baktığımızda da, ilgili Activity‘ye eklediğimiz etiketleri görebileceğiz.

Burada “trc.TraceId:80d2ff767c026a20d3bf170ce6df7c0c” ifadesine dikkat edelim. Biraz sonra uygulama logları ve “trace” verilerini ilişkilendirmede hatırlamak iyi olacaktır. 😉

Yukarıda kodlarda hatırlarsanız .AddNpgsql(); metotu ile PostgreSql veri tabanına yapılacak sorguları da takip etmek istemiştik. Kısaca ona da baktığımızda “Entity Framework” ile yaptığımız sorguyu da görebiliriz.

Uygulama logları ve “trace” ilişkisi

Dağıtık olarak belli sistemler ile iletişim halinde olan çözümlerde kullandığımız uygulama log’larını, uygulama içindeki akışlar ve iletişimler ile ilişkilendirmemiz gerekir ki, loglar anlamlı birer ifadeler olsun. OpenTelemetry ile bir .NET uygulamasındaki akışları takip edebildiğimizi kabaca göstermeye çalışmıştım. Şimdi de uygulama loglarımızın bu akışlar ya da “trace”‘ler içerisinde ilişkilendirilmesine bakalım hızlıca. 

Yukarıda yazdığımız .AddTracingSupport() metoduna benzer bir şekilde .AddLoggingSupport() metotu ile OpenTelemetry Logging desteğini de uygulamamızda tanımlayalım.  

............
.......................
builder.Services.AddTracingSupport(builder.Configuration);
builder.Logging.AddLoggingSupport(builder.Configuration);
var app = builder.Build();
..................
..........
....
public static ILoggingBuilder AddLoggingSupport(this ILoggingBuilder loggingBuilder, IConfiguration configuration)
{
    var telemetryConfiguration = TelemetryConfiguration.Init(configuration);

    loggingBuilder.AddOpenTelemetry(options =>
    {
        options.IncludeFormattedMessage = true;
        options.IncludeScopes = true;
        options.ParseStateValues = true;

        options.AddConsoleExporter();

        switch (telemetryConfiguration.Exporter)
        {
            case "otlp":
                options.AddOtlpExporter(otlpOptions =>
                {
                    otlpOptions.Endpoint = new Uri(configuration.GetValue<string>("Otlp:Endpoint"));
                });
                break;
            default:
                options.AddConsoleExporter();
                break;
        }

    });

    return loggingBuilder;
}

Burada dikkat edersiniz ki .AddOpenTelemetry() extension metodunu ILoggingBuilder arayüzü üzerinden .NET uygulamamıza ekliyoruz. Microsoft.Extensions.Logging.* namespace‘i üzerinden logların OpenTelemetry API’ları üzerinden “trace” verileri ile ilişkilendirilmesi sağlanıyor.

Burada altını çizmek istediğim bir nokta daha var. OpenTelemetry‘nin .NET API’ları tarafında Logging başlığı altında çok fazla bir dışa aktarım yöntemi yani “Exporter” yok. OpenTelemetry Collector’ı kullanıp, onun üzerinden OTLP protokolü üzerinden logları direkt farklı araçlara; ElasticSearch, Azure Monitor, Datadog…gibi, atmak mümkün. Ama şu an genel olarak OpenTelemetry Logging de daha olgunlaşma aşamasında olduğu için .NET API’ları da gelişecektir diye düşünüyorum, -ki zaten açık kaynak da olduğu için aslında bir kaç PR(pull request)‘e bakıyor iş. 😀

En basit standart yöntem olarak bu örnekte logları stdout’a(a.k.a Console) yazdırarak şimdilik devam edebiliriz. Aslında bu kadar, ekstra başka bir şey yapmaya gerek yok. Standart olarak .NET API’larını kullanarak loglama yapımızı kurduysak, yani Microsoft.Extensions.Logging.* objelerini kullanıyorsak artık loglarımız da “trace” verileri ile ilişkilenmiş olacaktır.

Kodlara ayrıntılı bakma fırsatı bulduğunuzda aşağıdaki gibi standart bir loglama göreceksiniz.

_logger.LogInformation(_context.Forecasts.Count().ToString()+" records in database");
_logger.LogInformation("Weather Forecast for {days} days is requested.", fordays);

Uygulamanın konsol loglarını takip ettiğinizde de aşağıdakine benzer bir şekilde logları görebileceksiniz. Burada dikkat edersiniz ki, benim az önce yukarıdaki ekranlarda paylaştığım “trace” verilerinde “trc.TraceId” şeklinde etiketlediğim değer ile loglardaki aynı. Yani bu demek oluyor ki, paylaştığım örnek sorguya ait bir log bu aşağıdaki.

info: SomeWebApp.API.Controllers.WeatherForecastController[0]
36 records in database
LogRecord.TraceId: 80d2ff767c026a20d3bf170ce6df7c0c
LogRecord.SpanId: 3c830d5f52978dd7
LogRecord.Timestamp: 2022-04-10T16:29:28.1082770Z
LogRecord.EventId: 0
LogRecord.CategoryName: SomeWebApp.API.Controllers.WeatherForecastController
LogRecord.LogLevel: Information
LogRecord.TraceFlags: Recorded
LogRecord.FormattedMessage: 36 records in database
LogRecord.StateValues (Key:Value):
{OriginalFormat} 36 records in database
LogRecord.ScopeValues (Key:Value):
info: SomeWebApp.API.Controllers.WeatherForecastController[0]
Weather Forecast for 20 days is requested.
LogRecord.TraceId: 80d2ff767c026a20d3bf170ce6df7c0c
LogRecord.SpanId: 3c830d5f52978dd7
LogRecord.Timestamp: 2022-04-10T16:29:28.1085680Z
LogRecord.EventId: 0
LogRecord.CategoryName: SomeWebApp.API.Controllers.WeatherForecastController
LogRecord.LogLevel: Information
LogRecord.TraceFlags: Recorded
LogRecord.FormattedMessage: Weather Forecast for 20 days is requested.
LogRecord.StateValues (Key:Value):
days 20
{OriginalFormat} Weather Forecast for {days} days is requested.
LogRecord.ScopeValues (Key:Value):

Böylece OpenTelemetry API’larını kullanarak loglarımız ile “trace” verilerimizi ilişkilendirebilmiş olduk. Bu sayede loglar daha anlamlı bir hale bürünmüş oldu.

Ve son…

Biraz uzun bir yazı oldu sanırım ve belki biraz da hızlı. OpenTelemetry‘nin amacı ve kullanım şekilleri hakkında biraz olsun bir katkım olabilecekse ne mutlu. Eğer dağıtık sistemler ve uygulama performans monitörü(a.k.a APM) uğraşıyorsanız, OpenTelemetry‘i pas geçmeyin derim. Orta vadede çok daha etken bir proje olarak karşımızda olacak diye düşünüyorum.

Yine klasik olarak her türlü soru, fikir ve düşüncenizi bekliyorum. Bir sonraki yazıda görüşmek üzere diyerek bitiriyorum, mutlu kodlamalar.

Kaynaklar: