Spring Boot Micrometer ve Prometheus: Üretimde Actuator Metrikleri
Spring Boot 4 üzerinde Spring Boot Micrometer ve Prometheus entegrasyonu: Actuator erişim modeli, scrape yapılandırması, HTTP metrikleri, sağlık probe'ları ve uyarı kuralları.

Spring Boot Actuator sağlık, metrik ve tanılama endpointleri sunar; Micrometer ise bunların arkasındaki metrik cephesidir. micrometer-registry-prometheus bağımlılığı eklendiğinde, Prometheus'un sabit bir aralıkla scrape ettiği /actuator/prometheus endpointi oluşur. Aşağıdaki örnekler Spring Boot 4.1 (güncel GA sürümü, 4.2 release candidate aşamasında), Micrometer 1.16 ve Prometheus Java istemcisi 1.x'i hedefler.
Spring Boot Micrometer ile Prometheus entegrasyonu üç adımdan oluşur: spring-boot-starter-actuator ve micrometer-registry-prometheus eklenir, prometheus değeri management.endpoints.web.exposure.include listesine yazılır, ardından bir Prometheus scrape_config tanımı /actuator/prometheus adresine yönlendirilir. JVM, HTTP, HikariCP ve sistem metrikleri ek kod gerekmeden yayımlanır.
Spring Boot Micrometer Kurulumu: Actuator ve Prometheus Registry
Gerekli Maven Bağımlılıkları
Actuator starter'ı endpointleri etkinleştirir ve Micrometer'ı projeye dahil eder. Prometheus registry her ölçeri Prometheus ekspozisyon formatında biçimlendirir. AspectJ starter'ı yalnızca @Timed ve @Counted anotasyonları için gereklidir.
<!-- pom.xml -->
<!-- Actuator + Micrometer + Prometheus (Spring Boot 4.x) -->
<dependencies>
<!-- Spring Boot Actuator: monitoring endpoints, includes Micrometer -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Micrometer Registry Prometheus (Prometheus Java client 1.x) -->
<!-- Exposes metrics in Prometheus format at /actuator/prometheus -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<!-- AspectJ for @Timed and @Counted -->
<!-- Spring Boot 4 renamed spring-boot-starter-aop to spring-boot-starter-aspectj -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aspectj</artifactId>
</dependency>
</dependencies>Eski tutoriallarda sürümle ilgili iki tuzak bulunur. Micrometer 1.13'ten (Spring Boot 3.3) bu yana micrometer-registry-prometheus, Prometheus Java istemcisi 1.x üzerine kuruludur; eski 0.x kodu, Micrometer Prometheus dokümantasyonunun yalnızca geçici bir çözüm olarak sunduğu, kullanımdan kaldırılmış micrometer-registry-prometheus-simpleclient modülünde yer alır. Spring Boot 4'te ise hâlâ spring-boot-starter-aop bildiren bir pom.xml artık çözümlenmez: Spring Boot 4.0 geçiş rehberi bu starter'ın adını spring-boot-starter-aspectj olarak değiştirir.
Actuator Endpointlerinin Yapılandırılması
Varsayılan olarak HTTP üzerinden yalnızca health endpointi açıktır. /actuator/prometheus endpointi registry classpath'e eklendiği anda var olur, ancak ekspozisyon listesine eklenene kadar 404 döner.
Spring Boot 3.4, açma/kapama anahtarlarını (management.endpoints.enabled-by-default, management.endpoint.<id>.enabled) bir erişim modeliyle değiştirdi ve eski özellikler kullanımdan kaldırıldı. Opt-in kalıbı varsayılan erişimi none olarak ayarlar ve her endpoint için ayrı ayrı read-only erişim verir:
# application.yml
# Actuator configuration for production (Spring Boot 3.4+ access model)
management:
endpoints:
# Every endpoint is inaccessible unless granted below
access:
default: none
web:
exposure:
# Endpoints exposed over HTTP
# health, info, prometheus are the minimum for monitoring
include: health,info,prometheus,metrics,env,loggers
base-path: /actuator
endpoint:
health:
access: read-only
show-details: when-authorized
show-components: when-authorized
info:
access: read-only
prometheus:
access: read-only
metrics:
access: read-only
env:
access: read-only
# Mask sensitive values for anonymous callers
show-values: when-authorized
loggers:
# unrestricted allows POST to change a log level at runtime
access: unrestrictedEkspozisyon ve erişim iki ayrı denetimdir. exposure.include, bir endpointin HTTP üzerinden eşlenip eşlenmeyeceğine karar verir; access ise eşlendikten sonra endpointin neye izin verdiğini belirler. Hâlâ enabled: true kullanan bir özellik dosyası, kullanımdan kaldırma uyarısıyla çalışmaya devam eder ve geçiş için doğru an budur.
show-details: when-authorized seçeneği sağlık ayrıntılarını yalnızca uygun role sahip kimliği doğrulanmış kullanıcılara gösterir. Endpointlerin kendisi özel bir SecurityFilterChain ile korunur:
// Securing Actuator endpoints (Spring Boot 4, Spring Security 7)
package com.example.monitoring.config;
import static org.springframework.security.web.access.IpAddressAuthorizationManager.hasIpAddress;
import org.springframework.boot.security.autoconfigure.actuate.web.servlet.EndpointRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration(proxyBeanMethods = false)
public class ActuatorSecurityConfig {
@Bean
SecurityFilterChain actuatorSecurityFilterChain(HttpSecurity http) throws Exception {
return http
.securityMatcher(EndpointRequest.toAnyEndpoint())
.authorizeHttpRequests(auth -> auth
// Health and info public for load balancers
.requestMatchers(EndpointRequest.to("health", "info")).permitAll()
// Prometheus accessible from the internal network only
.requestMatchers(EndpointRequest.to("prometheus")).access(hasIpAddress("10.0.0.0/8"))
// Other endpoints restricted to admins
.anyRequest().hasRole("ACTUATOR_ADMIN")
)
.httpBasic(basic -> {})
.build();
}
}EndpointRequest, Spring Boot 4'te Actuator endpoint referansında gösterildiği gibi org.springframework.boot.security.autoconfigure.actuate.web.servlet paketine taşındı. authorizeHttpRequests içindeki Java AuthorizedUrl DSL'inde hasIpAddress metodu yoktur: IP kuralı, Spring Security 6.3'ten beri mevcut olan IpAddressAuthorizationManager.hasIpAddress üzerinden tanımlanır.
Micrometer ile Özel Metrikler
Uygulama Sayaçları, Gauge'ları ve Timer'ları
Micrometer, farklı kullanım senaryolarına uygun çeşitli metrik türleri sunar. Counter'lar kümülatif olayları, gauge'lar anlık değerleri ve timer'lar bir işlemin süresini ölçer.
// Custom business metrics service
package com.example.monitoring.metrics;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.stereotype.Service;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
@Service
public class OrderMetricsService {
// Counter for orders created
private final Counter ordersCreatedCounter;
// Timer to measure processing duration
private final Timer orderProcessingTimer;
// Atomic value for pending orders gauge
private final AtomicInteger pendingOrdersCount = new AtomicInteger(0);
public OrderMetricsService(MeterRegistry registry) {
// Exported as orders_created_total: the _total suffix is added by the registry
this.ordersCreatedCounter = Counter.builder("orders.created")
.description("Total number of orders created")
.tag("channel", "web")
.register(registry);
// Timer with histogram buckets: percentiles computed server-side in PromQL
this.orderProcessingTimer = Timer.builder("orders.processing.duration")
.description("Order processing duration")
.publishPercentileHistogram()
.register(registry);
// Gauge linked to atomic value
// Read on each scrape
Gauge.builder("orders.pending.count", pendingOrdersCount, AtomicInteger::get)
.description("Number of orders pending processing")
.register(registry);
}
public void recordOrderCreated() {
ordersCreatedCounter.increment();
pendingOrdersCount.incrementAndGet();
}
public void recordOrderProcessed(Runnable processingLogic) {
// Measures execution duration
orderProcessingTimer.record(processingLogic);
pendingOrdersCount.decrementAndGet();
}
public <T> T recordOrderProcessedWithResult(Supplier<T> processingLogic) {
return orderProcessingTimer.record(processingLogic);
}
}Prometheus registry ile iki adlandırma ayrıntısı önemlidir. Micrometer ölçer adları nokta kullanır ve snake case'e dönüştürülür (orders.processing.duration, orders_processing_duration_seconds olur). Counter'lara _total soneki otomatik eklenir; bu nedenle bir counter'ı orders.created.total olarak adlandırmak yalnızca gürültü ekler.
publishPercentiles(0.5, 0.95, 0.99) yüzdelikleri her JVM'in içinde hesaplar ve bu değerler instance'lar arasında toplanamaz. publishPercentileHistogram() ise histogram_quantile() fonksiyonunun tüm filo genelinde topladığı bucket'ları yayımlar; Prometheus kullanıldığında seçilmesi gereken seçenek budur.
@Timed ve @Counted Anotasyonları
Kalıp kodu önlemek için Micrometer, metotları AspectJ aracılığıyla araçsallaştıran anotasyonlar sunar.
// Automatic instrumentation with annotations
package com.example.monitoring.service;
import io.micrometer.core.annotation.Counted;
import io.micrometer.core.annotation.Timed;
import org.springframework.stereotype.Service;
@Service
public class PaymentService {
private final PaymentValidator paymentValidator;
public PaymentService(PaymentValidator paymentValidator) {
this.paymentValidator = paymentValidator;
}
// @Timed creates a Timer
// Measures each call and publishes count, sum, max and histogram buckets
@Timed(
value = "payment.process.duration",
description = "Payment processing duration",
histogram = true
)
public PaymentResult processPayment(PaymentRequest request) {
// Call through another bean: the proxy intercepts it
paymentValidator.validate(request);
return executePayment(request);
}
// @Counted increments a counter on each call
// Useful for discrete events
@Counted(
value = "payment.refunds",
description = "Total number of refunds"
)
public void refundPayment(String transactionId) {
// Refund logic
}
}# application.yml
# Enables TimedAspect, CountedAspect and the other Micrometer annotation aspects
management:
observations:
annotations:
enabled: trueSpring Boot 3.2'den bu yana TimedAspect ve CountedAspect bean'lerini elle tanımlamak gerekmez: classpath'te spring-boot-starter-aspectj varken management.observations.annotations.enabled=true ayarı bunları otomatik yapılandırır. Özelliğin varsayılan değeri false olduğundan "@Timed hiçbir şey yapmıyor" şikâyetlerinin çoğu buradan kaynaklanır.
@Timed ve @Counted anotasyonları yalnızca proxy üzerinden çağrılan Spring bean'lerinin public metotlarında çalışır. Aynı sınıf içinde bir metottan diğerine yapılan çağrı proxy'yi atlar ve araçsallaştırılmaz; private bir metottaki anotasyon ise yok sayılır. Zaten araçsallaştırılmış bir Spring MVC controller'ına anotasyon eklemek yinelenen gözlemler üretir.
Spring Boot mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
Özel Sağlık Endpointleri
İş Sağlık Göstergeleri
Health Indicator'lar dış bağımlılıkların ve kritik iş bileşenlerinin durumunu doğrular. Spring Boot, veritabanları, Redis ve diğer yaygın hizmetler için varsayılan göstergeler sunar. Spring Boot 4'te sağlık API'si kendi spring-boot-health modülünde yer alır; Health ve HealthIndicator, org.springframework.boot.actuate.health paketinden org.springframework.boot.health.contributor paketine taşındı.
// Health indicator for payment gateway (Spring Boot 4 packages)
package com.example.monitoring.health;
import org.springframework.boot.health.contributor.Health;
import org.springframework.boot.health.contributor.HealthIndicator;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import java.time.Duration;
import java.time.Instant;
@Component
public class PaymentGatewayHealthIndicator implements HealthIndicator {
private final RestClient restClient;
private final String gatewayHealthUrl;
public PaymentGatewayHealthIndicator(RestClient.Builder restClientBuilder) {
this.restClient = restClientBuilder.build();
this.gatewayHealthUrl = "https://api.payment-gateway.com/health";
}
@Override
public Health health() {
Instant start = Instant.now();
try {
// Call gateway health endpoint
var response = restClient.get()
.uri(gatewayHealthUrl)
.retrieve()
.toBodilessEntity();
Duration responseTime = Duration.between(start, Instant.now());
if (response.getStatusCode().is2xxSuccessful()) {
return Health.up()
.withDetail("responseTime", responseTime.toMillis() + "ms")
.withDetail("statusCode", response.getStatusCode().value())
.build();
} else {
return Health.down()
.withDetail("statusCode", response.getStatusCode().value())
.withDetail("reason", "Unexpected status code")
.build();
}
} catch (Exception e) {
Duration responseTime = Duration.between(start, Instant.now());
return Health.down()
.withDetail("error", e.getClass().getSimpleName())
.withDetail("message", e.getMessage())
.withDetail("responseTime", responseTime.toMillis() + "ms")
.build();
}
}
}Bu gösterge /actuator/health altında paymentGateway adıyla görünür: bu ad, HealthIndicator soneki çıkarılmış bean adıdır.
Kubernetes için Sağlık Grupları
Spring Boot 4, liveness ve readiness probe'larını yalnızca Kubernetes algılandığında değil, her platformda varsayılan olarak etkinleştirir. /actuator/health/liveness ve /actuator/health/readiness grupları hiçbir yapılandırma olmadan mevcuttur; LivenessStateHealthIndicator veya ReadinessStateHealthIndicator bean'lerini elle tanımlamak gereksizdir. Yapılandırma yalnızca bir gruba bileşen eklemek veya özel gruplar tanımlamak için gerekir:
# application.yml
# Health groups configuration for Kubernetes
management:
endpoint:
health:
probes:
# true by default in Spring Boot 4
enabled: true
# Also serve the groups at /livez and /readyz on the main server port
add-additional-paths: true
group:
# Readiness: can the application receive traffic?
readiness:
include: readinessState,db
# Custom group for critical dependencies
critical:
include: db,paymentGateway
show-details: when-authorizedLiveness grubu livenessState ile sınırlı kalmalıdır. Liveness grubuna bir veritabanı veya dış ağ geçidi eklemek, bu bağımlılık çöktüğünde Kubernetes'in tüm pod'ları yeniden başlatmasına yol açar ve kısmi bir kesintiyi tam kesintiye dönüştürür. Dış bağımlılıklar readiness grubuna aittir; o da yalnızca uygulama bunlar olmadan hiçbir isteğe yanıt veremiyorsa.
Kubernetes probe'ları daha sonra özel endpointleri işaret eder:
# kubernetes-deployment.yml
# Kubernetes probes configuration
spec:
containers:
- name: order-service
livenessProbe:
httpGet:
path: /livez
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /readyz
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3add-additional-paths: true ile probe'lar, Actuator ayrı bir yönetim portunda çalışsa bile uygulama portunda kalır; böylece bozulan ana sunucu probe tarafından tespit edilir.
Spring Boot Prometheus Entegrasyonu ve Grafana
Prometheus Scraping Yapılandırması
Prometheus, /actuator/prometheus endpointini periyodik olarak sorgulayarak metrikleri toplar. Prometheus varsayılan olarak /metrics yolunu kullandığından scrape yapılandırması hedefleri ve metrik yolunu tanımlar.
# prometheus.yml
# Prometheus configuration for Spring Boot
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'spring-boot-apps'
metrics_path: '/actuator/prometheus'
scrape_interval: 10s
static_configs:
- targets:
- 'order-service:9090'
- 'payment-service:9090'
- 'inventory-service:9090'
# Relabeling to strip the port from the instance label
relabel_configs:
- source_labels: [__address__]
target_label: instance
regex: '([^:]+):\d+'
replacement: '${1}'
# Kubernetes service discovery
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
# Only scrape pods with annotation
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)Hedefler 9090 portunu kullanır, çünkü aşağıdaki üretim yapılandırması Actuator'ı ayrı bir yönetim portuna taşır. management.server.port olmadan scrape hedefi uygulama portudur.
Varsayılan Olarak Yayımlanan Spring Boot Actuator Metrikleri
Micrometer ile birlikte Actuator, JVM, süreç, HikariCP ve HTTP metriklerini hiç kod yazmadan sunar. Aşağıdaki PromQL sorguları, üretimde ilk izlenen metrikleri kapsar:
# PromQL queries for JVM monitoring
# Heap memory usage, summed across heap pools
sum by (instance) (jvm_memory_used_bytes{area="heap"})
# Heap usage percentage
sum by (instance) (jvm_memory_used_bytes{area="heap"})
/ sum by (instance) (jvm_memory_max_bytes{area="heap"}) * 100
# Live threads
jvm_threads_live_threads
# Garbage collection: seconds spent paused per second
rate(jvm_gc_pause_seconds_sum[5m])
# GC pauses per minute
rate(jvm_gc_pause_seconds_count[1m]) * 60
# CPU used by the JVM process
process_cpu_usage
# Active database connections
hikaricp_connections_active
# Connection pool utilization
hikaricp_connections_active / hikaricp_connections_max * 100jvm_memory_used_bytes{area="heap"}, bellek havuzu başına bir seri döndürür (G1 ile Eden, Survivor, Old Gen). Bunu sum by (instance) olmadan maksimuma bölmek, dashboard'da yanlış görünen havuz bazlı oranlar üretir.
Bir MeterBinder bean'i, varsayılanların kapsamadığı uygulama düzeyindeki gauge'ları ekler:
// Additional JVM metrics
package com.example.monitoring.metrics;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.MeterBinder;
import org.springframework.stereotype.Component;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
@Component
public class CustomJvmMetrics implements MeterBinder {
@Override
public void bindTo(MeterRegistry registry) {
OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
// System load average
Gauge.builder("system.load.average", osBean, OperatingSystemMXBean::getSystemLoadAverage)
.description("System load average over 1 minute")
.register(registry);
// Available processors count
Gauge.builder("system.cpu.count", osBean, OperatingSystemMXBean::getAvailableProcessors)
.description("Number of available processors")
.register(registry);
// Application uptime
Gauge.builder("application.uptime.seconds",
ManagementFactory.getRuntimeMXBean(),
bean -> bean.getUptime() / 1000.0)
.description("Application uptime in seconds")
.register(registry);
}
}system.cpu.count ve process.uptime zaten Micrometer'ın yerleşik binder'ları tarafından yayımlanır. Örnek yalnızca kalıbı gösterir; gerçek bir binder yalnızca varsayılanlarda eksik olanı kaydetmelidir.
Kullanıma Hazır Grafana Dashboard'ları
Grafana, standart Actuator metrikleri üzerinde çalışan Spring Boot topluluk dashboard'ları sunar. Elle yazılan bir dashboard timeseries ve stat panel türlerini kullanır:
{
"annotations": {
"list": []
},
"panels": [
{
"title": "Request Rate",
"type": "timeseries",
"targets": [
{
"expr": "sum by (method, uri, status) (rate(http_server_requests_seconds_count{application=\"$application\"}[5m]))",
"legendFormat": "{{method}} {{uri}} - {{status}}"
}
]
},
{
"title": "Response Time P99",
"type": "timeseries",
"targets": [
{
"expr": "histogram_quantile(0.99, sum by (le, method, uri) (rate(http_server_requests_seconds_bucket{application=\"$application\"}[5m])))",
"legendFormat": "{{method}} {{uri}}"
}
]
},
{
"title": "Error Rate",
"type": "stat",
"targets": [
{
"expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\",status=~\"5..\"}[5m])) / sum(rate(http_server_requests_seconds_count{application=\"$application\"}[5m])) * 100"
}
]
}
]
}Eski tutoriallardan kopyalanan birçok dashboard hâlâ graph ve singlestat panel türlerini kullanır. Singlestat Grafana 8'de kaldırıldı ve Grafana 12 AngularJS desteğini tamamen kaldırdı; bu nedenle bu paneller ancak timeseries ve stat türlerine geçirildikten sonra görüntülenir.
Bir topluluk dashboard'unu içe aktarmak için: Grafana > Dashboards > New > Import, ardından ID 4701 (JVM Micrometer) veya 12900 (SpringBoot APM) girilir. İçe aktarmadan sonra panel türleri kontrol edilmelidir: topluluk dashboard'larının eski revizyonları, Grafana 12'nin dönüştürmesi gereken Angular paneller içerebilir.
Prometheus ile Uyarılar
Temel Uyarı Kuralları
Prometheus uyarı kuralları, metrikler kritik eşikleri aştığında bildirimleri tetikler.
# alerting-rules.yml
# Alert rules for Spring Boot applications
groups:
- name: spring-boot-alerts
rules:
# Alert if application is down
- alert: ApplicationDown
expr: up{job="spring-boot-apps"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Application {{ $labels.instance }} is down"
description: "{{ $labels.instance }} has been down for more than 1 minute"
# Alert on HTTP error rate
- alert: HighErrorRate
expr: |
sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m])) by (application)
/
sum(rate(http_server_requests_seconds_count[5m])) by (application)
> 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "High error rate on {{ $labels.application }}"
description: "Error rate is {{ $value | humanizePercentage }}"
# Alert on P99 latency, aggregated per application
- alert: HighLatency
expr: |
histogram_quantile(0.99,
sum by (le, application) (rate(http_server_requests_seconds_bucket[5m]))
) > 2
for: 5m
labels:
severity: warning
annotations:
summary: "High latency on {{ $labels.application }}"
description: "P99 latency is {{ $value | humanizeDuration }}"
# Heap memory alert
- alert: HighHeapUsage
expr: |
sum by (instance) (jvm_memory_used_bytes{area="heap"})
/ sum by (instance) (jvm_memory_max_bytes{area="heap"})
> 0.85
for: 5m
labels:
severity: warning
annotations:
summary: "High heap memory usage on {{ $labels.instance }}"
description: "Heap usage is at {{ $value | humanizePercentage }}"
# Database connection pool exhausted alert
- alert: DatabaseConnectionPoolExhausted
expr: |
hikaricp_connections_active
/ hikaricp_connections_max
> 0.9
for: 2m
labels:
severity: critical
annotations:
summary: "Database connection pool nearly exhausted"
description: "{{ $value | humanizePercentage }} of connections in use"
# Excessive GC alert
- alert: HighGCPause
expr: |
rate(jvm_gc_pause_seconds_sum[5m])
/ rate(jvm_gc_pause_seconds_count[5m])
> 0.5
for: 5m
labels:
severity: warning
annotations:
summary: "High GC pause time"
description: "Average GC pause is {{ $value | humanizeDuration }}"Bu uyarılar en yaygın üretim sorunlarını kapsar: erişilebilirlik, performans ve kaynaklar. HighLatency kuralı sum by (le, ...) gerektirir: bu olmadan histogram_quantile her seri için (URI, metot ve durum başına) ayrı bir P99 hesaplar ve üç çağrılı tek bir yavaş endpoint uyarıyı tetikler.
Spring Boot HTTP Metrikleri ve Veritabanı Metrikleri
HTTP İsteklerinin Otomatik Araçsallaştırılması
Spring Boot, gelen her Spring MVC ve WebFlux isteğini Observation API üzerinden araçsallaştırır ve method, uri, status, outcome ve exception etiketleriyle http.server.requests timer'ını yayımlar.
# application.yml
# HTTP metrics configuration
management:
metrics:
distribution:
# Histogram buckets for server-side percentiles
percentiles-histogram:
http.server.requests: true
# SLO buckets: count requests faster than each boundary
slo:
http.server.requests: 100ms, 500ms, 1s, 2s
tags:
# Common tags added to every meter
application: ${spring.application.name}
environment: ${spring.profiles.active:default}Birçok örnekte görülen WebMvcTagsContributor ve WebMvcTagsProvider arayüzleri Spring Boot 3.0'da kaldırıldı. Özel etiketler artık Spring Boot metrik referansında belgelendiği gibi DefaultServerRequestObservationConvention genişletilerek eklenir:
// Adds custom tags to http.server.requests (Spring Boot 3.0+)
package com.example.monitoring.config;
import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
import org.springframework.http.server.observation.ServerRequestObservationContext;
import org.springframework.stereotype.Component;
import jakarta.servlet.http.HttpServletRequest;
@Component
public class TenantObservationConvention extends DefaultServerRequestObservationConvention {
@Override
public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) {
HttpServletRequest request = context.getCarrier();
String userId = request.getHeader("X-User-Id");
String tenantId = request.getHeader("X-Tenant-Id");
// Keep the default tags and add two bounded ones
return super.getLowCardinalityKeyValues(context).and(
KeyValue.of("user.type", userId != null ? "authenticated" : "anonymous"),
KeyValue.of("tenant", tenantId != null ? tenantId : "default")
);
}
}Yalnızca düşük kardinaliteli anahtar-değer çiftleri Prometheus etiketine dönüşür. tenant etiketi birkaç düzine tenant ile kabul edilebilir, binlerce tenant ile ise bir kardinalite sorununa dönüşür. Yüksek kardinaliteli değerler (kullanıcı ID'si, sipariş ID'si) yalnızca trace'leri besleyen getHighCardinalityKeyValues içine aittir. Observation API'nin tracing tarafı OpenTelemetry ile Spring Boot gözlemlenebilirliği makalesinde ayrıntılı olarak ele alınır.
HikariCP ve SQL Sorgu Metrikleri
HikariCP havuz metrikleri, genel jdbc.connections gauge'larının yanında, havuz adıyla etiketlenmiş olarak hikaricp önekiyle otomatik yayımlanır. Havuz adının ayarlanması etiketi okunabilir kılar:
# application.yml
# HikariCP pool configuration
spring:
datasource:
hikari:
# Becomes the "pool" tag on hikaricp_* metrics
pool-name: OrderServicePool
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000Sorgu düzeyinde süre ölçümü Spring Boot tarafından sağlanmaz. datasource-micrometer projesi DataSource'u sarmalar ve her bağlantı, sorgu ve result set için bir gözlem oluşturur; bu da hem metrik hem trace span'leri üretir. 2.x serisi Spring Boot 4'ü, 1.x serisi Spring Boot 3'ü hedefler:
<!-- pom.xml -->
<!-- JDBC observations: 2.x for Spring Boot 4, 1.x for Spring Boot 3 -->
<dependency>
<groupId>net.ttddyy.observation</groupId>
<artifactId>datasource-micrometer-spring-boot</artifactId>
<version>2.0.1</version>
</dependency>Bu yaklaşım, DataSource'u elle yazılmış bir listener ile ProxyDataSourceBuilder içine saran manuel yöntemin yerini alır; o yöntem Spring Boot'un DataSource otomatik yapılandırmasını atlıyor ve her Boot yükseltmesinde senkronize tutulmak zorundaydı. Yavaş sorgular genellikle Spring Data JPA N+1 sorgu çözümleri makalesinde ele alınan N+1 kalıbına işaret eder.
Üretim için En İyi Uygulamalar
Metrik Kardinalitesi
Aşırı kardinalite Prometheus'un performansını düşürür. Her benzersiz etiket kombinasyonu ayrı bir zaman serisi oluşturur.
// AVOID: unbounded cardinality
package com.example.monitoring.antipattern;
@Service
public class AntiPatternHighCardinality {
private final MeterRegistry registry;
// BAD: userId creates one series per user
public void trackUserAction(String userId, String action) {
Counter.builder("user.actions")
.tag("userId", userId) // Millions of possible values
.tag("action", action)
.register(registry)
.increment();
}
}// Controlled cardinality
package com.example.monitoring.bestpractice;
@Service
public class GoodPracticeCardinality {
private final MeterRegistry registry;
// GOOD: user category instead of ID
public void trackUserAction(User user, String action) {
Counter.builder("user.actions")
.tag("userType", user.getSubscriptionType()) // FREE, PREMIUM, ENTERPRISE
.tag("action", action)
.register(registry)
.increment();
}
// GOOD: let a timer histogram bucket durations
public void trackResponseTime(long responseTimeMs) {
Timer.builder("response.time")
.publishPercentileHistogram()
.register(registry)
.record(Duration.ofMillis(responseTimeMs));
}
}Histogram bucket'larına sahip bir Timer, elle oluşturulmuş bir bucket etiketi taşıyan counter'a göre gecikme sorularını daha iyi yanıtlar: PromQL bucket'lardan herhangi bir yüzdeliği hesaplayabilir ve bucket sınırları servisler arasında tutarlı kalır.
Üretime Hazır Yapılandırma
# application-production.yml
# Production configuration (Spring Boot 4)
management:
server:
# Separate port for management endpoints, not exposed publicly
port: 9090
endpoints:
access:
default: none
web:
exposure:
include: health,info,prometheus
endpoint:
health:
access: read-only
show-details: when-authorized
probes:
add-additional-paths: true
info:
access: read-only
prometheus:
access: read-only
observations:
annotations:
enabled: true
metrics:
distribution:
percentiles-histogram:
http.server.requests: true
minimum-expected-value:
http.server.requests: 1ms
maximum-expected-value:
http.server.requests: 30s
tags:
application: ${spring.application.name}
environment: production
version: ${app.version:unknown}access.default: none ile env, beans, configprops ve mappings gibi endpointler tek tek listelemeye gerek kalmadan erişilemez olur. management.prometheus.metrics.export.enabled varsayılan olarak true değerindedir ve step ayarının Prometheus üzerinde hiçbir etkisi yoktur: bu pull tabanlı bir registry'dir ve scrape aralığı prometheus.yml içinde belirlenir.
Actuator ve Prometheus ile Sık Karşılaşılan Hatalar
| Belirti | Neden | Çözüm |
|---|---|---|
/actuator/prometheus 404 döner | Endpoint ekspozisyon listesinde değil veya registry eksik | prometheus değerini management.endpoints.web.exposure.include listesine eklemek ve micrometer-registry-prometheus bağımlılığının classpath'te olduğunu doğrulamak |
| Prometheus hedefi "server returned HTTP status 401" gösterir | Spring Security, health dışındaki tüm endpointleri korur | prometheus endpointini scraper'ın ağına açmak veya scrape yapılandırmasında basic auth tanımlamak |
@Timed hiçbir metrik üretmez | management.observations.annotations.enabled varsayılan olarak false veya AspectJ eksik | Özelliği true yapmak ve spring-boot-starter-aspectj eklemek |
Yükseltmeden sonra spring-boot-starter-aop bulunamaz | Spring Boot 4'te adı değişti | spring-boot-starter-aspectj kullanmak |
Boot 4'te Health veya HealthIndicator importu başarısız olur | Paket org.springframework.boot.health.contributor altına taşındı | Importları güncellemek |
management.endpoint.*.enabled için kullanımdan kaldırma uyarıları | Açma/kapama modeli Spring Boot 3.4'te değiştirildi | management.endpoints.access.default ve management.endpoint.<id>.access kullanmak |
| Dashboard'daki yüzdelikler gerçeklikten çok farklı | publishPercentiles değerleri pod'lar arasında ortalanıyor | publishPercentileHistogram() ve histogram_quantile() kullanmak |
Spring Boot Micrometer Mülakat Soruları
Actuator, orta ve kıdemli seviye pozisyonlara yönelik Spring Boot mülakatlarının çoğunda gündeme gelir. Deneyimli bir profili junior bir profilden ayıran yanıtlar:
- Liveness ile readiness arasındaki fark: liveness Kubernetes'e pod'u yeniden başlatmasını, readiness ise trafiği yönlendirmeyi durdurmasını söyler. Veritabanını liveness grubuna koyan bir aday, bunu hiç bir kesinti sırasında yaşamamıştır.
- Neden doğrudan Prometheus istemcisi değil de Micrometer: Micrometer bir cephedir. Aynı
CounterveTimerkodu, tek bir bağımlılık değiştirilerek Prometheus, Datadog veya OTLP'ye aktarılır; Spring'in kendi araçsallaştırması (HTTP, JDBC havuzu, önbellekler) da bu cepheye göre yazılmıştır. - İstemci tarafı yüzdelikler ile histogramlar: istemci tarafında hesaplanan yüzdelikler instance'lar arasında toplanamaz, histogramlar toplanabilir. Bu, konuyla ilgili en yaygın tuzak sorudur.
- Kardinalite nedir ve Prometheus'u nasıl bozar: her benzersiz etiket kombinasyonu için bir zaman serisi; kullanıcı ID'si etiketi bellek kullanımını kullanıcı sayısıyla çarpar.
- Actuator nasıl güvenli hale getirilir: ekspozisyonu sınırlamak, erişim seviyelerini ayarlamak, yönetimi ayrı bir portta çalıştırmak ve özel bir
SecurityFilterChainile korumak.
Bu türden daha fazla soru Spring Boot Actuator mülakat soruları modülünde yer alır.
Kaynaklar
- Spring Boot 4.0 Migration Guide:
spring-boot-starter-aopadınınspring-boot-starter-aspectjolarak değişmesi, varsayılan olarak etkin probe'lar, yenispring-boot-healthmodülü - Spring Boot 3.4 Release Notes:
enabled-by-defaultveendpoint.<id>.enablederişim modeli lehine kullanımdan kaldırıldı - Spring Boot Actuator endpoint referansı: erişim özellikleri,
EndpointRequestpaketi,org.springframework.boot.health.contributoriçindeki health indicator'lar, probe grupları ve ek yollar - Spring Boot metrik referansı:
DefaultServerRequestObservationConvention, HikariCP vejdbc.connectionsmetrikleri, simpleclient'ın kullanımdan kaldırılması - Spring Boot gözlemlenebilirlik referansı:
management.observations.annotations.enabledve AspectJ gereksinimi - Micrometer Prometheus dokümantasyonu: Prometheus Java istemcisi 1.x registry'si ve eski simpleclient modülü
- GitHub'da datasource-micrometer: Spring Boot 4 için 2.x, Spring Boot 3 için 1.x
- Grafana: Grafana 12'de AngularJS'in kaldırılması: Graph ve Singlestat panellerinin yerini Time series ve Stat aldı
- Spring Boot sürümleri: güncel GA sürümü 4.1.2, ön sürümde 4.2.0-RC1
Spring Boot Micrometer ve Prometheus Hakkında Akılda Tutulması Gerekenler
- Çalışan bir scrape için üç adım: Actuator starter'ı,
micrometer-registry-prometheus, ekspozisyon listesindeprometheus - 3.4'ten beri erişim modeli:
management.endpoints.access.default: noneile endpoint başınaread-only, kullanımdan kaldırılanenabledbayraklarının yerini alır - Spring Boot 4 değişiklikleri:
spring-boot-starter-aspectj,org.springframework.boot.health.contributoriçinde sağlık API'si,org.springframework.boot.security.autoconfigure.actuate.web.servletiçindeEndpointRequest, varsayılan olarak etkin probe'lar - Anotasyonlar bir anahtar gerektirir:
management.observations.annotations.enabled=true, elle tanımlananTimedAspectbean'i yok - Observation API üzerinden HTTP etiketleri:
DefaultServerRequestObservationConventiongenişletilir, etiketler düşük kardinaliteli tutulur - İstemci yüzdelikleri yerine histogramlar:
publishPercentileHistogram()ve herhistogram_quantileiçindesum by (le) - Liveness yerel kalır: dış bağımlılıklar readiness grubuna veya özel bir gruba girer, asla liveness grubuna girmez
Bu varsayılanlar bir Spring Boot 4 servisine pod'lar arasında doğru toplanan metrikler, genel ağa hiçbir şey sızdırmayan endpointler ve tek bir yavaş URI yerine gerçek olaylarda tetiklenen uyarılar sağlar.
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Spring Boot kodundaki hatayı bulabilir misin?
Gerçek bir kod parçası, gizli bir hata, günde bir deneme. Denemek için hesap gerekmez.

