Android uygulamalarda loglama pratikleri Log4Net Crashlytics Slack

OpenSignal’in Android fragmentasyon verilerine göre, Ağustos 2014 itibariyle piyasada 18,796 farklı Android cihaz bulunuyor. Bu cihaz çeşitliliğine, çözünürlük, versiyon, yüzlerce 3rd party kütüphaneyi de eklersek Android uygulamaların bakımı ve desteği, geliştirme süreçlerinden çok daha zorlayıcı bir hal alıyor. Mobil uygulama geliştirmek zaten zorken, Android bu zorluğu bence biraz daha perçinliyor. Bu yüzden hata raporları ve client loglama çok fazla önem kazanıyor. Sunucu tarafında ise genelde kendi API’mızı kullandığımız için, hata raporlarının ötesinde istekleri izleyebilmemiz önemli. Daha fazla loglamanın öneminden bahsedip sıkmamak için, kafa ütülemeden direk konuya gireceğim. Ancak yine de hepimizin bildiği bir gerçeği tekrar edeyim; “Loglama her çeşit uygulamanın en hayati parçasıdır.”.

Loglama için uzun süre RDBMS vs metin dosyaları kullandım ancak log tabloları/dosyaları genellikle (özellikle yüksek hacimli uygulamalarda) en fazla depolama alanı tüketen kısım oluyor. Ve tablo/dosya büyüdükçe analiz etmek, arama yapmak çok daha zor bir hal alıyor. Bu yüzden bir süredir cloud uygulamalardan faydalanıyorum. Bu yazının sonunda aşağıdaki tabloda göstermeye çalıştığım yapıyı kurmuş olacağız. Sebepleri ve kurulum adımları için yazının devamını okuyabilirsiniz.

Android’de hata raporlama maceralarım

Yazdığım kurumsal bir Android uygulamasında, client tarafını uzaktan izleyebilmek için 2010 yılından şimdiye kadar farklı yöntemler denedim;

Manuel loglama denemeleri

Denediğim en primitif yöntemlerdi. Önce hata raporu ve logları eşzamanlı olarak sunucuya göndermeyi planladım. Ancak bu yöntemin en önemli handikapı, network katmanında oluşan hataları yakalayamıyor olmamdı. Ayrıca analiz zamanlarında loglanacak seviyeyi düşürdüğümde, sunucudaki yükü birden artıyordum. Ardından logları client üzerinde log dosyalarına yazmaya ve gerektiğinde bu dosyalara erişebilmeyi planladım ama bu daha kötü bir fikirdi. Çünkü io ile çalışmak sorun yaratabiliyordu, hem de log dosyaları güvenlik problemi oluşturuyordu. 
 Ardından bazı loglama frameworklerinden faydalandım. slf4j AndroLog gibi frameworklerin ciddi faydasını görsem de production ortamında hata raporu ve loglara uzaktan erişmek için hala sağlıklı bir yöntem üretememiştim. Hazır bir çözüm arayışına girdim.

ACRA

  • ACRA çok başarılı bir kütüphane. Kurulumu çok kolay. Ancak benim denediğim zamanlarda büyük bir dezavantajı vardı. Backend’i yoktu, logları yazmak için Google Docs kulanıyordu. Oldukça verimsiz bir yöntemdi. İlişkimiz kısa sürdü.
  • Not: Artık Acralyzer adında open source bir backend varmış. Ama henüz denemedim.

Bulut servisler

Mobil analitik konusunda özelleşmiş bir çok servis, artık hata raporlama araçları ile birlikte geliyor. Geliştiriciler arasında yaygın olarak kullanıldığını gördüğüm 3 farklı servis;

Ben 2 önemli sebepten Crashlytics’i tercih ettim;

  • Ücretsiz
  • Hazır slack entegrasyonu var

Crashlytics

2011 yılında ortaya çıkan Crashlytics, 2013 yılında Twitter tarafından 100 milyon $’a satın aldıktan sonra kurumsal planı da dahil olmak üzere tamamen ücretsiz hale geldi.

Kurulumu

Gördüğüm en basit kuruluma aşamalarına sahip SDK olabilir. Buradan üye olup, plugin’i indirip, yönergeleri takip ederseniz SDK’yı uygulamanız ekleyebilirsiniz.

Kullanımı

Hata loglamak için;

try { HataFırlatanKodBlogum(); } catch (Exception e) { Crashlytics.logException(e); }

Eğer isterseniz raporlarınıza, mevcut kullanıcıyı tanımlayan bir ifade atayabilirsiniz;

