İçeriğe geç
Haydar Demir

ÇalışmalarMobil gözlemlenebilirlik

Uçtan Uca Mobil Gözlemlenebilirlik Altyapısı

Dağınık hata, ekran ve ağ sinyallerini tek bir kullanıcı zaman çizelgesinde birleştirerek üretim sorunlarının bağlamıyla analiz edilmesini sağlayan Flutter gözlemlenebilirlik altyapısı.

Neden böyle bir altyapıya ihtiyaç duyduk?

Veri eksiği yoktu; farklı araçlarda çok sayıda sinyal vardı. Sorun, bu sinyallerin birbirine bağlanamamasıydı.

Hataların ve uygulama loglarının bir kısmı Sentry’deydi. Kullanıcının geçtiği sayfalar ve bazı ürün olayları Firebase Analytics’teydi. Ağ istekleri ise ikisinde de doğal olarak görünmüyordu. Geliştirici cihazında Chucker ile istekleri incelemek mümkündü ama üretimde sorun yaşayan kullanıcının telefonuna sonradan bağlanıp aynı kaydı açmanın yolu yoktu. Oturum analizi araçları kullanıcının ekranda ne yaptığını gösterebiliyordu, fakat başarısız isteğin teknik nedenini ya da uygulamanın durumunu açıklamıyordu.

Elimizde genellikle hatanın türü ve stack trace’i, kullanıcının ziyaret ettiği ekranlar, ayrı bir yerde duran ürün analitiği ve varsa kullanıcının anlattığı son işlem oluyordu.

Bir hatayı çözmek için gereken sıra ise görünmüyordu: kullanıcı hangi akışı başlattı, hangi seçimleri yaptı, hangi ekranlardan geçti, en son hangi ağ isteği gönderildi, istek ne kadar sürdü, hangi durum koduyla döndü ve uygulama ardından hangi hatayı üretti?

Panelleri kullanıcı kimliği ve yaklaşık saat üzerinden eşleştirmek kesin bir analiz sayılmazdı, tahminden öteye geçmiyordu. Randevu oluşturma gibi çok adımlı ve ödeme içerebilen akışlarda “işlem başarısız oldu” bilgisi yetmiyordu. Hatanın tesis seçiminde, uygun saat sorgusunda, yetkilendirmede, ödeme dönüşünde ya da son kayıt adımında oluşması bambaşka aksiyonlar gerektiriyordu.

Hedef

Yeni bir log ekranı yapmak istemiyorduk. Amacımız, mevcut hata yakalama ve analitik araçları arasındaki bağlam boşluğunu kapatmaktı.

Bir hata kaydı açıldığında şu soruların cevabı tek bir pakette bulunmalıydı:

  1. Bu hata hangi uygulama oturumunda oluştu?
  2. Kullanıcı hangi iş akışının içindeydi?
  3. Hangi ekrandan hangi ekrana geçti?
  4. Hata öncesindeki kullanıcı ve iş olayları nelerdi?
  5. Son ağ isteğinin adresi, süresi ve sonucu neydi?
  6. Uygulama sürümü, platform, dil ve bağlantı durumu neydi?
  7. Aynı hata birden fazla katmandan tekrar raporlandı mı?

Temel yaklaşım: her şeyi yerelde kaydet, yalnızca hata anını yükle

Her olayı anında sunucuya göndermek hem gereksiz trafik hem de büyük miktarda gürültü üretecekti. Bunun yerine gezinme, kullanıcı aksiyonu, iş olayı, ağ isteği, durum değişikliği ve hata gibi sinyaller bellekte sınırlı kapasiteli bir zaman çizelgesine yazılıyor.

Bu yapı bir ring buffer gibi çalışıyor: kapasite dolunca en eski kayıt çıkıyor ve en güncel bağlam kalıyor. Sorunsuz ilerleyen bir oturumun kayıtları cihazdan hiç çıkmıyor. Bir hata yakalandığında ise son olaylardan bir anlık görüntü alınıp hata bilgisiyle birlikte tek paket halinde yükleniyor.

Ekranlar + kullanıcı aksiyonları + iş olayları + ağ istekleri
                              ↓
                    Yerel zaman çizelgesi
                              ↓ hata oluşursa
             Bağlamlı hata anlık görüntüsü ve yükleme

Böylece bütün oturumları ayrıntılı saklamadan, sorunlu oturumda hatadan önce neler olduğunu koruyabildik.

Üç seviyeli ilişkilendirme modeli

Tek bir kimlik bütün analiz ihtiyaçlarını karşılamıyordu, bu yüzden bağlamı üç seviyede modelledik.

