OpenTelemetry Temelleri: Modern Observability için Başlangıç Rehberi
Pratik örnekler, yaygın hatalar ve terminoloji sözlüğü ile OpenTelemetry'nin trace, metric ve log sistemlerini kapsayan başlangıç rehberi.
Dağıtık sistemlerde tek bir kullanıcı request’i düzinelerce servisten geçer; bu yüzden bir hatayı ya da latency spike’ını sadece log’larla açıklamak çoğu zaman mümkün olmaz. Ortak bir telemetri standardı olmadan her servis kendi formatında veri üretir ve mühendisler trace, metrik ve log’ları uyumsuz backend’ler arasında elle eşleştirmek zorunda kalır. OpenTelemetry (OTel), bu uyumsuzluğu ortadan kaldıran açık kaynak framework’tür: telemetriyi üretmek, işlemek ve export etmek için dilden ve runtime’dan bağımsız tek bir yol.
İlk kurulumda varsayılan dar tutulur. Auto-instrumentation’ı aç, telemetriyi bir OpenTelemetry Collector üzerinden geçir ve trace’ler doğru görünene kadar sampling’i %100’de bırak. Bu üç adım, neredeyse hiç uygulama kodu yazmadan birbiriyle ilişkili trace, metrik ve log verir; sampling oranı, backend seçimi ve custom span gibi sonraki kararları da geri alınabilir bırakır.
Metrikler ve alert’ler, bir şeyin yanlış gittiğini fark etmeyi halleder. Daha zor olan, neden bozulduğunu ve problemin dağıtık sistemde nerede başladığını anlamaktır; observability tam bu boşluğu doldurur.
OpenTelemetry, Kubernetes’ten sonra CNCF’in en aktif ikinci projesi; Google, Microsoft, Amazon, Uber ve büyük observability sağlayıcılarının çoğu projeye katkı veriyor. Bu genişlik, projenin pratik gerekçesi: bir kez yazılan instrumentation, backend değiştiğinde de çalışmaya devam ediyor.
Observability Temelleri#
Observability Nedir?#
Observability, bir sistemin internal state’ini external output’larını inceleyerek anlama yeteneğidir. Önceden tanımlanmış metrikler ve alert’lere dayanan ve “ne bozuk” sorusunu yanıtlayan monitoring’den farklı olarak, observability soruları önceden bilmeden sistem davranışı hakkında rastgele sorular sormanızı sağlar.
Farkı düşünün:
Monitoring: “API error rate %5’in üzerinde” Observability: “Kullanıcı X’in checkout request’i neden 14:23’te payment service’te başarısız oldu ve o anda database query pattern’leri nasıldı?”
Monitoring size bilinen problemlerin ne zaman oluştuğunu söyler. Observability bilinmeyen problemleri araştırmanıza ve production’daki sistem davranışını anlamanıza yardımcı olur.
Üç Observability Signal’i#
Modern observability, tam sistem görünürlüğü için birlikte çalışan ve birbiriyle ilişkilendirilen üç signal’e dayanır:
Trace’ler#
Bir trace, bir request’in dağıtık sistemlerdeki tüm yolculuğunu span’lerden oluşan Directed Acyclic Graph (DAG) olarak kaydeder. Her span tek bir operation’ı temsil eder ve şunları içerir:
- Start ve end timestamp’leri - Kesin operation timing’i
- Operation adı - Açıklayıcı identifier (örn. “GET /api/orders”)
- Parent-child ilişkileri - Operation’ların nasıl nest olduğu ve bağlandığı
- Attribute’lar - HTTP method, status code, database name gibi metadata
- Event’ler - Span sırasında belirli zaman noktaları (örn. “cache miss”, “retry attempt”)
- Status - Success, error veya unset
Trace’ler “Bu request her service’te ne kadar sürdü?” ve “Hangi service başarısızlığa neden oldu?” gibi soruları yanıtlar.
Metrikler#
Metrikler, zaman içinde sistem performansını temsil eden aggregate edilmiş sayısal veridir; 95th percentile latency’nin ne olduğu ya da service’in saniyede kaç request handle ettiği gibi soruların arkasındaki sayılar:
- Counter’lar - Monoton artan değerler (toplam request, toplam error)
- Gauge’lar - Anlık değerler (mevcut memory kullanımı, aktif connection’lar)
- Histogram’lar - Değer dağılımı (latency percentile’ları, response boyutları)
- UpDownCounter’lar - Artan veya azalan değerler (queue depth, concurrent kullanıcılar)
Log’lar#
Log’lar, service’lerden belirli event’ler hakkında detaylı context sağlayan timestamp’li mesajlardır. trace_id ve span_id kullanarak trace’lerle correlation yapıldığında, log’lar önemli ölçüde daha değerli hale gelir ve bir trace’ten tam error mesajına ve exception anındaki variable değerlerine atlama yapmanızı sağlar.
Üç Signal’in Önemi#
Her signal farklı insight’lar sağlar:
- Metrikler problemleri tespit eder ve zaman içinde trend’leri gösterir
- Trace’ler request flow’unu açıklar ve bottleneck’leri tanımlar
- Log’lar detaylı event-level bilgi sağlar
Asıl değer üç signal’in birlikte ilişkilendirilmesinden gelir. Metriklere dayalı bir alert tetiklendiğinde, error status’üne göre trace’leri filtreleyerek başarısız request’leri bulursun, sorunlu servisi tespit etmek için trace detaylarını incelersin, sonra root cause’u anlamak için ilişkili log’lara bakarsın. Bu birleşik troubleshooting akışı, Mean Time To Resolution (MTTR) süresini kısaltır.
OpenTelemetry Mimarisi#
OpenTelemetry, birbirine bağlı birkaç component aracılığıyla eksiksiz bir observability framework sağlar. Bu mimariyi anlamak, instrumentation’ı etkili bir şekilde implement etmenize ve sorunlar ortaya çıktığında troubleshoot etmenize yardımcı olur.
API Layer#
OpenTelemetry API, implementation detaylarını belirlemeden telemetri generate etmek için language-specific interface’ler tanımlar. API versiyonlar arasında stabil’dir ve şunları sağlar:
- Tracer, meter ve logger oluşturmak için interface’ler
- Context propagation mekanizmaları
- SDK configure edilmediğinde no-op implementation’lar
- Instrumentation code ile telemetri export arasında decoupling
Instrumentation kodunu API’ye göre yazarsın ve SDK gerçek implementation’ı sağlar. Bu ayrım esneklik sağlar - application kodunu değiştirmeden SDK implementation’larını değiştirebilirsin.
SDK Implementation#
SDK, API specification’ı implement eder ve şunları handle eder:
- TracerProvider initialization - Tracing infrastructure’ını kurar
- Span lifecycle management - Span’leri oluşturur, yönetir ve export eder
- Metric instrument registration - Counter’ları, gauge’ları, histogram’ları configure eder
- Context propagation - Operation’lar arasında trace context’ini korur
- Resource attribute’ları - Service’i tanımlar (name, version, environment)
- Sampling kararları - Hangi trace’lerin tutulacağına karar verir
- Exporter configuration - Telemetriyi destination’lara gönderir
Instrumentation Library’leri#
Pre-built instrumentation library’leri, popüler framework’lerden kod değişikliği olmadan otomatik olarak telemetri yakalar. Bu library’ler, operation’ları intercept etmek ve otomatik olarak span generate etmek için bytecode injection (Java), monkey-patching (Python, Node.js) veya middleware (Go) gibi teknikler kullanır.
Yaygın instrumentation library’leri:
- HTTP server’lar - Express, FastAPI, Spring Boot, Gin
- HTTP client’lar - Axios, requests, HttpClient
- Database’ler - PostgreSQL, MongoDB, Redis, MySQL
- RPC framework’leri - gRPC, Thrift
- Message queue’lar - Kafka, RabbitMQ, SQS, Pub/Sub
Auto-instrumentation, uygulama koduna dokunmadan framework katmanındaki ihtiyacın büyük kısmını karşılar. Sonra business-specific operation’lar için manual instrumentation eklersin.
OpenTelemetry Collector#
Collector, configurable pipeline’lar aracılığıyla telemetriyi alıp işleyip export eden vendor-agnostic bir proxy’dir. Application’lar ile observability backend’leri arasında ara katman görevi görür ve şunları sağlar:
Temel avantajlar:
- Buffering ve retry’lar - Backend unavailability’yi gracefully handle eder
- Protocol translation - Telemetri formatları arasında convert eder (OTLP, Jaeger, Zipkin)
- Data preprocessing - Telemetriyi filtreler, transform eder ve enrich eder
- Multi-backend export - Aynı anda birden fazla destination’a telemetri gönderir
- Centralized configuration - Telemetri pipeline’larını tek yerden yönetir
- Resource optimization - Batching ve compression network overhead’i azaltır
Collector pipeline component’leri:
- Receiver’lar - Telemetriyi kabul eder (OTLP, Jaeger, Prometheus, Zipkin)
- Processor’lar - Veriyi transform eder (batch, memory_limiter, attributes, filter)
- Exporter’lar - Backend’lere gönderir (Jaeger, Prometheus, Elasticsearch, cloud services)
Başlarken: Pratik Implementation#
Önce auto-instrumentation geliyor; en az işle en hızlı sonucu veren adım o. Ardından hiçbir library’nin bilemeyeceği business operation’lar için manual instrumentation geliyor.
Node.js Auto-Instrumentation#
İlk olarak gerekli paketleri yükle:
npm install @opentelemetry/sdk-node \
@opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-trace-otlp-grpc \
@opentelemetry/semantic-conventions
Application kodunu import etmeden önce OpenTelemetry’yi initialize etmek için tracing.js oluştur:
// tracing.js - Bunu ilk olarak import et, diğer import'lardan önce
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const { Resource } = require('@opentelemetry/resources');
const { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } = require('@opentelemetry/semantic-conventions');
// Service'ini tanımlamak için resource attribute'ları configure et
const resource = new Resource({
[ATTR_SERVICE_NAME]: 'payment-service',
[ATTR_SERVICE_VERSION]: '1.2.0',
});
// OTLP exporter'ı configure et (local dev için console exporter'a değiştir)
const traceExporter = new OTLPTraceExporter({
url: 'http://localhost:4317', // OpenTelemetry Collector endpoint
});
// Auto-instrumentation ile SDK'yı initialize et
const sdk = new NodeSDK({
resource,
traceExporter,
instrumentations: [getNodeAutoInstrumentations()],
});
// SDK'yı başlat
sdk.start();
// Graceful shutdown
process.on('SIGTERM', () => {
sdk.shutdown()
.then(() => console.log('Tracing terminated'))
.catch((error) => console.log('Error terminating tracing', error))
.finally(() => process.exit(0));
});
Application entry point’ini tracing’i önce import edecek şekilde güncelle:
// server.js
require('./tracing'); // İlk import olmalı
const express = require('express');
const app = express();
app.get('/api/orders/:id', async (req, res) => {
// Bu HTTP request otomatik olarak trace ediliyor
const order = await fetchOrder(req.params.id);
res.json(order);
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Application’ını başlat:
node server.js
Her HTTP request artık ek kod olmadan otomatik olarak trace ediliyor. Auto-instrumentation HTTP method, URL, status code ve response time’ı otomatik olarak yakalar.
Python Auto-Instrumentation#
OpenTelemetry paketlerini yükle:
pip install opentelemetry-distro \
opentelemetry-exporter-otlp \
opentelemetry-instrumentation-flask
Flask app’ini oluşturmadan önce instrumentation’ı initialize et:
# app.py
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.instrumentation.flask import FlaskInstrumentor
# Resource attribute'larını configure et (anahtarlar semantic convention adları)
resource = Resource.create({
"service.name": "order-service",
"service.version": "1.0.0",
})
# Tracer provider'ı initialize et
provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4317"))
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
# Flask app'i oluştur
from flask import Flask, jsonify
app = Flask(__name__)
# Flask'ı auto-instrument et
FlaskInstrumentor().instrument_app(app)
@app.route('/api/orders/<order_id>')
def get_order(order_id):
# Bu endpoint otomatik olarak trace ediliyor
order = fetch_order_from_db(order_id)
return jsonify(order)
if __name__ == '__main__':
app.run(port=5000)
Application’ını çalıştır:
python app.py
Business Logic için Manual Instrumentation#
Auto-instrumentation framework-level operation’ları yakalar, ama business logic için manual span’lere ihtiyacın var:
// Node.js manual instrumentation
const { trace, SpanStatusCode } = require('@opentelemetry/api');
async function processPayment(orderId, amount) {
const tracer = trace.getTracer('payment-service');
// Bu business operation için custom span oluştur
return await tracer.startActiveSpan('processPayment', async (span) => {
try {
// Business context'i span attribute olarak ekle
span.setAttribute('order.id', orderId);
span.setAttribute('payment.amount', amount);
span.setAttribute('payment.currency', 'USD');
// Payment processing'i simulate et
const result = await chargeCustomer(amount);
// Result bilgisini ekle
span.setAttribute('payment.transaction_id', result.transactionId);
span.setAttribute('payment.status', 'success');
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error) {
// Error bilgisini kaydet
span.recordException(error);
span.setStatus({
code: SpanStatusCode.ERROR,
message: error.message,
});
throw error;
} finally {
span.end(); // Her zaman span'i bitir
}
});
}
Python manual instrumentation:
# Python manual instrumentation
from opentelemetry import trace
from opentelemetry.trace import Status, StatusCode
tracer = trace.get_tracer(__name__)
def process_order(order_id):
with tracer.start_as_current_span("process_order") as span:
try:
# Business context ekle
span.set_attribute("order.id", order_id)
span.set_attribute("order.type", "express")
# Business logic'i gerçekleştir
order = validate_order(order_id)
span.set_attribute("order.items_count", len(order.items))
inventory_result = check_inventory(order)
span.set_attribute("inventory.available", inventory_result.available)
# Payment operation için nested span
with tracer.start_as_current_span("charge_payment") as payment_span:
payment_span.set_attribute("payment.amount", order.total)
payment_result = charge_customer(order)
payment_span.set_attribute("payment.status", payment_result.status)
span.set_status(Status(StatusCode.OK))
return {"success": True, "order_id": order_id}
except Exception as e:
span.record_exception(e)
span.set_status(Status(StatusCode.ERROR, str(e)))
raise
Service’ler Arası Context Propagation#
Distributed tracing’in çalışması için trace context’inin service boundary’leri arasında flow etmesi gerekir. OpenTelemetry, W3C Trace Context standardını kullanarak context’i HTTP header’larında propagate eder.
Auto-instrumentation bunu otomatik olarak handle eder HTTP request’leri için:
// Service A, Service B'ye HTTP request yapar
const axios = require('axios');
// OpenTelemetry auto-instrumentation otomatik olarak
// traceparent ve tracestate header'larını bu request'e inject eder
const response = await axios.get('http://service-b/api/data');
Manual HTTP client’lar veya message queue’lar için context’i explicit olarak inject et:
const { propagation, context } = require('@opentelemetry/api');
// Mevcut context'i al
const ctx = context.active();
// Context'i HTTP header'larına inject et
const headers = {};
propagation.inject(ctx, headers);
// Propagate edilmiş header'larla request yap
await fetch('http://service-b/api/data', { headers });
Python context propagation:
from opentelemetry import propagate
import requests
# Trace context'ini header'lara inject et
headers = {}
propagate.inject(headers)
# Propagate edilmiş context ile request yap
response = requests.get('http://service-b/api/data', headers=headers)
OpenTelemetry Collector Configuration#
Collector, application’lar ile observability backend’leri arasında kritik bir ara katman görevi görür. İşte production-ready bir configuration:
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
# Memory limiter OOM crash'lerini önler
memory_limiter:
check_interval: 1s
limit_mib: 512
spike_limit_mib: 128
# Batch processor network overhead'i azaltır
batch:
timeout: 200ms
send_batch_size: 8192
send_batch_max_size: 10000
# Resource attribute'ları ekle
resource:
attributes:
- key: deployment.environment
value: production
action: upsert
exporters:
# Trace'leri OTLP ile Jaeger'a gönder; Jaeger OTLP'yi doğrudan kabul ediyor
# ve ayrı jaeger exporter'ı collector'dan kaldırıldı
otlp/jaeger:
endpoint: jaeger-collector:4317
tls:
insecure: true
# Metrikleri Prometheus'a gönder
prometheus:
endpoint: "0.0.0.0:8889"
# Debug için console exporter
debug:
verbosity: normal
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch, resource]
exporters: [otlp/jaeger, debug]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheus]
Collector’ı Docker ile deploy et:
docker run -d --name otel-collector \
-p 4317:4317 \
-p 4318:4318 \
-p 8889:8889 \
-v $(pwd)/otel-collector-config.yaml:/etc/otel-collector-config.yaml \
otel/opentelemetry-collector-contrib:latest \
--config=/etc/otel-collector-config.yaml
Burada contrib imajı önemli: Prometheus exporter’ı ve ilerideki tail sampling processor’ı core dağıtımda değil, contrib’de bulunuyor.
Collector Deployment Pattern’leri#
Agent Pattern:
- Her application’ın yanında sidecar veya DaemonSet olarak collector deploy et
- Gateway’e göndermeden önce local aggregation yapar
- Network trafiğini azaltır ama resource consumption’ı artırır
Gateway Pattern:
- Centralized collector service
- Tüm application’lar gateway’e telemetri gönderir
- Yönetimi basitleştirir ama olası bir darboğaz oluşturur
Hierarchical Pattern (Önerilen):
- Agent’lar basic processing ile locally collect eder
- Gateway’ler heavy processing ve routing yapar
- Reliability ve performance’ın en iyi dengesi
Temel Kavramlar#
Semantic Convention’lar#
Semantic convention’lar, interoperability için attribute isimlendirmeyi standardize eder. Herkes method, request.method veya http_method gibi varyasyonlar yerine http.method kullandığında, observability tool’ları service’ler arasında consistent analiz sağlar.
Attribute isimlendirme kuralları:
- Lowercase ve underscore kullan (snake_case)
- Namespace prefix ile başla (
http.,db.,messaging.) - Custom attribute’lar için reverse domain notation kullan (
com.company.attribute)
Yaygın semantic convention namespace’leri:
// HTTP operation'lar
span.setAttribute('http.method', 'GET');
span.setAttribute('http.route', '/api/orders/:id');
span.setAttribute('http.status_code', 200);
span.setAttribute('http.url', 'https://api.example.com/orders/123');
// Database operation'lar
span.setAttribute('db.system', 'postgresql');
span.setAttribute('db.name', 'orders_db');
span.setAttribute('db.statement', 'SELECT * FROM orders WHERE id = $1');
span.setAttribute('db.operation', 'SELECT');
// Messaging operation'lar
span.setAttribute('messaging.system', 'kafka');
span.setAttribute('messaging.destination', 'order-events');
span.setAttribute('messaging.operation', 'publish');
// RPC operation'lar
span.setAttribute('rpc.system', 'grpc');
span.setAttribute('rpc.service', 'OrderService');
span.setAttribute('rpc.method', 'GetOrder');
// Exception bilgisi
span.setAttribute('exception.type', 'PaymentException');
span.setAttribute('exception.message', 'Insufficient funds');
Sampling Stratejileri#
Ölçek büyüdükçe trace’lerin %100’ünü toplamak aşırı pahalı hale gelir. Saniyede 10.000 request işleyen yoğun trafikli bir servis, çok büyük hacimde telemetri üretir. Sampling, görünürlüğü korurken maliyeti düşürür.
Head-Based Sampling:
Karar trace root’ta verilir. Basit probabilistic sampling tüm trace’lerin bir yüzdesini tutar:
// Tüm trace'lerin %10'unu sample et
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { TraceIdRatioBasedSampler } = require('@opentelemetry/sdk-trace-base');
const sdk = new NodeSDK({
sampler: new TraceIdRatioBasedSampler(0.1), // %10 sampling
// ... diğer config
});
Tail-Based Sampling:
Karar trace tamamlandıktan sonra verilir. Collector, tutup tutmamaya karar vermeden önce full trace’i inceler. Bu intelligent sampling sağlar:
- Error’ların %100’ünü tut
- Latency threshold’ları aşan trace’leri tut
- Belirli kullanıcılar veya operation’lar için trace’leri tut
- Başarılı fast trace’leri drop et
Tail-based sampling collector configuration gerektirir:
# Collector tail sampling configuration
processors:
tail_sampling:
decision_wait: 10s
num_traces: 100000
policies:
- name: errors
type: status_code
status_code:
status_codes: [ERROR]
- name: slow-traces
type: latency
latency:
threshold_ms: 1000
- name: probabilistic
type: probabilistic
probabilistic:
sampling_percentage: 5
Pratik sampling stratejisi:
- Implementation sırasında %100 sampling ile başla
- Instrumentation’ı validate ettikten sonra da her trace’i export etmeye devam et; neyin saklanacağına tail-based kurallar karar versin
- Head-based sampling’i ancak collector ingest’i maliyet yaratmaya başladığında ekle; bu durumda tail kuralları yalnızca sample edilen kısmı görür
- Veri değerini anladıkça baseline sampling’i azalt
- Sampling effectiveness’ını monitor et - gerçek issue’ları yakalıyor musun?
Resource Attribute’ları#
Resource attribute’ları telemetri kaynağını tanımlar ve bir service’ten gelen tüm signal’lerde consistent olmalıdır:
const { Resource } = require('@opentelemetry/resources');
const { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } = require('@opentelemetry/semantic-conventions');
const resource = new Resource({
[ATTR_SERVICE_NAME]: 'payment-service',
[ATTR_SERVICE_VERSION]: '2.1.0',
'deployment.environment': 'production',
'service.instance.id': process.env.HOSTNAME,
'cloud.provider': 'aws',
'cloud.region': 'us-east-1',
'cloud.availability_zone': 'us-east-1a',
});
Resource attribute’ları şunları sağlar:
- Environment veya version’a göre trace’leri filtreleme
- Region veya availability zone’a göre metrikleri gruplama
- Issue’ları belirli deployment’larla correlation yapma
- Infrastructure-level pattern’leri anlama
Yaygın Hatalar ve Çözümleri#
1. Late Initialization#
Auto-instrumentation module import’larını intercept etmeye dayandığı için, OpenTelemetry SDK’yı application framework’lerini import ettikten sonra initialize etmek missed instrumentation’a neden olur: trace’ler eksik HTTP veya database span’leriyle incomplete döner. OpenTelemetry’yi herhangi bir application import’undan önce import et ve initialize et:
// Yanlış - app tracing'den önce import edilmiş
const express = require('express');
require('./tracing');
// Doğru - tracing önce import edilmiş
require('./tracing');
const express = require('express');
2. High Cardinality Attribute’lar#
Span attribute olarak sınırsız değerler eklemek (user ID’ler, timestamp’ler, query parameter’lı tam URL’ler) milyonlarca farklı kombinasyon oluşturur ve storage’ı boğar; sonuç çok yüksek storage maliyeti, yavaş query’ler ve veriyi reddeden bir backend’dir. Bunun yerine attribute’lar için sınırlı değer kümeleri kullan ve sınırsız değerleri span event olarak sakla:
// Yanlış - high cardinality
span.setAttribute('user.id', userId); // Milyonlarca unique kullanıcı
span.setAttribute('order.timestamp', Date.now()); // Her request'te unique
span.setAttribute('http.url', fullUrlWithParams); // Request başına unique
// Doğru - bounded değerler
span.setAttribute('http.route', '/api/orders/:id'); // Sınırlı route'lar
span.setAttribute('user.tier', 'premium'); // Az sayıda tier
span.addEvent('order_created', { 'order.id': orderId }); // Event, attribute değil
3. Eksik Context Propagation#
Service boundary’leri arasında trace context propagate edilmediğinde trace’ler ayrı operation’lar gibi görünen parçalara bölünür: her service izole span’ler gösterir ve service’ler arası request flow’unu takip edemezsin. HTTP client’ların trace context header’larını içerdiğinden emin ol; auto-instrumentation kullan ya da context’i manuel olarak inject et:
# Context propagation'ı verify et
from opentelemetry import propagate
import requests
headers = {}
propagate.inject(headers) # Trace context'i inject et
print(f"Headers: {headers}") # traceparent içermeli
response = requests.get('http://service-b/api/data', headers=headers)
4. Memory Limiter’ları Unutmak#
Memory limiter olmayan collector’lar, traffic spike’larında telemetri buffering mevcut memory’yi aştığında crash olur; sonuç OOM error’larıyla gelen collector crash’leri ve telemetri verilerinde gap’lerdir. Batch processor’dan önce her zaman memory_limiter processor’ı configure et:
processors:
memory_limiter:
check_interval: 1s
limit_mib: 512 # Container memory'nin %80'i
batch:
timeout: 200ms
service:
pipelines:
traces:
processors: [memory_limiter, batch] # memory_limiter İLK
5. Over-Instrumentation#
Her function call için span oluşturmak çok yüksek sayıda span üretir: basit bir request için 500+ span’li trace’ler, yavaş query’ler ve tanımlanması zor bottleneck’ler. Sadece service boundary’lerinde ve kritik business operation’larda instrument et:
// Yanlış - çok fazla span
function processOrder(order) {
return tracer.startActiveSpan('processOrder', (span1) => {
const validated = tracer.startActiveSpan('validateOrder', (span2) => {
const result = tracer.startActiveSpan('checkFields', (span3) => {
// Her küçük fonksiyon trace ediliyor
});
});
});
}
// Doğru - stratejik span'ler
function processOrder(order) {
return tracer.startActiveSpan('processOrder', async (span) => {
try {
span.setAttribute('order.id', order.id);
// Internal validation trace edilmiyor - zaten hızlı
validateOrder(order);
// Sadece önemli operation'ları trace et
return await tracer.startActiveSpan('chargePayment', async (paymentSpan) => {
try {
return await chargeCustomer(order.total);
} finally {
paymentSpan.end();
}
});
} finally {
span.end();
}
});
}
Kural: İyi tasarlanmış bir trace’te servis başına 5-15 span hedefle.
6. Semantic Convention’ları Yok Saymak#
Standard convention’lar yerine custom attribute isimleri kullanmak interoperability’yi bozar ve automatic backend analizini engeller: dashboard’lar populate olmaz, service map’ler oluşmaz, standard metrikler eksik kalır. Her zaman OpenTelemetry semantic convention’larına refer et:
// Yanlış - custom isimler
span.setAttribute('request_method', 'GET');
span.setAttribute('status', 200);
span.setAttribute('db_type', 'postgres');
// Doğru - semantic convention'lar
span.setAttribute('http.method', 'GET');
span.setAttribute('http.status_code', 200);
span.setAttribute('db.system', 'postgresql');
7. Collector Olmadan Doğrudan Export#
Application’ların doğrudan observability backend’lerine export etmesi tight coupling yaratır ve single point of failure oluşturur: backend down olduğunda application takılır, retry logic ya da buffering devreye girmez. Application’lar ile backend’ler arasında her zaman OpenTelemetry Collector kullan:
// Doğru - collector'a export et
const traceExporter = new OTLPTraceExporter({
url: 'http://otel-collector:4317', // Doğrudan backend'e değil collector'a
});
İstisna: Collector’ın getirdiği ek yükün taşınamadığı serverless function’lar doğrudan export edebilir.
8. Tutarsız Resource Attribute’ları#
Service’lerin resource attribute’ları için farklı formatlar kullanması correlation’ı ve filtrelemeyi engeller: servisler dashboard’larda mükerrer görünür, environment’a göre filtre yapılamaz, bağımlılık grafiği kopar. Organizasyon genelinde resource attribute’ları standardize et:
// Standardize edilmiş format
const resource = new Resource({
'service.name': 'payment-service', // Her zaman kebab-case
'service.version': '1.2.0', // Her zaman semantic versioning
'deployment.environment': 'production', // Her zaman lowercase: dev/staging/production
});
9. Instrumentation’ı Test Etmemek#
Instrumentation kodu test edilmediğinde production’da failure’lar ortaya çıkar: eksik span’ler ve yanlış attribute’lar orada keşfedilir. Instrumentation için test’ler yaz:
const { InMemorySpanExporter } = require('@opentelemetry/sdk-trace-base');
describe('Payment Processing', () => {
let spanExporter;
beforeEach(() => {
spanExporter = new InMemorySpanExporter();
// SDK'yı in-memory exporter ile configure et
});
it('creates span with correct attributes', async () => {
await processPayment('order-123', 99.99);
const spans = spanExporter.getFinishedSpans();
expect(spans).toHaveLength(1);
expect(spans[0].name).toBe('processPayment');
expect(spans[0].attributes['order.id']).toBe('order-123');
expect(spans[0].attributes['payment.amount']).toBe(99.99);
});
});
10. Erken Agresif Sampling#
Rollout sırasında aggressive sampling (%0.1) implement etmek instrumentation issue’larını keşfetmeyi önler: problemler için trace’ler eksik kalır ve instrumentation validate edilemez. Initial implementation sırasında yüksek sampling rate (%50-100) ile başla, veri değerini anladıkça kademeli olarak azalt.
OpenTelemetry Terminoloji Sözlüğü#
Temel Kavramlar#
Observability Bir sistemin internal state’ini external output’larını (trace’ler, metrikler, log’lar) inceleyerek anlama yeteneği. Önceden tanımlanmış monitoring olmadan sistem davranışı hakkında rastgele sorular sormanızı sağlar.
Telemetry Sistemlerin operation’larını tanımlayan verileri. OpenTelemetry context’inde trace’leri, metrikleri ve log’ları ifade eder.
Signal Telemetri verisinin bir kategorisi. OpenTelemetry üç signal tanımlar: trace’ler, metrikler ve log’lar.
Instrumentation Telemetri verisi generate eden kod. Automatic (library’ler aracılığıyla) veya manual (custom kod) olabilir.
Tracing Terminolojisi#
Trace Tek bir request’in dağıtık sistemlerdeki yolculuğunun span’lerden oluşan Directed Acyclic Graph (DAG) olarak eksiksiz kaydı.
Span Bir trace içindeki tek bir operation; start time, end time, operation adı, attribute’lar, event’ler ve parent-child ilişkileri içerir.
Trace Context Span’leri complete trace’lere correlate etmek için service boundary’leri arasında propagate edilen metadata. Trace ID, span ID ve sampling kararını içerir.
Span Attribute’ları
Metadata sağlayan span’lere eklenmiş key-value pair’ler (örn. http.method, db.statement).
Span Event’leri Span lifetime sırasında discrete occurrence’ları temsil eden timestamp’li mesajlar (örn. “cache miss”, “retry attempt”).
Span Status Operation’ın succeed olup olmadığını (OK), fail olup olmadığını (ERROR) veya status’ün unknown (UNSET) olduğunu gösterir.
Parent Span Child span’leri başlatan span, operation nesting’i gösteren hiyerarşik ilişkiler oluşturur.
Root Span Bir trace’teki ilk span, request’in sisteme entry point’ini temsil eder.
Context Propagation Trace context’ini service boundary’leri arasında geçirmek için mekanizma, distributed tracing’i mümkün kılar. HTTP header’ları aracılığıyla W3C Trace Context standardını kullanır.
Baggage Cross-cutting concern’lar (user ID, feature flag’ler) için trace context’i ile birlikte propagate edilen key-value pair’ler. Span verisine dahil değil.
Metrik Terminolojisi#
Metric Zaman içinde capture edilen sistem performansının aggregate edilmiş sayısal ölçümü.
Counter Cumulative değerleri temsil eden monoton artan metrik (toplam request’ler, toplam error’lar).
Gauge Artabilen veya azalabilen point-in-time ölçüm (mevcut memory kullanımı, aktif connection’lar).
Histogram Min, max, sum, count ve bucket’ları kaydeden ölçüm dağılımı (latency dağılımı, response boyutları).
UpDownCounter Artabilen veya azalabilen metrik, yukarı ve aşağı giden değerleri track eder (queue depth, concurrent kullanıcılar).
Metric Instrument Ölçümleri kaydetmek için API interface’i (meter aracılığıyla oluşturulur).
Aggregation Metrik ölçümlerini zaman içinde ve dimension’lar arasında combine etme yöntemi.
Collector Terminolojisi#
Collector Configurable pipeline’lar aracılığıyla telemetri verisini alıp işleyip export eden vendor-agnostic proxy.
Receiver Çeşitli protokoller (OTLP, Jaeger, Zipkin, Prometheus) aracılığıyla telemetri verisini kabul eden collector component’i.
Processor Telemetri verisini transform eden collector component’i (batching, filtering, attribute modification, sampling).
Exporter İşlenmiş telemetriyi observability backend’lerine gönderen collector component’i.
Pipeline Collector’daki receiver’lar, processor’lar ve exporter’lar aracılığıyla configure edilmiş telemetri flow’u.
OTLP (OpenTelemetry Protocol) Telemetri verisini transmit etmek için native protokol. gRPC (port 4317) ve HTTP (port 4318) destekler.
Sampling Terminolojisi#
Sampling İstatistiksel önemi korurken sadece trace’lerin bir subset’ini tutarak telemetri volume’ünü azaltma tekniği.
Head-Based Sampling Complete trace’i görmeden trace root’ta verilen sampling kararı. Basit ama intelligent kararlar veremez.
Tail-Based Sampling Trace tamamlandıktan sonra verilen sampling kararı, trace karakteristiklerine (error’lar, latency) dayalı intelligent sampling’i mümkün kılar.
Sampling Rate Tutulan trace’lerin yüzdesi. 0.1, trace’lerin %10’unun sample edildiği anlamına gelir.
Sampler Configure edilmiş stratejiye dayalı sampling kararları veren component.
Semantic Convention Terminolojisi#
Semantic Convention’lar Interoperability sağlayan attribute’lar, metrikler ve resource attribute’ları için standardize edilmiş isimlendirme convention’ları.
Resource Attribute’ları Telemetri kaynağını tanımlayan attribute’lar (service adı, version, environment, cloud provider).
Attribute Namespace
İlgili attribute’ları gruplayan prefix (http.*, db.*, messaging.*).
Span Kind Span’in trace’teki rolünü tanımlayan kategori: INTERNAL, SERVER, CLIENT, PRODUCER, CONSUMER.
API ve SDK Terminolojisi#
API Implementation’ı prescribe etmeden telemetri generate etmenin nasıl yapılacağını tanımlayan language-specific interface’ler.
SDK Telemetri generate etmek ve export etmek için gerçek functionality sağlayan API specification’ın implementation’ı.
TracerProvider Tracer’ları oluşturmak için factory, exporter’lar, sampler’lar ve processor’larla configure edilir.
Tracer Belirli bir instrumentation scope içinde span’leri oluşturmak için interface (library veya service).
MeterProvider Metrik kaydetmek için kullanılan meter’ları oluşturmak için factory.
Meter Metrik instrument’ları (counter’lar, gauge’lar, histogram’lar) oluşturmak için interface.
Resource Resource attribute’larla tanımlanan telemetri üreten entity’nin immutable temsili.
Propagator Trace context’ini boundary’ler arasında inject etmekten ve extract etmekten sorumlu component.
Yaygın Kısaltmalar#
OTel - OpenTelemetry OTLP - OpenTelemetry Protocol CNCF - Cloud Native Computing Foundation APM - Application Performance Monitoring SLI - Service Level Indicator SLO - Service Level Objective MTTR - Mean Time To Resolution MTTD - Mean Time To Detection RED - Rate, Errors, Duration (key metrikler) DAG - Directed Acyclic Graph W3C - World Wide Web Consortium
Öğrenme Yolu ve Kaynaklar#
Önerilen Öğrenme Yolu#
-
Local environment’ı kur Experimentation için Docker Compose kullanarak OpenTelemetry Collector ve Jaeger deploy et
-
Sample bir service’i instrument et Hemen sonuç görmek için Node.js veya Python’da auto-instrumentation ile başla
-
Manual instrumentation ekle Manual instrumentation pattern’lerini anlamak için business logic için custom span’ler oluştur
-
Staging’e deploy et Realistic traffic ile collector configuration’ı ve sampling stratejilerini test et
-
Correlation implement et Log’lara trace_id ekle ve trace’lerden metrikler generate et
-
Production’a roll out yap Non-critical service’lerle başlayarak incremental olarak deploy et
Resmi Kaynaklar#
Dokümantasyon:
- OpenTelemetry Official Docs (yeni sekmede açılır)
- Semantic Conventions Reference (yeni sekmede açılır)
- Collector Documentation (yeni sekmede açılır)
Community:
- OpenTelemetry GitHub (yeni sekmede açılır)
- CNCF Slack #opentelemetry (yeni sekmede açılır)
- Community Meetings (yeni sekmede açılır)
Öğrenme:
- OpenTelemetry Demo Application (yeni sekmede açılır)
- CNCF OpenTelemetry Certification (yeni sekmede açılır)
- Codelabs and Tutorials (yeni sekmede açılır)
Önerilen Backend’ler#
Open Source:
- Jaeger - Distributed tracing odaklı
- Grafana Tempo - Scalable trace storage
- Prometheus - Metrik monitoring
- SigNoz - Unified observability platform
Commercial:
- Datadog - Comprehensive observability
- Honeycomb - Query-driven exploration
- New Relic - Full-stack observability
- Grafana Cloud - Tek OTLP endpoint arkasında yönetilen Tempo, Mimir ve Loki
Sonuç#
Bu varsayılan, ilk kurulumların çoğunda geçerli. Bu varsayılanı üç durumda bırakman gerekir. Cold start bütçesi dar olan serverless function’lar collector’ı atlayıp doğrudan export edebilir. Trafiği tam sampling’i pahalı hale getiren servisler kararı SDK’dan collector’a taşımalı: trace’lerin tamamı export edilir, error’ları ve yavaş olanları tail-based kurallar seçer. Bu kuralların önündeki probabilistic head sampler ise trace’leri collector görmeden düşürür; düşürdüklerini tail politikaları hiç göremez. Bir vendor agent’ı üzerinde zaten standartlaşmış ekipler ise, planlarında OTLP ingest açılana kadar o agent’ın kendi instrumentation’ından daha fazla fayda görebilir.
Pratik bir sonraki adım: OpenTelemetry Demo uygulamasını local’de Jaeger’a bağlayarak çalıştır, ürettiği span’leri kendi instrumentation’ının ürettikleriyle karşılaştır ve aradaki boşluklara bakarak sırada neyi instrument etmen gerektiğine karar ver.
Kaynaklar#
- OpenTelemetry Dokümantasyonu (yeni sekmede açılır) - OpenTelemetry için resmi başlangıç kılavuzları, kavramlar ve dil SDK’ları
- OpenTelemetry Spesifikasyonu (yeni sekmede açılır) - Trace, metrik, log ve OTLP’yi tanımlayan resmi spesifikasyon
- OpenTelemetry Collector (yeni sekmede açılır) - Vendor-agnostic telemetri pipeline’ı için mimari, configuration ve deployment kılavuzu
- Semantic Convention’lar (yeni sekmede açılır) - Diller ve framework’ler genelinde tutarlı telemetri için standartlaştırılmış attribute adları ve değerleri
- OpenTelemetry Instrumentation Kavramları (yeni sekmede açılır) - Otomatik ve manuel instrumentation yaklaşımlarına genel bakış
- OpenTelemetry Demo Uygulaması (yeni sekmede açılır) - Uçtan uca OpenTelemetry kurulumunu gösteren referans mikroservis uygulaması
İlgili yazılar
Yeşil ışıklı dashboard'lardan, dağıtık izleme ile sistem davranışını ve iş etkisini anlatan observability sistemlerine geçişin yolu.
observability · monitoring · opentelemetry +3
Bir UI parçasının arkasındaki ince sunum servisi yapışkan koda dönüşür. Port-ve-adaptör, çekirdeği somut hiçbir şeye bağımlı bırakmayarak bunu sürdürülebilir tutar.
architecture · nodejs · typescript +3
Tek bir PII branded type'ı observability API imzalarınıza yerleştirin; TypeScript hassas alanları runtime redactor görmeden, çağrı yerinde reddetsin.
typescript · zod · observability +3
Webhook, imzalı URL ve servisler arası kimlik doğrulama için pratik bir HMAC-SHA-256 rehberi: üç dilde çalışan kod ve dijital imzaya geçmenin gerektiği sınır.
security · encryption · webhooks +4
Kurumsal LLM uygulamaları için production-grade prompt engineering rehberi: sistematik tasarım, güvenlik, observability ve maliyet optimizasyonu.
prompt-engineering · llm · ai-tools +6