Crashlytics.setUserIdentifier("Login adı, eposta vs..");

Ya da farklı veri tipleri için tamamen özelleşmiş ifadeler tanımlayabilirsiniz;

Crashlytics.setInt("versiyon", 3); Crashlytics.setString("konum_saglayici", "network"); Crashlytics.setBool("sim_takili", true);

Hata raporunun dışında, seviye seviye normal log da tutabilirsiniz.

Crashlytics.log(Log.INFO, "TAG", "Bu cihaz rootlanmış.");

Not: Crashlytics, veri kullanımını minimize edebilmek için, önce logları biriktirip ardından belirli aralıklarla sunucuya gönderiyor.

Nitekim Android tarafında loglama konusuna çözüm ürettik, peki ya sunucu?

Sunucu logları

Sunucuda loglama yapmanın bir best-practice’i olduğunu söylemek zor. Uygulamadanın yapısına göre tuttuğunuz log değişiklik gösterecektir. Bu yüzden bu yazıda, sunucu logları ile ilgili “nerede, hangi seviyede, nasıl log tutmalıyız?” sorularının kesin cevaplarını bulamazsınız. Ancak kendini kanıtlamış yaygın çözümlerden bahsedebilirim.

Loglama hassas bir konudur. Çünkü sistemi izlemek ve analiz etmek için kullandığımız bir aracın sorun çıkartmasını istemezsiniz. Kendini kanıtlamış yöntemler kullanırsanız, kafanızı yastığa daha huzurlu koyabilirsiniz.

Apache log4Net

Log4Net, efsane java loglama frameworkü log4j’nin .Net portudur. log4js, log4c, log4perl gibi bir çok 3.party port geliştirildiği için, bir çok yazılımcı zaten bu mimariye çok alışkın. Ama bilmeyenler için ben yine de anlatmaya çalışayım.

Kurulum

Nuget üzerinde log4net diye aratarak kurulum yapabilirsiniz. 
 Nuget linki 
 Websitesi 
 Direk binary linkleri

Yapısı

Log4Net 3 ana bileşenden oluşur; loggers, appenders and layouts.

Loggers

Logger, log tutmak için kullanılan arayüzünün genel ifadesidir. Bu bileşen ile hiyerarşik loglama yapılabilir. Örneğin; “eferhatg.forms” adında bir logger’ımız olsun. Bu loggerda ERROR seviyesinde log tuttuğumuzu varsayalım. Bu logger’ın altındaki tüm classlarınızda bu seviyede log tutulacaktır. Ancak eğer isterseniz “eferhatg.forms.login” hiyerarşisini FATAL, ya da “eferhatg.forms.payment” hiyerarşisini INFO seviyesinde tanımlayabilirsiniz. 
 Logger sınıfı bize 7 farklı seviye verir. Bunlar ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF ‘dur. Açıklamaya gerek duymuyorum.

Appenders

Appender logun nerede tutulacağı ile ilgilidir. Yaygın olarak webconfig üzerinde XML ile tanımlanır. Bu sayede koda müdahale etmeden loglama için yeni hedefler tanımlayabilir ve yönetebilirsiniz. Örneğin aynı anda hem MSSQL üzerinde, hem metin dosyası üzerinde veya bir bulut servis üzerinde log tutabilir, istediğiniz zaman seviyelerini değiştirebilir, kapatıp açabilirsiniz. Log4Net standart olarak şu appenderları içerir.

Layouts

Layout adından da anlaşılacağı gibi logun nasıl tutulacağı ile ilgilidir. Layout, logda nelerin yazacağı ve nasıl yazacağı ile ilgili patterni tanımlr. Örn ilk satırdaki pattern, ikinci satırdaki gibi bir sonuç üretir.

"%date %thread %-5level %logger %message%newline" "[2015-05-29 12:37:12,860] [69] [INFO] [eferhatg.forms.payment] [Ödeme alındı.] "

Eğer mevcut ifadelerin yanı sıra kendi özelleşmiş ifadelerinizi eklemek isterseniz, Context nesnesinden faydalanabilirsiniz.

Konfigürasyon

Hemen web.config’i açın ve önce aşağıdaki gibi <configSections> taginin en üstüne log4net namespace'ini deklare edin.

<configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net, Version=1.2.13.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a"/> ... </configSections>

