# Loglama ve Monitoring

Bu bölümde loglama için mimarimizi nasıl oluşturmalıyız, **best practice**’ler nelerdir sorularına yanıt arayacağız. Burada bahsedeceğim konuların bir çoğu sadece **Mikroservis** uygulamalara özel şeyler olmayacak, dolayısıyla M**onolith** mimaride uygulama geliştirenler için de ilgi çekici olacağını düşünüyorum. Konuyu iki ana başlıkta ele alacağız;

* Loglama Üzerine Genel Tavsiyeler
* Uçtan Uca Loglama Mimarisi

## Loglama Üzerine Genel Tavsiyeler

Mikroservis Mimari’leri dağıtık sistemlerin bir türü olarak ele alabiliriz.Dağıtık sistemlerde her bir servisi ve bu servisin üzerinde çalıştığı sunucuyu **trace** etmek **(**&#x69;zleme&#x6B;**),** beklenmedik durumlarda hatanın kaynağına hızlı ve etkili şekilde inebilmek için çok önemlidir.

Servislerimizin her birisi farklı bir sunucuda sunulurken, oluşan uygulama loglarımız da haliyle dağıtık yapıda olacaktır. Buna ek olarak bir de **cloud** üzerinde **auto-scaled** bir yapınız varsa, o zaman hangi log hangi sunucuda ve ne zaman oluşmuş gibi soruların yanıtını almak için biraz daha fazla efor harcamanız gerekiyor. Zira bildiğiniz gibi **auto-scale** yapıda yük durumuna göre dinamik olarak yeni sunucular çok kısa sürede devreye alınıp aynı şekilde kapatılabiliyor. Loglama mimarimizi, bu kapatılan sunucularda log kaybı yaşamamak üzere kurgulamamız gerekmekte. Yani kısaca cluster’ınızın auto-scale olup olmaması da mimariyi kurgularken göz önünde bulundurmamız gereken konulardan birisi olmalı.

### Logların Merkezileştirilmesi <a href="#f252" id="f252"></a>

Yapımız dağıtık bir yapı olduğundan, ilk düşünmemiz gereken konu tüm servislerimizin ürettiği logların tek yerden erişilebilir olmasıdır. Bir servisin logunu file’a, bir diğerinin DB’ye bir başkasının kuyruğa yazdığı bir ortamda oluşan bir hatanın kaynağına inmek saatler alabilir.

Merkezileştirme için çeşitli yöntemler mevcut. Mimari bölümünde biraz daha detaylı ele alacağımız için burada sadece kaçınmamız gereken bir **bad practice’**&#x64;en bahsetmek isterim.

Mikroservis Mimari’yi uygularken, isteriz ki servislerimiz atomik olsun, yani sadece bir işe, bir domain’e odaklansın. Diğer servislere bağımlılığı sıfır noktasında olsun. Hal böyleyken her türlü ihtiyaç için irili ufaklı servisler oluşturmaya başlarız. İş loglamaya geldiğinde de yine bir http log service oluşturup loglama işini de bu servise yükleyerek diğer tüm servislerin bu servis üzerinden log atmasını isteyebiliriz. İlk bakışta her şey normalmiş gibi gözükse de servis sayısı, dolayısıyla üretilen log miktarı arttıkça loglama işlemi için oluşan http trafiği baş ağrısı olabiliyor. Yani bu maddeden çıkaracağımız ders, loglama işini üstlenen bir http service oluşturmaktan kaçınmamız gerektiği.

### Merkezileştirme İçin Aggregation Tool Kullanma <a href="#id-77df" id="id-77df"></a>

**Log Aggregation** araçları, tüm loglarımızı tek bir ortak lokasyonda ve benzer formatta birleştirme ihtiyacı üzerine ortaya çıkmıştır. Bu birleştirme işi için farklı yöntemler mevcut. Burada seçeceğiniz yöntem, servis loglarınızı nerede ve nasıl tuttuğunuza göre belirlenecektir.

Örneğin, siz her servisin kendi sunucusunda bir dizine .txt olarak log atmasına karar verdiniz. Bu logları birleştirme için bir job yazar, belirli aralıklarla tek ortak bir dizine tüm txt’leri toplayabilirsiniz. Tercih kötü olsa bile sonuçta bir **log aggregation** yapmış olursunuz aslında.

Daha doğru olan ise, bu iş için geliştirilen ücretli veya ücretsiz bir araç kullanmanız yönünde. **ELK (elastic search, logstash,kibana)** stack’inin aggregation aracı olan **logstash**’i önerebilirim mesela. ELK’dan, mimari bölümde bahsedeceğimiz için burada fazla detaya girmiyorum

### Log’daki Tüm Alanların Aranabilir Olması <a href="#id-295f" id="id-295f"></a>

Loglarınızı sorgularken bazı alanlar üzerinden daha sık sorgulama yapmanız gayet doğaldır. Ancak siz her durumda mevcut alanların tümü üzerinden sorgulama yapabilecek ve hızlıca yanıt alabilecek bir yapı oluşturmaya odaklanırsanız iyi edersiniz.

Hız için veri tabanı üzerinde index oluşturma tabi ilk aklımıza gelen çözüm. **CustomerId**, **UserId**, **LogLevel** gibi alanlar üzerinden index’lenme olmazsa olmazdır, ancak kritik bir anda örneğin **InstanceId** alanı üzerinden sorgulama yapmanız gerekirse, dakikalarca beklemek biraz can sıkıcı olabilir.

Eğer çözüm index oluşturmaksa neden tüm alanlar için oluşturmuyoruz diye düşünebilirsiniz. Index’leme işi maliyetlidir. Çok fazla veya çok büyük boyutlu index’ler memory’de büyüklüğüne göre yer kaplayacaktır. Üstelik bununla da kalmaz, her bir insert/delete/update komutundan sonra bu indexlerin güncellenmesi gerekmektedir. Bu da sizin insert/delete/update sürelerinizin uzamasına yol açabilir.

Ancak bu index’leme işini uygulama veri tabanında değil de ,bir log aggregation aracı üzerinde yaparsanız performans kaybının önüne geçebilirsiniz.

### Log Seviyesinin (Log Level) Dinamik Ayarlanması <a href="#id-3716" id="id-3716"></a>

Atılan her bir satır log’un mevcut kaynaklarınızı tükettiğini ve aynı zamanda log sorgulama maliyetinizi (süresini) artırdığını unutmamanız gerekiyor. Bu bağlamda, servislerimiz ayaktayken, **runtime’**&#x64;a log seviyemizi dinamik olarak,uygulamamızı kesintiye uğratmadan değiştirmek isteyebiliriz.Elbette kullandığımız yardımcı kütüphanenin (varsa) bunu destekliyor olması gerekmekte.

Bu özelliği ağırlıklı olarak **Production** ortamımızda kullanabiliriz. Örneğin geçici bir süreliğine Debug seviyesinde log atılmasını isteyebiliriz. Servisimiz ayağı kalkarken en detaylı log seviyesini pasife çekip, sonrasında aktif hale getirerek aslında bize bir faydası dokunmayan, gereksiz bir sürü logdan kurtulabiliriz.

### Asenkron Loglama <a href="#id-2bc2" id="id-2bc2"></a>

Loglama işlemini hangi yolla yaparsanız yapın, log’u atacak olan birimin asenkron çalışması uygulama performansı açısından önemlidir.

Asenkron loglamada, loglamayı yapacak olan **logger thread**, o an yürütülmekte olan transaction’ın (http isteğinin) thread’ini block’lamayacağı için herhangi bir performans kaybına neden olmayacaktır. Ek olarak uygulamanızın loglama işleminin sonucuyla da ilgilenmemesi gerekiyor. Bir başka deyişle loglar **fire-and-forget** prensibi ile gönderilmelidir.

### CorrelationId Kullanımı <a href="#id-9179" id="id-9179"></a>

Bir transaction’ın icra edilmesi için arka planda 3–5 farklı web servisin birbirlerini tetiklediği bir senaryoyu ele alalım. Burada yapılan işlem aslında tek bir işlem. Örneğin bir satın alma işlemi. Ancak arka planda 5 farklı servis çalışmakta ve, 5 adet log atıldığını biliyoruz. Bu işlemin loglarına erişmek istediğimizde bu 5 adet log için bir grup numarası olsaydı ve bu no ile sorgulama yaptığımızda sadece bu 5 adet logu görebilseydik güzel olurdu değil mi?

Bu gruplamayı yapmak için **X-Correlation-Id** http header’ını kullanabiliriz. Bu header’a işlem bazında **unique** bir değer set etmemiz gerekmekte. Ek olarak log tablomuza da CorrelationId adında bir alanda eklememiz gerekiyor tabi. Artık geriye, bu header’ı her servis **request** ve **response’**&#x75;için doğru şekilde set etmek olacaktır. Örnek senaryomuzdaki 5 adet servisten her birisi bir sonraki servise bu CorrelationId değerini geçecektir. Kulaktan kulağa oyunu gibi düşünebilirsiniz.

### Detaylı ve Anlaşılabilir Log <a href="#id-72f8" id="id-72f8"></a>

Gün içinde bir servisin loglarını inceleme gereği duyuyorsak, bir şeyler ters gidiyor demektir, tabi istisnai durumlar olabilir. Gerek development aşamasında gerekse production ortamımızda loglar tabiri caizse elimiz ayağımız oluyor. Tabi yeteri kadar detaylı bilgi verebildikleri zaman. Bu da yine bizim elimizde olan bir şey. Özellikle **error log**’ları mümkün olduğunca detaylı ve önemli bilgi içermelidir. Detaylı yanında önemli de dedim çünkü gereksiz bir sürü detay içeren ama hatanın kaynağına inmeye yardımcı olacak bilgiyi içermeyen logun kimseye bir faydası olmayacaktır.

Sözün özü, eğer loglarınızı incelerken “Keşke şu bilgi de elimizde olsaydı” diye iç geçiriyorsanız, o bilgi için de ek bir alan açmanın zamanı gelmiş demektir.

### Log Zamanı İçin Utc Time Kullanımı <a href="#d9fe" id="d9fe"></a>

Özellikle servislerin cloud üzerinde sunulduğu senaryolarda, mevcut sunucuların veya yük oluşması anında devreye alınacak yeni sunucuların hangi time zone’da olacağıyla ilgilenmek istemezsiniz. Sizin için önemli olan sunucunun performansı ve yüksek erişilebilirliği olacaktır.

Ancak iş loglarınızı sorgulamaya gelince, log zamanının hangi time zone’a göre atılacağı konusu önem arz ediyor. Zamana göre order by ettiğiniz logların işlem sırasına göre hatalı olarak gelmesi istemezsiniz. Burada tavsiyem Utc time kullanmanızdır. Böylece çok farklı lokasyonlardaki servislerden atılan loglarda bile zaman karmaşası yaşamamış olursunuz. Bu arada eğer sizin için logu atan sunucunun local zamanı da önemliyse, log veri tabanınıza **TimeZone** adında opsiyonel bir alan ekleyip time zone bilgisini de buraya yazabilirsiniz.

### Instance Id Ekleme <a href="#b0e8" id="b0e8"></a>

Eğer servislerinizi herhangi bir cloud provider üzerinden sunuyorsanız ve auto-scaling özelliğini kullanıyorsanız, yukarıda da bahsettiğim gibi yük durumuna göre sürekli yeni sunucular (instance) eklenip çıkarılacaktır. Log’un hangi instance’dan atıldığı, logu atan sunucunun hala aktif olup olmadığı gibi konular sizin için önemliyse, logunuza **InstanceId** alanını ekleyerek bu sorunu çözebilirsiniz.

### Log Alert Sistemi <a href="#id-8b19" id="id-8b19"></a>

Loglama alt yapınız artık olgunlaştı, loglarınız sorunsuz şekilde akıyor, sorgular gayet hızlı çalışıyor ne mutlu size. Artık işi bir adım öteye taşımak gerekiyor.

Eğer canlı ortamda oluşan hatalarınız için biraz daha proaktif olmak ve daha hızlı aksiyon almak istiyorsanız mutlaka bir alarm sistemini devreye almanız gerekiyor. Bu sistemi sağlayan ücretli ücretsiz birçok **third party**araç mevcut olmakla birlikte kendi implementasyonunuzu da pek ala yapabilirsiniz tabi.

Bu tarz alarm yapılarıyla örneğin, gelen log daki **LogDetail** alanında “**DB Connection Timeout**” hatası geçiyorsa şu kişi veya kişilere slack’ten mesaj at veya sms at gibi aksiyonlar alabildiğimiz için oldukça faydalı olduğunu söyleyebilirim.

Alarm konusu için bir süre deneyimleme fırsatı bulduğum [**Seq**](https://getseq.net/) i incelemenizi öneririm. Tabi [**ELK** ](https://www.elastic.co/elk-stack)ile de veri değişikliklerini izleyerek aynı şekilde alarm kuralları oluşturabilmeniz mümkün.

## Uçtan Uca Loglama Mimarisi <a href="#id-67d8" id="id-67d8"></a>

Bu bölümde, yukarıda yaptığımız öneriler ışığında örnek bir mimari tasarlayıp, çizdiğim diagram üzerinden yorumlamaya çalışacağım.

Hatırlarsanız loglamayı bir servis üzerinden http request ile yapmanın kötü bir fikir olduğundan bahsetmiştik. Ancak aynı zamanda logları bir şekilde merkezileştirmemiz gerektiğini de söyledik. Yapmamız gereken şey çokta karmaşık değil aslında.

Her Mikroservis, oluşturduğu logları aracı bir başka http servis olmaksızın asekron olarak bir kuyruğa gönderebilir. Buradaki kuyruk, **Apache** **Kafka** veya **RabbitMQ** gibi bir **message broker** olabileceği gibi, kuyruk kullanmadan doğrudan bir **RDBMS** veya **NoSQL** veri tabanı da olabilir. Bu tamamen sizin log yoğunluğuna bağlı olarak yaptığınız tasarıma kalmış. Benim tavsiyem, **Kafka** veya **RabbitMQ** ikilisinden birini kullanmanız yönünde olur. Eğer doğrudan bir veri tabanına yazacaksanız da bu RDBMS yerine bir NoSQL veri tabanı olmalı.

Loglarınızı doğrudan bir kuyruğa göndererek veri tabanı sunucunuzu ekstra ve büyük bir yükten kurtarmış olursunuz. Veri tabanı sunucunuz sizin en değerli kaynağınızdır. ve unutmayın DB sunucuları ram’i sever yani bol bol kullanır.

Bir diğer aklıma gelen konu da uygulamanız ayağı kalkarken, henüz veri tabanı bağlantısı yapılmadan önce loglamaya değer bazı bilgileriniz olabilir ancak veri tabanı bağlantısı henüz sağlanmadığı için bu bilgileri loglayamayacaksınız.

Kuyruk yapıları AMQP protokolünü desteklerler. Bu protokol Http’nin aksine asenkron çalışır ve mesajları alıcısına teslim etme garantisi verir. Tabi sizin kuyruğu nasıl konfigüre ettiğiniz de önemlidir. Servisler arası iletişimi incelediğimiz bölümde AMQP’den daha detaylı bahsetmiştik hatırlarsanız.

Tüm bu bilgiler ışığında çizdiğim aşağıdaki basit mimariyi siz de uygulamalarınız için rahatlıkla uygulayabilirsiniz.<br>

![Mikroservis Uygulamalar İçin Loglama Mimarisi](https://1782653364-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Ln4QgtYUxSA7w8IT9Dc%2F-Ln4Yzb-mnEvNhA3r19J%2F-Ln4ZC0UEvnKCKOi2YSd%2Fimage.png?alt=media\&token=e6374221-04c7-4ac7-a275-9f8e9f23df0b)

Son olarak mimariyi oluşturan bu 5 parçadan kısaca bahsetmek gerekirse;

### Microservices <a href="#e2bd" id="e2bd"></a>

Farklı format ve yoğunluklarda loglar üreten ve kuyruğa gönderen mevcut microservislerimiz mimarinin ilk bacağını oluşturmakta. Dikkat ederseniz servisler ve kuyruk arasında başka aracı bir servis bulunmuyor.

### **Message Queue** <a href="#e78b" id="e78b"></a>

Mikroservis’lerimizin ürettikleri logları **publish** ettikleri kuyruğu tanımladığımız message broker’ımız. Log yoğunluğuna göre bu broker’ları da yatayda ölçekleyebilirsiniz yalnız, Kafka yatayda ölçeklenebiliyorken yanılmıyorsam RabbitMQ bu imkanı vermiyor. RabbitMQ da, iki farklı sunucu kurup aktif-aktif çalışmasını sağlayabiliyorsunuz ancak yükü 2 sunucu arasında dağıtarak yatayda ölçekleneyim diyemiyorsunuz.

### **Aggregation Tool** <a href="#e632" id="e632"></a>

Aslında ELK bütün olarak bir log aggregation aracıdır demek de yanlış olmaz. **Logstash**, logları ilk karşılayan, isteğinize göre manipüle edebilen ve sonrasında Elasticsearch’e gönderen taraf olduğu için ben, Logstash ELK stack’inin log aggregation tool’udur demeyi tercih ediyorum.

### **NoSQL Document DB** <a href="#id-4d3b" id="id-4d3b"></a>

Elasticsearch, bir arama motoru gibi çalışmak üzere optimize edilmiş, NoSQL document tipi veri tabanıdır. ELK stack’inde logların fiziksel olarak saklandığı, indexlendiği ve sorgulandığı yer burasıdır.

### Visualization Tool <a href="#id-2b45" id="id-2b45"></a>

ELK stack’ini tercih ettiğimiz için loglarımızı listelemek, sorgulamak ve çeşitli faydalı grafiklerden faydalanmak için bu stack’in sunmuş olduğu Kibana visualiztion tool’unu kullanıyoruz. Kibana, kullanışlı arayüzü, log sorgularken işimizi kolaylaştıran query syntax’ı, oluşturulan log raporlarını excel export edebilme gibi birçok özelliği bünyesinde barındırıyor.
