Monolitik uygulamada tek log dosyasıyla idare ederdiniz. 20 mikroservis arasında gezinen bir isteği takip etmek için ise distributed tracing şart. OpenTelemetry (OTel), CNCF'in trace/metric/log standardıdır - vendor-agnostic, tüm diller destekli.

Temel Kavramlar

İlgili rehberler: Yazılım geliştirme süreçleri · PostgreSQL optimizasyonu · Git ileri seviye komutlar · Redis nedir, nasıl kullanılır · Docker ile deploy · Docker Compose: Çoklu Servis Mimarisi ve Production Deployment

  • Trace: Bir isteğin tüm yolculuğu (root span + çocukları)
  • Span: Trace içindeki tek bir iş birimi (DB çağrısı, HTTP request)
  • Trace context: Servisler arası geçen ID (W3C traceparent header)
  • Exporter: Trace'i backend'e gönderen (Jaeger, Zipkin, OTLP)

Node.js Auto-Instrumentation

bash
npm i @opentelemetry/api @opentelemetry/sdk-node \
      @opentelemetry/auto-instrumentations-node \
      @opentelemetry/exporter-trace-otlp-http
javascript
// tracing.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');

const sdk = new NodeSDK({
    resource: new Resource({
        [SemanticResourceAttributes.SERVICE_NAME]: 'webapp',
        [SemanticResourceAttributes.SERVICE_VERSION]: '1.2.3'
    }),
    traceExporter: new OTLPTraceExporter({
        url: 'http://otel-collector:4318/v1/traces'
    }),
    instrumentations: [getNodeAutoInstrumentations()]
});

sdk.start();

process.on('SIGTERM', () => sdk.shutdown());
bash
# server.js'i çalıştırırken tracing.js'i önce yükle
node -r ./tracing.js server.js

Manuel Span

javascript
const { trace, SpanStatusCode } = require('@opentelemetry/api');
const tracer = trace.getTracer('webapp');

async function calculateReport(userId) {
    return tracer.startActiveSpan('calculate-report', async span => {
        span.setAttribute('user.id', userId);
        try {
            const data = await fetchData(userId);
            span.setAttribute('data.count', data.length);
            const result = processData(data);
            span.setStatus({ code: SpanStatusCode.OK });
            return result;
        } catch (err) {
            span.recordException(err);
            span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
            throw err;
        } finally {
            span.end();
        }
    });
}

Context Propagation

Bir servis başka bir servise HTTP çağrısı yaptığında traceparent header otomatik eklenir. Receiver service bu header'ı okuyup aynı trace'e devam eder.

text
# Otomatik eklenen header
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
# versiyon-trace_id-span_id-flags

OTel Collector

Her servisin doğrudan Jaeger'a göndermesi yerine, aradaki Collector trace'i toplar, sample'lar, zenginleştirir ve backend'e gönderir. Mimaride fan-out noktası.

yaml
# otel-collector-config.yaml
receivers:
  otlp:
    protocols:
      grpc: { endpoint: 0.0.0.0:4317 }
      http: { endpoint: 0.0.0.0:4318 }

processors:
  batch: {}
  tail_sampling:
    policies:
      - name: errors
        type: status_code
        status_code: { status_codes: [ERROR] }
      - name: slow
        type: latency
        latency: { threshold_ms: 500 }
      - name: baseline
        type: probabilistic
        probabilistic: { sampling_percentage: 1 }

exporters:
  otlp/jaeger:
    endpoint: jaeger:4317
    tls: { insecure: true }
  logging: {}

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch, tail_sampling]
      exporters: [otlp/jaeger, logging]

Jaeger UI

Jaeger'ı Docker ile tek container olarak kaldırabilirsiniz. Trace'leri service, operation, duration, tag ile arar ve zaman akışını grafiksel gösterir.

bash
docker run -d --name jaeger \
    -p 4317:4317 \
    -p 16686:16686 \
    jaegertracing/all-in-one:latest
# UI: http://localhost:16686

Sampling Stratejisi

  • Head sampling: istek başında karar, ucuz ama hata trace'lerini kaçırabilir
  • Tail sampling: tüm trace'i topla, sonra karar ver (collector'da). Hatalar her zaman saklanır
  • Probabilistic: %1-5 örneklem genelde yeter
  • Rate-limiting: saniyede max N trace

Metric ve Log Entegrasyonu

OTel sadece trace değil - metric (Prometheus alternative) ve log'ları da aynı SDK ile export eder. 3'ü birlikte: bir span'den ilgili log'lara ve metric'lere atlamak mümkün (Observability 2.0).

Vendor Seçimi

  • Self-hosted: Jaeger, Tempo + Grafana, SigNoz
  • SaaS: Datadog APM, New Relic, Honeycomb, Lightstep
  • OTel'in en büyük faydası: vendor-agnostic - exporter değiştirerek vendor değiştirebilirsin

Modern Yazılım Geliştirme ve DevOps Pratikleri

Profesyonel yazılım geliştirme süreci üç pillar üzerine kuruludur: kaynak kontrolü (Git + GitHub/GitLab pull request akışı, code review zorunlu), CI/CD pipeline (otomatik test + lint + build + deploy), ve gözlemlenebilirlik (Sentry/Datadog/Grafana ile log, metric, trace toplama). Test piramidi (unit > integration > e2e) ile kod kalitesini garantilemek, mikroservis mimarisinde Docker container ve Kubernetes orkestrasyonu kullanmak, REST veya GraphQL API tasarımında OpenAPI/GraphQL Schema sözleşmesi tutmak modern standardlardır. Yazılım geliştirme yaşam döngüsü boyunca (gereksinim - tasarım - implementasyon - test - deploy - bakım) Agile/Scrum sprintleri 1-2 hafta, DevOps takımları sürekli teslim (continuous delivery) prensibiyle çalışır.