Şimdi konfigurasyonu halletmeliyiz. <configuration> taginin altına <log4net> adında bir tag oluşturup içerisinde appender'larımızı tanımlıyoruz. Tasarımınıza uygun appender'a şuradan erişebilirsiniz. Ben şimdilik RollingFileAppender kullanacağım.

RollingFileAppender’ın text dosyaya log tutan ve belirleyeceğiniz şartlarda log dosyalarını arşivleyen bir yapısı var. Aşağıdaki örnekte log dosyası 10MB’a ulaştığında, mevcut olanı arşivleyip yeni log dosyası oluşturuyor.

<log4net> <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender"> <file value="LOG\log.txt"/> <appendToFile value="true"/> <rollingStyle value="Size"/> <maxSizeRollBackups value="50"/> <maximumFileSize value="10MB"/> <staticLogFileName value="true"/> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date %thread %-5level %logger %message%newline"/> </layout> </appender> ... <log4net>

Örnekte gördüğünüz üzere her appender için layout oluşturuyoruz. Bu sayede tuttuğumuz logu her depolama tercihi için ayrı ayrı özelleştirebiliriz.

Şimdi <log4net> taginin sonuna <root> diye bir tag oluşturuyor ve hangi appender'ın hangi seviye için çalışacağını belirtiyoruz. Örneğin RollingFileAppender'ımızı INFO seviyesi için çalıştıracaksak log4net için yaptığımız tüm konfigurasyonun son hali aşağıdaki gibi olacaktır.

<configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net, Version=1.2.13.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a"/> </configSections> <log4net> <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender"> <file value="LOG\log.txt"/> <appendToFile value="true"/> <rollingStyle value="Size"/> <maxSizeRollBackups value="50"/> <maximumFileSize value="10MB"/> <staticLogFileName value="true"/> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date %thread %-5level %logger %message%newline"/> </layout> </appender> <root> <level value="INFO"/> <appender-ref ref="RollingFileAppender"/> </root> <log4net> </configuration>

Son olarak uygulamanızın başlangıç kısmında yukarıda yazdığımız konfigürasyonu başlatmalısınız; 
 WebApi için WebApiConfig.cs içerisine veya 
 MVC için RouteConfig.cs içerisine veya 
 Web Uygulaması için Global.asax içerisine aşağıdaki satırı eklemelisiniz.

log4net.Config.XmlConfigurator.Configure();

Not 1: Diğer yolu da AssemblyInfo.cs içerisine aşağıdaki satırı eklemek;

//ConfigFile masaüstü uygulamalar için app.config olacak. [assembly: log4net.Config.XmlConfigurator(ConfigFile="Web.config", Watch = true)]

Not 2: Eğer kurulum ile ilgili sorun yaşarsanız, log4net’i debug edebilmek için <appSettings> taginin altına aşağıdaki key'i ekleyebilirsiniz. Böylelikle console'da log4net'in kendi loglarını görebilirsiniz.

<appSettings> <add key="log4net.Internal.Debug" value="true"/> </appSettings>

Kullanımı

Artık log tutmaya hazırsınız. Log tutmak için log tutacağınız class’ın en üstünde ILog interface’ini türetmelisiniz. GetLogger methoduna verdiğiniz parametre logger olarak loglarda gözükecektir. Örneğin aşağıdaki gibi interface oluşturursak, loglarda logger olarak “eferhatg.forms.payment” gözükecektir.

namespace eferhatg.forms { public class payment { private static readonly ILog Log = LogManager.GetLogger(typeof(payment)); } }

Ve artık log tutmaya başlayabilirsiniz;

namespace eferhatg.forms { public class payment { private static readonly ILog Log = LogManager.GetLogger(typeof(payment)); public void pay(){ Log.Info("Ödeme Alınacak."); //Bu satırın ürettiği log aşağıdaki gibi olacaktır. //[2015-05-29 12:37:12,860] [69] [INFO] [eferhatg.forms.payment] [Ödeme Alınacak.] } } }

Logentries

Şu an log4net kullanarak metin dosyalarına loglama yapabiliyoruz. Ama log tutmak analiz için tam anlamıyla yeterli değil, logları canlı izleyebilmek, loglar içerisinde (full-text search) detaylı arama yapabilmek, gerektiğinde alarm üretmek gerekecektir. Eğer bulutta log tutmak ile ilgili bir engeliniz yoksa, tüm bunlar için en doğru yöntem bir loglama servisi olacaktır.

Papertrail ve Logentries yaptığımız testler sonucunda, biz Logentries’de karar kıldık. İkisi de çok iyi servisler, ancak biz Logentries’in fiyat modelini ve yeteneklerini daha çok beğendik.

Peki log4net ile tuttuğumuz sunucu loglarını Logentries’e nasıl aktarırız?

Kurulum ve Entegrasyon

Log4net kurarak zaten işin zor kısmını halletmiştik. Bundan sonra sadece 4 basit adımımız var;

