Uygulamalar Aslında Konuşur
Bu yazıda bir geliştirici olarak geliştirdiğimiz uygulamalar ile nasıl etkileşim kurabileceğimize yönelik bir bakış açısı oluşturmaya çalışacağım.
Geliştirdiğimiz uygulamaları aslında birer yaşayan organizma olarak düşünüp ona göre tasarlasak hayatımız daha kolay olabilir.
Bir geliştirici olarak düşünüyorum da; uygulamaların problemlerini kolayca anlayabilsem, bir derdi olduğunda hemen bana geri bildirim verse, baktığımda içinde ne var ne yok görebilsem; röntgenini çekebilsem ne kadar güzel olur. Bunlar olursa hemen teşhis koyup tedavi yapabilirim. Sürekli etkileşim halinde olmak içimi rahatlatır, her şey yolunda diyebilirim.
Geliştirme yaparken genelde kodlamaya odaklanıyoruz. Hemen isterleri analiz edip geliştirmeyi yapalım, testlerimizi yapalım ve teslim edelim telaşında olabiliyoruz. Fakat geliştirdiğimiz uygulamayı yaşayan bir organizma yapma hedefimiz biraz arka planda kalabiliyor.
Uygulamalar nasıl konuşur?
-
Kolay anlaşılabilir loglar
-
Takip edilebilir metrikler
Kolay anlaşılabilir loglar
Geliştirme yaparken genelde kodlamaya odaklanıyoruz. Hemen isterleri analiz edip geliştirmeyi yapalım, testlerimizi yapalım ve teslim edelim telaşında olabiliyoruz. Fakat geliştirdiğimiz uygulamayı yaşayan bir organizma yapma hedefimiz biraz arka planda kalabiliyor.
Bu noktada logları hatırlamalıyız. Test driven geliştirme yapanlar için test kodları, geliştirmenin asli unsuru olduğu gibi log oluşturacak kodları yazmak da aynı şey olmalı. Örnek vermek için testi seçtim ama her ne şekilde geliştiriyor isek geliştirelim loglar, geliştirmenin asli unsurlarından birisi olmalı. Hep, nereye nasıl anlaşılır bir log eklerim endişesini taşımalıyız.
Log dediğimizde aklımızda log seviyeleri gelir(ERROR, WARN, INFO, DEBUG, TRACE). En temelde ERROR ve DEBUG seviyelerini kullanarak bile oldukça faydalı çözümler oluşturabiliriz.
public class InvoiceDTO{
private invoiceNumber;
private String customerId;
private PaymentType paymentType;
...
public String toString(){...}
}
public Invoice createInvoice(InvoiceDTO invoiceDTO){
(1)log.debug("Fatura oluşturuluyor... Fatura bilgileri {}", invoiceDTO);
if(!hasCustomerPaymentDetail(invoiceDTO.customerId,invoiceDTO.paymentType)){
(2)throw new NoPaymentTypeDefinitionForCustomerException(String.format("%s numaralı müşteri için %s ödeme tipi tanımı yapılmamış!",invoiceDTO.customerId, invoiceDTO.paymentType));
}
if(InvoiceDTO.PaymentType.CREDIT_CARD.equals(paymentType)){
(3)log.debug("{} numaralı fatura için kredi kartı ödemesi {} bankası üzerinden yapılıyor.",invoiceNumber,bankService.getCode());
}
...
...
(4)log.debug("Fatura {} id'si ile kaydedildi.", invoice.getId());
}
Yukarıda debug log yazmayı örneklemeye çalıştım.
İşlemin (1)en başında dışarıdan gelen fatura bilgilerini de içeren bir debug log ekledim. Burada doğrudan toString çalışıyor bazı durumlarda bu çok gerekli olmayabilir. Sadece önemli olan verileri log’a yazdırabilirsiniz.
Yine akışın içerisinde çeşitli (3)kurallara bağlı çalışan kod blokları içerisinde önemli olduğunu düşündüğünüz bir durum varsa oraya log ekleyebilirsiniz. Burada kredi kartı ödemesi ile önemli bir durum var ve bununla ilgili bazı bilgileri buraya koymalıyım diye düşünerek ekliyorum.
Son olarak işlemin (4)en sonunda debug log konulabilir. İşlem sonucunda bazı bilgileri görmek faydalı olacaktır.
Söylemeye çalıştığım şey; uygulama çalışırken neler yaşandığını anlamaya yardımcı olacak şekilde, loglara bilgiler yazdırmaya çalışmak. Gerekli olduğunu düşündüğünüz her yere log yazabilirsiniz. Ben örneklemek için böyle birşey yaptım.
ERROR level loglar yine log açısından önemli. Error level logları kullandığımız frameworkler var ise genelde otomatik olarak yönetiyorlar. Uygulamada bir hata oluştuğunda bunları uygun şekilde loga ERROR level’de ekliyorlar. Burada önemli olan istisnai durumları özelleşmiş exception class’ları ile yönetmemiz. Eğer örneğin (4)müşteri için ödeme tipi tanımı yapılmamışsa özel olarak oluşturulmuş NoPaymentTypeDefinitionForCustomerException istisnasını fırlatabiliriz.
Log demek IO demek, bu, uygulama için bir işlem maliyeti. Diğer yandan asıl kodun okunurluluğunu da düşürmemek için dengeyi tutturmak önemli bir konu. Bu sebepten size, uygulamayı anlamakta yardımcı olacak en önemli bilgileri loglamak daha efektif olacaktır.
Eğer loglamada temel olarak bunları yaparsak, benzetme açısından, uygulama, içinden dahi olsa birşeyler gerçirmeye başlayacaktır. İçinden dedim çünkü loglar hala log dosyalarında. Şöyle düşünürsek; log dosyalarını sürekli manuel olarak takip edemeyeceğimiz için hala etkileşim sağlamış sayılmayız. Uygulamanın içinden geçenleri hala duyamadık.
Etkileşime geçelim
Loglarımızı güzelce yazdık, artık uygulamayı konuşturalım. Buradan sonrası daha çok infrastructure ile ilgili. Uygulamamız güzel çalışıyor, logları ilgili outputlara yazıyor. Artık bu logların toplanıp kolayca erişilebilmesi, izlenebilmesi, okunabilmesi gerekli. Burada genelde aklımıza gelen ELK Stack oluyor. En azından benimi için öyle. Bu konu ile ilgili bir yazı yazmıştım merak edenler aşağıdaki linkten ulaşabilirler.
https://medium.com/devopsturkiye/spring-boot-syslog-ng-elasticsearch-i%CC%87le-centralized-logging-51518ad21ec6
Logları toplayıp Kibana ile izlemek tam bir etkileşim sayılmaz. Etkileşim karşılıklı olmalı. Ben kibanaya girip manuel birşeyler aramamalıyım.
Bunun için Elasticsearch Alerting Plugin’i kullanılabilir. Tabi bu pluging X-Pack ile birlikte geliyor.
Bu pluginin çalışması, çeşitli şartlar belirleyip o şartların belirli periyotlarda gerçekleşmesi durumunda kullanıcıları bilgilendirmesi mantığına dayanıyor. Örneğin son 5 dk içerisinde hata alan kredi kartı ödemesi işlemi sayısı 10’dan fazla ise size iletilmesini isteyebilirsiniz. Ya da hatalı giriş denemelerinin iletilmesini isteyebilirsiniz. Birçok durumdan haberdar olmanızı sağlayabilirsiniz.
X-Pack ücretli bir ürün. Buna alternatif olarak Elastalert ürününü kullanabilirsiniz. Birçok farklı türde rule oluşturup uygulamalarınızı konuşturabilirsiniz. Önümüzdeki günlerde Elastalert ile ilgili bir yazı yazmayı hedefliyorum.
Takip edilebilir metrikler
Uygulamaların çalışma zamanı durumlarını takip etmek etkileşim kanallarından birisi olabilir. Bunun için prometheus ve grafana ikilisi oldukça yaygın kullanıma sahip. Bilindiği üzere prometheus time series db , grafana ise temelde time series verilerini görselleştirmeyi sağlayan bir araç.
Uygulamalarımızın çalışma zamanında kendi durumuna dair birçok bilgiye sahip. Fakat bu bilgilerin prometheus gibi bir databasede saklanması gerekli . Bunun için Java/Spring tarafında Actuator modülü ile uygulamaların çalışma zamanı metriklerini sağlayan api’lar sağlanabiliyor. Prometheus’a bu api’ları datasource olarak vererek bu metrikleri sürekli saklayabiliriz. Peki bu metrikler neler olabilir;
-
Memory
-
CPU
-
Error Log sayıları
-
Request count/ Response time
-
Thread pool
-
Connection pool
-
Garbage Collector
gibi birçok metrik takip edilebilir. Yine grafana üzerinde bu metriklere bağlı olarak alertler oluşturularak etkileşim sağlanabilir.
Sonuç olarak;
Geliştirme yaparken açıklayıcı debug logları yazmak ve özelleşmiş exceptionlar oluşturmak uygulamanın bize anlamlı bir şeyler söylemesine yardımcı olur. Onun dışında uygulamamızın sağlıklı olup olmadığını anlamak için çeşitli metrikler belirleyip bunları takip etmeliyiz.
Eğer olumsuz değerler görüp bunlara yönelik iyileştirici aksiyonlar almamız, daha sürdürülebilir uygulamalara sahip olmamıza yardımcı olacaktır.
Diğer yandan bu bilgileri çeşitli alertler oluşturarak en yakın zamanda bize iletilebilmesini sağlamak karşılıklı etkileşim için önemli bir adım olacaktır.