Python'da Datadog APM'i OpenTelemetry ile Değiştirmek
Selamlar, bu yazımda Python tarafında uzun zamandır 'standart' kabul edilen Datadog ddtrace kütüphanesinden OpenTelemetry SDK'larına nasıl geçileceğine bakacağız. Konu basit gibi duruyor ama detayda biraz dirsek çürütmek gerekiyor; özellikle custom span'leri ve DogStatsD metriklerini çoktan unuttuysanız geri dönüp bakacağınız epey kod birikmiş olabilir.
Açıkçası ddtrace'in en sevmediğim yanı, vendor lock-in'i. Datadog faturanız büyüdükçe çıkış kapısı dar gelmeye başlıyor. OpenTelemetry tam burada işe yarıyor: aynı kavramların satıcıdan bağımsız bir karşılığı, OTLP konuşan herhangi bir backend'e (Tempo, Jaeger, Honeycomb, OneUptime, Grafana Cloud, fark etmiyor) gönderebilirsiniz.
Neden ddtrace'ten ayrılalım?
Bence asıl gerekçe teknik değil, ekonomik. Üç maddeyle özetlersem:
- Vendor lock-in yok: Aynı kodla farklı backend'lere yayın yapabiliyorsunuz. Bugün Datadog, yarın self-hosted Tempo.
- Maliyet kontrolü: Datadog'un host başına fiyatlandırması belli bir ölçeğin üzerinde canınızı yakar. Backend'i siz seçince bütçeyi siz kurarsınız.
- Endüstri standardı: OpenTelemetry artık CNCF'in incubating projesi değil, mezunu. Ekibe yeni katılan biri büyük ihtimalle zaten biliyor.
Kavramlar birebir oturuyor: ddtrace'teki Tracer OpenTelemetry'de Tracer, set_tag yerine set_attribute, tracer.trace() yerine start_as_current_span(), ddtrace-run yerine opentelemetry-instrument. Yani çoğu yerde 'arama-değiştirme' düzeyinde bir migration.
Paketleri kurmak ve eski izleri silmek
Önce ddtrace'i ve onun çevresindeki Datadog paketlerini sökün:
pip uninstall ddtrace datadog datadog-api-client
Sonra OpenTelemetry tarafını kurun. opentelemetry-bootstrap komutu sizin sanal ortamdaki paketleri tarayıp uygun instrumentation'ları otomatik olarak yüklüyor; bu çok değerli bir kolaylık:
pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp \
opentelemetry-distro opentelemetry-instrumentation
opentelemetry-bootstrap -a install
Uygulama kodunda da temizlik şart. from ddtrace import patch_all gibi satırları, TraceMiddleware(...) çağrılarını ve DD_* çevre değişkenlerini deployment yapılandırmanızdan çıkarın. Tek tek aramak zorsa, grep -ri ddtrace . iyi bir başlangıç.
Auto-instrumentation: en kolay göç yolu
Eğer kodda manuel ddtrace kullanmadıysanız işiniz şaşırtıcı derecede kolay. ddtrace-run'ı opentelemetry-instrument ile değiştiriyorsunuz ve birkaç çevre değişkeni ayarlıyorsunuz:
export OTEL_SERVICE_NAME='odeme-servisi'
export OTEL_EXPORTER_OTLP_ENDPOINT='http://otel-collector:4317'
export OTEL_RESOURCE_ATTRIBUTES='deployment.environment=production,service.version=2.4.0'
opentelemetry-instrument gunicorn myapp.wsgi:application --workers 4
Bu kadar. Flask, Django, FastAPI, requests, psycopg2, redis, celery ve daha onlarcası kutudan çıkar çıkmaz izlenir. Şahsi kanaatim, çoğu ekip bu noktada zaten %80 yolu almış oluyor.
Manuel span'leri taşımak
Custom trace yazdıysanız iş biraz daha el oyunu gerektiriyor. Patternler benziyor ama metot adları farklı:
from opentelemetry import trace
# Modül başına bir tracer almak iyi pratik
tracer = trace.get_tracer('myapp.orders', '1.0.0')
def siparisi_isle(siparis_id, urunler):
# tracer.trace() yerine start_as_current_span
with tracer.start_as_current_span('siparisi_isle') as span:
# set_tag yerine set_attribute
span.set_attribute('order.id', siparis_id)
span.set_attribute('order.item_count', len(urunler))
with tracer.start_as_current_span('odeme_al') as alt_span:
try:
sonuc = odeme_yap(siparis_id, hesapla(urunler))
alt_span.set_attribute('payment.success', sonuc.basarili)
except Exception as hata:
# Tüm stack trace otomatik kaydedilir
alt_span.record_exception(hata)
alt_span.set_status(
trace.Status(trace.StatusCode.ERROR, str(hata))
)
raise
Burada gözden kaçan iki nokta var. Birincisi, record_exception zaten stack trace'i tüm detayıyla yakalıyor; eski error.msg, error.type etiketlerini elle koymanıza gerek yok. İkincisi, span'i ERROR durumuna çekmek için set_status çağırmayı unutmayın - bu olmazsa backend'de hata olarak görünmez.
Metrikleri OTLP'ye çevirmek
DogStatsD'den OpenTelemetry Metrics API'sine geçiş, trace tarafından biraz daha kafa yorucu çünkü kavramlar farklı: statsd.gauge callback tabanlı observable_gauge'a dönüşüyor.
from opentelemetry import metrics
meter = metrics.get_meter('myapp.orders', '1.0.0')
# statsd.increment karşılığı
siparis_sayaci = meter.create_counter(
name='orders.placed',
description='Verilen toplam siparis sayisi',
unit='orders',
)
# statsd.histogram karşılığı
isleme_suresi = meter.create_histogram(
name='orders.processing_time',
unit='ms',
)
def siparis_ver(siparis):
baslangic = time.time()
isle(siparis)
sure_ms = (time.time() - baslangic) * 1000
siparis_sayaci.add(1, {'region': 'tr-west', 'order.type': siparis.tip})
isleme_suresi.record(sure_ms, {'region': 'tr-west'})
Tag'ler artık 'attribute' olarak adlandırılıyor ama mantık aynı: zenginleştirici metadata. Cardinality'ye dikkat edin; user_id'yi attribute yaparsanız metrik backend'iniz dakikalar içinde teslim olur.
Framework notları
Auto-instrumentation çoğu zaman yeter ama programmatic kurulum gerektiğinde Flask için tek satır:
from opentelemetry.instrumentation.flask import FlaskInstrumentor
FlaskInstrumentor().instrument_app(app)
Django için DjangoInstrumentor().instrument() çağrısı wsgi.py ya da manage.py içinde, FastAPI için FastAPIInstrumentor.instrument_app(app). Üçü de aynı imza, aynı mantık.
Sık karşılaşılan tuzaklar
- Eski
DD_*değişkenlerini bırakmak: Konteynerde unutulanDD_AGENT_HOSTsizi yanıltır; ddtrace kalkmış görünse de log'larda hâlâ Datadog izi ararsanız sebep budur. Deployment manifest'lerini grep'leyin. - Hem
opentelemetry-instrumenthem programmatic kurulum: İkisi birden çalışınca span'ler ya iki kere açılır ya da tracer provider çakışır. Tek bir yol seçin. - Cardinality patlatmak:
set_attribute('user.email', ...)veyaadd(1, {'request.id': ...})gibi şeyler hem traces hem metrics tarafında backend'inizi şişirir. Yüksek-cardinality alanları sadece span attribute olarak ve gerektikçe kullanın. - Sampling'i unutmak: ddtrace'in
DD_TRACE_SAMPLE_RATEkarşılığıOTEL_TRACES_SAMPLERveOTEL_TRACES_SAMPLER_ARG. Production'daparentbased_traceidratioile başlayın, %100 örneklemeyle dev ortamı yakarsınız.
Kapanış
Bu yazıda ddtrace'ten OpenTelemetry'ye geçişi adım adım gördük: paketler, auto-instrumentation, manuel span'ler, metrikler ve framework özel notları. Bana sorarsanız ekibinizdeki bir hafta sonu refactor'ü olarak başlayıp, ileride Datadog faturasını yarıya indiren stratejik bir hamleye dönüşebilir. Umarım faydalı olur, bir sonraki yazıda görüşmek üzere.