  • Bir Logentries hesabı yarat 
     https://logentries.com adresinden bir hesap yaratıyoruz. Sonra arayüzden [Logs->Add New Log] diyerek .NET için Token TCP bir log kaydı oluşturuyoruz.
  • Logentries plugin’ini kurmak 
     Nuget kullanarak projemize logentries.log4net plugin’ini kuruyoruz.
  • Web.Config’e logentries için bir appender eklemek. 
     Web.config’i açıp <log4net> tagi içerisine aşağıdaki appender'i ekliyoruz.
<appender name="LeAppender" type="log4net.Appender.LogentriesAppender, LogentriesLog4net"> <Debug value="true" /> <HttpPut value="false" /> <Ssl value="false" /> <layout type="log4net.Layout.PatternLayout"> <param name="ConversionPattern" value="%date %thread %-5level %logger %message%newline" /> </layout> </appender>
  • <appSettings>'e LOGENTRIES_TOKEN değeri eklemek 
     Oluşturduğunuz log'un TOKEN değerini(Logentries arayüzünden edinebilirsiniz.) appSettings altına LOGENTRIES_TOKEN key'i ile ekliyoruz.
<appSettings> <add key="LOGENTRIES_TOKEN" value="***Burada sizin TOKEN'ınız olacak"/> ... </appSettings>

Bu kadar! Artık loglarınızı Logentries üzerinde görebilirsiniz.

İletişim Güçtür

Android istemci loglarını Crashlytics üzerinde, sunucu loglarını ise Logentries üzerinde tutmaya başladık. Artık alarmlar üreterek önemli olaylardan veya hatalardan hemen haberdar olabiliriz. Alarmlar için pek tabi öncelikli iletişim kanalım emaildi. Slack ile tanışıncaya kadar. Neden mi?

Neden Slack

Slack’i denemek, aslında anlatmaktan daha kolay. Kabaca “onlarca farklı bulut uygulama ile entegre olabilen bir haberleşme servisi.” diyebiliriz.

  1. Email alternatifi 
     Özellikle projeye ekiplerinde şimdiye kadarki en güçlü email alternatifi.
  2. Tüm gelişmeler tek bir yerde 
     Projeye ait tartışmalarız, alarmlar, dosyalar, testler… aklınıza gelebilecek tüm bileşenler ve gelişmeler tek bir ekranda.
  3. Çok güçlü bir iletişim aracı 
     Kanallar(channels) yardımı ile, email ile chat arasında, ikisinden de güçlü bir ekip iletişimi sağlıyor.
  4. Bir çok servis ile entegrasyon seçeneği 
     Crashlytics, Logentries, Papertrail, Google Drive, Bitbucket, Dropbox, Heroku, Jenkins ve onlarcası.
  5. Arama yapmak kolay ve efektif 
     Emailde arama yapmak bazen işkenceye dönüşebiliyor. Slack ile derinlerdeki bir bilgiye tek bir arama kutusuyla kolayca erişebiliyoruz.
  6. Dosya paylaşmak kolay 
     İster Google Drive veya Dropbox’ı entegre ederek, ister direk upload ederek.
  7. Kod parçaları paylaşımı 
     İstediğiniz syntaxda kod paylaşabilirsiniz.
  8. Mobil uygulaması var 
     Projenize dair tüm gelişmelerden 7/24 haberdar olabilirsiniz.

Özellikle bizim gibi home-office çalışan bireylerseniz, eminim Slack ekip iletişiminizi önemli ölçüde arttıracaktır. Kullanmasanız bile mutlaka deneyin. ;)

Loglama candır

Bu yazıda android uygulamalar için istemci ve sunucu loglamaları ile ilgili güncel pratiğimi paylaştım. Yazıdaki tüm bileşenler ve bulut servisler yarın değişebilir. Ancak değişmeyecek tek şey log tutmanın kendisi olmalı. Çünkü insanız, mutlaka hata yapacağız. Bu hatadan hemen haberdar olmak ve doğru analiz edebilmek, en az hatayı düzeltmek kadar önemli.

Projelerinizle konuşmanın tek yolu loglama.

İyi çalışmalar.


Originally published at www.eferhatg.com on June 2, 2015.