Oturum kimliği, uygulamanın o kullanım süresini temsil ediyor. Destek ekibi bununla aynı kullanıcının farklı zamanlardaki denemelerini ayırabiliyor.

Yolculuk kimliği, randevu oluşturma gibi belirli bir iş akışının başı ile sonu arasındaki olayları grupluyor. Kullanıcı uygulamada uzun süre kalmış olsa bile analiz yalnızca ilgili akışa daraltılabiliyor.

Trace kimliği, tek bir ağ isteğini sonucuyla ve ardından oluşan hatayla ilişkilendiriyor. Her istek için ayrı kimlik üretildiğinden arka arkaya gönderilen çağrılar birbirine karışmıyor.

Bunlara her hata için üretilen benzersiz hata kimliği de ekleniyor. Destek ve geliştirme ekipleri aynı olaydan konuşurken bu kimliği ortak referans olarak kullanıyor.

Ağ görünürlüğü

Her servise elle log eklemek yerine ağ katmanını merkezi bir Dio interceptor üzerinden izliyoruz. Böylece modül sayısı arttıkça izlenmeyen yerler oluşmuyor.

Her istek başladığında zaman ve trace bilgisi oluşturuluyor. Yanıt ya da hata geldiğinde HTTP metodu, göreli adres, durum kodu ve geçen süre zaman çizelgesine ekleniyor. Başarısız isteklerde hata tipiyle birlikte temizlenmiş bir istek ve yanıt özeti de kaydedilebiliyor. Dosya gibi uygun olmayan yanıt tiplerinde gövde kaydedilmiyor.

Gözlemlenebilirlik paketini sunucuya gönderen tanılama isteğini interceptor bilerek dışarıda bırakıyor. Yoksa tanılama isteği başarısız olduğunda yeni bir log üretip sonsuz bir raporlama döngüsü başlatabilirdi.

Ekran takibinden kullanıcı yolculuğuna

Firebase Analytics ekran görüntülemelerini zaten topluyordu ama analitik verisi hata ayıklamak için tasarlanmamıştı. Yeni katman aynı navigasyon kaynağını dinleyip mevcut ve önceki ekranı yerel zaman çizelgesine ekliyor. Ekran bağlamı böylece hata kaydının içinde yer alıyor.

Ekran adları da tek başına yetmiyordu. Randevu akışında kullanıcının hangi tesisi seçtiğini, hangi saate geçtiğini, özet ekranını açıp açmadığını ya da onay düğmesine basıp basmadığını ekran adından anlamak mümkün değil. Bu yüzden kritik akışlar için anlamlı iş olayları tanımladık:

  • Yolculuğun başlaması,
  • Tesis, uzmanlık, sağlık profesyoneli, tarih ve saat seçimi,
  • Özet ekranının açılması,
  • Avantaj veya indirim seçimi,
  • Onay ve yetkilendirme adımları,
  • Ödeme, sonlandırma ve kayıt sonucunun başarılı ya da başarısız olması.

Bu olaylar genel bir analitik metriği değil, belirli bir işlemin kronolojisini oluşturuyor. Yolculuk ekran açılınca başlıyor, akıştan çıkılınca kapanıyor; başka özelliklerdeki olaylar aynı analize karışmıyor.

Hata anlık görüntüsü

Bir hata yakalandığında yalnızca exception metni gönderilmiyor. Anlık görüntü şunları içeriyor:

  • Hata, oturum, yolculuk ve trace kimlikleri,
  • Exception türü, mesajı ve stack trace,
  • Son ekran ile önceki ekran,
  • Son ağ isteği ve yanıt durumu,
  • Hata öncesindeki güncel zaman çizelgesi,
  • Kullanıcı referansı,
  • Uygulama sürümü, işletim sistemi, cihaz bilgisi ve dil,
  • O an bilinen bağlantı türleri.

Flutter framework hataları ve yakalanmamış platform hataları uygulama başlarken ortak bir hata facade’ine bağlanıyor. Ağ ve model dönüşüm hataları da aynı yoldan geçtiği için özellik ekiplerinin her hata için ayrı raporlama kodu yazması gerekmiyor.

Aynı exception nesnesinin kısa süre içinde farklı katmanlardan yeniden raporlanıp raporlanmadığı da kontrol ediliyor. Yakın zaman penceresindeki tekrarlar eleniyor, böylece tek bir olay birden fazla tanılama kaydı üretmiyor.

Sağlık uygulamasında veri güvenliği

Daha fazla bağlam toplamak, kişisel ya da sağlık verisini kontrolsüz biçimde loglamak anlamına gelmemeli. Bu yüzden olay verileri ve başarısız ağ gövdeleri merkezi bir temizleme katmanından geçiyor.

