← Kursa Dön
📄 Text · 25 min

Micrometer Metrics

Giriş

Monitoring (izleme) dünyasında metrikler, uygulamanızın nabzını ölçen sayısal değerlerdir. "Son 5 dakikada kaç sipariş alındı?", "Ortalama yanıt süresi ne?", "Bellekte ne kadar alan kullanılıyor?" — tüm bu sorular metriklerle cevaplanır. Micrometer, Spring Boot'un varsayılan metrik toplama kütüphanesidir ve SLF4J'nin logging için yaptığını metrikler için yapar: bir facade (cephe) katmanı sunarak farklı monitoring sistemleriyle (Prometheus, Datadog, New Relic, InfluxDB vb.) entegrasyonu tek bir API üzerinden sağlar.

Micrometer Facade Yapısı

Micrometer'ın gücü soyutlama katmanında yatar. Uygulamanızda metrik toplama kodunu bir kez yazarsınız; Micrometer, hangi backend kullanıyorsanız ona uygun formatta aktarır:

┌─────────────────────────┐
│    Uygulama Kodu         │
│  (Counter, Timer, Gauge) │
└───────────┬─────────────┘
            │ Micrometer API
┌───────────▼─────────────┐
│    MeterRegistry         │
│  (composite registry)    │
└─┬──────┬──────┬────────┘
  │      │      │
  ▼      ▼      ▼
Prom   DD    Influx  ... (backend'ler)

MeterRegistry, tüm metrik kayıtlarını yöneten merkezi bileşendir. Spring Boot, classpath'teki bağımlılığa göre otomatik olarak uygun registry'yi yapılandırır.

Metrik Türleri

1. Counter — Sayaç

Yalnızca artan bir değerdir. Sıfırlanamaz (uygulama yeniden başlatıldığında sıfırlanır). Kullanım alanları: toplam istek sayısı, hata sayısı, işlenen mesaj sayısı.

@Service
public class OrderService {

    private final Counter orderCounter;
    private final Counter orderFailureCounter;

    public OrderService(MeterRegistry registry) {
        this.orderCounter = Counter.builder("orders.created")
            .description("Total number of orders created")
            .tag("type", "all")
            .register(registry);

        this.orderFailureCounter = Counter.builder("orders.failed")
            .description("Total number of failed order attempts")
            .register(registry);
    }

    public Order createOrder(OrderRequest request) {
        try {
            Order order = processOrder(request);
            orderCounter.increment();
            return order;
        } catch (Exception e) {
            orderFailureCounter.increment();
            throw e;
        }
    }
}

2. Timer — Zamanlayıcı

Bir işlemin süresini ve çağrılma sayısını ölçer. Hem count hem de toplam süreyi takip eder; böylece ortalama, percentile (yüzdelik dilim) gibi istatistikler hesaplanabilir.

@Service
public class PaymentService {

    private final Timer paymentTimer;

    public PaymentService(MeterRegistry registry) {
        this.paymentTimer = Timer.builder("payment.processing")
            .description("Time taken to process payments")
            .tag("gateway", "stripe")
            .publishPercentiles(0.5, 0.95, 0.99) // p50, p95, p99
            .publishPercentileHistogram()
            .sla(Duration.ofMillis(200), Duration.ofMillis(500), Duration.ofSeconds(1))
            .register(registry);
    }

    public PaymentResult processPayment(PaymentRequest request) {
        return paymentTimer.record(() -> {
            // Ödeme işlemi burada yapılır
            return gateway.charge(request);
        });
    }
}

@Timed anotasyonu ile daha deklaratif bir yaklaşım mümkündür. Bunun için TimedAspect bean'i gerekir:

@Configuration
public class MetricsConfig {

    @Bean
    public TimedAspect timedAspect(MeterRegistry registry) {
        return new TimedAspect(registry);
    }
}

@Service
public class ProductService {

    @Timed(value = "product.search", description = "Product search time",
           percentiles = {0.5, 0.95, 0.99})
    public List<Product> search(String query) {
        return productRepository.findByNameContaining(query);
    }
}

3. Gauge — Gösterge

Anlık bir değeri temsil eder. Artabilir veya azalabilir. Kullanım: aktif bağlantı sayısı, kuyruk büyüklüğü, cache boyutu, bellekteki nesne sayısı.

@Component
public class QueueMetrics {

    private final BlockingQueue<Task> taskQueue;

    public QueueMetrics(BlockingQueue<Task> taskQueue, MeterRegistry registry) {
        this.taskQueue = taskQueue;

        // Gauge, lambda ile anlık değer okur
        Gauge.builder("task.queue.size", taskQueue, BlockingQueue::size)
            .description("Current number of tasks in the queue")
            .register(registry);

        Gauge.builder("task.queue.remaining", taskQueue,
                q -> q.remainingCapacity())
            .description("Remaining capacity in the task queue")
            .register(registry);
    }
}

Counter vs Gauge farkı: Counter her zaman artar (monotonic), Gauge ise anlık snapshot verir. "Toplam sipariş sayısı" → Counter. "Şu anda işlenen sipariş sayısı" → Gauge.

4. DistributionSummary — Dağılım Özeti

Timer'a benzer ancak süre yerine herhangi bir dağılımı ölçer (ör. sipariş tutarları, dosya boyutları, istek boyutları).

@Service
public class OrderAnalyticsService {

    private final DistributionSummary orderAmountSummary;

    public OrderAnalyticsService(MeterRegistry registry) {
        this.orderAmountSummary = DistributionSummary
            .builder("order.amount")
            .description("Distribution of order amounts in USD")
            .baseUnit("usd")
            .publishPercentiles(0.5, 0.75, 0.95)
            .minimumExpectedValue(1.0)
            .maximumExpectedValue(10000.0)
            .register(registry);
    }

    public void recordOrder(Order order) {
        orderAmountSummary.record(order.getTotalAmount());
    }
}

Tag'ler (Etiketler)

Tag'ler, metrikleri boyutlandırmanızı sağlar. Aynı metrik adını farklı etiketlerle kaydettiğinizde, sorgulama sırasında filtreleme ve gruplama yapabilirsiniz:

Counter.builder("http.requests")
    .tag("method", "GET")
    .tag("endpoint", "/api/users")
    .tag("status", "200")
    .register(registry)
    .increment();

Tag best practice'leri:

  • Tag değerlerinde düşük kardinalite kullanın (status: 200, 404, 500 ✓ — userId: her kullanıcı için farklı ✗)

  • Tag isimleri tutarlı olsun (camelCase veya dot.notation)

  • Yüksek kardinalite tag'ler memory sorunlarına yol açar

Common Tags — Global Etiketler

Tüm metriklere otomatik eklenen ortak tag'ler tanımlayabilirsiniz:

@Configuration
public class MetricsConfig {

    @Bean
    public MeterRegistryCustomizer<MeterRegistry> commonTags() {
        return registry -> registry.config()
            .commonTags(
                "application", "order-service",
                "environment", "production",
                "region", "eu-west-1"
            );
    }
}

Otomatik Metrikler

Spring Boot Actuator, Micrometer üzerinden birçok metriği otomatik toplar:

  • JVM: jvm.memory.used, jvm.gc.pause, jvm.threads.live

  • HTTP: http.server.requests (metot, URI, status, exception bilgisiyle)

  • DataSource: hikaricp.connections.active, hikaricp.connections.idle

  • Cache: cache.gets, cache.puts, cache.evictions

  • Tomcat: tomcat.sessions.active.current, tomcat.threads.busy

Bu otomatik metrikler, uygulamanızın genel sağlığını izlemek için yeterli bir başlangıç noktası sunar.