Yazan:
Anthony Fillion-MailletSharpSkill kurucusu
10 yılı aşkın süredir fullstack geliştirici. SharpSkill’i yönetiyor ve burada yayımlanan her şeyden sorumlu.
26 Ekim 2026 tarihinde güncellendi
Etiketler
Paylaş
İlgili makaleler

2026'da Spring Boot Gözlemlenebilirlik: OpenTelemetry, Dağıtık İzleme ve Mülakat Soruları
OpenTelemetry ve Micrometer Tracing ile Spring Boot gözlemlenebilirliği hakkında kapsamlı rehber. Dağıtık izleme kurulumu, Observation API, OTLP export yapılandırması ve teknik mülakat hazırlığı.

Spring Boot Yapılandırılmış Loglama 2026: Logback ve OpenTelemetry ile Üretim JSON Logları
Spring Boot 4.x yapılandırılmış loglama rehberi. Yerel JSON desteği, OpenTelemetry starter, MDC izleme ve üretim gözlemlenebilirliği için ELK Stack entegrasyonu.

2026'da freelance Java Spring Boot geliştirici işleri: sözleşmeleri nerede bulunur, günlük ücretler, nasıl hazırlanılır
2026'da Spring Boot freelance sözleşmelerini nerede bulacağınız, Avrupa genelindeki günlük ücretler ve teknik mülakatlara nasıl hazırlanacağınız.