API İzleme ve Performans Analizi: Metrik Toplama Rehberi
Production ortamında bir API’nin aniden yavaşladığını fark ettiğinizde, elinizin altında doğru metrikler yoksa körce uçuyorsunuz demektir. Hangi endpoint yavaş, hangi istemci fazla istek atıyor, p99 gecikme süresi nerede patlıyor? Bu soruların cevabını verebilmek için sistematik bir metrik toplama altyapısı şart. Bu yazıda API izleme ve performans analizini, gerçek dünya senaryolarıyla ele alacağız.
API Metriklerini Anlamak
API izleme denince akla ilk gelen şey genellikle “uptime” kontrolüdür, ama bu buzdağının görünen kısmı. Gerçek anlamda sağlıklı bir API izleme sistemi çok daha fazlasını kapsar.
Temel metrik kategorileri şunlardır:
- Latency (Gecikme): İsteğin alınmasından yanıtın gönderilmesine kadar geçen süre
- Throughput (İş Hacmi): Birim zamanda işlenen istek sayısı (RPS – Requests Per Second)
- Error Rate (Hata Oranı): Toplam istekler içindeki hatalı yanıt yüzdesi
- Saturation (Doygunluk): Sistemin kapasitesinin ne kadarının kullanıldığı
- Availability (Erişilebilirlik): Hizmetin ayakta olduğu sürenin yüzdesi
Google’ın SRE kitabında bu ilk dördü “Four Golden Signals” olarak geçer ve iyi bir başlangıç noktasıdır.
Percentile Metriklerin Önemi
Ortalama gecikme sizi yanıltır. 100 isteğin 99’u 10ms’de tamamlanıyor ama biri 5 saniye sürüyorsa, ortalama 60ms görünür ve sorun gizlenir. Bu yüzden p50, p95, p99 ve p99.9 değerlerine bakmak zorundasınız.
Gerçek dünya örneği: Bir e-ticaret API’sinde ortalama yanıt süresi 150ms’de seyrederken, p99 değeri 8 saniyeye çıkmıştı. Ortalamaya bakan ekip “her şey normal” diyordu, ama kullanıcıların yüzde biri her işlemde 8 saniye bekliyordu. Black Friday’de bu oran kaosa dönüştü.
Nginx Access Log’larından Metrik Toplama
Çoğu API NGINX veya Apache’nin arkasında çalışır. Log formatını zenginleştirerek değerli veriler toplayabilirsiniz.
# /etc/nginx/nginx.conf - Zenginleştirilmiş log formatı
log_format api_metrics '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time '
'uct=$upstream_connect_time '
'uht=$upstream_header_time '
'urt=$upstream_response_time '
'cs=$upstream_cache_status '
'api_key=$http_x_api_key';
access_log /var/log/nginx/api_access.log api_metrics;
Bu format ile her istek için upstream bağlantı süresi, header süresi ve toplam yanıt süresi ayrı ayrı loglanır. Şimdi bu logları ayrıştıralım:
#!/bin/bash
# api_metrics_parser.sh - Nginx loglarından anlık metrik çıkarma
LOG_FILE="/var/log/nginx/api_access.log"
INTERVAL=60 # Son 60 saniyeyi analiz et
# Son 1 dakikanın loglarını filtrele ve metrikleri hesapla
awk -v interval=$INTERVAL '
BEGIN {
total = 0
errors = 0
total_rt = 0
max_rt = 0
}
{
# request_time değerini al
for(i=1; i<=NF; i++) {
if($i ~ /^rt=/) {
rt = substr($i, 4)
total_rt += rt
if(rt > max_rt) max_rt = rt
}
if($i ~ /^[0-9]{3}$/ && $i+0 >= 400) errors++
}
total++
}
END {
if(total > 0) {
printf "Total Requests: %dn", total
printf "Error Count: %dn", errors
printf "Error Rate: %.2f%%n", (errors/total)*100
printf "Avg Response Time: %.3fsn", total_rt/total
printf "Max Response Time: %.3fsn", max_rt
}
}
' "$LOG_FILE"
Prometheus ile Sistematik Metrik Toplama
NGINX log parsing iyi bir başlangıç ama production için Prometheus + Grafana kombinasyonu çok daha güçlü. nginx-prometheus-exporter veya doğrudan uygulama tarafında metrik expose etme yoluna gidebilirsiniz.
Uygulama tarafında Python Flask örneği:
# app.py - Flask API ile Prometheus metrik entegrasyonu
from flask import Flask, request, jsonify
from prometheus_client import Counter, Histogram, Gauge, generate_latest
import time
import functools
app = Flask(__name__)
# Metrik tanımları
REQUEST_COUNT = Counter(
'api_requests_total',
'Toplam API istek sayısı',
['method', 'endpoint', 'status_code']
)
REQUEST_LATENCY = Histogram(
'api_request_duration_seconds',
'API istek süresi',
['method', 'endpoint'],
buckets=[0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0]
)
ACTIVE_REQUESTS = Gauge(
'api_active_requests',
'Anlık aktif istek sayısı'
)
def track_metrics(f):
@functools.wraps(f)
def decorated(*args, **kwargs):
ACTIVE_REQUESTS.inc()
start_time = time.time()
status_code = 500
try:
response = f(*args, **kwargs)
status_code = response[1] if isinstance(response, tuple) else 200
return response
finally:
duration = time.time() - start_time
ACTIVE_REQUESTS.dec()
REQUEST_COUNT.labels(
method=request.method,
endpoint=request.endpoint,
status_code=status_code
).inc()
REQUEST_LATENCY.labels(
method=request.method,
endpoint=request.endpoint
).observe(duration)
return decorated
@app.route('/metrics')
def metrics():
return generate_latest(), 200, {'Content-Type': 'text/plain'}
@app.route('/api/v1/users', methods=['GET'])
@track_metrics
def get_users():
# Simüle edilmiş iş mantığı
return jsonify({'users': []}), 200
Prometheus scrape konfigürasyonu:
# /etc/prometheus/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'api-service'
static_configs:
- targets: ['localhost:5000']
metrics_path: '/metrics'
scrape_interval: 10s
- job_name: 'nginx-exporter'
static_configs:
- targets: ['localhost:9113']
Gerçek Zamanlı Anomali Tespiti
Metrikleri toplamak yeterli değil. Anormal durumları otomatik tespit etmeniz gerekiyor. İşte basit ama etkili bir alerting scripti:
#!/bin/bash
# api_alert.sh - Threshold tabanlı uyarı sistemi
PROMETHEUS_URL="http://localhost:9090"
SLACK_WEBHOOK="https://hooks.slack.com/services/XXX/YYY/ZZZ"
# Fonksiyon: Prometheus'a sorgu at
query_prometheus() {
local query=$1
curl -s "${PROMETHEUS_URL}/api/v1/query"
--data-urlencode "query=${query}" |
python3 -c "import sys,json; data=json.load(sys.stdin);
print(data['data']['result'][0]['value'][1] if data['data']['result'] else '0')"
}
# Slack bildirimi gönder
send_alert() {
local message=$1
local severity=$2
curl -s -X POST "$SLACK_WEBHOOK"
-H 'Content-type: application/json'
--data "{
"text": ":${severity}: API Alert: ${message}"
}"
}
# Error rate kontrolü
ERROR_RATE=$(query_prometheus 'rate(api_requests_total{status_code=~"5.."}[5m]) / rate(api_requests_total[5m]) * 100')
if (( $(echo "$ERROR_RATE > 5" | bc -l) )); then
send_alert "Error rate ${ERROR_RATE}% - Threshold: 5%" "red_circle"
fi
# P99 latency kontrolü
P99_LATENCY=$(query_prometheus 'histogram_quantile(0.99, rate(api_request_duration_seconds_bucket[5m]))')
if (( $(echo "$P99_LATENCY > 2" | bc -l) )); then
send_alert "P99 latency ${P99_LATENCY}s - Threshold: 2s" "warning"
fi
# RPS kontrolü (DDoS veya traffic spike)
RPS=$(query_prometheus 'sum(rate(api_requests_total[1m]))')
if (( $(echo "$RPS > 1000" | bc -l) )); then
send_alert "Unusual traffic spike: ${RPS} RPS" "rotating_light"
fi
echo "Alert check completed at $(date)"
Bu scripti cron’a ekleyin:
# crontab -e
*/2 * * * * /opt/scripts/api_alert.sh >> /var/log/api_alerts.log 2>&1
API Gateway Seviyesinde Metrik Toplama
Eğer Kong, AWS API Gateway veya benzeri bir çözüm kullanıyorsanız, metrikler daha merkezi toplanabilir. Kong örneği:
# Kong Prometheus plugin aktivasyonu
curl -X POST http://localhost:8001/plugins
--data "name=prometheus"
--data "config.status_code_metrics=true"
--data "config.latency_metrics=true"
--data "config.bandwidth_metrics=true"
--data "config.upstream_health_metrics=true"
# Belirli bir servis için rate limiting + metrik
curl -X POST http://localhost:8001/services/user-service/plugins
--data "name=rate-limiting"
--data "config.minute=100"
--data "config.hour=1000"
--data "config.policy=redis"
--data "config.redis_host=localhost"
Distributed Tracing ile Derinlemesine Analiz
Microservice mimarisinde bir istek onlarca servisten geçebilir. Hangi servis gecikmeye neden oluyor? OpenTelemetry ile bunu izleyebilirsiniz:
# tracing_setup.py - OpenTelemetry entegrasyonu
from opentelemetry import trace
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
def setup_tracing(app, service_name):
# Jaeger exporter konfigürasyonu
jaeger_exporter = JaegerExporter(
agent_host_name="jaeger-agent",
agent_port=6831,
)
provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(jaeger_exporter))
trace.set_tracer_provider(provider)
# Flask ve requests kütüphanesini otomatik instrument et
FlaskInstrumentor().instrument_app(app)
RequestsInstrumentor().instrument()
return trace.get_tracer(service_name)
# Özel span ekleme
tracer = setup_tracing(app, "user-service")
@app.route('/api/v1/users/<int:user_id>')
def get_user(user_id):
with tracer.start_as_current_span("get_user_from_db") as span:
span.set_attribute("user.id", user_id)
span.set_attribute("db.system", "postgresql")
# DB sorgusu
user = db.query(f"SELECT * FROM users WHERE id = {user_id}")
if not user:
span.set_attribute("user.found", False)
return jsonify({'error': 'User not found'}), 404
span.set_attribute("user.found", True)
return jsonify(user), 200
Grafana Dashboard ile Görselleştirme
Metrikleri topladıktan sonra görselleştirme kritik. İşte temel paneller için Prometheus sorguları:
# Önemli Prometheus sorguları - Grafana panellerine eklenecek
# 1. RPS by endpoint
sum(rate(api_requests_total[5m])) by (endpoint)
# 2. Error rate by endpoint (%)
sum(rate(api_requests_total{status_code=~"5.."}[5m])) by (endpoint) /
sum(rate(api_requests_total[5m])) by (endpoint) * 100
# 3. P50, P95, P99 latency
histogram_quantile(0.50, sum(rate(api_request_duration_seconds_bucket[5m])) by (le, endpoint))
histogram_quantile(0.95, sum(rate(api_request_duration_seconds_bucket[5m])) by (le, endpoint))
histogram_quantile(0.99, sum(rate(api_request_duration_seconds_bucket[5m])) by (le, endpoint))
# 4. Aktif bağlantı sayısı
sum(api_active_requests) by (instance)
# 5. Top 10 yavaş endpoint
topk(10, histogram_quantile(0.99,
sum(rate(api_request_duration_seconds_bucket[10m])) by (le, endpoint)
))
# 6. API key bazında istek dağılımı
sum(rate(api_requests_total[5m])) by (api_key)
# 7. HTTP status code dağılımı
sum(rate(api_requests_total[5m])) by (status_code)
Grafana’yı Prometheus datasource ile bağlamak için:
# Grafana datasource konfigürasyonu (API ile)
curl -X POST http://admin:admin@localhost:3000/api/datasources
-H "Content-Type: application/json"
-d '{
"name": "Prometheus",
"type": "prometheus",
"url": "http://localhost:9090",
"access": "proxy",
"isDefault": true,
"jsonData": {
"timeInterval": "10s",
"httpMethod": "POST"
}
}'
Performans Bottleneck Analizi
Metrikler toplandıktan sonra gerçek iş başlıyor: sorunun kaynağını bulmak. Tipik bottleneck senaryoları ve nasıl tespit edileceği:
Veritabanı gecikmeleri: P99 yüksek ama CPU düşükse genellikle DB sorunudur. Slow query log’u aktifleştirin:
# PostgreSQL slow query log
# /etc/postgresql/14/main/postgresql.conf
log_min_duration_statement = 500 # 500ms'den uzun sorguları logla
log_statement = 'none'
log_duration = off
# Canlı yavaş sorguları görmek için
psql -U postgres -c "
SELECT pid, now() - pg_stat_activity.query_start AS duration, query, state
FROM pg_stat_activity
WHERE (now() - pg_stat_activity.query_start) > interval '5 seconds'
AND state != 'idle'
ORDER BY duration DESC;"
Memory leak: Zaman içinde artan response time, genellikle memory leak işarettir. Şüphelendiğinizde:
# Uygulama process memory takibi
watch -n 5 'ps aux --sort=-%mem | head -20'
# Prometheus ile memory trend sorgusu
# process_resident_memory_bytes - zamanla artıyor mu?
rate(process_resident_memory_bytes[1h])
Connection pool tükenmesi: Ani latency artışları ve “connection refused” hataları:
# PostgreSQL connection sayısı
psql -U postgres -c "
SELECT count(*), state, wait_event_type, wait_event
FROM pg_stat_activity
GROUP BY state, wait_event_type, wait_event
ORDER BY count DESC;"
# Max connection limitini kontrol et
psql -U postgres -c "SHOW max_connections;"
SLO ve SLA Takibi
Metrik toplama altyapısı kurulduktan sonra SLO (Service Level Objective) takibini otomatikleştirebilirsiniz. Örneğin “API’nin %99.9’u 500ms altında yanıt vermeli” SLO’su için:
# SLO compliance hesaplama scripti
#!/bin/bash
# slo_report.sh
PROMETHEUS_URL="http://localhost:9090"
SLO_LATENCY_THRESHOLD=0.5 # 500ms
SLO_TARGET=99.9 # %99.9
# Son 30 günlük SLO compliance
COMPLIANCE=$(curl -s "${PROMETHEUS_URL}/api/v1/query_range"
--data-urlencode 'query=sum(rate(api_request_duration_seconds_bucket{le="0.5"}[5m])) / sum(rate(api_request_duration_seconds_count[5m])) * 100'
--data-urlencode "start=$(date -d '30 days ago' +%s)"
--data-urlencode "end=$(date +%s)"
--data-urlencode "step=3600" |
python3 -c "
import sys, json
data = json.load(sys.stdin)
values = [float(v[1]) for v in data['data']['result'][0]['values'] if v[1] != 'NaN']
avg = sum(values) / len(values) if values else 0
print(f'{avg:.3f}')
")
echo "30-Day SLO Compliance: ${COMPLIANCE}%"
echo "SLO Target: ${SLO_TARGET}%"
if (( $(echo "$COMPLIANCE < $SLO_TARGET" | bc -l) )); then
echo "UYARI: SLO hedefi tutturulamiyor!"
# Error budget hesapla
REMAINING_BUDGET=$(echo "scale=4; ($COMPLIANCE - (100 - $SLO_TARGET)) / (100 - $SLO_TARGET) * 100" | bc)
echo "Error Budget Kullanimi: ${REMAINING_BUDGET}%"
fi
Kapasite Planlama için Trend Analizi
Mevcut büyüme trendine bakarak kaç ay sonra kapasiteyi artırmanız gerektiğini tahmin edebilirsiniz:
# Haftalık RPS trend raporu
curl -s "http://localhost:9090/api/v1/query_range"
--data-urlencode 'query=sum(rate(api_requests_total[1h]))'
--data-urlencode "start=$(date -d '90 days ago' +%s)"
--data-urlencode "end=$(date +%s)"
--data-urlencode "step=86400" |
python3 << 'EOF'
import sys, json
import numpy as np
data = json.load(sys.stdin)
values = [(float(v[0]), float(v[1])) for v in data['data']['result'][0]['values'] if v[1] != 'NaN']
timestamps = np.array([v[0] for v in values])
rps_values = np.array([v[1] for v in values])
# Linear regression ile trend hesapla
coeffs = np.polyfit(timestamps, rps_values, 1)
daily_growth = coeffs[0] * 86400 # Günlük artış
print(f"Mevcut RPS: {rps_values[-1]:.2f}")
print(f"Gunluk ortalama artis: {daily_growth:.3f} RPS")
print(f"30 gun sonra beklenen RPS: {rps_values[-1] + (daily_growth * 30):.2f}")
print(f"90 gun sonra beklenen RPS: {rps_values[-1] + (daily_growth * 90):.2f}")
EOF
Pratik Öneriler ve Dikkat Edilecekler
Metrik toplama altyapısı kurarken sıkça yapılan hatalar:
- Cardinality patlaması: Her benzersiz değer için ayrı metrik serisi oluşturursanız Prometheus belleği patlar. API key’i veya user ID’yi label olarak kullanmaktan kaçının.
- Scrape interval seçimi: Çok sık scrape (5 saniyenin altı) gereksiz yük yaratır, çok seyrek (30 saniyenin üstü) anlık sorunları kaçırır. 10-15 saniye makul bir orta yol.
- Log rotation ihmal etmek: Detaylı access log her gün GB’lerce veri üretebilir. Logrotate konfigürasyonu zorunludur.
- Alert yorgunluğu: Çok fazla alert tanımlamak, ekibin alertleri görmezden gelmesine yol açar. Başlangıçta 5-6 kritik alerte odaklanın.
- Metrik güvenliği:
/metricsendpoint’ini herkese açık bırakmayın. IP kısıtlaması veya basic auth ekleyin.
Kapasite planlaması açısından şunu unutmayın: Metriklerin size anlattığı hikayeyi anlamak, toplamaktan daha önemli. Haftalık düzenli bakış ve aylık trend review toplantıları, sorunları büyümeden yakalamak için kritik.
Sonuç
API izleme ve performans analizi, bir kez kurulup unutulan bir sistem değil. Altyapınız büyüdükçe, metrik stratejiniz de evrilmeli. Bu yazıda anlattıklarımı özetleyecek olursak:
Önce NGINX loglarını zenginleştirerek düşük maliyetle başlayın. Ardından Prometheus + Grafana stack’ini kurarak sistematik metrik toplama altyapısı oluşturun. Latency için mutlaka percentile değerlerine bakın, ortalamaya güvenmeyin. Alert sistemini kurarken az ama etkili alarm tanımlayın. Distributed tracing’i microservice geçişiyle birlikte planlayın ve SLO takibini otomatikleştirerek kapasite planlamasını veriye dayandırın.
En kötü durum, bir prodüksiyon olayı sırasında “ne olduğunu bilmiyoruz” demek zorunda kalmaktır. Doğru metrik altyapısı sizi bu durumdan korur ve daha da önemlisi, sorunlar büyümeden önce sinyal verir. Sisteminizi kör uçmak yerine full telemetri ile yönetin.
