Bu ilk makalemde, .NET
uygulamalarımızda Trace ve Debug sınıflarının
yardımıyla loglama mekanizmasının kolay bir şekilde nasıl
oluşturulabileği ve yönetilebileceği konusu üzerinde
çalışmalar yapacağız.
System.Diagnostics isim
alanı bize tracing ve debuging yönetimleri ile ilgili
kullanabileceğimiz iki temel sınıf sunar. Trace ve Debug
sınıfları. Bu sınıflar proje geliştirme aşamasında debug mesajlarını, sürüm
aşamasında ise gerekli log bilgilerini ilgili kaynaklara yazdırma açısından
bize bir çok kolaylık sunar. İki sınıfın tüm özellikleri aynıdır, ikisi
arasındaki tek fark Debug sınıfı release uygulaması içerisine derlenmez.
Trace ve Debug
sınıfları kendilerine gelen bilgiyi biriktirip, bir veya birden
fazla kaynağa yönlendirebilirler. Bunu da TraceListener objeleri
sayesinde gerçekleştirirler. Trace ve Debug
sınıflarının Listeners property si TraceListener
tipinde koleksiyon referansı tutar. Bu koleksiyona varolan hazır
TraceListener objelerini ekleyebildiğimiz gibi , kendimiz de bir
TraceListener sınıfı oluşturup kullanabiliriz. Hem Trace hem Debug
sınıfı aynı TraceListenerCollection nesnesini
paylaşırlar. Eğer Trace nesnesine bir listener eklersek, bu
Debug nesnesi için de geçerli olur. Tam tersi de geçerlidir.
TraceListener
sınıfları
1-)
DefaultTraceListener
Bu Listener
sınıfı varsayılan olarak Trace ve Debug
sınıflarının TraceListenerCollection ‘una eklenmiştir.
Trace ve Debug sınıflarına iletilen bilgiyi Visual Studio
ortamında Output penceresine iletir. (yukardaki örneklerde olduğu
gibi)
2-)
TextWriterTraceListener
Bu Listener
sınıfı ise Trace ve Debug sınıflarına iletilen
bilgiyi herhangi bir Stream sınıfına yönlendirir. (FileStream,
NetworkStream, MemoryStream vs...) Aşağıdaki
örnek, bir log dosyasına Trace outputlarını yönlendirmeye bir
örnektir.
FileStream fs = new
FileStream("C:\\myLog.txt",FileMode.OpenOrCreate);
TextWriterTraceListener textListener = new TextWriterTraceListener(fs);
Trace.Listeners.Add(textListener);
Trace.WriteLine("Trace sınıfının yazdırdığı satır");
Debug.WriteLine("Debug sınıfının yazdırdığı satır");
Trace.Flush();
fs.Close(); |
Göründüğü
gibi, bir FileStream ile oluşturduğumuz TextWriterTraceListener
nesnesini Listener koleksiyonuna ekledikten sonra (Trace veya Debug
sınıfının Listener koleksiyonu olabilir farketmez) Trace ve
Debug sınıfları kendilerine gelen bilgiyi, ilgili dosyaya
aktarmıştır. Tabi aynı zamanda Default olarak eklenmiş
olan DefaultTraceListener nesnesi ile de Visual Studio‘nun Output penceresinde
de bu mesajları görebiliriz. Programı Debug modda
çalıştırdığımız da yukardaki
çıktıyı alırız, fakat Release modda
çalıştırsaydık Debug sınıfının metotları
koda eklenmeyeceği için “Debug
sınıfının
yazdırdığı satır”
çıktısını göremeyecektik.
3-)
EventLogTraceListener
Bu Listener
sınıfı da adından da anlaşılabileceği gibi
Trace ve Debug sınıflarına iletilen bilgiyi Windows Event Log
una yönlendirir. Aşağıda örnek bir kullanım
gösterilmiştir.
if( !EventLog.SourceExists("TraceDebugTest") )
{
EventLog.CreateEventSource("TraceDebugTest","myLogs");
}
EventLog myEvent = new EventLog();
myEvent.Source = "TraceDebugTest";
EventLogTraceListener eventListener = new
EventLogTraceListener(myEvent);
Trace.Listeners.Add(eventListener);
Trace.WriteLine("Trace sınıfının yazdırdığı satır");
Debug.WriteLine("Debug sınıfının yazdırdığı satır");
Trace.Flush(); |
TraceListener
sınıflarını config dosyasından yönetmek
TraceListener nesnelerimizi
kod içerisinde initilaze edip Trace sınıfına eklemek yerine, bu
işlemleri .config dosyasından da yönetebiliriz. Böylece ilerde,
uygulamamızın loglarını text dosyada tutmaktan vazgeçip,
Event Log da, ya da kendi oluşturacağımız bir TraceListener
nesnesi yardımıyla veritabanında tutmak istediğimizde,
config dosyasından gerekli düzeltmeleri yapıp, kodumuzu
değiştirmeden ve tekrar derlemeden log yönetimimizi
değiştirmiş oluruz. Tabi eğer kendi
TraceListener’ımızı yazdıysak bu dll ‘ i ilgili yere
koymamız gerekecektir. Aşağıda
listener nesnelerimizi yönetecek, örnek bir config file içeriği
gösterilmiştir.
Yukardaki config içeriğini özetleyecek olursak, listeners
altındaki remove tagı varsayılan olarak Trace
sınıflarına eklenen DefaultTraceListener nesnesini
kaldırır. add tagları ise örnek olarak TextWriterTraceListener
ve EventLogTraceListener sınıflarının eklenme
şeklini göstermektedir. name attribute ‘u ekleyeceğimiz
listener için nesne ismi, type attribute’u ekleyeceğimiz Listener
nesnesinin isim alanı ile birlikte tip ismi, initializeData attribute’u
ise listener nesneleri için başlangıç datasını temsil eder.
TextWriterTraceListener için buraya log dosyasının path’ini, EventLogTraceListener
nesnesi için se EventLog kaynağını verebiliriz. Bu veriler bu
nesnelerin, constructor’ı tek bir string parametre alan versiyonları
için anlamlıdır. Kendi listener’ımızı bunu dikkate
alarak oluşturmamız anlamlı olur. Config dosyasını bu şekilde düzenledikten sonra sadece
Trace.WriteLine("Ayarlar config dosyasından alınmıştır"); |
Kod satırı
ile hem text dosyaya, hem de event loga mesajlarımız iletilmiş
olur.
TraceSwitch
Şuana kadar hep tracing ve debugging kodlarını
uygulamamızda kullandık, ama tam olarak trace ve debug
sınıflarının davranış biçimlerini kontrol etmeyi
görmedik. İstersek trace ve debug çıktı mesajlarını
gruplandırabilir (error,warning,info) ve yine config dosyasından
yapacağımız bir ayar ile, uygulamamızı sadece hata
mesajlarını yazdırma, ya da hata ve uyarı mesajlarını
yazdırma moduna getirebiliriz. Bu kolaylığı bize
sağlayan ise TraceSwitch sınıfıdır.
TraceSwitch
Çalışma Seviyeleri
Off |
0 |
TraceListeners ‘a hiç mesaj gönderilmez |
Error |
1 |
TraceListeners ‘a
sadece hata mesajları
gönderilir |
Warning |
2 |
TraceListeners ‘a
hata ve uyarımesajları
gönderilir |
Info |
3 |
TraceListeners ‘a
hata,uyarı ve bilgi mesajları
gönderilir |
Verbose |
4 |
TraceListeners ‘a
tüm mesajlar gönderilir |
Şimdi TraceSwitch nesne yönetiminin config dosyasından
nasıl yapıldığını görelim.
Aşağıdaki kodda config dosyamızdaki switch
nesnemizi kod tarafında nasıl elde ettiğimiz ve trace yada debug
mesajlarını nasıl
gruplandırdığımızı görebiliriz.
static TraceSwitch mySw = new TraceSwitch("mySwitch","Tanım
Bilgisi");
private void Form1_Load(object sender, System.EventArgs e)
{
Trace.WriteLineIf(mySw.TraceError,"Hata Mesajı");
Trace.WriteLineIf(mySw.TraceWarning,"Uyarı Mesajı");
Trace.WriteLineIf(mySw.TraceInfo,"Bilgi Mesajı");
Trace.WriteLineIf(mySw.TraceVerbose,"Tüm Mesajlar");
} |
Trace ve Debug sınıfının WriteLineIf metodu,
aldığı ilk parametredeki seviye bilgisine göre ilgili
mesajları TraceListener nesnelerine gönderir veya göndermez. Config dosyasındaki mySwitch nesnemizin value attribute’u (seviye
bilgisi)
Seviye 0 olursa hiç mesaj iletilmez,
Seviye 1 olursa ”Hata Mesajı”,
Seviye 2 olursa “Hata Mesajı”“Uyarı Mesajı”
Seviye 3 olursa “Hata Mesajı”,“Uyarı Mesajı,“Bilgi Mesajı”
Seviye 4 olursa “Hata Mesajı”,“Uyarı Mesajı,“Bilgi Mesajı”,“Tüm Mesajlar”
stringleri ilgili Listener sınıfına iletilmiş
olur.
Kendi TraceListener ‘ımızı Oluşturmak
.NET tarafından bize sağlanan özelleştirilmiş
TraceListener‘lar çoğu uygulama için yeterli olabilir. Fakat bizlere kendi
TraceListener ‘ımızı kolayca yaratma imkanı da
sunulmuştur. Kendi TraceListener ‘ımızı oluşturmak
için yazacağımız sınıfı TraceListener
sınıfından türetmemiz yeterli olacaktır. TraceListener
sınıfının birçok virtual ve abstract metodu
bulunmaktadır fakat en basit anlamda bir Listener yaratmak için Write
ve WriteLine metodlarını override etmemiz yeterli
olacaktır.
Daha sonra TraceListener’ımızı değiştirmek
için , oluşturduğumuz dll’i , uygulamamızın bin klasörü
içine kopyalamak, ve de .config dosyasından gerekli düzeltmeleri yapmak
yeterli olacaktır. İşin en güzel yanı,
uygulamamızın kod tarafında herhangi bir değişiklik
yapmamıza ve de tekrar derlememize gerek kalmamasıdır.
Aşağıda çok basit anlamda Sql Server ‘ a Trace
mesajlarını ileten DBTraceClass’ın kodları
yeralmaktadır. Bu uygulama için Sql de ID,Tarih,Mesaj kolonlarına
sahip bir tablo yeterli olacaktır.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
namespace CustomTraceListeners
{
public class DBTraceClass : TraceListener
{
private string ConnectionString;
private void InsertDB(string message)
{
SqlConnection
con = new SqlConnection(ConnectionString);
con.Open();
SqlCommand
cmd = new SqlCommand("INSERT INTO Traces (Tarih,Mesaj) VALUES
(@Tarih,@Mesaj)",con);
cmd.Parameters.Add("@Tarih",DateTime.Now.ToString());
cmd.Parameters.Add("@Mesaj",message);
cmd.ExecuteNonQuery();
con.Close();
}
public DBTraceClass(string
ConnectionString)
{
this.ConnectionString = ConnectionString;
}
public override void Write(string
message)
{
InsertDB(message);
}
public override void WriteLine(string
message)
{
InsertDB(message);
}
}
} |
Oluşturduğumuz bu dll i uygulamamızın bin klasörüne
kopyalıp config dosyasını aşağıdaki gibi
düzenleyelim.
TraceListener’ımızn type attribute’unu
Namespace.ClassAdı , DLLismi şeklinde
yapmayı unutmayalım.Constructor ‘ a gitmesi gereken ConnectionString
‘imizie de initializeData attribute’una yazabiliriz.
static TraceSwitch mySw = new TraceSwitch("mySwitch"," Tanım Bilgisi
");
private void Form1_Load(object sender, System.EventArgs e)
{
Trace.WriteLineIf(mySw.TraceError,"Hata Mesajı");
Trace.WriteLineIf(mySw.TraceWarning,"Uyarı Mesajı");
Trace.WriteLineIf(mySw.TraceInfo,"Bilgi Mesajı");
Trace.WriteLineIf(mySw.TraceVerbose,"Tüm Mesajlar");
} |
Basit bir şekilde yukarıdaki kod parçasını
çalıştırdığımızda db görüntüsü
aşağıdaki gibi olacaktır.
Buarada Switch değerinin 3 olduğunu ve bu yüzden son
mesajın yazılmadığını belirtmek isterim.
Ayrıca Write, ve WriteLine metotlarını override
etmemize rağmen WriteLineIf metodu switch yapımıza uygun
bir şekilde çalışmış oldu. Bu ilk makalem, umarım sizlere Tracing için faydalı bir
kaynak olmuştur. Bundan sonra yeni makalerde görüşmek üzere hepinize
iyi çalışmalar dilerim.
Erhan Koçak
[email protected]
Makale:
Derinlemesine Trace ve Debug Kullanımı C#, Visual C# ve .NET Erhan Koçak
|