Basit TXT Loglama Servisi (Channel Tabanlı) – Asenkron ve Thread-Safe .NET Loglama
Merhaba, bazen uygulama içinde sadece basit bir txt loglaması yeterli olur. Özellikle küçük servisler, arka plan işler veya batch uygulamaları için büyük log kütüphaneleri kurmak istemeyiz.
Benim de buna benzer bir ihtiyacım olduğunda, asenkron çalışan ve dosyaya yazan küçük bir servis hazırladım.
Bu yazıda, o çözümü adım adım paylaşmak istiyorum.
- Log mesajlarını diske (txt olarak) yazmak
- Asenkron çalışmak (uygulamanın akışını yavaşlatmamak)
- Kanal yapısı (
System.Threading.Channels) ile thread-safe ve verimli bir yazma kuyruğu oluşturmak - Günlük bazlı log dosyaları oluşturmak (örneğin:
Logs/20251103.txt)
Channel<T> yapısı sayesinde, log mesajlarını önce bir kuyruğa yazıyoruz.
Arka planda çalışan bir tek görev (Task) bu mesajları sırayla alıp diske kaydediyor.
Bu sayede:
- Aynı anda birden fazla thread log yazmak istediğinde çakışma olmaz,
- Dosya erişim hataları azalır,
- Performans kaybı yaşanmaz.
Log Servis Kodu
public static class LogServis
{
private static readonly string _logKlasoru = "Logs";
private static readonly Channel<LogMesaj> _logChannel;
private static readonly Task _logyazmaTask;
static LogServis()
{
if (!Directory.Exists(_logKlasoru))
Directory.CreateDirectory(_logKlasoru);
_logChannel = Channel.CreateBounded<LogMesaj>(new BoundedChannelOptions(1000)
{
FullMode = BoundedChannelFullMode.Wait
});
_logyazmaTask = Task.Run(LogYazmaIslemi);
}
public static async Task LogYazAsync(string mesaj, Exception? ex = null)
{
try
{
await _logChannel.Writer.WriteAsync(new LogMesaj
{
Zaman = DateTime.Now,
Mesaj = mesaj,
Hata = ex
});
}
catch
{
// Channel doluysa veya kapanmışsa sessizce geç
}
}
private static async Task LogYazmaIslemi()
{
await foreach (LogMesaj log in _logChannel.Reader.ReadAllAsync())
{
try
{
string tarih = log.Zaman.ToString("yyyyMMdd");
string dosyaYolu = Path.Combine(_logKlasoru, $"{tarih}.txt");
StringBuilder sb = new();
sb.AppendLine("--------------------------------------------------");
sb.AppendLine($"Tarih : {log.Zaman:yyyy-MM-dd HH:mm:ss}");
sb.AppendLine($"Mesaj : {log.Mesaj}");
if (log.Hata != null)
{
sb.AppendLine($"Hata : {log.Hata.Message}");
sb.AppendLine($"StackTrace: {log.Hata.StackTrace}");
}
await File.AppendAllTextAsync(dosyaYolu, sb.ToString(), Encoding.UTF8);
}
catch
{
// Hata olsa bile devam et
}
}
}
// Uygulama kapanırken çağır
public static async Task KapatAsync()
{
_logChannel.Writer.Complete();
await _logyazmaTask;
}
}
public class LogMesaj
{
public DateTime Zaman { get; set; }
public string Mesaj { get; set; } = "";
public Exception? Hata { get; set; }
}
Kullanımı
Basit bir çağrıyla log yazmak mümkün:
await LogServis.LogYazAsync(
$"[PlaywrightServis] Manuel login başlatılıyor. siteId={siteId}, connectionId={connectionId}");
Eğer hata bilgisi de eklemek istiyorsan:
await LogServis.LogYazAsync("[PlaywrightServis.await oldPage.CloseAsync()]", ex);
Program.cs Entegrasyonu
Uygulama sonlanırken KapatAsync() metodunun çağrılması için aşağıdaki kodu ekleyebilirsin:
IHostApplicationLifetime lifetime = app.Services.GetRequiredService<IHostApplicationLifetime>();
lifetime.ApplicationStopping.Register(async () =>
{
await LogServis.KapatAsync();
});
Bu yöntemle:
- Thread-safe,
- Asenkron,
- Günlük dosya bazlı
bir loglama mekanizması elde etmiş oluyorsun.
Küçük servisler, konsol uygulamaları veya background job’lar için oldukça pratik bir çözüm.
Selamlar!
