.NET 8'den .NET 11'e Logging'de Gelenler: Enrichment, Redaction, FakeLogger, Buffering ve Generic LoggerMessage
.NET'te logging iki trenle ilerliyor: ILogger ve source generator runtime ile, enrichment, redaction, FakeLogger, sampling ve buffering ise aylık sürüm alan Microsoft.Extensions.Telemetry paketleriyle geliyor. Hangi özelliğin hangi sürümde geldiğini, .NET 10'daki CA1873 ve obsolete olan AddServiceLogEnricher'ı, .NET 11'deki generic LoggerMessage'ı ve eski Serilog Startup kodunun nasıl okunacağını gerçek çıktılarla gösteriyoruz.
Sürüm notu: Bu yazıdaki kodlar .NET 10, Microsoft.Extensions.Telemetry 10.11, Microsoft.Extensions.Diagnostics.Testing 10.11 ve Serilog.AspNetCore 10.0 ile derlenip denenmiştir. .NET 11 özellikleri yazım tarihinde RC (Release Candidate) aşamasında olduğu için kodsuz anlatılmıştır.
Önce Tahmin Et
Devraldığın eski bir Bookstore projesinde appsettings.json şöyle: "Microsoft.AspNetCore": "Warning". Program'da da Host.CreateDefaultBuilder(args).UseSerilog() ve bir Startup class'ı var. Projeyi .NET 10'a taşıyıp bir istek atıyorsun. Konsolda "Request starting" ve "Executing endpoint" gibi framework'ün Information satırlarını görür müsün? Görürsen neden?
İkinci soru daha yeni bir kodla ilgili: AddApplicationLogEnricher ile her log'a service.name eklemek istiyorsun, başka hiçbir şey yapmıyorsun. Log'da service.name ne yazar?
Bu yazı serinin geriye bakan bölümü. Blok A'da .NET 10 üzerinde her konuyu ayrı ayrı derinlemesine işledik; burada hangi özelliğin hangi sürümde geldiğini, eski kodda karşına ne çıkacağını ve .NET 11'in getirdiklerini topluyoruz.
Sürüm Haritası
Önce bir not: .NET'te logging'in iki ayrı yayın treni var. ILogger, filtreler, console provider ve LoggerMessage source generator'ı runtime ile, yani yılda bir gelen .NET sürümüyle yayınlanıyor. Enrichment, redaction, FakeLogger, sampling ve buffering ise dotnet/extensions repository'sinden çıkan Microsoft.Extensions.Telemetry ailesinde; bu paketler neredeyse her ay yeni bir minor sürüm alıyor. Sampling'in "hangi .NET sürümüyle geldiği" sorusunun doğru cevabı bu yüzden bir paket sürümü. NuGet'teki yayın tarihlerine göre harita:
| Dönem | Özellik | Nerede |
|---|---|---|
| .NET 8 (Kasım 2023) | Enrichment, redaction, [LogProperties], FakeLogger | Microsoft.Extensions.Telemetry ailesi 8.0.0 |
| .NET 8 | LoggerMessage'da EventId'siz constructor'lar | Runtime |
| .NET 8 | HTTP (Hypertext Transfer Protocol) logging'de CombineLogs, Duration, IHttpLoggingInterceptor | ASP.NET Core 8 |
| .NET 9 (Kasım 2024) | Source generator'ın primary constructor desteği | Runtime |
| .NET 9 dönemi (Mart 2025) | Random ve trace-based sampling | Microsoft.Extensions.Telemetry 9.3.0 |
| .NET 9 dönemi (Mayıs 2025) | Global ve per-request buffering | Microsoft.Extensions.Telemetry 9.5.0, Microsoft.AspNetCore.Diagnostics.Middleware 9.5.0 |
| .NET 10 (Kasım 2025) | CA1873 analyzer'ı, paketlerin 10.x serisi | SDK (Software Development Kit), Microsoft.Extensions.Telemetry 10.0.0 → 10.11.0 |
| .NET 11 (RC) | Generic LoggerMessage method'ları, HTTP logging'de stream pooling | Runtime, ASP.NET Core 11 |
.NET 8: Microsoft.Extensions.Telemetry Ailesi
.NET 8 ile birlikte Microsoft, telemetry ve compliance için dotnet/extensions repository'sinden yeni bir paket ailesi yayınladı. ASP.NET Core Web API (Application Programming Interface) serisinin ".NET 8 ile ASP.NET Core Yenilikleri" yazısında framework tarafını görmüştük; logging tarafında dört parça var.
Enrichment
Enrichment, her log kaydına otomatik olarak ortam bilgisi ekliyor: process id, thread id, servis adı, ortam. Serilog'daki enricher'ların Microsoft.Extensions.Logging karşılığı. Bookstore'un kargo servisinde:
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
// service.name ve deployment.environment bu metadata'dan okunur
builder.Host.UseApplicationMetadata();
builder.Logging.EnableEnrichment();
builder.Services.AddProcessLogEnricher(o => o.ThreadId = true);
builder.Services.AddApplicationLogEnricher(o => o.ApplicationName = true);
builder.Services.AddSingleton<ShipmentService>();
İnternetteki .NET 8 dönemi örneklerin çoğu AddServiceLogEnricher kullanıyor. 10.11 ile derlediğimde gerçek uyarı şu oldu:
Program.cs(7,1): warning EXTOBS0002: 'ApplicationEnricherServiceCollectionExtensions.AddServiceLogEnricher(IServiceCollection, Action<ApplicationLogEnricherOptions>)' is obsolete: 'This API is obsolete and will be removed in a future version. Instead of the AddServiceLogEnricher() methods, consider using the respective AddApplicationLogEnricher() methods.' (https://aka.ms/dotnet-extensions-warnings/EXTOBS0002)
Yeni adı AddApplicationLogEnricher. Baştaki ikinci sorunun cevabı ise UseApplicationMetadata() satırı olmadan alınan gerçek çıktıda: "service.name":"" ve "deployment.environment":"". Enricher değeri kendisi bulmuyor, Microsoft.Extensions.AmbientMetadata.Application paketinin doldurduğu ApplicationMetadata'dan okuyor; metadata yoksa alan boş string olarak yazılıyor, hata da vermiyor. Satırı ekledikten sonra JSON (JavaScript Object Notation) console'daki gerçek kayıt:
{"EventId":3001,"LogLevel":"Information","Category":"Bookstore.Shipping.ShipmentService","Message":"Shipment created","State":{"Message":"Microsoft.Extensions.Logging.ExtendedLogger+ModernTagJoiner","thread.id":"7","process.pid":"22236","service.name":"Bookstore.Shipping","deployment.environment":"Production","shipment.WeightKg":2.4,"shipment.ParcelCount":2,"shipment.Carrier":"FastCargo","shipment.OrderId":"01a1b8de-8d15-7651-b472-6d4f3064e0f6","{OriginalFormat}":"Shipment created"}}
Alan adları OpenTelemetry semantic convention'larına uyuyor (service.name, process.pid); log'ları OTLP (OpenTelemetry Protocol) ile dışarı verdiğinde bu adlar alıcı tarafta tanıdık gelir. State.Message içindeki ExtendedLogger+ModernTagJoiner metni ise Telemetry paketinin logger'ı devreye girdiğinde JSON console formatter'ının gösterdiği bir yan etki; içerikle ilgisi yok.
[LogProperties]
Yukarıdaki kayıttaki shipment.* alanları [LogProperties]'ten geliyor:
public sealed record Shipment(Guid OrderId, string Carrier, int ParcelCount, decimal WeightKg);
public sealed partial class ShipmentService(ILogger<ShipmentService> logger)
{
public Shipment Ship(Guid orderId, int parcelCount)
{
Shipment shipment = new(orderId, "FastCargo", parcelCount, parcelCount * 1.2m);
LogShipmentCreated(shipment);
return shipment;
}
// .NET 8: [LogProperties] object'i property'lere açar
// .NET 9: primary constructor'daki logger'ı source generator doğrudan kullanır
[LoggerMessage(3001, LogLevel.Information, "Shipment created")]
private partial void LogShipmentCreated([LogProperties] Shipment shipment);
}
Bu attribute'un dikkat isteyen tarafını, yani object'e yarın eklenecek bir e-posta alanının sessizce log'a girmesini ve data classification ile nasıl önleneceğini serinin "Log'larda Hassas Veri" yazısında gördük. Redaction'ın kendisi (EnableRedaction, ErasingRedactor, HmacRedactor) de aynı .NET 8 paket ailesinden geliyor ve orada ayrıntılı.
FakeLogger
.NET 8'den önce "bu method şu event'i yazıyor mu?" testini yazmak için ya ILogger'ı mock'lardın ya da kendi in-memory provider'ını yazardın. Mock'lamak can sıkıcıydı, çünkü LogInformation bir extension method ve asıl çağrı generic Log<TState>'e gidiyor. Microsoft.Extensions.Diagnostics.Testing paketindeki FakeLogger bunu bitirdi:
[Fact]
public void Ship_WritesShipmentCreatedEvent_WithShipmentProperties()
{
FakeLogger<ShipmentService> logger = new();
ShipmentService service = new(logger);
service.Ship(Guid.Parse("01a1b8de-8d15-7651-b472-6d4f3064e0f6"), parcelCount: 3);
FakeLogRecord record = logger.LatestRecord;
Assert.Equal(3001, record.Id.Id);
Assert.Equal(LogLevel.Information, record.Level);
Assert.Equal("3", record.GetStructuredStateValue("shipment.ParcelCount"));
Assert.Equal("FastCargo", record.GetStructuredStateValue("shipment.Carrier"));
Assert.Equal(1, logger.Collector.Count);
}
Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: 10 ms - Bookstore.Shipping.Tests.dll (net10.0)
Dikkat: GetStructuredStateValue değeri string olarak döndürüyor, 3 değil "3" ile karşılaştırıyoruz. Message metnini değil EventId'yi ve property'leri test etmek, template metni değiştiğinde testin gereksiz yere kırılmasını önler. "Log'da e-posta geçmiyor" türü defansif testleri serinin yedinci yazısında bu logger'la yazdık.
HTTP Logging
ASP.NET Core 8, HTTP logging middleware'ine üç şey ekledi: isteğin toplam süresini yazan HttpLoggingFields.Duration, request ve response'u istek sonunda tek kayıtta birleştiren CombineLogs ve istek başına neyin loglanacağına karar veren IHttpLoggingInterceptor. Endpoint bazında ayar için WithHttpLogging ve [HttpLogging] da bu sürümde geldi. Hepsini, yalnız 4xx/5xx yanıtlarda body loglayan bir interceptor ile birlikte serinin "HTTP Logging" yazısında çalıştırdık.
Son olarak runtime tarafında küçük ama kullanışlı bir değişiklik: LoggerMessageAttribute'a EventId istemeyen constructor overload'ları eklendi (LoggerMessage(LogLevel level, string message) gibi). EventId vermezsen generator bir değer üretiyor. Kolaylık gibi görünse de serinin "High-Performance Logging" yazısındaki event kataloğu yaklaşımında EventId'yi kendin vermeni öneririm; üretilen bir id'ye dashboard ya da alarm bağlamak istemezsin.
.NET 9: Primary Constructor, Sampling ve Buffering
.NET 9'un logging source generator'ı primary constructor'ı tanıyor. Yukarıdaki ShipmentService'te LogShipmentCreated bir instance method ve generator, logger'ı primary constructor parametresinden alıyor; ayrıca bir _logger field'ı tanımlamadık. .NET 8'de aynı kodu yazmak için logger'ı field'a atamak gerekiyordu.
.NET 9'da dikkat edilmesi gereken bir davranış değişikliği de var: IHttpClientFactory'nin Trace seviyesinde yazdığı log'lar artık header değerlerini varsayılan olarak içermiyor. Belirli header'ların değerini görmek istiyorsan RedactLoggedHeaders ile açıkça izin vermen gerekiyor. Eski bir projede "Authorization header'ı log'da görünüyordu, güncellemeden sonra kayboldu" diye bir şikâyet duyarsan sebep bu ve aslında iyi bir değişiklik.
Sampling ve buffering ise .NET 9 döneminde Telemetry paketinin minor sürümleriyle geldi: sampling 9.3.0 (Mart 2025), buffering 9.5.0 (Mayıs 2025). Yani projen .NET 9 hedefliyor olsa bile paket 9.2 ise bu API'ler yok. İkisini de serinin "Log Level'ları ve Maliyet" yazısında 10.11 sürümüyle ölçtük; o yazıda konfigürasyon bölümünün yeri ve buffer'dan gelen kayıtların scope ile TraceId'yi kaybetmesi gibi sürüme bağlı ayrıntılar var.
.NET 10: Paketlerin Güncel Durumu
.NET 10 LTS (Long Term Support) sürümü logging API'lerine büyük bir yenilik getirmedi; değişiklik daha çok araç ve paket tarafında. En görünür olanı yeni CA1873 analyzer kuralı ("Avoid potentially expensive logging"). Aynı iki satırlık kodu iki SDK ile derledim ve dotnet build çıktısındaki uyarıları koda göre saydım; .editorconfig'te CA1848 ve CA1873'ün seviyesi warning:
int[] data = [1, 2, 3];
app.Logger.LogDebug("Data {Data}", string.Join(",", data));
# SDK 8.0.419, net8.0
2 warning CA1848
# SDK 10.0.402, net10.0
2 warning CA1848: For improved performance, use the LoggerMessage delegates instead of calling 'LoggerExtensions.LogDebug
2 warning CA1873: Evaluation of this argument may be expensive and unnecessary if logging is disabled
SDK 8 aynı satırda CA1873 üretmiyor; kural .NET 10 SDK'sıyla geldi ve varsayılan olarak suggestion seviyesinde. string.Join, Debug kapalı olsa bile çalışıyor; kural tam olarak bunu yakalıyor.
Paket tarafında Microsoft.Extensions.Telemetry ailesi 11 Kasım 2025'te 10.0.0 ile başladı ve yazım tarihinde 10.11.0'da. Aylık sürümlerde API adları değişebiliyor: AddServiceLogEnricher'ın obsolete olması bunun örneği. Serilog tarafında Serilog.AspNetCore 10.0.0, 28 Kasım 2025'te yayınlandı; paket sürümü hedeflediğin .NET sürümünü izliyor, yani .NET 10 projesinde 10.x kullan. Serilog'un kendisi 4.4 sürümünde; sink'lerdeki batching desteği Haziran 2024'te çıkan Serilog 4.0 ile çekirdeğe girdi. Serinin "Serilog Sink'leri" yazısındaki IBatchedLogEventSink örnekleri bu yüzden ek bir paket istemiyor.
.NET 11'de Gelenler
.NET 11'in GA (General Availability) tarihi 10 Kasım 2026; bu yazıyı yazdığım gün henüz RC aşamasındaydı ve makinemde .NET 11 SDK'sı yoktu. O yüzden aşağıdakileri Microsoft Learn'deki ".NET 11 yenilikleri" sayfalarından aktarıyorum, kod vermiyorum.
Logging source generator'ı artık generic method'ları destekliyor: [LoggerMessage] ile işaretlenmiş bir method TCode : struct, Enum gibi constraint'li bir tip parametresi alabiliyor ve enum ya da struct değerleri boxing olmadan loglanabiliyor; allows ref struct anti-constraint'i ise desteklenmiyor ve SYSLIB1011 hatası veriyor.
HTTP logging middleware'i ResponseBufferingStream instance'larını pool'luyor; response body logging ya da interceptor açıkken istek başına allocation azalıyor.
ASP.NET Core, HTTP server Activity'sine OpenTelemetry semantic convention attribute'larını (http.request.method, url.path, http.response.status_code gibi) kendisi ekliyor; daha önce bunun için OpenTelemetry.Instrumentation.AspNetCore paketi gerekiyordu. İstemezsen Microsoft.AspNetCore.Hosting.SuppressActivityOpenTelemetryData AppContext switch'i ile kapatabiliyorsun. Bu doğrudan bir logging özelliği değil, ama log'larındaki TraceId'nin bağlandığı span'ın zenginleşmesi demek; ayrıntısı ilerideki DevOps › Observability serisinde.
Eski Kodu Okumak: Serilog 2/3 Tarzı Startup
.NET 6 öncesi bir projede logging kurulumu büyük ihtimalle şöyle görünür. Bu kodu Serilog.AspNetCore 10.0 ile .NET 10'da derledim; tek bir uyarı bile çıkmadı ve çalıştı:
public class Program
{
public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
try
{
Log.Information("Starting web host");
CreateHostBuilder(args).Build().Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging => logging.AddAzureWebAppDiagnostics())
.UseSerilog()
.ConfigureWebHostDefaults(web => web.UseStartup<Startup>());
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRouting();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSerilogRequestLogging();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/books/{id:int}", (int id, ILogger<Startup> logger) =>
{
logger.LogInformation("Book {BookId} viewed", id);
return Results.Ok(new { id });
});
});
}
}
Tek bir istek için konsol (uzun "Content root path" satırını çıkardım):
[05:14:50 INF] Starting web host
[05:14:50 INF] Now listening on: http://localhost:17451
[05:14:50 INF] Application started. Press Ctrl+C to shut down.
[05:14:50 INF] Hosting environment: Production
[05:14:53 INF] Request starting HTTP/1.1 GET http://localhost:17451/books/7 - null null
[05:14:53 INF] Executing endpoint 'HTTP: GET /books/{id:int}'
[05:14:53 INF] Book 7 viewed
[05:14:53 INF] Setting HTTP status code 200.
[05:14:53 INF] Writing value of type '<>f__AnonymousType0`1' as Json.
[05:14:53 INF] Executed endpoint 'HTTP: GET /books/{id:int}'
[05:14:53 INF] HTTP GET /books/7 responded 200 in 28.1874 ms
[05:14:53 INF] Request finished HTTP/1.1 GET http://localhost:17451/books/7 - 200 null application/json; charset=utf-8 31.6995ms
Baştaki ilk sorunun cevabı evet: appsettings.json'da "Microsoft.AspNetCore": "Warning" yazmasına rağmen framework'ün bütün Information satırları orada. Sebep, bu kurulumda seviyeleri Serilog'un kod içindeki configuration'ı belirliyor (Override("Microsoft", LogEventLevel.Information)); appsettings.json'daki Logging section'ına kimse bakmıyor. Eski bir projede "neden bu kadar log var?" sorusunu cevaplarken ilk bakacağın yer LoggerConfiguration zinciri, appsettings değil.
Okurken dikkat edeceğin diğer noktalar:
CreateDefaultBuilderMicrosoft.Extensions.Logging'in console provider'ını da ekliyor, ama çıktıda tek birinfo:satırı yok.UseSerilog()logger factory'nin yerini alıyor ve varsayılan hâliyle diğer provider'lara yazmıyor. Aynı sebepleAddAzureWebAppDiagnostics()satırı da fiilen ölü kod: Serilog kurulduktan sonra o provider'a tek kayıt gitmez.- Microsoft.Extensions.Logging.AzureAppServices paketi ölü değil; 10.0.14 sürümüyle hâlâ yayınlanıyor ve Azure App Service'in "Diagnostics logs" ve "Log stream" özellikleri için var. Ama Serilog ile birlikte kullanılıyorsa yukarıdaki sebeple işe yaramıyor; ya Serilog'un kendi sink'ini kullan ya da stdout'a yaz.
Log.Loggerstatik ve uygulamanın başında tek seferde kuruluyor; startup hatası olduğunda bileLog.Fatalyazılabiliyor. Bugünkü karşılığı serinin "Serilog Derinlemesine" yazısındakiCreateBootstrapLoggerile iki aşamalı başlatma.Startupclass'ınıWebApplication'a taşıma adımlarını ASP.NET Core Web API serisinin "Eski .NET Core 3.1 ve .NET 5 Projelerini Okumak" yazısında anlattık; logging tarafında yapman gereken,UseSerilog()'ubuilder.Services.AddSerilog(...)'a çevirmek ve configuration'ıReadFrom.Configurationile appsettings'e taşımak.
Üretimde Dikkat
Microsoft.Extensions.Telemetry ailesinin bütün paketlerini aynı sürümde tut. Bu yazıdaki örneklerin hepsini tek bir sürümle (10.11.0) derledim; aylık sürümlerde API adları değişebildiği için uygulamayla test projesinin farklı sürümlerle derlenmesi, birinde çalışan kodun diğerinde uyarı vermesine yol açar. Dependabot ya da Renovate gibi bir araçla bu paketleri tek bir grup olarak güncelle.
Paket güncellemesinden sonra derleme uyarılarını oku. EXTOBS0002 gibi obsolete uyarıları bu ailede gerçek bir sinyal: method bir sonraki major sürümde kalkacak. TreatWarningsAsErrors açık bir projede bu uyarı build'i kırar, kapalıysa sessizce birikir; ikisi de istemediğin bir durum.
Sampling ve buffering'i "paket sürümü" ile düşün. Projen .NET 10 hedeflese bile Directory.Packages.props'ta Telemetry 9.2 sabitlenmişse bu özellikler yok. Bir özelliğin kullanılabilirliğini sorarken önce paketin sürümüne bak.
.NET 11'e geçişte logging tarafında kırıcı bir değişiklik beklemiyorum; generic LoggerMessage eklemeli bir yenilik. Ama yerleşik OpenTelemetry attribute'ları açık geldiği için, OpenTelemetry.Instrumentation.AspNetCore'u da kullanmaya devam eden bir projede span attribute'larının nasıl göründüğünü geçişten sonra bir kez kontrol et.
Sık Düşülen Tuzaklar
Birincisi, enricher'ı ekleyip metadata'yı unutmak. Gördüğün gibi service.name hata vermeden boş string olarak yazılıyor. Birden fazla servisin log'u aynı yerde toplanıyorsa, servis adı boş olan kayıtlar hiçbir filtreye takılmaz.
İkincisi, eski projede iki logging configuration'ını birden düzenlemek. Serilog kuruluyken appsettings.json'daki Logging:LogLevel'ı değiştirip sonucu bekleyen ekip arkadaşı hiçbir değişiklik görmez ve "configuration reload çalışmıyor" diye yanlış bir sonuca varır.
Üçüncüsü, blog yazısındaki API adına güvenmek. .NET 8 döneminden kalan örneklerdeki AddServiceLogEnricher, Microsoft Learn'deki sampler configuration'ının yeri gibi ayrıntılar paket sürümüyle değişti. Bir örneği kopyalarken kendi paket sürümünle derleyip çıktıya bir kez bak.
Dördüncüsü, FakeLogger testinde formatlanmış message metnini karşılaştırmak. Template'teki bir kelimeyi değiştirmek bütün testleri kırar. EventId, seviye ve structured property'ler daha sağlam bir contract.
Küçük Bir Deneme
★☆☆ Kargo servisinde UseApplicationMetadata() satırını kaldır ve uygulamayı ASPNETCORE_ENVIRONMENT=Staging ile çalıştır. deployment.environment ne yazıyor? Satırı geri ekleyince ne değişiyor?
★★☆ ShipmentServiceTests'e, parcelCount sıfır olduğunda Warning seviyesinde yeni bir event yazıldığını doğrulayan bir test ekle. Önce testi yaz, kırmızıyı gör, sonra event'i [LoggerMessage] ile ekle.
★★★ Eski Startup projesini WebApplication.CreateBuilder ve builder.Services.AddSerilog'a taşı, seviyeleri ReadFrom.Configuration ile appsettings.json'a al. Taşımadan önce ve sonra aynı istek için konsoldaki satır sayısını karşılaştır.
Bu yazıyla ".NET'te Logging" serisini bitirdik. Neyi loglayacağına karar vermekten başladık; ucuz log yazmayı, Serilog'u ve sink'lerini, Seq'te sorgulamayı, correlation'ı, hassas veriyi, HTTP logging'i ve maliyeti ölçtük. Buradan sonraki doğal adım log'u metric ve trace ile aynı resimde görmek: OpenTelemetry Collector, Grafana ve alerting tarafını ilerideki DevOps › Observability serisinde ele alacağız.