Token, kimlik numarası, telefon, e-posta, ad-soyad ve ödeme kartı gibi hassas alanlar maskeleniyor. İç içe nesneler ve JSON biçimindeki metinler de aynı kuralla temizleniyor. Hata gövdelerine uzunluk sınırı konarak büyük yanıtların log sistemini doldurması engelleniyor.

Bu kuralın merkezi olması önemli. Her geliştiricinin her olayda hassas alanları hatırlamasına güvenmek yerine güvenli varsayılanı altyapı uyguluyor.

Örnek bir hata analizi

Eskiden destek kaydında yalnızca “randevu tamamlanamadı” notu ve bir exception görülebiliyordu. Yeni anlık görüntü şuna benzer bir kronoloji sunabiliyor:

Randevu yolculuğu başladı
→ tesis ve uzmanlık seçildi
→ saat seçildi
→ özet ekranı açıldı
→ onay düğmesine basıldı
→ yetkilendirme isteği başarılı oldu
→ ödeme isteği hata verdi
→ yolculuk başarısız olarak işaretlendi

Bu görünüm, sorunun kullanıcı navigasyonundan mı, istemci durumundan mı, ağ koşullarından mı yoksa belirli bir servis adımından mı kaynaklandığını ilk incelemede daraltıyor. Sentry’deki hatayı, Firebase Analytics’teki ekranı ve yerel ağ kaydını tahminle birleştirmeye gerek kalmıyor; hata anı kendi teknik bağlamıyla birlikte geliyor.

Mimari kararlar

Özellik modülleri Sentry’yi, Firebase’i ya da backend tanılama servisini doğrudan bilmiyor. Gezinme, iş olayı, ağ ve hata kayıtları ortak ObservabilityManager sözleşmesinden geçiyor.

Ağ istekleri ve ekran geçişleri merkezi olarak otomatik yakalanıyor. İş olaylarını ise yalnızca ürün açısından kritik adımlara bilerek ekliyoruz. Her butona log koymak yerine analizi değiştiren karar noktalarını izliyoruz.

Normal bir oturumun bütün ayrıntıları sürekli gönderilmiyor. Güncel bağlam yerelde duruyor ve yalnızca anlamlı bir hata oluştuğunda paketleniyor.

Log yükleme isteği ağ gözlemcisinin dışında tutuluyor, böylece raporlama altyapısı kendi kendini tetiklemiyor.

Gözlemlenebilirlik yöneticisi ağ katmanından önce hazırlanıyor; ağ interceptor’ü ve navigasyon gözlemcisi sonra aynı örneğe bağlanıyor. Uygulama açılışının erken aşamalarından itibaren tek ve tutarlı bir oturum korunuyor.

Sonuç

Mevcut izleme araçlarını kaldırmadık; her biri iyi olduğu alanda kullanılmaya devam ediyor. Değişen şey, hata analizinin artık araçlar arasında elle korelasyon kurmaya bağlı olmaması. Üretimdeki bir sorunu stack trace’in yanında kullanıcının son adımları, aktif iş akışı, ekran geçişleri, ağ çağrıları ve çalışma ortamıyla birlikte inceleyebiliyoruz.

Ekip artık “hata nerede oluştu?” sorusunun yanında “kullanıcı bu hataya nasıl geldi?” sorusunu da cevaplayabiliyor. Destek kaydındaki sorun yeniden üretilemese bile ilk analiz için gereken teknik bağlam elimizde oluyor.

Öğrendiklerim

  • Çok sayıda log tutmak gözlemlenebilirlik sağlamıyor. Birbirine bağlanamayan binlerce kayıt, az ama bağlamı olan bir zaman çizelgesinden daha az işe yarıyor.
  • Analitik ile hata ayıklamanın amacı farklı. Aynı ekran olayını kullanabilirler ama hata analizi istek sonucuna, süreye, duruma ve exception bağlamına da ihtiyaç duyuyor.
  • En değerli log, hatadan hemen önceki olay. Sınırlı bir ring buffer bu bağlamı düşük maliyetle koruyor.
  • Korelasyon kimlikleri ekipler arasında ortak bir dil oluşturuyor. Oturum, yolculuk, istek ve hata seviyelerini ayrı modellemek analizi doğru yere daraltıyor.
  • Sağlık uygulamasında maskeleme ve veri sınırları baştan tasarlanmalı; sonradan eklenen bir filtre olarak düşünülmemeli.
  • Kritik akışları iş adımlarıyla izlemek gerekiyor. “POST başarısız” bilgisi ancak “ödeme başlatıldı” ya da “randevu kaydı tamamlanamadı” bağlamıyla bir aksiyona dönüşüyor.

Aramak için yazın