4 min read

PostSharp ile log yazma

Bu yazıda PostSharp ile AOP yani aspect-oriented programming kullanarak bir uygulamadaki metotlar her çalıştığında log yazılmasını sağlayacağız.
PostSharp ile log yazma

Postsharp IL Weaving denilen bir yöntemle, kodunuzun belli başlı yerlerine sanki dokuma yapar gibi ilaveler yapar. Yani aslında siz bir aspect yazdığınızda bu aspect IL koduna (Intermediate language, derlemeyle oluşan makine dili diyelim) yazdığınız aspect'in içeriğini ekler. Sonuçta sizin bunları elle tek tek yazmanıza gerek kalmaz. Bunu sizin yerinize PostSharp yapar.

Bu şekilde loglama, hata yönetimi, validasyon, vb aklınıza gelebilecek her türlü işlemi yaptırabilirsiniz. Sizi büyük oranda kod tekrarından kurtaracaktır.

Dikkat: Runtime değil, compile-time

Aspect yazarken aklınızdan çıkarmamanız gereken en önemli konu yazdığınız kodun dinamik olamayacağıdır. Bu aspectler derlenen koda yazılıp gömüldüğünden dolayı asla runtime değil, daima compile-time üzerinden düşünmeniz gerekiyor. Örneğin runtime sırasında tipi belli olacak yani dynamic tipteki bir nesneyi aspect içerisinde tanımlayamazsınız.

Postsharp ile log aspect yazma

Postsharp ile log yazdırmak için özel bir attribute yazabiliyoruz. Bunu istediğimiz metodun başına annotation olarak eklememiz loglama işlemi için yeterli olacaktır.

Kendi logger nesnenizi Dependency injection ile bağladığınız taktirde bu şekilde aspect içerisinde kullanabilirsiniz. Konsola, metin dosyasına veya veritabanına log yazdırabilirsiniz. Kendi uygulamanızda log yazdırmak için hangi logger sınıfını tercih edeceğiniz size kalmış.

Ben burada aslında NLog logger kullanıyorum ancak zaten DI kullandığımdan detaya girmeye gerek görmüyorum.

Burada DI kullanımı ile gelen soyutlamanın konuyu ne kadar basitleştirdiğini görebilirsiniz. Biz sadece tanımı yapıyoruz, logger sınıfı da kendi bildiği yöntemler log yazıyor. Logger sınıfı değişse bile, DI ile bağlantı kurulduğundan bu yazdığımız kodlar hiç bir problem yaşamayacak. Eğer ileride NLog yerine başka bir kütüphanenin logger'ına geçmek istersem yine aynı Logger arayüzü üzerinden metotlar çağrılacağından postsharp aspecti bunu farkında bile olmayacak.

 [Serializable]
 [ProvideAspectRole(StandardRoles.Tracing)]  
public class LogPanelActionAttribute : OnMethodBoundaryAspect
{
    public static Logger Logger = LogManager.GetCurrentClassLogger();
    //...
Bir postsharp log aspect örneği.

Tek bir metot için log yazdırma

Sadece tek bir metot için log yazılacaksa, bu metodun başında aspect'i belirtmemiz yeterlidir.

Bir sınıfın bütün metotları için log yazdırma

Genelde bir tek metodu değil, bir sınıftaki bütün metotları bu şekilde kayıt düşülmesini isteriz. Eğer metot değil, de sınıfın başında aspect'i belirtirseniz, bu sınıfın bütün metotları için bu şekilde log yazılmasını sağlayabilirsiniz.

Bu sınıftaki bütün metotlar için LogPanelActionAttribute adlı aspect uygulanacak ve eğer metotlar başarıyla sonlanırsa loglara detayları yazılacak.

Metod başarıyla sonlandığı zaman log yazılması : OnSuccess

Bir metod çalışıp herhangi bir hata almadan return edileceği zaman, return etmeden hemen önce bir başarı logu yazılmasını sağlayabiliriz.

 public override void OnSuccess(MethodExecutionArgs args)
{
    //...
    var methodName = args.Method != null ? args.Method.Name : "";
    var returnValue = args.ReturnValue;
    var returnType = returnValue == null ? "" : returnValue.GetType().Name;
    Logger.Info($"OnSuccess : {methodName} | DeclaringType:{ args.Method.DeclaringType.Name} | ReturnType: {returnType} | ReturnValue : { returnValue } | User: {username}");
}
Metot başarılı sonlandığı taktirde log yazılır.
Bu aspect ile konsola yazılan log örneği. Metodun adı, ait olduğu sınıf, return tipi, işlemi yapan kullanıcı adı bilgileri sıralanmış.
Aynı aspect ile veritabanına da log yazdırabilirsiniz. Aspect kodları logger kodlarından bağımsızdır.

Metoda girildiği anda log yazılması : OnEntry

Bir metoda girildiği anda, metodun kapsamı yani {} parantezleri içinde tanımlanan ilk satır çalışmadan hemen önce log yazdırabiliriz. Bu tür loglarda genelde metodun adı ve argümanlarının ne olduğu bilgisi ile ilgileniriz.

public override void OnEntry(MethodExecutionArgs args)
{
    //...
    Logger.Info($"OnEntry : className: {className}; methodName:{methodName}; arguments:{arguments}");
}
Metoda girildiği anda log yazılır.

Metod hata verdiği anda log yazılması: OnException

Bir metod çalıştığı esnada bir hata verildiğinde başarısız bir return yapılmadan hemen önce hata ile ilgili bilgileri konsola yazdırabiliriz.

public override void OnException(MethodExecutionArgs args)
{
    var Message = args.Exception.ToString();
    var StackTrace = args.Exception.StackTrace;
    Logger.Info($"OnException : {args.Method.Name} |  | DeclaringType:{ args.Method.DeclaringType.Name} | Message: {Message} | User: {principal.Identity.Name} | StackTrace:{StackTrace}");         
}   
Metod hata verdiği anda bir sonraki komuta geçilmeden önce log yazılır.

Son söz.

Bu yazıda PostSharp ile AOP yani aspect-oriented programming'den faydalanarak tek satırla bir sınıftaki bütün metotların çalışma detaylarını nasıl loglayabileceğimizi gördük. Şimdiden yazdığınız aspect kodlarını logger'dan bağımsız tutarsanız, ileride logger değiştirdiğinizde aspectleriniz aynen çalışmaya devam edecektir. Bu aspect'leri bir kütüphane haline getirerek diğer projelerinizde de gönül rahatlığıyla dilediğinizi seçip kullanabilirsiniz.

Devam eden yazılarda PostSharp ile daha farklı amaçlara yönelik aspectler yazarak tekrar tekrar yazdığımız bir çok işlemi kolaylaştıracağız. Siz de aklınıza gelen çeşitli fikirleri aspect olarak yazmayı denerseniz, aspectlerin çalışma mantığını daha iyi kavrayabilirsiniz. Bu tarz hayal ettiğimiz kolaylıkları aspect olarak gerçeğe dönüştürmeye çalışmak gerçekten de çok öğretici bir deneyim oluyor. AOP'yi derinlemesine öğrenmek isteyenlere tavsiye ediyorum.

Yazdığınız aspect kodlarının derleme ile beraber belirli noktalara gömülüp o şekilde kalacağını unutmayın. Bu durum bizim için pek ideal olmasa da, aspect yazarken runtime değil, compile-time üzerinden düşünmeniz gerekiyor. Devam yazılarında görüşmek üzere, hoşçakalın.