Jetimza MVP Gözlemlenebilirlik & Denetim Tasarımı
jet-api için log, metrik, iz, denetim ve alarm mimarisi. imza.io SignGate ve Infrastructure'da hâlihazırda çalışan desen referans alınmıştır.
Bir kullanıcı "imzam olmadı" dediğinde, destek ekibi tek bir işlem kimliğinden yola çıkarak o isteğin log'una, dağıtık izine ve altyapı yanıtına dakikalar içinde ulaşabilmelidir.
Üç Düzlem İlkesi
Gözlemlenebilirlik tasarımlarının en sık yapılan hatası, birbirinden çok farklı üç ihtiyacı tek mekanizmayla çözmeye çalışmaktır. Sonuç genellikle şudur: her veritabanı değişikliğini yazan bir denetim kancası — yazma yolunu yavaşlatır, okunamayacak kadar gürültülü bir tablo üretir ve asıl sorulan sorulara yine cevap veremez. Jetimza'da üç düzlem ayrı tutulur.
Telemetri
- Ne
- Yapısal log, metrik, dağıtık iz, profil
- Nerede
- Loki · Prometheus · Tempo · Pyroscope
- Hacim
- Yüksek — örneklenebilir
- Saklama
- Günler / haftalar
- Doğruluk
- Kaynak değildir. Kaybı tolere edilir
- Soru
- "Sistem sağlıklı mı, nerede yavaşladı, ne hata verdi?"
İş & Denetim
- Ne
- Token hareketleri, işlem geçişleri, yönetsel eylemler
- Nerede
- PostgreSQL — append-only tablolar
- Hacim
- Düşük — işlem başına birkaç satır
- Saklama
- Yıllar
- Doğruluk
- Doğruluk kaynağı. Kaybı kabul edilemez
- Soru
- "Bu kullanıcının bakiyesi neden bu, bu işlem kimin ve ne oldu?"
Toplama
- Ne
- Günlük özetler, huni oranları
- Nerede
- PostgreSQL —
daily_*_stats - Hacim
- Çok düşük — gün başına birkaç satır
- Saklama
- Kalıcı
- Doğruluk
- Dondurulmuş gerçek — geçmişe dönük değişmez
- Soru
- "Ürün çalışıyor mu, dönüşüm nasıl gidiyor?"
correlation_id ve trace_id. Bu iki
değer iş satırlarında kolon olarak tutulur (bkz. veritabanı dokümanı).
Destek ekranındaki bir işlemden log'a ve ize geçiş bu köprüyle olur. Köprü olmazsa
telemetri ve iş verisi iki ayrı ada olarak kalır ve hiçbiri tek başına soruyu cevaplayamaz.
Ne YAPMAYACAĞIZ
- Kayıt katmanının kaydetme adımına takılan, her satır değişikliğini bir denetim tablosuna yazan genel kanca
- Uygulama log'larını veritabanına yazmak
- Metrik etiketlerine kullanıcı kimliği, telefon, kimlik numarası veya işlem kimliği koymak
- Panoların ham işlem tablolarını canlı taraması
- Her isteği tek tek loglayıp "gözlemlenebilirlik var" demek
Referans: Mevcut Altyapıda Ne Var
Aşağıdakiler imza.io'da bugün çalışan bileşenlerdir; jet-api sıfırdan bir şey icat etmeyecek, aynı deseni ve aynı toplama altyapısını kullanacaktır.
| Katman | Bileşen | Jetimza'da karşılığı |
|---|---|---|
| Log | Serilog + ortam bazlı yapılandırma | Aynı desen: serilog.{Environment}.json |
| Log bağlamı | RequestContextEnricher | RequestPath, RequestMethod, ClientIP, UserAgent, CorrelationId — aynı alan seti + UserId |
| İlişkilendirme | CorrelationIdMiddleware | X-Correlation-ID başlığı; yoksa etkin izin kimliği; o da yoksa yeni üretilir. Yanıt başlığına geri yazılır |
| Metrik | OpenTelemetry + alan bazlı Meter'lar | SignGate 8 ayrı Meter kullanıyor; jet-api 6 Meter kullanacak |
| Adlandırma | imzaio_signgate_signature_duration_seconds | imzaio_jet_<alan>_<ölçüm>_<birim> — aynı kural |
| Histogram | HistogramBuckets (alan bazlı) | SignGate Easy için 0,5–300 sn kova seti kullanıyor çünkü insan PIN giriyor. Jetimza imza kovaları aynı gerekçeyle geniş |
| İz | OTel tracing + OTLP exporter filtresi | AspNetCore, HttpClient enstrümantasyonu; dışa aktarım isteklerinin ize düşmemesi için filtre |
| Sağlık | /health · /alive · /ready | Aynı üçlü |
| Profil | Pyroscope | Sürekli profilleme — CPU darboğazlarında tek gerçek araç |
| Toplama | Alloy → Loki / Prometheus / Tempo | Aynı toplayıcı; jet-api yalnızca OTLP uç noktasını gösterir |
| Alarm | Prometheus kuralları + Alertmanager | Her alarmda severity, component, team ve runbook_url |
| Pano | Grafana (signgate, easy, clm, log-analytics, tracing) | Yeni pano seti: funnel, signature-health, token-economy, sms |
SignGate'in denetim izi SignGate.Data altında amaca özel bir
olay şemasıdır: cihaz → çalışan örnek → bağlantı → istek → olay, ayrıca kimlik ve
sertifika kayıtları. Üzerine raporlama için önceden toplanmış günlük istatistik tablosu.
Yani denetim, kayıt katmanına takılan genel bir kanca değil, alanın kendi olay
modelidir. Jetimza da aynısını yapar: token_ledger,
transaction_events ve audit_events.
Log Tasarımı
Her log satırında bulunması zorunlu alanlar
| Alan | Kaynağı ve niçin |
|---|---|
| timestamp | UTC. Yerel saat asla yazılmaz. |
| level | Aşağıdaki seviye disiplinine göre. |
| service | imzaio-jet-api. Tek serviste bile zorunlu: Loki'de servis bazlı ayrım yapılabilsin. |
| environment | Ortam ayrımı. Aynı Loki birden çok ortamı toplar. |
| correlation_id | Ara katmandan. İş kayıtlarıyla köprü. |
| trace_id · span_id | OTel bağlamından. Log satırından ize tek tıkla geçiş. |
| user_id | Kimliği doğrulanmış isteklerde. Telefon veya kimlik numarası değil. |
| request_path · request_method | Zenginleştiriciden. |
| client_ip · user_agent | Zenginleştiriciden. |
Seviye disiplini
| Seviye | Ne yazılır |
|---|---|
| Error | İşlem tamamlanamadı ve müdahale gerekebilir: altyapı erişilemedi, beklenmeyen istisna, token muhasebesi tutarsızlığı. |
| Warning | İşlem tamamlanamadı ama beklenen bir sonuç: yanlış OTP, Easy kurulu değil, kart takılı değil, bakiye yetersiz. Bunlar hata değil kullanıcı durumudur; Error yazmak alarm gürültüsü üretir. |
| Information | İş olayları: OTP istendi/doğrulandı, kayıt tamamlandı, işlem başlatıldı/sonuçlandı, token askıya alındı/kesinleşti, gözcü işlemi kapattı. |
| Debug | Yalnızca geliştirme. Üretimde kapalı. |
SignGate'te varsayılan log seviyesi Warning bırakıldığı için
imza işlemlerinin log'ları tamamen kesilmişti; bir sorun yaşandığında elde
hiçbir iz kalmadı. Ders: iş olayları Information seviyesindedir
ve varsayılan seviye Information olmalıdır. Gürültü,
Microsoft.* ve System.* ad alanları
Warning'e çekilerek kısılır — kendi kodumuz kısılarak değil.
Log'a ASLA yazılmayacaklar
- OTP kodu — ne düz ne özet hâliyle; hata ayıklama gerekçesiyle bile
- Kimlik numarası — veritabanında şifreli saklanıyor olması log'a yazılabileceği anlamına gelmez; gövde loglaması kapalı olmalı, gösterim daima maskeli
- Telefon numarası — maskeli yazılır:
+90 532 *** ** 67 - Oturum ve yenileme jetonları, yetkilendirme başlığı
- Belge içeriği, imza baytları, PIN
- Tam istek/yanıt gövdeleri — yalnızca hata durumunda, alan bazlı seçilerek ve maskelenerek
Uygulama içinde, log yazılmadan önce — toplayıcıda değil. Toplayıcıda maskelemek, verinin bir kez de olsa maskesiz üretildiği anlamına gelir; yerel dosya log'u veya konsol çıktısı açıksa veri oradan sızar. Maskeleme kaynağında yapılır.
Metrik Tasarımı
SignGate'in ayrımını izleyerek metrikler alan bazlı ölçüm gruplarına bölünür. Bu, hem dışa aktarımın seçmeli yapılabilmesini hem de panoların temiz kalmasını sağlar.
Ölçüm listesi
| Metrik | Tip | Etiketler ve amaç |
|---|---|---|
| imzaio_jet_otp_requested_total | counter | purpose — SMS talebi hacmi |
| imzaio_jet_otp_verified_total | counter | purpose, result (success·invalid_code·expired·max_attempts) — OTP başarı oranı |
| imzaio_jet_sms_delivery_total | counter | provider, status — teslim oranı |
| imzaio_jet_sms_delivery_seconds | histogram | provider — kullanıcının kodu bekleme süresi |
| imzaio_jet_sms_cost_micros_total | counter | provider — tek gerçek değişken maliyet |
| imzaio_jet_registration_total | counter | result — üyelik dönüşümü |
| imzaio_jet_token_grant_total | counter | kind — dağıtılan token |
| imzaio_jet_token_movement_total | counter | reason (hold·commit·release·expiry) — muhasebe akışı |
| imzaio_jet_token_stuck_holds | gauge | — askıda kalmış token sayısı; muhasebe sağlığının tek göstergesi |
| imzaio_jet_transaction_total | counter | type, channel |
| imzaio_jet_transaction_succeeded_total | counter | type, channel |
| imzaio_jet_transaction_failed_total | counter | type, channel, failure_code |
| imzaio_jet_transaction_duration_seconds | histogram | type, channel — uçtan uca süre |
| imzaio_jet_transaction_queue_seconds | histogram | type — kuyrukta bekleme; işçi yetersizliğinin ilk işareti |
| imzaio_jet_transaction_inflight | gauge | channel — anlık açık işlem |
| imzaio_jet_transaction_expired_total | counter | channel — gözcünün kapattıkları |
| imzaio_jet_preflight_total | counter | channel, reason — kullanıcı hangi engele takılıyor |
| imzaio_jet_signgate_duration_seconds | histogram | operation — dış bağımlılığın payı |
| imzaio_jet_signgate_failed_total | counter | operation, error |
Histogram kovaları — alan bazlı
Tek bir kova seti tüm ölçümlere uymaz. SignGate bunu doğru yapmış: Easy kanalı için 0,5–300 saniye aralığı kullanılıyor çünkü arada insan var, PIN giriyor. Jetimza aynı mantığı uygular.
Kova seçimi keyfi değildir: alarm eşiği hangi değerse, o değerin bir kova sınırı olması gerekir. Aksi hâlde yüzdelik hesabı kovalar arasında doğrusal tahmin yapar ve alarm eşiği etrafında yanlış sonuç verir.
Metrik etiketlerine kullanıcı kimliği, telefon, kimlik numarası, işlem kimliği veya belge adı konulmaz. Her benzersiz etiket birleşimi ayrı bir zaman serisi üretir; kullanıcı kimliği etiketi 9 bin kullanıcıda 9 bin seri demektir ve bu Prometheus'u birkaç günde tüketir. Bu tür ayrıntı log'a ve ize aittir, metriğe değil. Metrik "kaç tane ve ne kadar sürdü" sorusunu; log ve iz "hangisi ve neden" sorusunu cevaplar.
İz & Profil
Dağıtık iz, imza akışının hangi adımında zaman geçtiğini gösteren tek araçtır: jet-api'nin kendi işi mi, kuyrukta bekleme mi, SignGate çağrısı mı, yoksa kullanıcının PIN girmesi mi.
İz kapsamları
| Kapsam | İçerdiği öznitelikler |
|---|---|
| POST /v1/signatures | Gelen istek. transaction.id, transaction.type, channel, document.size |
| token.hold | Askıya alma. grant.kind, balance_after |
| transaction.queue_wait | Kuyrukta geçen süre — işçi darboğazı burada görünür |
| signgate.sign_file | Dış çağrı. operation, signgate.request_ref, http.status_code |
| token.commit / token.release | Muhasebenin kapanışı |
İmza işlemleri %100 örneklenir. Bunlar düşük hacimli ve yüksek değerli olaylardır; bir imzanın izi kaybolursa o kullanıcının şikâyeti araştırılamaz. Sağlık uçları ve statik istekler ize hiç alınmaz. Dışa aktarım isteklerinin kendisinin ize düşmemesi için filtre uygulanır — SignGate'te bu filtre geri besleme döngüsünü önlemek için zaten var.
Profil
Pyroscope sürekli profillemesi ilk günden açılır. Gerekçe deneyimseldir: imza altyapısında yaşanan CPU baskısı sorunlarında darboğazın nerede olduğunu gösteren tek araç sürekli profillemeydi; olaydan sonra açmak, olayı kaçırmak demektir.
Denetim Kaydı
Denetim, veritabanındaki üç append-only tabloya dayanır. Hiçbiri kayıt katmanına takılan genel bir kanca değildir; her biri alanın kendi olay modelidir.
| Tablo | Neyi kanıtlar | Yazılma anı |
|---|---|---|
| token_ledger | Bakiyenin her kuruşunun nereden geldiği ve nereye gittiği | Token değişimini yapan iş adımının kendi işlem bloğunda |
| transaction_events | Bir işlemin hayat hikâyesi; kim ilerletti, nerede koptu | Her durum geçişinde |
| audit_events | Hesap sorulabilir yönetsel ve güvenlik eylemleri | Yalnızca anlamlı eylemlerde |
audit_events'e yazılan eylemler
| Eylem | Niçin denetlenir |
|---|---|
| token_request.approved / rejected | MVP'de onay elle veriliyor — kim, ne zaman, hangi gerekçeyle sorusu cevapsız kalamaz |
| token.manual_grant | Elle token tanımlama; ekonominin dışına çıkan tek yol |
| user.suspended / reactivated | Hesap erişimini etkileyen eylem |
| invite.disabled | Kötüye kullanım müdahalesi |
| invite_redemption.fraud_flagged | Ödül iptali; kullanıcıyı doğrudan etkiler |
| consent.revoked | KVKK açısından kanıtlanması gereken irade |
| auth.refresh_token_reuse_detected | Jeton hırsızlığı şüphesi — güvenlik olayı |
| object.deleted_by_retention | Belgenin saklama süresi dolduğu için silindiğinin kanıtı |
Her satır güncellemesini yazan bir denetim mekanizması üç sorun üretir: yazma yolu yavaşlar, tablo okunamayacak kadar büyür, ve en önemlisi gürültü içinde asıl olay kaybolur. Bir operatörün 200 token'ı elle tanımlaması ile bir kullanıcının son giriş zamanının güncellenmesi aynı tabloda aynı ağırlıkta duramaz. Denetim, hesap sorulabilir eylemlerin kaydıdır; veri değişikliklerinin dökümü değil.
Alarm & Servis Düzeyi Hedefleri
Alarmlar, SignGate'in kural dosyalarındaki biçimi izler: her kuralda önem derecesi, bileşen, takım ve çalıştırma kitabı bağlantısı bulunur. Bağlantısı olmayan alarm, gece 3'te uyanan kişiye hiçbir şey söylemez.
| Alarm | Önem | Koşul ve gerekçe |
|---|---|---|
| OtpDeliveryFailureRate | warning | Başarısız gönderim oranı 10 dakika boyunca %5'i aşarsa. Kullanıcı üye olamıyor demektir — huninin en üstü kırılır. |
| OtpDeliveryStalled | critical | 15 dakikadır hiç teslim yok ama talep var. Sağlayıcı tamamen düşmüş. |
| OtpVerificationRateLow | warning | Doğrulanan / istenen oranı 30 dakika boyunca %50'nin altında. Ya SMS gecikiyor ya akışta bir kusur var. |
| SignatureSuccessRateLow | warning | Kanal bazında başarı oranı 15 dakika boyunca %85'in altında. |
| SignatureLatencyHigh | warning | P95 süre kanal eşiğini aşarsa (Easy 180 sn, mobil 120 sn). |
| TokenStuckHolds | critical | Askıda kalmış token sayısı sıfırdan büyük ve artıyorsa. Kullanıcının bakiyesi haksız yere kilitli demektir; para benzeri bir hatadır. |
| TransactionQueueBacklog | warning | Kuyruk bekleme P95'i 30 saniyeyi aşarsa. İşçi yetersiz ya da takılmış. |
| WatchdogExpiryRateHigh | warning | Süre aşımıyla kapanan işlem oranı yükselirse. Altyapı yanıt vermiyor olabilir. |
| SignGateUnavailable | critical | Dış çağrı hata oranı %50'yi aşarsa. İmza tamamen durmuş demektir. |
| SmsCostSpike | warning | Saatlik SMS maliyeti normalin katına çıkarsa. Kötüye kullanımın faturaya yansımadan önce yakalandığı tek yer. |
Bu değer metrik akışından değil, veritabanından periyodik olarak okunur: sonuçlanmış görünmeyen ama askı kaydı olan işlemler sayılır. İş doğruluğunu ilgilendiren ölçümlerde kaynak daima doğruluk kaynağıdır — sayaçların kendisi değil. Sayaç kaybolabilir; veritabanı kaybolmaz.
Panolar
| Pano | Kaynak | İçerik ve kime hitap eder |
|---|---|---|
| jetimza-funnel | PostgreSQL | Ürün panosu. Günlük özet tablolarından: OTP başarı oranı, üyelik dönüşümü, üyelik → ilk imza, davet dönüşümü. Stratejinin dört başarı ölçütü birebir burada. |
| jetimza-signature-health | Prometheus | Mühendislik panosu. Kanal bazında başarı oranı, süre yüzdelikleri, hata kodu dağılımı, kuyruk bekleme, açık işlem sayısı, ön-uçuş engelleri. |
| jetimza-token-economy | PostgreSQL + Prometheus | Dağıtılan/harcanan token, askıdakiler, ek talep kuyruğu, davet ödülleri, kullanıcı başına ortalama tüketim. |
| jetimza-sms | Prometheus | Sağlayıcı bazında teslim oranı, gecikme, saatlik maliyet, hata kodu dağılımı. |
| jetimza-logs | Loki | Hata akışı, ilişkilendirme kimliğiyle arama, seviye dağılımı. |
Dönüşüm oranları kesin olmalıdır ve geçmişe dönük değişmemelidir. Metrik sistemi örnekleme, kesinti ve saklama sınırı olan bir sistemdir; üç ay önceki dönüşüm oranını oradan okumak güvenilir değildir. Günlük özet tabloları bu yüzden var: pano onları okur, sayı değişmez. Mühendislik panoları ise anlıklığı önemsediği için Prometheus'tan beslenir. Aynı ayrımı imza.io altyapısında sertifika yaşam döngüsü panosu da yapıyor — kaynağı doğrudan PostgreSQL.
Saklama, Maliyet & KVKK
| Veri | Önerilen süre | Gerekçe |
|---|---|---|
| Log (Loki) | 30 gün | Olay incelemesi için yeterli; kişisel veri riski taşıdığı için uzun tutulmaz |
| Metrik (Prometheus) | 15 gün ham | Uzun dönem eğilim için kayıt kuralları ile seyreltilmiş seri |
| İz (Tempo) | 7 gün | Hacmi büyük, değeri kısa ömürlü |
| Profil (Pyroscope) | 7 gün | Performans incelemesi penceresi |
| otp_challenges | 90 gün | Kötüye kullanım incelemesi; sonrası gereksiz kişisel veri |
| sms_deliveries | 1 yıl | Maliyet mutabakatı ve sağlayıcı anlaşmazlıkları |
| token_ledger | Kalıcı | Para benzeri kayıt; silinemez |
| transactions · transaction_events | Uzun | Hukuki sonuç doğuran işlem; süre hukuk görüşüyle belirlenir |
| audit_events | Kalıcı | Hesap verebilirlik |
| daily_*_stats | Kalıcı | Kişisel veri içermez, hacmi ihmal edilebilir |
| Belgeler (nesne deposu) | delete_after · ~15 dk yönelimi | Politika kodda değil veride. Kısa saklama seçilirse "İşlemlerim" işlemi gösterir ama belgeyi indirtemez — sınır kullanıcıya imza anında söylenmeli |
| Kimlik numarası | Hesap ömrü | Şifreli saklanır (aydınlatma metni kapsamında). Silme talebinde ilk anonimleştirilen alandır |
Kullanıcı verisinin silinmesi istendiğinde token hareketleri ve imza kayıtları silinmez — hukuki sonuç doğuran ve hesap verebilirlik taşıyan kayıtlardır. Bunun yerine kullanıcı kaydı yumuşak silinir, kişisel alanlar anonimleştirilir ve işlem kayıtları kimliksizleştirilmiş hâlde kalır. Bu yaklaşımın aydınlatma metninde açıkça anlatılması gerekir; şemanın buna hazır olması yetmez.
Uygulama Sırası
Gözlemlenebilirlik sonradan eklenen bir katman değildir; ilk dikey kesitle birlikte gelir. Aşağıdaki sıra, her adımın kendinden önceki adımı gerektirmesine göre kurulmuştur.
| Sıra | Adım | Neden bu sırada |
|---|---|---|
| 1 | İlişkilendirme ara katmanı + log zenginleştirici | Diğer her şeyin bağlanacağı kimlik burada doğar. İlk kod satırıyla birlikte gelmezse sonradan geriye dönük eklemek her yeri dolaşmak demektir |
| 2 | Serilog + seviye disiplini + maskeleme | Maskeleme sonra eklenirse, o zamana kadar üretilen log'lar kişisel veri taşır |
| 3 | correlation_id / trace_id kolonları | Şema göçüyle birlikte; sonradan eklenen kolon geçmiş kayıtlar için boş kalır |
| 4 | OTel: iz + hazır enstrümantasyon + sağlık uçları | Yürüyen iskeletin ilk gününde çalışır durumda olmalı |
| 5 | Alan metrikleri (auth, token, transaction) | İş kodu yazılırken eklenir; ayrı bir "metrik ekleme" turu israftır |
| 6 | Denetim olayları | Yönetsel eylemler ortaya çıktıkça |
| 7 | Günlük özet işi + ürün panosu | Ölçülecek gerçek kullanıcı verisi oluştuktan sonra anlamlı |
| 8 | Alarm kuralları + çalıştırma kitapları | Eşikler ancak gerçek veriyle kalibre edilebilir; erken konan eşik ya sürekli çalar ya hiç çalmaz |
Metrik kaç tane ve ne kadar sürdü sorusunu, log ve iz hangisi ve neden sorusunu, veritabanı ne oldu ve kim sorumlu sorusunu cevaplar. Üçü birbirinin yerine geçmez.